@ -1,38 +1,22 @@
# include <memory.h>
# include <memory.h>
# include <stdlib.h>
# include <stdlib.h>
# include <stdio.h>
# include <stdio.h>
# include <vector>
# include <unordered_map>
# include "bsp.h"
# include "bsp.h"
# include "rectpack/finders_interface.h"
# include "rectpack/finders_interface.h"
# include "ps1bsp.h"
static char path [ _MAX_PATH ] ;
static char path [ _MAX_PATH ] ;
int process_entities ( dheader_t * header , FILE * f )
int process_entities ( const world_t * world )
{
{
fseek ( f , header - > entities . offset , SEEK_SET ) ;
char * entities = ( char * ) malloc ( ( header - > entities . size + 1 ) * sizeof ( char ) ) ;
if ( entities = = NULL )
return 0 ;
memset ( entities , 0 , ( header - > entities . size + 1 ) * sizeof ( char ) ) ;
fread ( entities , sizeof ( char ) , header - > entities . size , f ) ;
printf ( " Entities list: \n %s \n " , entities ) ;
free ( entities ) ;
printf ( " Entities list: \n %s \n " , world - > entities ) ;
return 1 ;
return 1 ;
}
}
int process_textures ( const char * bspname , dheader_t * header , FILE * f )
int process_textures ( const world_t * world )
{
{
fseek ( f , header - > miptex . offset , SEEK_SET ) ;
mipheader_t mipheader ;
fread ( & mipheader . numtex , sizeof ( long ) , 1 , f ) ;
mipheader . offset = ( long * ) malloc ( mipheader . numtex * sizeof ( long ) ) ;
if ( mipheader . offset = = NULL )
return 0 ;
fread ( mipheader . offset , sizeof ( long ) , mipheader . numtex , f ) ;
using spaces_type = rectpack2D : : empty_spaces < false > ;
using spaces_type = rectpack2D : : empty_spaces < false > ;
using rect_type = rectpack2D : : output_rect_t < spaces_type > ;
using rect_type = rectpack2D : : output_rect_t < spaces_type > ;
@ -49,22 +33,18 @@ int process_textures(const char* bspname, dheader_t* header, FILE* f)
std : : vector < rect_type > rectangles ;
std : : vector < rect_type > rectangles ;
miptex_t miptex ;
// Try some texture packing and see if we fit inside the PS1's VRAM
// Try some texture packing and see if we fit inside the PS1's VRAM
for ( int texNum = 0 ; texNum < mipheader . numtex ; + + texNum )
for ( int texNum = 0 ; texNum < world - > mipheader . numtex ; + + texNum )
{
{
unsigned long miptexOffset = header - > miptex . offset + mipheader . offset [ texNum ] ;
fseek ( f , miptexOffset , SEEK_SET ) ;
fread ( & miptex , sizeof ( miptex_t ) , 1 , f ) ;
miptex_t * miptex = & world - > miptexes [ texNum ] ;
//printf("Texture %d (%dx%d): %.16s\n", texNum, miptex.width, miptex.height, miptex.name);
//printf("Texture %d (%dx%d): %.16s\n", texNum, miptex.width, miptex.height, miptex.name);
// Shrink the larger textures, but keep smaller ones at their original size
// Shrink the larger textures, but keep smaller ones at their original size
int ps1mip = miptex . width > 64 | | miptex . height > 64 ? 1 : 0 ;
int ps1mip = miptex - > width > 64 | | miptex - > height > 64 ? 1 : 0 ;
if ( strcmp ( miptex . name , " clip " ) & & strcmp ( miptex . name , " trigger " ) )
rectangles . emplace_back ( rectpack2D : : rect_xywh ( 0 , 0 , miptex . width > > ps1mip , miptex . height > > ps1mip ) ) ;
if ( strcmp ( miptex - > name , " clip " ) & & strcmp ( miptex - > name , " trigger " ) )
rectangles . emplace_back ( rectpack2D : : rect_xywh ( 0 , 0 , miptex - > width > > ps1mip , miptex - > height > > ps1mip ) ) ;
else
else
rectangles . emplace_back ( rectpack2D : : rect_xywh ( 0 , 0 , 0 , 0 ) ) ;
rectangles . emplace_back ( rectpack2D : : rect_xywh ( 0 , 0 , 0 , 0 ) ) ;
}
}
@ -81,7 +61,7 @@ int process_textures(const char* bspname, dheader_t* header, FILE* f)
)
)
) ;
) ;
printf ( " %d textures. Packed texture atlas size: %d x %d \n " , mipheader . numtex , result_size . w , result_size . h ) ;
printf ( " %d textures. Packed texture atlas size: %d x %d \n " , world - > mipheader . numtex , result_size . w , result_size . h ) ;
unsigned char * atlas = ( unsigned char * ) malloc ( result_size . w * result_size . h * sizeof ( unsigned char ) ) ;
unsigned char * atlas = ( unsigned char * ) malloc ( result_size . w * result_size . h * sizeof ( unsigned char ) ) ;
if ( atlas = = NULL )
if ( atlas = = NULL )
return 0 ;
return 0 ;
@ -89,36 +69,30 @@ int process_textures(const char* bspname, dheader_t* header, FILE* f)
memset ( atlas , 0 , result_size . w * result_size . h * sizeof ( unsigned char ) ) ;
memset ( atlas , 0 , result_size . w * result_size . h * sizeof ( unsigned char ) ) ;
// Try to construct the texture atlas, see what we get
// Try to construct the texture atlas, see what we get
for ( int texNum = 0 ; texNum < mipheader . numtex ; + + texNum )
for ( int texNum = 0 ; texNum < world - > mipheader . numtex ; + + texNum )
{
{
unsigned long miptexOffset = header - > miptex . offset + mipheader . offset [ texNum ] ;
fseek ( f , miptexOffset , SEEK_SET ) ;
fread ( & miptex , sizeof ( miptex_t ) , 1 , f ) ;
miptex_t * miptex = & world - > miptexes [ texNum ] ;
char * outName = miptex . name ;
char * outName = miptex - > name ;
if ( * outName = = ' * ' | | * outName = = ' + ' )
if ( * outName = = ' * ' | | * outName = = ' + ' )
outName + + ;
outName + + ;
for ( int mipLevel = 0 ; mipLevel < 4 ; + + mipLevel )
for ( int mipLevel = 0 ; mipLevel < 4 ; + + mipLevel )
{
{
unsigned long mipOffset = * ( & miptex . offset1 + mipLevel ) ;
fseek ( f , miptexOffset + mipOffset , SEEK_SET ) ;
unsigned char * texBytes = world - > textures [ texNum * 4 + mipLevel ] ;
size_t numBytes = ( miptex . width * miptex . height ) > > mipLevel ;
unsigned char * texBytes = ( unsigned char * ) malloc ( sizeof ( unsigned char ) * numBytes ) ;
fread ( texBytes , sizeof ( unsigned char ) , numBytes , f ) ;
FILE * fout ;
sprintf_s ( path , _MAX_PATH , " textures/%s-%s-mip%d-%dx%d.raw " , bspname , outName , mipLevel , miptex . width > > mipLevel , miptex . height > > mipLevel ) ;
fopen_s ( & fout , path , " wb " ) ;
if ( fout ! = NULL )
FILE * fraw ;
sprintf_s ( path , _MAX_PATH , " textures/%s-%s-mip%d-%dx%d.raw " , world - > name , outName , mipLevel , miptex - > width > > mipLevel , miptex - > height > > mipLevel ) ;
fopen_s ( & fraw , path , " wb " ) ;
if ( fraw ! = NULL )
{
{
fwrite ( texBytes , sizeof ( unsigned char ) , numBytes , fout ) ;
fclose ( fout ) ;
size_t numBytes = ( miptex - > width * miptex - > height ) > > mipLevel ;
fwrite ( texBytes , sizeof ( unsigned char ) , numBytes , fraw ) ;
fclose ( fraw ) ;
}
}
const auto & rectangle = rectangles [ texNum ] ;
const auto & rectangle = rectangles [ texNum ] ;
if ( miptex . width > > mipLevel = = rectangle . w ) // This is the mip level we've previously decided we want for our PS1 atlas
if ( miptex - > width > > mipLevel = = rectangle . w ) // This is the mip level we've previously decided we want for our PS1 atlas
{
{
//printf("Writing texture %s mip %d to position: (%d, %d) w = %d, h = %d\n", miptex.name, mipLevel, rectangle.x, rectangle.y, rectangle.w, rectangle.h);
//printf("Writing texture %s mip %d to position: (%d, %d) w = %d, h = %d\n", miptex.name, mipLevel, rectangle.x, rectangle.y, rectangle.w, rectangle.h);
for ( int y = 0 ; y < rectangle . h ; + + y )
for ( int y = 0 ; y < rectangle . h ; + + y )
@ -126,13 +100,11 @@ int process_textures(const char* bspname, dheader_t* header, FILE* f)
memcpy_s ( atlas + ( ( rectangle . y + y ) * result_size . w + rectangle . x ) , rectangle . w * sizeof ( unsigned char ) , texBytes + ( y * rectangle . w ) , rectangle . w * sizeof ( unsigned char ) ) ;
memcpy_s ( atlas + ( ( rectangle . y + y ) * result_size . w + rectangle . x ) , rectangle . w * sizeof ( unsigned char ) , texBytes + ( y * rectangle . w ) , rectangle . w * sizeof ( unsigned char ) ) ;
}
}
}
}
free ( texBytes ) ;
}
}
}
}
FILE * fatlas ;
FILE * fatlas ;
sprintf_s ( path , _MAX_PATH , " %s-atlas-%dx%d.raw " , bsp name, result_size . w , result_size . h ) ;
sprintf_s ( path , _MAX_PATH , " %s-atlas-%dx%d.raw " , world - > name , result_size . w , result_size . h ) ;
fopen_s ( & fatlas , path , " wb " ) ;
fopen_s ( & fatlas , path , " wb " ) ;
if ( fatlas ! = NULL )
if ( fatlas ! = NULL )
{
{
@ -141,26 +113,15 @@ int process_textures(const char* bspname, dheader_t* header, FILE* f)
}
}
free ( atlas ) ;
free ( atlas ) ;
free ( mipheader . offset ) ;
return 1 ;
return 1 ;
}
}
int process_vertices ( dheader_t * header , FILE * f )
int process_vertices ( const world_t * world )
{
{
int numVertices = header - > vertices . size / sizeof ( vertex_t ) ;
int numFaces = header - > faces . size / sizeof ( face_t ) ;
vertex_t * vertices = ( vertex_t * ) malloc ( header - > vertices . size ) ;
if ( vertices = = NULL )
return 0 ;
fseek ( f , header - > vertices . offset , SEEK_SET ) ;
fread ( vertices , sizeof ( vertex_t ) , numVertices , f ) ;
vec3_t min = { FLT_MAX , FLT_MAX , FLT_MAX } , max = { - FLT_MAX , - FLT_MAX , - FLT_MAX } ;
vec3_t min = { FLT_MAX , FLT_MAX , FLT_MAX } , max = { - FLT_MAX , - FLT_MAX , - FLT_MAX } ;
for ( int vertIdx = 0 ; vertIdx < numVertices ; + + vertIdx )
for ( int vertIdx = 0 ; vertIdx < world - > numVertices ; + + vertIdx )
{
{
vertex_t * vert = & vertices [ vertIdx ] ;
vertex_t * vert = & world - > vertices [ vertIdx ] ;
if ( vert - > X > max . x ) max . x = vert - > X ;
if ( vert - > X > max . x ) max . x = vert - > X ;
if ( vert - > Y > max . y ) max . y = vert - > Y ;
if ( vert - > Y > max . y ) max . y = vert - > Y ;
if ( vert - > Z > max . z ) max . z = vert - > Z ;
if ( vert - > Z > max . z ) max . z = vert - > Z ;
@ -169,54 +130,269 @@ int process_vertices(dheader_t* header, FILE* f)
if ( vert - > Z < min . z ) min . z = vert - > Z ;
if ( vert - > Z < min . z ) min . z = vert - > Z ;
}
}
printf ( " %d vertices, %d faces, min = (%f, %f, %f), max = (%f, %f, %f) \n " , numVertices , numFaces , min . x , min . y , min . z , max . x , max . y , max . z ) ;
printf ( " %d vertices, %d faces, min = (%f, %f, %f), max = (%f, %f, %f) \n " , world - > numVertices , world - > numFaces , min . x , min . y , min . z , max . x , max . y , max . z ) ;
const int fixedScale = 1 < < 14 ;
const int fixedScale = 1 < < 14 ;
int fixedMin [ 3 ] = { ( int ) ( min . x * fixedScale ) , ( int ) ( min . y * fixedScale ) , ( int ) ( min . z * fixedScale ) } ;
int fixedMin [ 3 ] = { ( int ) ( min . x * fixedScale ) , ( int ) ( min . y * fixedScale ) , ( int ) ( min . z * fixedScale ) } ;
int fixedMax [ 3 ] = { ( int ) ( max . x * fixedScale ) , ( int ) ( max . y * fixedScale ) , ( int ) ( max . z * fixedScale ) } ;
int fixedMax [ 3 ] = { ( int ) ( max . x * fixedScale ) , ( int ) ( max . y * fixedScale ) , ( int ) ( max . z * fixedScale ) } ;
printf ( " Fixed point min = (%d, %d, %d), max = (%d, %d, %d) \n " , fixedMin [ 0 ] , fixedMin [ 1 ] , fixedMin [ 2 ] , fixedMax [ 0 ] , fixedMax [ 1 ] , fixedMax [ 2 ] ) ;
printf ( " Fixed point min = (%d, %d, %d), max = (%d, %d, %d) \n " , fixedMin [ 0 ] , fixedMin [ 1 ] , fixedMin [ 2 ] , fixedMax [ 0 ] , fixedMax [ 1 ] , fixedMax [ 2 ] ) ;
return 1 ;
return 1 ;
}
}
int process_bsp ( const char * bspname )
typedef struct
{
{
FILE * f ;
dheader_t header ;
vertex_t * vertex ;
unsigned short index ;
} vertexref_t ;
sprintf_s ( path , _MAX_PATH , " %s.bsp " , bspname ) ;
fopen_s ( & f , path , " rb " ) ;
if ( f = = NULL )
int process_faces ( const world_t * world )
{
// Write some data to a file
FILE * fbsp ;
fopen_s ( & fbsp , " test.ps1bsp " , " wb " ) ;
if ( ! fbsp )
return 0 ;
return 0 ;
fread ( & header , sizeof ( dheader_t ) , 1 , f ) ;
printf ( " Header model version: %d \n " , header . version ) ;
ps1bsp_header_t outHeader = { 0 } ; // Write an empty placeholder header first
fwrite ( & outHeader , sizeof ( ps1bsp_header_t ) , 1 , fbsp ) ;
// TODO: convert vertices and group them by material properties (texture, lightmap) and floating origin zone (based on leaf data), duplicate where necessary
// Organize vertex indices so we can shuffle them around and duplicate them, while keeping track of which vertex is where
std : : unordered_map < vertex_t * , vertexref_t > vertexRefs ;
for ( unsigned short i = 0 ; i < world - > header . vertices . size / sizeof ( vertex_t ) ; + + i )
{
vertex_t * v = & world - > vertices [ i ] ;
vertexRefs [ v ] = vertexref_t { v , i } ;
}
// Write vertex data to a file (no vertex splitting yet)
for ( unsigned short i = 0 ; i < world - > header . vertices . size / sizeof ( vertex_t ) ; + + i )
{
// TODO: we should respect the ordering from vertexRef->index here but meh, problem for later
vertex_t * inVertex = & world - > vertices [ i ] ;
ps1bsp_vertex_t outVertex = { 0 } ;
if ( inVertex - > X > - 8192 & & inVertex - > X < 8192 & & inVertex - > Y > - 8192 & & inVertex - > Y < 8192 & & inVertex - > Z > - 8192 & & inVertex - > Z < 8192 )
{
outVertex . x = ( short ) ( inVertex - > X * 4 ) ;
outVertex . y = ( short ) ( inVertex - > Y * 4 ) ;
outVertex . z = ( short ) ( inVertex - > Z * 4 ) ;
}
outVertex . baseLight = 128 ;
outVertex . finalLight = 128 ;
fwrite ( & outVertex , sizeof ( ps1bsp_vertex_t ) , 1 , fbsp ) ;
}
outHeader . numVertices = ( unsigned short ) ( world - > header . vertices . size / sizeof ( vertex_t ) ) ;
std : : vector < vertexref_t * > faceVerts ;
for ( int faceIdx = 0 ; faceIdx < world - > numFaces ; + + faceIdx )
{
face_t * face = & world - > faces [ faceIdx ] ;
for ( int edgeListIdx = 0 ; edgeListIdx < face - > ledge_num ; + + edgeListIdx )
{
short edgeIdx = world - > edgeList [ face - > ledge_id + edgeListIdx ] ;
int vertIndex = edgeIdx > 0 ?
world - > edges [ edgeIdx ] . vertex0 :
world - > edges [ - edgeIdx ] . vertex1 ;
vertex_t * v = & world - > vertices [ vertIndex ] ;
faceVerts . push_back ( & vertexRefs [ v ] ) ;
}
printf ( " Face %d: %d vertices \n " , faceIdx , faceVerts . size ( ) ) ;
// Triangulate face into polygons (triangle fan, the naive method)
// Write polygon and face data to a file
faceVerts . clear ( ) ;
}
// Write final header
fseek ( fbsp , 0 , SEEK_SET ) ;
fwrite ( & outHeader , sizeof ( ps1bsp_header_t ) , 1 , fbsp ) ;
fclose ( fbsp ) ;
return 1 ;
}
int process_bsp ( const world_t * world )
{
// Test reading the entity string data
// Test reading the entity string data
if ( ! process_entities ( & header , f ) )
if ( ! process_entities ( world ) )
{
{
fclose ( f ) ;
return 0 ;
return 0 ;
}
}
// Test exporting texture data
// Test exporting texture data
if ( ! process_textures ( bspname , & header , f ) )
if ( ! process_textures ( world ) )
{
{
fclose ( f ) ;
return 0 ;
return 0 ;
}
}
// Inspect vertex data
// Inspect vertex data
if ( ! process_vertices ( & header , f ) )
if ( ! process_vertices ( world ) )
{
return 0 ;
}
// Inspect faces/edges data
if ( ! process_faces ( world ) )
{
{
fclose ( f ) ;
return 0 ;
return 0 ;
}
}
return 1 ;
}
int load_bsp ( const char * bspname , world_t * world )
{
FILE * f ;
dheader_t * header = & world - > header ;
world - > name = bspname ;
sprintf_s ( path , _MAX_PATH , " %s.bsp " , bspname ) ;
fopen_s ( & f , path , " rb " ) ;
if ( f = = NULL )
return 0 ;
fread ( header , sizeof ( dheader_t ) , 1 , f ) ;
printf ( " Header model version: %d \n " , header - > version ) ;
// Load entities
fseek ( f , header - > entities . offset , SEEK_SET ) ;
world - > entitiesLength = header - > entities . size + 1 ;
world - > entities = ( char * ) malloc ( world - > entitiesLength * sizeof ( char ) ) ;
if ( world - > entities = = NULL )
return 0 ;
memset ( world - > entities , 0 , world - > entitiesLength * sizeof ( char ) ) ;
fread ( world - > entities , sizeof ( char ) , world - > entitiesLength , f ) ;
// Load textures
mipheader_t * mipheader = & world - > mipheader ;
fseek ( f , header - > miptex . offset , SEEK_SET ) ;
fread ( & mipheader - > numtex , sizeof ( long ) , 1 , f ) ;
mipheader - > offset = ( long * ) malloc ( mipheader - > numtex * sizeof ( long ) ) ;
if ( mipheader - > offset = = NULL )
return 0 ;
fread ( mipheader - > offset , sizeof ( long ) , mipheader - > numtex , f ) ;
world - > miptexes = ( miptex_t * ) malloc ( mipheader - > numtex * sizeof ( miptex_t ) ) ;
if ( world - > miptexes = = NULL )
return 0 ;
const int numMipLevels = 4 ;
world - > textures = ( unsigned char * * ) malloc ( mipheader - > numtex * numMipLevels * sizeof ( unsigned char * ) ) ;
if ( world - > textures = = NULL )
return 0 ;
memset ( world - > textures , 0 , mipheader - > numtex * numMipLevels * sizeof ( unsigned char * ) ) ;
for ( int texNum = 0 ; texNum < mipheader - > numtex ; + + texNum )
{
miptex_t * miptex = & world - > miptexes [ texNum ] ;
unsigned long miptexOffset = header - > miptex . offset + mipheader - > offset [ texNum ] ;
fseek ( f , miptexOffset , SEEK_SET ) ;
fread ( miptex , sizeof ( miptex_t ) , 1 , f ) ;
for ( int mipLevel = 0 ; mipLevel < numMipLevels ; + + mipLevel )
{
unsigned long mipOffset = * ( & miptex - > offset1 + mipLevel ) ;
fseek ( f , miptexOffset + mipOffset , SEEK_SET ) ;
size_t numBytes = ( miptex - > width * miptex - > height ) > > mipLevel ;
unsigned char * texBytes = ( unsigned char * ) malloc ( sizeof ( unsigned char ) * numBytes ) ;
if ( texBytes = = NULL )
return 0 ;
fread ( texBytes , sizeof ( unsigned char ) , numBytes , f ) ;
world - > textures [ texNum * numMipLevels + mipLevel ] = texBytes ;
}
}
// Load vertices
world - > numVertices = header - > vertices . size / sizeof ( vertex_t ) ;
world - > vertices = ( vertex_t * ) malloc ( header - > vertices . size ) ;
if ( world - > vertices = = NULL )
return 0 ;
fseek ( f , header - > vertices . offset , SEEK_SET ) ;
fread ( world - > vertices , sizeof ( vertex_t ) , world - > numVertices , f ) ;
// Load edges
world - > numEdges = header - > edges . size / sizeof ( edge_t ) ;
world - > edges = ( edge_t * ) malloc ( header - > edges . size ) ;
if ( world - > edges = = NULL )
return 0 ;
fseek ( f , header - > edges . offset , SEEK_SET ) ;
fread ( world - > edges , sizeof ( edge_t ) , world - > numEdges , f ) ;
world - > edgeListLength = header - > ledges . size / sizeof ( unsigned short ) ;
world - > edgeList = ( unsigned short * ) malloc ( header - > ledges . size ) ;
if ( world - > edgeList = = NULL )
return 0 ;
fseek ( f , header - > ledges . offset , SEEK_SET ) ;
fread ( world - > edgeList , sizeof ( unsigned short ) , world - > edgeListLength , f ) ;
// Load faces
world - > numFaces = header - > faces . size / sizeof ( face_t ) ;
world - > faces = ( face_t * ) malloc ( header - > faces . size ) ;
if ( world - > faces = = NULL )
return 0 ;
fseek ( f , header - > faces . offset , SEEK_SET ) ;
fread ( world - > faces , sizeof ( face_t ) , world - > numFaces , f ) ;
world - > faceListLength = header - > lface . size / sizeof ( unsigned short ) ;
world - > faceList = ( unsigned short * ) malloc ( header - > lface . size ) ;
if ( world - > faceList = = NULL )
return 0 ;
fseek ( f , header - > lface . offset , SEEK_SET ) ;
fread ( world - > faceList , sizeof ( unsigned short ) , world - > faceListLength , f ) ;
fclose ( f ) ;
fclose ( f ) ;
return 1 ;
return 1 ;
}
}
void free_bsp ( world_t * world )
{
free ( world - > faces ) ;
free ( world - > faceList ) ;
free ( world - > edges ) ;
free ( world - > edgeList ) ;
free ( world - > vertices ) ;
for ( int i = 0 ; i < world - > mipheader . numtex ; + + i )
{
free ( world - > textures [ i ] ) ;
}
free ( world - > textures ) ;
free ( world - > miptexes ) ;
free ( world - > mipheader . offset ) ;
free ( world - > entities ) ;
}
int main ( int argc , char * * argv )
int main ( int argc , char * * argv )
{
{
return ! process_bsp ( argv [ 1 ] ) ;
world_t world = { 0 } ;
if ( ! load_bsp ( argv [ 1 ] , & world ) )
return 1 ;
int result = process_bsp ( & world ) ;
free_bsp ( & world ) ;
return ! result ;
}
}