diff --git a/libs/picomodel.h b/libs/picomodel.h index 98d5452f..f33c6da6 100644 --- a/libs/picomodel.h +++ b/libs/picomodel.h @@ -105,6 +105,7 @@ struct picoSurface_s int numVertexes, maxVertexes; picoVec3_t *xyz; picoVec3_t *normal; + picoIndex_t *smoothingGroup; int numSTArrays, maxSTArrays; picoVec2_t **st; @@ -216,6 +217,9 @@ const picoModule_t **PicoModuleList( int *numModules ); picoModel_t *PicoLoadModel( char *name, int frameNum ); +typedef size_t(*PicoInputStreamReadFunc)(void* inputStream, unsigned char* buffer, size_t length); +picoModel_t* PicoModuleLoadModelStream(const picoModule_t* module, void* inputStream, PicoInputStreamReadFunc inputStreamRead, size_t streamLength, int frameNum); + /* model functions */ picoModel_t *PicoNewModel( void ); @@ -330,12 +334,12 @@ picoVertexCombinationHash_t *PicoFindVertexCombinationInHashTable( picoVertexCom picoVertexCombinationHash_t *PicoAddVertexCombinationToHashTable( picoVertexCombinationHash_t **hashTable, picoVec3_t xyz, picoVec3_t normal, picoVec3_t st, picoColor_t color, picoIndex_t index ); /* specialized functions */ -int PicoFindSurfaceVertexNum( picoSurface_t *surface, picoVec3_t xyz, picoVec3_t normal, int numSTs, picoVec2_t *st, int numColors, picoColor_t *color ); +int PicoFindSurfaceVertexNum( picoSurface_t *surface, picoVec3_t xyz, picoVec3_t normal, int numSTs, picoVec2_t *st, int numColors, picoColor_t *color, picoIndex_t smoothingGroup ); void PicoFixSurfaceNormals( picoSurface_t *surface ); int PicoRemapModel( picoModel_t *model, char *remapFile ); -void PicoAddTriangleToModel( picoModel_t *model, picoVec3_t** xyz, picoVec3_t** normals, int numSTs, picoVec2_t **st, int numColors, picoColor_t **colors, picoShader_t* shader ); +void PicoAddTriangleToModel( picoModel_t *model, picoVec3_t** xyz, picoVec3_t** normals, int numSTs, picoVec2_t **st, int numColors, picoColor_t **colors, picoShader_t* shader, picoIndex_t* smoothingGroup ); /* end marker */ #ifdef __cplusplus diff --git a/libs/picomodel/lwo/clip.c b/libs/picomodel/lwo/clip.c index 8a9d2a62..9ea2586b 100644 --- a/libs/picomodel/lwo/clip.c +++ b/libs/picomodel/lwo/clip.c @@ -22,6 +22,35 @@ void lwFreeClip( lwClip *clip ){ if ( clip ) { lwListFree( (void*) clip->ifilter, (ListFreeFunc) lwFreePlugin ); lwListFree( (void*) clip->pfilter, (ListFreeFunc) lwFreePlugin ); + + switch ( clip->type ) { + case ID_STIL: + _pico_free( clip->source.still.name ); + break; + + case ID_ISEQ: + _pico_free( clip->source.seq.prefix ); + _pico_free( clip->source.seq.suffix ); + break; + + case ID_ANIM: + _pico_free( clip->source.anim.name ); + _pico_free( clip->source.anim.server ); + _pico_free( clip->source.anim.data ); + break; + + case ID_XREF: + _pico_free( clip->source.xref.string ); + break; + + case ID_STCC: + _pico_free( clip->source.cycle.name ); + break; + + default: + break; + } + _pico_free( clip ); } } diff --git a/libs/picomodel/picointernal.c b/libs/picomodel/picointernal.c index d4fd8b44..c6f396ce 100644 --- a/libs/picomodel/picointernal.c +++ b/libs/picomodel/picointernal.c @@ -161,35 +161,23 @@ void *_pico_realloc( void **ptr, size_t oldSize, size_t newSize ){ * as custom clone size (the string is cropped to fit into mem * if needed). -sea */ -char *_pico_clone_alloc( char *str, int size ){ - char *cloned; - size_t cloneSize; +char *_pico_clone_alloc( const char *str ) { + char* cloned; /* sanity check */ if ( str == NULL ) { return NULL; } - /* set real size of cloned string */ - cloneSize = ( size < 0 ) ? strlen( str ) : size; - /* allocate memory */ - cloned = _pico_alloc( cloneSize + 1 ); /* bugfix! */ + cloned = _pico_alloc(strlen(str) + 1); if ( cloned == NULL ) { return NULL; } - /* zero out memory allocated by cloned string */ - memset( cloned,0,cloneSize ); - /* copy input string to cloned string */ - if ( cloneSize < strlen( str ) ) { - memcpy( cloned,str,cloneSize ); - cloned[ cloneSize ] = '\0'; - } - else { - strcpy( cloned,str ); - } + strcpy(cloned, str); + /* return ptr to cloned string */ return cloned; } @@ -275,6 +263,19 @@ void _pico_printf( int level, const char *format, ... ){ _pico_ptr_print( level,str ); } +/* _pico_first_token: +* trims everything after the first whitespace-delimited token +*/ + +void _pico_first_token( char *str ) { + if ( !str || ! * str ) { + return; + } + while (*str && !isspace(*str)) + *str++; + *str = '\0'; +} + /* _pico_strltrim: * left trims the given string -sea */ @@ -542,7 +543,7 @@ float _pico_big_float( float src ){ * case-insensitive strstr. -sea */ char *_pico_stristr( char *str, const char *substr ){ - const int sublen = strlen( substr ); + const size_t sublen = strlen( substr ); while ( *str ) { if ( !_pico_strnicmp( str,substr,sublen ) ) { @@ -604,24 +605,24 @@ int _pico_nofname( const char *path, char *dest, int destSize ){ * returns ptr to filename portion in given path or an empty * string otherwise. given 'path' is not altered. -sea */ -char *_pico_nopath( const char *path ){ - char *src; - src = (char *)path + ( strlen( path ) - 1 ); +const char *_pico_nopath( const char *path ){ + const char *src; if ( path == NULL ) { - return (char *)""; + return ""; } - if ( !strchr( (char *)path,'/' ) && !strchr( (char *)path,'\\' ) ) { - return ( (char *)path ); + if ( !strchr( path,'/' ) && !strchr( path,'\\' ) ) { + return ( path ); } + src = path + ( strlen( path ) - 1 ); while ( ( src-- ) != path ) { if ( *src == '/' || *src == '\\' ) { return ( ++src ); } } - return (char *)""; + return ""; } /* _pico_setfext: diff --git a/libs/picomodel/picointernal.h b/libs/picomodel/picointernal.h index 12b8cd7c..8e9bba25 100644 --- a/libs/picomodel/picointernal.h +++ b/libs/picomodel/picointernal.h @@ -116,7 +116,7 @@ extern void ( *_pico_ptr_print )( int, const char* ); void *_pico_alloc( size_t size ); void *_pico_calloc( size_t num, size_t size ); void *_pico_realloc( void **ptr, size_t oldSize, size_t newSize ); -char *_pico_clone_alloc( char *str, int size ); +char *_pico_clone_alloc( const char *str ); void _pico_free( void *ptr ); /* files */ @@ -124,6 +124,7 @@ void _pico_load_file( char *name, unsigned char **buffer, int *bufSiz void _pico_free_file( void *buffer ); /* strings */ +void _pico_first_token(char *str); char *_pico_strltrim( char *str ); char *_pico_strrtrim( char *str ); int _pico_strchcount( char *str, int ch ); diff --git a/libs/picomodel/picomodel.c b/libs/picomodel/picomodel.c index 767fc74f..b8f185cd 100644 --- a/libs/picomodel/picomodel.c +++ b/libs/picomodel/picomodel.c @@ -143,7 +143,46 @@ void PicoSetPrintFunc( void ( *func )( int, const char* ) ){ } } +picoModel_t *PicoModuleLoadModel( const picoModule_t* pm, char* fileName, picoByte_t* buffer, int bufSize, int frameNum ){ + char *modelFileName, *remapFileName; + /* see whether this module can load the model file or not */ + if ( pm->canload( fileName, buffer, bufSize ) == PICO_PMV_OK ) { + /* use loader provided by module to read the model data */ + picoModel_t* model = pm->load( fileName, frameNum, buffer, bufSize ); + if ( model == NULL ) { + _pico_free_file( buffer ); + return NULL; + } + + /* assign pointer to file format module */ + model->module = pm; + + /* get model file name */ + modelFileName = PicoGetModelFileName( model ); + + /* apply model remappings from .remap */ + if ( strlen( modelFileName ) ) { + /* alloc copy of model file name */ + remapFileName = _pico_alloc( strlen( modelFileName ) + 20 ); + if ( remapFileName != NULL ) { + /* copy model file name and change extension */ + strcpy( remapFileName, modelFileName ); + _pico_setfext( remapFileName, "remap" ); + + /* try to remap model; we don't handle the result */ + PicoRemapModel( model, remapFileName ); + + /* free the remap file name string */ + _pico_free( remapFileName ); + } + } + + return model; + } + + return NULL; +} /* PicoLoadModel() @@ -155,7 +194,6 @@ picoModel_t *PicoLoadModel( char *fileName, int frameNum ){ picoModel_t *model; picoByte_t *buffer; int bufSize; - char *modelFileName, *remapFileName; /* init */ @@ -194,38 +232,8 @@ picoModel_t *PicoLoadModel( char *fileName, int frameNum ){ continue; } - /* see whether this module can load the model file or not */ - if ( pm->canload( fileName, buffer, bufSize ) == PICO_PMV_OK ) { - /* use loader provided by module to read the model data */ - model = pm->load( fileName, frameNum, buffer, bufSize ); - if ( model == NULL ) { - _pico_free_file( buffer ); - return NULL; - } - - /* assign pointer to file format module */ - model->module = pm; - - /* get model file name */ - modelFileName = PicoGetModelFileName( model ); - - /* apply model remappings from .remap */ - if ( strlen( modelFileName ) ) { - /* alloc copy of model file name */ - remapFileName = _pico_alloc( strlen( modelFileName ) + 20 ); - if ( remapFileName != NULL ) { - /* copy model file name and change extension */ - strcpy( remapFileName, modelFileName ); - _pico_setfext( remapFileName, "remap" ); - - /* try to remap model; we don't handle the result */ - PicoRemapModel( model, remapFileName ); - - /* free the remap file name string */ - _pico_free( remapFileName ); - } - } - + model = PicoModuleLoadModel( pm, fileName, buffer, bufSize, frameNum ); + if ( model != NULL ) { /* model was loaded, so break out of loop */ break; } @@ -236,7 +244,50 @@ picoModel_t *PicoLoadModel( char *fileName, int frameNum ){ _pico_free_file( buffer ); } - /* return */ + return model; +} + +/* + FIXME: From 1.5; Unused yet +*/ + +picoModel_t *PicoModuleLoadModelStream( const picoModule_t* module, void* inputStream, PicoInputStreamReadFunc inputStreamRead, size_t streamLength, int frameNum ) { + picoModel_t *model; + picoByte_t *buffer; + int bufSize; + + /* init */ + model = NULL; + + if ( inputStream == NULL ) { + _pico_printf( PICO_ERROR, "PicoLoadModel: invalid input stream (inputStream == NULL)" ); + return NULL; + } + + if ( inputStreamRead == NULL ) { + _pico_printf( PICO_ERROR, "PicoLoadModel: invalid input stream (inputStreamRead == NULL) "); + return NULL; + } + + buffer = _pico_alloc( streamLength + 1 ); + + bufSize = (int)inputStreamRead( inputStream, buffer, streamLength ); + buffer[ bufSize ] = '\0'; + + { + // dummy filename + char fileName[128]; + fileName[0] = '.'; + strncpy( fileName + 1, module->defaultExts[ 0 ], 126 ); + fileName[127] = '\0'; + model = PicoModuleLoadModel( module, fileName, buffer, bufSize, frameNum ); + } + + /* free memory used by file buffer */ + if ( model != 0 ) { + _pico_free( buffer ); + } + return model; } @@ -261,10 +312,10 @@ picoModel_t *PicoNewModel( void ){ } /* clear */ - memset( model,0,sizeof( picoModel_t ) ); + memset( model, 0, sizeof( picoModel_t ) ); /* model set up */ - _pico_zero_bounds( model->mins,model->maxs ); + _pico_zero_bounds( model->mins, model->maxs ); /* set initial frame count to 1 -sea */ model->numFrames = 1; @@ -274,7 +325,6 @@ picoModel_t *PicoNewModel( void ){ } - /* PicoFreeModel() frees a model and all associated data @@ -283,7 +333,6 @@ picoModel_t *PicoNewModel( void ){ void PicoFreeModel( picoModel_t *model ){ int i; - /* sanity check */ if ( model == NULL ) { return; @@ -294,6 +343,10 @@ void PicoFreeModel( picoModel_t *model ){ _pico_free( model->name ); } + if ( model->fileName ) { + _pico_free( model->fileName ); + } + /* free shaders */ for ( i = 0; i < model->numShaders; i++ ) PicoFreeShader( model->shader[ i ] ); @@ -309,7 +362,6 @@ void PicoFreeModel( picoModel_t *model ){ } - /* PicoAdjustModel() adjusts a models's memory allocations to handle the requested sizes. @@ -364,7 +416,6 @@ int PicoAdjustModel( picoModel_t *model, int numShaders, int numSurfaces ){ } - /* ---------------------------------------------------------------------------- shaders ---------------------------------------------------------------------------- */ @@ -377,7 +428,6 @@ int PicoAdjustModel( picoModel_t *model, int numShaders, int numSurfaces ){ picoShader_t *PicoNewShader( picoModel_t *model ){ picoShader_t *shader; - /* allocate and clear */ shader = _pico_alloc( sizeof( picoShader_t ) ); if ( shader == NULL ) { @@ -397,9 +447,9 @@ picoShader_t *PicoNewShader( picoModel_t *model ){ shader->model = model; } /* setup default shader colors */ - _pico_set_color( shader->ambientColor,0,0,0,0 ); - _pico_set_color( shader->diffuseColor,255,255,255,1 ); - _pico_set_color( shader->specularColor,0,0,0,0 ); + _pico_set_color( shader->ambientColor, 0, 0, 0, 0 ); + _pico_set_color( shader->diffuseColor, 255, 255, 255, 1 ); + _pico_set_color( shader->specularColor, 0, 0, 0, 0 ); /* no need to do this, but i do it anyway */ shader->transparency = 0; @@ -410,7 +460,6 @@ picoShader_t *PicoNewShader( picoModel_t *model ){ } - /* PicoFreeShader() frees a shader and all associated data -sea @@ -435,7 +484,6 @@ void PicoFreeShader( picoShader_t *shader ){ } - /* PicoFindShader() finds a named shader in a model @@ -444,7 +492,6 @@ void PicoFreeShader( picoShader_t *shader ){ picoShader_t *PicoFindShader( picoModel_t *model, char *name, int caseSensitive ){ int i; - /* sanity checks */ if ( model == NULL || name == NULL ) { /* sea: null name fix */ return NULL; @@ -475,7 +522,6 @@ picoShader_t *PicoFindShader( picoModel_t *model, char *name, int caseSensitive } - /* ---------------------------------------------------------------------------- surfaces ---------------------------------------------------------------------------- */ @@ -518,7 +564,6 @@ picoSurface_t *PicoNewSurface( picoModel_t *model ){ } - /* PicoFreeSurface() frees a surface and all associated data @@ -526,7 +571,6 @@ picoSurface_t *PicoNewSurface( picoModel_t *model ){ void PicoFreeSurface( picoSurface_t *surface ){ int i; - /* dummy check */ if ( surface == NULL ) { return; @@ -535,9 +579,14 @@ void PicoFreeSurface( picoSurface_t *surface ){ /* free bits */ _pico_free( surface->xyz ); _pico_free( surface->normal ); + _pico_free( surface->smoothingGroup ); _pico_free( surface->index ); _pico_free( surface->faceNormal ); + if ( surface->name ) { + _pico_free( surface->name ); + } + /* free arrays */ for ( i = 0; i < surface->numSTArrays; i++ ) _pico_free( surface->st[ i ] ); @@ -551,7 +600,6 @@ void PicoFreeSurface( picoSurface_t *surface ){ } - /* PicoAdjustSurface() adjusts a surface's memory allocations to handle the requested sizes. @@ -561,7 +609,6 @@ void PicoFreeSurface( picoSurface_t *surface ){ int PicoAdjustSurface( picoSurface_t *surface, int numVertexes, int numSTArrays, int numColorArrays, int numIndexes, int numFaceNormals ){ int i; - /* dummy check */ if ( surface == NULL ) { return 0; @@ -591,6 +638,9 @@ int PicoAdjustSurface( picoSurface_t *surface, int numVertexes, int numSTArrays, if ( !_pico_realloc( (void *) &surface->normal, surface->numVertexes * sizeof( *surface->normal ), surface->maxVertexes * sizeof( *surface->normal ) ) ) { return 0; } + if ( !_pico_realloc( (void *) &surface->smoothingGroup, surface->numVertexes * sizeof( *surface->smoothingGroup ), surface->maxVertexes * sizeof( *surface->smoothingGroup ) ) ) { + return 0; + } for ( i = 0; i < surface->numSTArrays; i++ ) if ( !_pico_realloc( (void*) &surface->st[ i ], surface->numVertexes * sizeof( *surface->st[ i ] ), surface->maxVertexes * sizeof( *surface->st[ i ] ) ) ) { return 0; @@ -720,7 +770,7 @@ void PicoSetModelName( picoModel_t *model, char *name ){ _pico_free( model->name ); } - model->name = _pico_clone_alloc( name,-1 ); + model->name = _pico_clone_alloc( name ); } @@ -733,7 +783,7 @@ void PicoSetModelFileName( picoModel_t *model, char *fileName ){ _pico_free( model->fileName ); } - model->fileName = _pico_clone_alloc( fileName,-1 ); + model->fileName = _pico_clone_alloc( fileName ); } @@ -773,7 +823,7 @@ void PicoSetShaderName( picoShader_t *shader, char *name ){ _pico_free( shader->name ); } - shader->name = _pico_clone_alloc( name,-1 ); + shader->name = _pico_clone_alloc( name ); } @@ -786,7 +836,7 @@ void PicoSetShaderMapName( picoShader_t *shader, char *mapName ){ _pico_free( shader->mapName ); } - shader->mapName = _pico_clone_alloc( mapName,-1 ); + shader->mapName = _pico_clone_alloc( mapName ); } @@ -887,7 +937,7 @@ void PicoSetSurfaceName( picoSurface_t *surface, char *name ){ _pico_free( surface->name ); } - surface->name = _pico_clone_alloc( name,-1 ); + surface->name = _pico_clone_alloc( name ); } @@ -990,6 +1040,15 @@ void PicoSetFaceNormal( picoSurface_t *surface, int num, picoVec3_t normal ){ _pico_copy_vec( normal, surface->faceNormal[ num ] ); } +void PicoSetSurfaceSmoothingGroup( picoSurface_t *surface, int num, picoIndex_t smoothingGroup ){ + if ( num < 0 ) { + return; + } + if ( !PicoAdjustSurface( surface, num + 1, 0, 0, 0, 0 ) ) { + return; + } + surface->smoothingGroup[ num ] = smoothingGroup; +} void PicoSetSurfaceSpecial( picoSurface_t *surface, int num, int special ){ if ( surface == NULL || num < 0 || num >= PICO_MAX_SPECIAL ) { @@ -1284,7 +1343,7 @@ picoVec_t *PicoGetSurfaceNormal( picoSurface_t *surface, int num ){ -picoVec_t *PicoGetSurfaceST( picoSurface_t *surface, int array, int num ){ +picoVec_t *PicoGetSurfaceST( picoSurface_t *surface, int array, int num ){ if ( surface == NULL || array < 0 || array > surface->numSTArrays || num < 0 || num > surface->numVertexes ) { return NULL; } @@ -1335,6 +1394,12 @@ picoVec_t *PicoGetFaceNormal( picoSurface_t *surface, int num ){ return surface->faceNormal[ num ]; } +picoIndex_t PicoGetSurfaceSmoothingGroup( picoSurface_t *surface, int num ){ + if ( surface == NULL || num < 0 || num > surface->numVertexes ) { + return -1; + } + return surface->smoothingGroup[ num ]; +} int PicoGetSurfaceSpecial( picoSurface_t *surface, int num ){ if ( surface == NULL || num < 0 || num >= PICO_MAX_SPECIAL ) { @@ -1535,7 +1600,7 @@ picoVertexCombinationHash_t *PicoAddVertexCombinationToHashTable( picoVertexComb fixme: needs non-naive algorithm */ -int PicoFindSurfaceVertexNum( picoSurface_t *surface, picoVec3_t xyz, picoVec3_t normal, int numSTs, picoVec2_t *st, int numColors, picoColor_t *color ){ +int PicoFindSurfaceVertexNum( picoSurface_t *surface, picoVec3_t xyz, picoVec3_t normal, int numSTs, picoVec2_t *st, int numColors, picoColor_t *color, picoIndex_t smoothingGroup ){ int i, j; @@ -1557,6 +1622,11 @@ int PicoFindSurfaceVertexNum( picoSurface_t *surface, picoVec3_t xyz, picoVec3_t continue; } + /* check normal */ + if ( surface->smoothingGroup[ i ] != smoothingGroup ) { + continue; + } + /* check st */ if ( numSTs > 0 && st != NULL ) { for ( j = 0; j < numSTs; j++ ) @@ -1598,125 +1668,419 @@ int PicoFindSurfaceVertexNum( picoSurface_t *surface, picoVec3_t xyz, picoVec3_t fixes broken normals (certain formats bork normals) */ -#define MAX_NORMAL_VOTES 128 -#define EQUAL_NORMAL_EPSILON 0.01 -#define BAD_NORMAL_EPSILON 0.5 +//#define MAX_NORMAL_VOTES 128 +//#define EQUAL_NORMAL_EPSILON 0.01 +//#define BAD_NORMAL_EPSILON 0.5 +// +//void PicoFixSurfaceNormals( picoSurface_t *surface ){ +// int i, j, k, a, b, c, numVotes, faceIndex; +// picoVec3_t votes[ MAX_NORMAL_VOTES ]; +// picoVec3_t *normals, diff; +// picoVec4_t plane; +// +// +// /* dummy check */ +// if ( surface == NULL || surface->numVertexes == 0 ) { +// return; +// } +// +// /* fixme: handle other surface types */ +// if ( surface->type != PICO_TRIANGLES ) { +// return; +// } +// +// /* allocate normal storage */ +// normals = _pico_alloc( surface->numVertexes * sizeof( *normals ) ); +// if ( normals == NULL ) { +// _pico_printf( PICO_ERROR, "PicoFixSurfaceNormals: Unable to allocate memory for temporary normal storage" ); +// return; +// } +// +// /* zero it out */ +// memset( normals, 0, surface->numVertexes * sizeof( *normals ) ); +// +// /* walk vertex list */ +// for ( i = 0; i < surface->numVertexes; i++ ) +// { +// /* zero out votes */ +// numVotes = 0; +// +// /* find all the triangles that reference this vertex */ +// for ( j = 0, faceIndex = 0; j < surface->numIndexes; j += 3, faceIndex++ ) +// { +// /* get triangle */ +// a = surface->index[ j ]; +// b = surface->index[ j + 1 ]; +// c = surface->index[ j + 2 ]; +// +// /* ignore degenerate triangles */ +// if ( a == b || b == c || c == a ) { +// continue; +// } +// +// /* ignore indexes out of range */ +// if ( a < 0 || a >= surface->numVertexes || +// b < 0 || b >= surface->numVertexes || +// c < 0 || c >= surface->numVertexes ) { +// continue; +// } +// +// /* test triangle */ +// if ( a == i || b == i || c == i ) { +// /* if this surface has face normals */ +// if ( surface->numFaceNormals && faceIndex < surface->numFaceNormals ) { +// _pico_copy_vec( surface->faceNormal[ faceIndex ], plane ); +// if ( plane[ 0 ] == 0.f && plane[ 1 ] == 0.f && plane[ 2 ] == 0.f ) { +// /* if null normal, make plane from the 3 points */ +// if ( _pico_calc_plane( plane, surface->xyz[ a ], surface->xyz[ b ], surface->xyz[ c ] ) == 0 ) { +// continue; +// } +// } +// } +// /* make a plane from the 3 points */ +// else if ( _pico_calc_plane( plane, surface->xyz[ a ], surface->xyz[ b ], surface->xyz[ c ] ) == 0 ) { +// continue; +// } +// +// /* see if this normal has already been voted */ +// for ( k = 0; k < numVotes; k++ ) +// { +// _pico_subtract_vec( plane, votes[ k ], diff ); +// if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON && +// fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON && +// fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) { +// break; +// } +// } +// +// /* add a new vote? */ +// if ( k == numVotes && numVotes < MAX_NORMAL_VOTES ) { +// _pico_copy_vec( plane, votes[ numVotes ] ); +// numVotes++; +// } +// } +// } +// +// /* tally votes */ +// if ( numVotes > 0 ) { +// /* create average normal */ +// _pico_zero_vec( normals[ i ] ); +// for ( k = 0; k < numVotes; k++ ) +// _pico_add_vec( normals[ i ], votes[ k ], normals[ i ] ); +// +// /* normalize it */ +// if ( _pico_normalize_vec( normals[ i ] ) ) { +// /* test against actual normal */ +// if ( fabs( _pico_dot_vec( normals[ i ], surface->normal[ i ] ) - 1 ) > BAD_NORMAL_EPSILON ) { +// //% printf( "Normal %8d: (%f %f %f) -> (%f %f %f)\n", i, +// //% surface->normal[ i ][ 0 ], surface->normal[ i ][ 1 ], surface->normal[ i ][ 2 ], +// //% normals[ i ][ 0 ], normals[ i ][ 1 ], normals[ i ][ 2 ] ); +// _pico_copy_vec( normals[ i ], surface->normal[ i ] ); +// } +// } +// } +// } +// +// /* free normal storage */ +// _pico_free( normals ); +//} -void PicoFixSurfaceNormals( picoSurface_t *surface ){ - int i, j, k, a, b, c, numVotes, faceIndex; - picoVec3_t votes[ MAX_NORMAL_VOTES ]; - picoVec3_t *normals, diff; - picoVec4_t plane; +typedef struct _IndexArray IndexArray; +struct _IndexArray +{ + picoIndex_t* data; + picoIndex_t* last; +}; - - /* dummy check */ - if ( surface == NULL || surface->numVertexes == 0 ) { - return; - } - - /* fixme: handle other surface types */ - if ( surface->type != PICO_TRIANGLES ) { - return; - } - - /* allocate normal storage */ - normals = _pico_alloc( surface->numVertexes * sizeof( *normals ) ); - if ( normals == NULL ) { - _pico_printf( PICO_ERROR, "PicoFixSurfaceNormals: Unable to allocate memory for temporary normal storage" ); - return; - } - - /* zero it out */ - memset( normals, 0, surface->numVertexes * sizeof( *normals ) ); - - /* walk vertex list */ - for ( i = 0; i < surface->numVertexes; i++ ) - { - /* zero out votes */ - numVotes = 0; - - /* find all the triangles that reference this vertex */ - for ( j = 0, faceIndex = 0; j < surface->numIndexes; j += 3, faceIndex++ ) - { - /* get triangle */ - a = surface->index[ j ]; - b = surface->index[ j + 1 ]; - c = surface->index[ j + 2 ]; - - /* ignore degenerate triangles */ - if ( a == b || b == c || c == a ) { - continue; - } - - /* ignore indexes out of range */ - if ( a < 0 || a >= surface->numVertexes || - b < 0 || b >= surface->numVertexes || - c < 0 || c >= surface->numVertexes ) { - continue; - } - - /* test triangle */ - if ( a == i || b == i || c == i ) { - /* if this surface has face normals */ - if ( surface->numFaceNormals && faceIndex < surface->numFaceNormals ) { - _pico_copy_vec( surface->faceNormal[ faceIndex ], plane ); - if ( plane[ 0 ] == 0.f && plane[ 1 ] == 0.f && plane[ 2 ] == 0.f ) { - /* if null normal, make plane from the 3 points */ - if ( _pico_calc_plane( plane, surface->xyz[ a ], surface->xyz[ b ], surface->xyz[ c ] ) == 0 ) { - continue; - } - } - } - /* make a plane from the 3 points */ - else if ( _pico_calc_plane( plane, surface->xyz[ a ], surface->xyz[ b ], surface->xyz[ c ] ) == 0 ) { - continue; - } - - /* see if this normal has already been voted */ - for ( k = 0; k < numVotes; k++ ) - { - _pico_subtract_vec( plane, votes[ k ], diff ); - if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON && - fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON && - fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) { - break; - } - } - - /* add a new vote? */ - if ( k == numVotes && numVotes < MAX_NORMAL_VOTES ) { - _pico_copy_vec( plane, votes[ numVotes ] ); - numVotes++; - } - } - } - - /* tally votes */ - if ( numVotes > 0 ) { - /* create average normal */ - _pico_zero_vec( normals[ i ] ); - for ( k = 0; k < numVotes; k++ ) - _pico_add_vec( normals[ i ], votes[ k ], normals[ i ] ); - - /* normalize it */ - if ( _pico_normalize_vec( normals[ i ] ) ) { - /* test against actual normal */ - if ( fabs( _pico_dot_vec( normals[ i ], surface->normal[ i ] ) - 1 ) > BAD_NORMAL_EPSILON ) { - //% printf( "Normal %8d: (%f %f %f) -> (%f %f %f)\n", i, - //% surface->normal[ i ][ 0 ], surface->normal[ i ][ 1 ], surface->normal[ i ][ 2 ], - //% normals[ i ][ 0 ], normals[ i ][ 1 ], normals[ i ][ 2 ] ); - _pico_copy_vec( normals[ i ], surface->normal[ i ] ); - } - } - } - } - - /* free normal storage */ - _pico_free( normals ); +void indexarray_push_back(IndexArray* self, picoIndex_t value) { + *self->last++ = value; } +size_t indexarray_size(IndexArray* self) { + return self->last - self->data; +} +void indexarray_reserve(IndexArray* self, size_t size) { + self->data = self->last = _pico_calloc(size, sizeof(picoIndex_t)); +} +void indexarray_clear(IndexArray* self) { + _pico_free(self->data); +} + +typedef struct _BinaryTreeNode BinaryTreeNode; +struct _BinaryTreeNode +{ + picoIndex_t left; + picoIndex_t right; +}; + +typedef struct _BinaryTree BinaryTree; +struct _BinaryTree +{ + BinaryTreeNode* data; + BinaryTreeNode* last; +}; + +void binarytree_extend(BinaryTree* self) { + self->last->left = 0; + self->last->right = 0; + ++self->last; +} + +size_t binarytree_size(BinaryTree* self) { + return self->last - self->data; +} + +void binarytree_reserve(BinaryTree* self, size_t size) { + self->data = self->last = _pico_calloc(size, sizeof(BinaryTreeNode)); +} + +void binarytree_clear(BinaryTree* self) { + _pico_free(self->data); +} + +typedef int(*LessFunc)(void*, picoIndex_t, picoIndex_t); + +typedef struct _UniqueIndices UniqueIndices; +struct _UniqueIndices +{ + BinaryTree tree; + IndexArray indices; + LessFunc lessFunc; + void* lessData; +}; + +size_t UniqueIndices_size(UniqueIndices* self) { + return binarytree_size(&self->tree); +} + +void UniqueIndices_reserve(UniqueIndices* self, size_t size) { + binarytree_reserve(&self->tree, size); + indexarray_reserve(&self->indices, size); +} + +void UniqueIndices_init(UniqueIndices* self, LessFunc lessFunc, void* lessData) { + self->lessFunc = lessFunc; + self->lessData = lessData; +} + +void UniqueIndices_destroy(UniqueIndices* self) { + binarytree_clear(&self->tree); + indexarray_clear(&self->indices); +} + +picoIndex_t UniqueIndices_find_or_insert(UniqueIndices* self, picoIndex_t value) { + picoIndex_t index = 0; + + for (;; ) + { + if (self->lessFunc(self->lessData, value, self->indices.data[index])) { + BinaryTreeNode* node = self->tree.data + index; + if (node->left != 0) { + index = node->left; + continue; + } + else + { + node->left = (picoIndex_t)binarytree_size(&self->tree); + binarytree_extend(&self->tree); + indexarray_push_back(&self->indices, value); + return node->left; + } + } + if (self->lessFunc(self->lessData, self->indices.data[index], value)) { + BinaryTreeNode* node = self->tree.data + index; + if (node->right != 0) { + index = node->right; + continue; + } + else + { + node->right = (picoIndex_t)binarytree_size(&self->tree); + binarytree_extend(&self->tree); + indexarray_push_back(&self->indices, value); + return node->right; + } + } + + return index; + } +} + +picoIndex_t UniqueIndices_insert(UniqueIndices* self, picoIndex_t value) { + if (self->tree.data == self->tree.last) { + binarytree_extend(&self->tree); + indexarray_push_back(&self->indices, value); + return 0; + } + else + { + return UniqueIndices_find_or_insert(self, value); + } +} + +typedef struct picoSmoothVertices_s picoSmoothVertices_t; +struct picoSmoothVertices_s +{ + picoVec3_t* xyz; + picoIndex_t* smoothingGroups; +}; + +int lessSmoothVertex(void* data, picoIndex_t first, picoIndex_t second) { + picoSmoothVertices_t* smoothVertices = data; + + if (smoothVertices->xyz[first][0] != smoothVertices->xyz[second][0]) { + return smoothVertices->xyz[first][0] < smoothVertices->xyz[second][0]; + } + if (smoothVertices->xyz[first][1] != smoothVertices->xyz[second][1]) { + return smoothVertices->xyz[first][1] < smoothVertices->xyz[second][1]; + } + if (smoothVertices->xyz[first][2] != smoothVertices->xyz[second][2]) { + return smoothVertices->xyz[first][2] < smoothVertices->xyz[second][2]; + } + if (smoothVertices->smoothingGroups[first] != smoothVertices->smoothingGroups[second]) { + return smoothVertices->smoothingGroups[first] < smoothVertices->smoothingGroups[second]; + } + return 0; +} + +void _pico_vertices_combine_shared_normals(picoVec3_t* xyz, picoIndex_t* smoothingGroups, picoVec3_t* normals, picoIndex_t numVertices) { + UniqueIndices vertices; + IndexArray indices; + picoSmoothVertices_t smoothVertices = { xyz, smoothingGroups }; + UniqueIndices_init(&vertices, lessSmoothVertex, &smoothVertices); + UniqueIndices_reserve(&vertices, numVertices); + indexarray_reserve(&indices, numVertices); + + { + picoIndex_t i = 0; + for (; i < numVertices; ++i) + { + size_t size = UniqueIndices_size(&vertices); + picoIndex_t index = UniqueIndices_insert(&vertices, i); + if ((size_t)index != size) { + float* normal = normals[vertices.indices.data[index]]; + _pico_add_vec(normal, normals[i], normal); + } + indexarray_push_back(&indices, index); + } + } + + { + picoIndex_t maxIndex = 0; + picoIndex_t* i = indices.data; + for (; i != indices.last; ++i) + { + if (*i <= maxIndex) { + _pico_copy_vec(normals[vertices.indices.data[*i]], normals[i - indices.data]); + } + else + { + maxIndex = *i; + } + } + } + + UniqueIndices_destroy(&vertices); + indexarray_clear(&indices); +} + +typedef picoVec3_t* picoNormalIter_t; +typedef picoIndex_t* picoIndexIter_t; + +#define THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL 1 + +void _pico_triangles_generate_weighted_normals(picoIndexIter_t first, picoIndexIter_t end, picoVec3_t* xyz, picoVec3_t* normals) { + for (; first != end; first += 3) + { +#if (THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL) + picoVec3_t weightedNormal; + { + float* a = xyz[*(first + 0)]; + float* b = xyz[*(first + 1)]; + float* c = xyz[*(first + 2)]; + picoVec3_t ba, ca; + _pico_subtract_vec(b, a, ba); + _pico_subtract_vec(c, a, ca); + _pico_cross_vec(ca, ba, weightedNormal); + } +#endif + { + int j = 0; + for (; j < 3; ++j) + { + float* normal = normals[*(first + j)]; +#if ( !THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL ) + picoVec3_t weightedNormal; + { + float* a = xyz[*(first + ((j + 0) % 3))]; + float* b = xyz[*(first + ((j + 1) % 3))]; + float* c = xyz[*(first + ((j + 2) % 3))]; + picoVec3_t ba, ca; + _pico_subtract_vec(b, a, ba); + _pico_subtract_vec(c, a, ca); + _pico_cross_vec(ca, ba, weightedNormal); + } +#endif + _pico_add_vec(weightedNormal, normal, normal); + } + } + } +} + +void _pico_normals_zero(picoNormalIter_t first, picoNormalIter_t last) { + for (; first != last; ++first) + { + _pico_zero_vec(*first); + } +} + +void _pico_normals_normalize(picoNormalIter_t first, picoNormalIter_t last) { + for (; first != last; ++first) + { + _pico_normalize_vec(*first); + } +} + +double _pico_length_vec(picoVec3_t vec) { + return sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]); +} + +#define NORMAL_UNIT_LENGTH_EPSILON 0.01 +#define FLOAT_EQUAL_EPSILON( f, other, epsilon ) ( fabs( f - other ) < epsilon ) + +int _pico_normal_is_unit_length(picoVec3_t normal) { + return FLOAT_EQUAL_EPSILON(_pico_length_vec(normal), 1.0, NORMAL_UNIT_LENGTH_EPSILON); +} + +int _pico_normal_within_tolerance(picoVec3_t normal, picoVec3_t other) { + return _pico_dot_vec(normal, other) > 0.0f; +} + +void _pico_normals_assign_generated_normals(picoNormalIter_t first, picoNormalIter_t last, picoNormalIter_t generated) { + for (; first != last; ++first, ++generated) + { + if (!_pico_normal_is_unit_length(*first) || !_pico_normal_within_tolerance(*first, *generated)) { + _pico_copy_vec(*generated, *first); + } + } +} + +void PicoFixSurfaceNormals(picoSurface_t* surface) { + picoVec3_t* normals = (picoVec3_t*)_pico_calloc(surface->numVertexes, sizeof(picoVec3_t)); + + _pico_normals_zero(normals, normals + surface->numVertexes); + + _pico_triangles_generate_weighted_normals(surface->index, surface->index + surface->numIndexes, surface->xyz, normals); + _pico_vertices_combine_shared_normals(surface->xyz, surface->smoothingGroup, normals, surface->numVertexes); + + _pico_normals_normalize(normals, normals + surface->numVertexes); + + _pico_normals_assign_generated_normals(surface->normal, surface->normal + surface->numVertexes, normals); + + _pico_free(normals); +} /* PicoRemapModel() - sea @@ -1727,8 +2091,8 @@ void PicoFixSurfaceNormals( picoSurface_t *surface ){ #define _prm_error_return \ { \ - _pico_free_parser( p ); \ - _pico_free_file( remapBuffer ); \ + _pico_free_parser( p ); \ + _pico_free_file( remapBuffer ); \ return 0; \ } @@ -1744,7 +2108,7 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){ } /* load remap file contents */ - _pico_load_file( remapFile,&remapBuffer,&remapBufSize ); + _pico_load_file( remapFile, &remapBuffer, &remapBufSize ); /* check result */ if ( remapBufSize == 0 ) { @@ -1765,12 +2129,12 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){ while ( 1 ) { /* get next token in remap file */ - if ( !_pico_parse( p,1 ) ) { + if ( !_pico_parse( p, 1 ) ) { break; } /* skip over c++ style comment lines */ - if ( !_pico_stricmp( p->token,"//" ) ) { + if ( !_pico_stricmp( p->token, "//" ) ) { _pico_parse_skip_rest( p ); continue; } @@ -1781,7 +2145,7 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){ int level = 1; /* check bracket */ - if ( !_pico_parse_check( p,1,"{" ) ) { + if ( !_pico_parse_check( p, 1, "{" ) ) { _prm_error_return; } @@ -1791,15 +2155,14 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){ picoShader_t *shader; char *materialName; - /* get material name */ - if ( _pico_parse( p,1 ) == NULL ) { + if ( _pico_parse( p, 1 ) == NULL ) { break; } if ( !strlen( p->token ) ) { continue; } - materialName = _pico_clone_alloc( p->token,-1 ); + materialName = _pico_clone_alloc( p->token ); if ( materialName == NULL ) { _prm_error_return; } @@ -1816,26 +2179,26 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){ } /* get next token (assignment token or shader name) */ - if ( !_pico_parse( p,0 ) ) { + if ( !_pico_parse( p, 0 ) ) { _pico_free( materialName ); _prm_error_return; } /* skip assignment token (if present) */ - if ( !strcmp( p->token,"=>" ) || - !strcmp( p->token,"->" ) || - !strcmp( p->token,"=" ) ) { + if ( !strcmp( p->token, "=>" ) || + !strcmp( p->token, "->" ) || + !strcmp( p->token, "=" ) ) { /* simply grab the next token */ - if ( !_pico_parse( p,0 ) ) { + if ( !_pico_parse( p, 0 ) ) { _pico_free( materialName ); _prm_error_return; } } /* try to find material by name */ - shader = PicoFindShader( model,materialName,0 ); + shader = PicoFindShader( model, materialName, 0 ); /* we've found a material matching the name */ if ( shader != NULL ) { - PicoSetShaderName( shader,p->token ); + PicoSetShaderName( shader, p->token ); } /* free memory used by material name */ _pico_free( materialName ); @@ -1846,29 +2209,29 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){ } /* block for detailed single material remappings */ /* materials[ "m" ] { key data... } */ - else if ( !_pico_stricmp( p->token,"materials[" ) ) { + else if ( !_pico_stricmp( p->token, "materials[" ) ) { picoShader_t *shader; char *tempMaterialName; int level = 1; /* get material name */ - if ( !_pico_parse( p,0 ) ) { + if ( !_pico_parse( p, 0 ) ) { _prm_error_return; } /* temporary copy of material name */ - tempMaterialName = _pico_clone_alloc( p->token,-1 ); + tempMaterialName = _pico_clone_alloc( p->token ); if ( tempMaterialName == NULL ) { _prm_error_return; } /* check square closing bracket */ - if ( !_pico_parse_check( p,0,"]" ) ) { + if ( !_pico_parse_check( p, 0, "]" ) ) { _prm_error_return; } /* try to find material by name */ - shader = PicoFindShader( model,tempMaterialName,0 ); + shader = PicoFindShader( model, tempMaterialName, 0 ); /* free memory used by temporary material name */ _pico_free( tempMaterialName ); @@ -1881,7 +2244,7 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){ continue; } /* check opening bracket */ - if ( !_pico_parse_check( p,1,"{" ) ) { + if ( !_pico_parse_check( p, 1, "{" ) ) { _prm_error_return; } @@ -1889,7 +2252,7 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){ while ( 1 ) { /* get key name */ - if ( _pico_parse( p,1 ) == NULL ) { + if ( _pico_parse( p, 1 ) == NULL ) { break; } if ( !strlen( p->token ) ) { @@ -1908,26 +2271,26 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){ } /* remap shader name */ - if ( !_pico_stricmp( p->token,"shader" ) ) { - if ( !_pico_parse( p,0 ) ) { + if ( !_pico_stricmp( p->token, "shader" ) ) { + if ( !_pico_parse( p, 0 ) ) { _prm_error_return; } - PicoSetShaderName( shader,p->token ); + PicoSetShaderName( shader, p->token ); } /* remap shader map name */ - else if ( !_pico_stricmp( p->token,"mapname" ) ) { - if ( !_pico_parse( p,0 ) ) { + else if ( !_pico_stricmp( p->token, "mapname" ) ) { + if ( !_pico_parse( p, 0 ) ) { _prm_error_return; } - PicoSetShaderMapName( shader,p->token ); + PicoSetShaderMapName( shader, p->token ); } /* remap shader's ambient color */ - else if ( !_pico_stricmp( p->token,"ambient" ) ) { + else if ( !_pico_stricmp( p->token, "ambient" ) ) { picoColor_t color; picoVec3_t v; /* get vector from parser */ - if ( !_pico_parse_vec( p,v ) ) { + if ( !_pico_parse_vec( p, v ) ) { _prm_error_return; } @@ -1938,15 +2301,15 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){ color[ 3 ] = 1; /* set new ambient color */ - PicoSetShaderAmbientColor( shader,color ); + PicoSetShaderAmbientColor( shader, color ); } /* remap shader's diffuse color */ - else if ( !_pico_stricmp( p->token,"diffuse" ) ) { + else if ( !_pico_stricmp( p->token, "diffuse" ) ) { picoColor_t color; picoVec3_t v; /* get vector from parser */ - if ( !_pico_parse_vec( p,v ) ) { + if ( !_pico_parse_vec( p, v ) ) { _prm_error_return; } @@ -1957,10 +2320,10 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){ color[ 3 ] = 1; /* set new ambient color */ - PicoSetShaderDiffuseColor( shader,color ); + PicoSetShaderDiffuseColor( shader, color ); } /* remap shader's specular color */ - else if ( !_pico_stricmp( p->token,"specular" ) ) { + else if ( !_pico_stricmp( p->token, "specular" ) ) { picoColor_t color; picoVec3_t v; @@ -1976,7 +2339,7 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){ color[ 3 ] = 1; /* set new ambient color */ - PicoSetShaderSpecularColor( shader,color ); + PicoSetShaderSpecularColor( shader, color ); } /* skip rest */ _pico_parse_skip_rest( p ); @@ -2002,8 +2365,8 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){ void PicoAddTriangleToModel( picoModel_t *model, picoVec3_t** xyz, picoVec3_t** normals, int numSTs, picoVec2_t **st, int numColors, picoColor_t **colors, - picoShader_t* shader ){ - int i,j; + picoShader_t* shader, picoIndex_t* smoothingGroup ){ + int i, j; int vertDataIndex; picoSurface_t* workSurface = NULL; @@ -2038,7 +2401,7 @@ void PicoAddTriangleToModel( picoModel_t *model, picoVec3_t** xyz, picoVec3_t** int newVertIndex = PicoGetSurfaceNumIndexes( workSurface ); /* get the index of the vertex that we're going to store at newVertIndex */ - vertDataIndex = PicoFindSurfaceVertexNum( workSurface, *xyz[i], *normals[i], numSTs, st[i], numColors, colors[i] ); + vertDataIndex = PicoFindSurfaceVertexNum( workSurface, *xyz[i], *normals[i], numSTs, st[i], numColors, colors[i], smoothingGroup[i] ); /* the vertex wasn't found, so create a new vertex in the pool from the data we have */ if ( vertDataIndex == -1 ) { @@ -2058,6 +2421,8 @@ void PicoAddTriangleToModel( picoModel_t *model, picoVec3_t** xyz, picoVec3_t** { PicoSetSurfaceST( workSurface, j, vertDataIndex, st[i][j] ); } + + PicoSetSurfaceSmoothingGroup( workSurface, vertDataIndex, smoothingGroup[ i ] ); } /* add this vertex to the triangle */ diff --git a/libs/picomodel/picomodules.c b/libs/picomodel/picomodules.c index 6ccafc46..5f298492 100644 --- a/libs/picomodel/picomodules.c +++ b/libs/picomodel/picomodules.c @@ -54,6 +54,7 @@ extern const picoModule_t picoModuleMDC; extern const picoModule_t picoModuleMD2; extern const picoModule_t picoModuleFM; extern const picoModule_t picoModuleLWO; +extern const picoModule_t picoModuleTerrain; @@ -68,6 +69,7 @@ const picoModule_t *picoModules[] = &picoModuleMD2, /* quake2 md2 */ &picoModuleFM, /* heretic2 fm */ &picoModuleLWO, /* lightwave object */ + &picoModuleTerrain, /* picoterrain object */ &picoModuleOBJ, /* wavefront object */ NULL /* arnold */ }; @@ -81,7 +83,7 @@ const picoModule_t *picoModules[] = this param can be NULL when the count is not needed. */ -const picoModule_t **PicoModuleList( int *numModules ){ +const picoModule_t **PicoModuleList( int *numModules ) { /* get module count */ if ( numModules != NULL ) { for ( ( *numModules ) = 0; picoModules[ *numModules ] != NULL; ( *numModules )++ ) ; diff --git a/libs/picomodel/pm_3ds.c b/libs/picomodel/pm_3ds.c index 64b77fe6..4065f3e7 100644 --- a/libs/picomodel/pm_3ds.c +++ b/libs/picomodel/pm_3ds.c @@ -124,7 +124,7 @@ debugChunkNames[] = { CHUNK_OBJECT_UV, "CHUNK_OBJECT_UV" }, { 0, NULL } }; -static char *DebugGetChunkName( int id ){ +static char *DebugGetChunkName( int id ) { int i,max; /* imax? ;) */ max = sizeof( debugChunkNames ) / sizeof( debugChunkNames[0] ); @@ -397,6 +397,9 @@ static int GetMeshShader( T3dsLoaderPers *pers ){ return 0; } + /* ydnar: trim to first whitespace */ + _pico_first_token( shaderName ); + /* now that we have the shader name we need to go through all of */ /* the shaders and check the name against each shader. when we */ /* find a shader in our shader list that matches this name we */ @@ -418,7 +421,7 @@ static int GetMeshShader( T3dsLoaderPers *pers ){ /* we have a valid map name ptr */ if ( mapNamePtr != NULL ) { char temp[128]; - char *name; + const char *name; /* copy map name to local buffer */ strcpy( mapName,mapNamePtr ); @@ -523,7 +526,6 @@ static int DoNextEditorDataChunk( T3dsLoaderPers *pers, long endofs ){ /* read in surface name */ if ( !GetASCIIZ( pers,surfaceName,sizeof( surfaceName ) ) ) { return 0; /* this is bad */ - } //PicoGetSurfaceName /* ignore NULL name surfaces */ @@ -606,10 +608,13 @@ static int DoNextEditorDataChunk( T3dsLoaderPers *pers, long endofs ){ /* but for now we skip the new material's name ... */ if ( pers->shader ) { char *name = (char *)( pers->bufptr + pers->cofs ); - PicoSetShaderName( pers->shader,name ); + char *cleanedName = _pico_clone_alloc( name ); + _pico_first_token( cleanedName ); + PicoSetShaderName( pers->shader, cleanedName ); #ifdef DEBUG_PM_3DS - printf( "NewShader: '%s'\n",name ); + printf( "NewShader: '%s'\n", cleanedName ); #endif + _pico_free( cleanedName ); } } if ( chunk->id == CHUNK_MATDIFFUSE ) { diff --git a/libs/picomodel/pm_ase.c b/libs/picomodel/pm_ase.c index dc31dd5f..67f40d41 100644 --- a/libs/picomodel/pm_ase.c +++ b/libs/picomodel/pm_ase.c @@ -105,6 +105,25 @@ static aseSubMaterial_t* _ase_get_submaterial( aseMaterial_t* list, int mtlIdPar return subMtl; } +aseSubMaterial_t* _ase_get_submaterial_or_default( aseMaterial_t* materials, int mtlIdParent, int subMtlId ){ + aseSubMaterial_t* subMtl = _ase_get_submaterial( materials, mtlIdParent, subMtlId ); + if ( subMtl != NULL ) { + return subMtl; + } + + /* ydnar: trying default submaterial */ + subMtl = _ase_get_submaterial( materials, mtlIdParent, 0 ); + if ( subMtl != NULL ) { + return subMtl; + } + + _pico_printf( PICO_ERROR, "Could not find material/submaterial for id %d/%d\n", mtlIdParent, subMtlId ); + return NULL; +} + + + + static aseMaterial_t* _ase_add_material( aseMaterial_t **list, int mtlIdParent ){ aseMaterial_t *mtl = _pico_calloc( 1, sizeof( aseMaterial_t ) ); mtl->mtlId = mtlIdParent; @@ -173,64 +192,6 @@ static void _ase_print_materials( aseMaterial_t *list ){ } #endif //DEBUG_PM_ASE -/* ASE Face management */ -/* These are used to keep an association between a submaterial and a face definition */ -/* They are kept in parallel with the current picoSurface, */ -/* and are used by _ase_submit_triangles to lookup the proper material/submaterial IDs */ -typedef struct aseFace_s -{ - struct aseFace_s* next; - int mtlId; - int subMtlId; - int index[9]; -} aseFace_t; - -/* ASE Face management functions */ -void _ase_add_face( aseFace_t **list, aseFace_t **tail, aseFace_t *newFace ){ - /* insert as head of list */ - if ( !( *list ) ) { - *list = newFace; - } - else - { - ( *tail )->next = newFace; - } - - *tail = newFace; - newFace->next = NULL; - - //tag the color indices so we can detect them and apply the default color to them - newFace->index[6] = -1; - newFace->index[7] = -1; - newFace->index[8] = -1; -} - -aseFace_t* _ase_get_face_for_index( aseFace_t *list, int index ){ - int counter = 0; - aseFace_t* face = list; - - while ( counter < index ) - { - face = face->next; - counter++; - } - return face; -} -static void _ase_free_faces( aseFace_t** list, aseFace_t** tail ){ - aseFace_t* face = *list; - aseFace_t* tempFace = NULL; - - while ( face ) - { - tempFace = face->next; - _pico_free( face ); - face = tempFace; - } - - ( *list ) = NULL; - ( *tail ) = NULL; -} - /* todo: * - apply material specific uv offsets to uv coordinates */ @@ -274,7 +235,65 @@ static int _ase_canload( PM_PARAMS_CANLOAD ){ return PICO_PMV_OK; } +typedef struct aseVertex_s aseVertex_t; +struct aseVertex_s +{ + picoVec3_t xyz; + picoVec3_t normal; + picoIndex_t id; +}; +typedef struct aseTexCoord_s aseTexCoord_t; +struct aseTexCoord_s +{ + picoVec2_t texcoord; +}; + +typedef struct aseColor_s aseColor_t; +struct aseColor_s +{ + picoColor_t color; +}; + +typedef struct aseFace_s aseFace_t; +struct aseFace_s +{ + picoIndex_t indices[9]; + picoIndex_t smoothingGroup; + picoIndex_t materialId; + picoIndex_t subMaterialId; +}; +typedef aseFace_t* aseFacesIter_t; + +picoSurface_t* PicoModelFindOrAddSurface( picoModel_t *model, picoShader_t* shader ){ + /* see if a surface already has the shader */ + int i = 0; + for ( ; i < model->numSurfaces ; i++ ) + { + picoSurface_t* workSurface = model->surface[i]; + if ( workSurface->shader == shader ) { + return workSurface; + } + } + + /* no surface uses this shader yet, so create a new surface */ + + { + /* create a new surface in the model for the unique shader */ + picoSurface_t* workSurface = PicoNewSurface( model ); + if ( !workSurface ) { + _pico_printf( PICO_ERROR, "Could not allocate a new surface!\n" ); + return 0; + } + + /* do surface setup */ + PicoSetSurfaceType( workSurface, PICO_TRIANGLES ); + PicoSetSurfaceName( workSurface, shader->name ); + PicoSetSurfaceShader( workSurface, shader ); + + return workSurface; + } +} /* _ase_submit_triangles - jhefty use the surface and the current face list to look up material/submaterial IDs @@ -286,65 +305,199 @@ static int _ase_canload( PM_PARAMS_CANLOAD ){ indexes 6 7 8 = color indexes (new) */ -static void _ase_submit_triangles( picoSurface_t* surface, picoModel_t* model, aseMaterial_t* materials, aseFace_t* faces ){ - aseFace_t* face; - aseSubMaterial_t* subMtl; - picoVec3_t* xyz[3]; - picoVec3_t* normal[3]; - picoVec2_t* st[3]; - picoColor_t* color[3]; - int i; +#if 0 +typedef picoIndex_t* picoIndexIter_t; - face = faces; - while ( face != NULL ) +typedef struct aseUniqueIndices_s aseUniqueIndices_t; +struct aseUniqueIndices_s +{ + picoIndex_t* data; + picoIndex_t* last; + + aseFace_t* faces; +}; + +size_t aseUniqueIndices_size( aseUniqueIndices_t* self ) { + return self->last - self->data; +} + +void aseUniqueIndices_reserve( aseUniqueIndices_t* self, picoIndex_t size ) { + self->data = self->last = (picoIndex_t*)_pico_calloc( size, sizeof( picoIndex_t ) ); +} + +void aseUniqueIndices_clear( aseUniqueIndices_t* self ) { + _pico_free( self->data ); +} + +void aseUniqueIndices_pushBack( aseUniqueIndices_t* self, picoIndex_t index ) { + *self->last++ = index; +} + +picoIndex_t aseFaces_getVertexIndex( aseFace_t* faces, picoIndex_t index ) { + return faces[index / 3].indices[index % 3]; +} + +picoIndex_t aseFaces_getTexCoordIndex( aseFace_t* faces, picoIndex_t index ) { + return faces[index / 3].indices[( index % 3 ) + 3]; +} + +picoIndex_t aseFaces_getColorIndex( aseFace_t* faces, picoIndex_t index ) { + return faces[index / 3].indices[( index % 3 ) + 6]; +} + +int aseUniqueIndex_equal( aseFace_t* faces, picoIndex_t index, picoIndex_t other ) { + return aseFaces_getVertexIndex( faces, index ) == aseFaces_getVertexIndex( faces, other ) + && aseFaces_getTexCoordIndex( faces, index ) == aseFaces_getTexCoordIndex( faces, other ) + && aseFaces_getColorIndex( faces, index ) == aseFaces_getColorIndex( faces, other ); +} + +picoIndex_t aseUniqueIndices_insertUniqueVertex( aseUniqueIndices_t* self, picoIndex_t index ) { + picoIndexIter_t i = self->data; + for (; i != self->last; ++i ) + { + picoIndex_t other = (picoIndex_t)( i - self->data ); + if ( aseUniqueIndex_equal( self->faces, index, other ) ) { + return other; + } + } + + aseUniqueIndices_pushBack( self, index ); + return (picoIndex_t)( aseUniqueIndices_size( self ) - 1 ); +} + +static void _ase_submit_triangles_unshared( picoModel_t* model, aseMaterial_t* materials, aseVertex_t* vertices, aseTexCoord_t* texcoords, aseColor_t* colors, aseFace_t* faces, int numFaces, int meshHasNormals ) { + aseFacesIter_t i = faces, end = faces + numFaces; + + aseUniqueIndices_t indices; + aseUniqueIndices_t remap; + aseUniqueIndices_reserve( &indices, numFaces * 3 ); + aseUniqueIndices_reserve( &remap, numFaces * 3 ); + indices.faces = faces; + + for (; i != end; ++i ) { /* look up the shader for the material/submaterial pair */ - subMtl = _ase_get_submaterial( materials, face->mtlId, face->subMtlId ); + aseSubMaterial_t* subMtl = _ase_get_submaterial_or_default( materials, ( *i ).materialId, ( *i ).subMaterialId ); if ( subMtl == NULL ) { - /* ydnar: trying default submaterial */ - subMtl = _ase_get_submaterial( materials, face->mtlId, 0 ); - if ( subMtl == NULL ) { - _pico_printf( PICO_ERROR, "Could not find material/submaterial for id %d/%d\n", face->mtlId, face->subMtlId ); - return; - } + return; } - /* we pull the data from the surface using the facelist data */ - for ( i = 0 ; i < 3 ; i++ ) { - xyz[i] = (picoVec3_t*) PicoGetSurfaceXYZ( surface, face->index[ i ] ); - normal[i] = (picoVec3_t*) PicoGetSurfaceNormal( surface, face->index[ i ] ); - st[i] = (picoVec2_t*) PicoGetSurfaceST( surface, 0, face->index[ i + 3 ] ); - - if ( face->index [ i + 6] >= 0 ) { - color[i] = (picoColor_t*)PicoGetSurfaceColor( surface, 0, face->index[ i + 6 ] ); - } - else + picoSurface_t* surface = PicoModelFindOrAddSurface( model, subMtl->shader ); + int j; + /* we pull the data from the vertex, color and texcoord arrays using the face index data */ + for ( j = 0 ; j < 3 ; j++ ) { - color[i] = &white; - } + picoIndex_t index = (picoIndex_t)( ( ( i - faces ) * 3 ) + j ); + picoIndex_t size = (picoIndex_t)aseUniqueIndices_size( &indices ); + picoIndex_t unique = aseUniqueIndices_insertUniqueVertex( &indices, index ); + picoIndex_t numVertexes = PicoGetSurfaceNumVertexes( surface ); + picoIndex_t numIndexes = PicoGetSurfaceNumIndexes( surface ); + + aseUniqueIndices_pushBack( &remap, numIndexes ); + + PicoSetSurfaceIndex( surface, numIndexes, remap.data[unique] ); + + if ( unique == size ) { + PicoSetSurfaceXYZ( surface, numVertexes, vertices[( *i ).indices[j]].xyz ); + PicoSetSurfaceNormal( surface, numVertexes, vertices[( *i ).indices[j]].normal ); + PicoSetSurfaceST( surface, 0, numVertexes, texcoords[( *i ).indices[j + 3]].texcoord ); + + if ( ( *i ).indices[j + 6] >= 0 ) { + PicoSetSurfaceColor( surface, 0, numVertexes, colors[( *i ).indices[j + 6]].color ); + } + else + { + PicoSetSurfaceColor( surface, 0, numVertexes, white ); + } + + PicoSetSurfaceSmoothingGroup( surface, numVertexes, ( vertices[( *i ).indices[j]].id * ( 1 << 16 ) ) + ( *i ).smoothingGroup ); + } + } + } + } + + aseUniqueIndices_clear( &indices ); + aseUniqueIndices_clear( &remap ); +} + +#endif + +static void _ase_submit_triangles( picoModel_t* model, aseMaterial_t* materials, aseVertex_t* vertices, aseTexCoord_t* texcoords, aseColor_t* colors, aseFace_t* faces, int numFaces ){ + aseFacesIter_t i = faces, end = faces + numFaces; + for (; i != end; ++i ) + { + /* look up the shader for the material/submaterial pair */ + aseSubMaterial_t* subMtl = _ase_get_submaterial_or_default( materials, ( *i ).materialId, ( *i ).subMaterialId ); + if ( subMtl == NULL ) { + return; } - /* submit the triangle to the model */ - PicoAddTriangleToModel( model, xyz, normal, 1, st, 1, color, subMtl->shader ); + { + picoVec3_t* xyz[3]; + picoVec3_t* normal[3]; + picoVec2_t* st[3]; + picoColor_t* color[3]; + picoIndex_t smooth[3]; + int j; + /* we pull the data from the vertex, color and texcoord arrays using the face index data */ + for ( j = 0 ; j < 3 ; j++ ) + { + xyz[j] = &vertices[( *i ).indices[j]].xyz; + normal[j] = &vertices[( *i ).indices[j]].normal; + st[j] = &texcoords[( *i ).indices[j + 3]].texcoord; - /* advance to the next face */ - face = face->next; + if ( colors != NULL && ( *i ).indices[j + 6] >= 0 ) { + color[j] = &colors[( *i ).indices[j + 6]].color; + } + else + { + color[j] = &white; + } + + smooth[j] = ( vertices[( *i ).indices[j]].id * ( 1 << 16 ) ) + ( *i ).smoothingGroup; /* don't merge vertices */ + + } + + /* submit the triangle to the model */ + PicoAddTriangleToModel( model, xyz, normal, 1, st, 1, color, subMtl->shader, smooth ); + } } } +static void shadername_convert( char* shaderName ){ + /* unix-style path separators */ + char* s = shaderName; + for (; *s != '\0'; ++s ) + { + if ( *s == '\\' ) { + *s = '/'; + } + } +} + + /* _ase_load: * loads a 3dsmax ase model file. */ static picoModel_t *_ase_load( PM_PARAMS_LOAD ){ picoModel_t *model; - picoSurface_t *surface = NULL; picoParser_t *p; char lastNodeName[ 1024 ]; + aseVertex_t* vertices = NULL; + aseTexCoord_t* texcoords = NULL; + aseColor_t* colors = NULL; aseFace_t* faces = NULL; - aseFace_t* facesTail = NULL; + int numVertices = 0; + int numFaces = 0; + int numTextureVertices = 0; + int numTextureVertexFaces = 0; + int numColorVertices = 0; + int numColorVertexFaces = 0; + int vertexId = 0; + aseMaterial_t* materials = NULL; #ifdef DEBUG_PM_ASE @@ -357,8 +510,8 @@ static picoModel_t *_ase_load( PM_PARAMS_LOAD ){ #define _ase_error_return( m ) \ { \ _pico_printf( PICO_ERROR,"%s in ASE, line %d.",m,p->curLine ); \ - _pico_free_parser( p ); \ - PicoFreeModel( model ); \ + _pico_free_parser( p ); \ + PicoFreeModel( model ); \ return NULL; \ } /* create a new pico parser */ @@ -413,15 +566,49 @@ static picoModel_t *_ase_load( PM_PARAMS_LOAD ){ /* model mesh (originally contained within geomobject) */ else if ( !_pico_stricmp( p->token,"*mesh" ) ) { /* finish existing surface */ - //_ase_make_surface( model, &surface ); - _ase_submit_triangles( surface, model,materials,faces ); - _ase_free_faces( &faces,&facesTail ); + _ase_submit_triangles( model, materials, vertices, texcoords, colors, faces, numFaces ); + _pico_free( faces ); + _pico_free( vertices ); + _pico_free( texcoords ); + _pico_free( colors ); + } + else if ( !_pico_stricmp( p->token,"*mesh_numvertex" ) ) { + if ( !_pico_parse_int( p, &numVertices ) ) { + _ase_error_return( "Missing MESH_NUMVERTEX value" ); + } - /* allocate new pico surface */ - surface = PicoNewSurface( NULL ); - if ( surface == NULL ) { - PicoFreeModel( model ); - return NULL; + vertices = _pico_calloc( numVertices, sizeof( aseVertex_t ) ); + } + else if ( !_pico_stricmp( p->token,"*mesh_numfaces" ) ) { + if ( !_pico_parse_int( p, &numFaces ) ) { + _ase_error_return( "Missing MESH_NUMFACES value" ); + } + + faces = _pico_calloc( numFaces, sizeof( aseFace_t ) ); + } + else if ( !_pico_stricmp( p->token,"*mesh_numtvertex" ) ) { + if ( !_pico_parse_int( p, &numTextureVertices ) ) { + _ase_error_return( "Missing MESH_NUMTVERTEX value" ); + } + + texcoords = _pico_calloc( numTextureVertices, sizeof( aseTexCoord_t ) ); + } + else if ( !_pico_stricmp( p->token,"*mesh_numtvfaces" ) ) { + if ( !_pico_parse_int( p, &numTextureVertexFaces ) ) { + _ase_error_return( "Missing MESH_NUMTVFACES value" ); + } + } + else if ( !_pico_stricmp( p->token,"*mesh_numcvertex" ) ) { + if ( !_pico_parse_int( p, &numColorVertices ) ) { + _ase_error_return( "Missing MESH_NUMCVERTEX value" ); + } + + colors = _pico_calloc( numColorVertices, sizeof( aseColor_t ) ); + memset( colors, 255, numColorVertices * sizeof( aseColor_t ) ); /* ydnar: force colors to white initially */ + } + else if ( !_pico_stricmp( p->token,"*mesh_numcvfaces" ) ) { + if ( !_pico_parse_int( p, &numColorVertexFaces ) ) { + _ase_error_return( "Missing MESH_NUMCVFACES value" ); } } /* mesh material reference. this usually comes at the end of */ @@ -430,77 +617,63 @@ static picoModel_t *_ase_load( PM_PARAMS_LOAD ){ /* the material reference id (shader index) now. */ else if ( !_pico_stricmp( p->token,"*material_ref" ) ) { int mtlId; - aseFace_t* face; - - /* we must have a valid surface */ - if ( surface == NULL ) { - _ase_error_return( "Missing mesh for material reference" ); - } /* get the material ref (0..n) */ if ( !_pico_parse_int( p,&mtlId ) ) { _ase_error_return( "Missing material reference ID" ); } - /* fix up all of the aseFaceList in the surface to point to the parent material */ - /* we've already saved off their subMtl */ - face = faces; - while ( face != NULL ) { - face->mtlId = mtlId; - face = face->next; + int i = 0; + /* fix up all of the aseFaceList in the surface to point to the parent material */ + /* we've already saved off their subMtl */ + for (; i < numFaces; ++i ) + { + faces[i].materialId = mtlId; + } } } /* model mesh vertex */ else if ( !_pico_stricmp( p->token,"*mesh_vertex" ) ) { - picoVec3_t v; int index; - /* we must have a valid surface */ - if ( surface == NULL ) { - continue; + if ( numVertices == 0 ) { + _ase_error_return( "Vertex parse error" ); } /* get vertex data (orig: index +y -x +z) */ if ( !_pico_parse_int( p,&index ) ) { _ase_error_return( "Vertex parse error" ); } - if ( !_pico_parse_vec( p,v ) ) { + if ( !_pico_parse_vec( p,vertices[index].xyz ) ) { _ase_error_return( "Vertex parse error" ); } - /* set vertex */ - PicoSetSurfaceXYZ( surface,index,v ); + vertices[index].id = vertexId++; } /* model mesh vertex normal */ else if ( !_pico_stricmp( p->token,"*mesh_vertexnormal" ) ) { - picoVec3_t v; int index; - /* we must have a valid surface */ - if ( surface == NULL ) { - continue; + if ( numVertices == 0 ) { + _ase_error_return( "Vertex parse error" ); } /* get vertex data (orig: index +y -x +z) */ if ( !_pico_parse_int( p,&index ) ) { _ase_error_return( "Vertex parse error" ); } - if ( !_pico_parse_vec( p,v ) ) { + if ( !_pico_parse_vec( p,vertices[index].normal ) ) { _ase_error_return( "Vertex parse error" ); } - - /* set vertex */ - PicoSetSurfaceNormal( surface,index,v ); } /* model mesh face */ else if ( !_pico_stricmp( p->token,"*mesh_face" ) ) { picoIndex_t indexes[3]; int index; - /* we must have a valid surface */ - if ( surface == NULL ) { - continue; + if ( numFaces == 0 ) { + _ase_error_return( "Face parse error" ); } /* get face index */ @@ -526,75 +699,58 @@ static picoModel_t *_ase_load( PM_PARAMS_LOAD ){ _ase_error_return( "Face parse error" ); } - /* set face indexes (note interleaved offset!) */ - PicoSetSurfaceIndex( surface, ( index * 9 + 0 ), indexes[2] ); - PicoSetSurfaceIndex( surface, ( index * 9 + 1 ), indexes[1] ); - PicoSetSurfaceIndex( surface, ( index * 9 + 2 ), indexes[0] ); - /* parse to the subMaterial ID */ while ( 1 ) { - _pico_parse( p,0 ); - if ( !_pico_stricmp( p->token,"*MESH_MTLID" ) ) { - aseFace_t* newFace; - int subMtlId; - - _pico_parse_int( p, &subMtlId ); - newFace = _pico_calloc( 1, sizeof( aseFace_t ) ); - - /* we fix up the mtlId later when we parse the material_ref */ - newFace->mtlId = 0; - newFace->subMtlId = subMtlId; - newFace->index[0] = indexes[2]; - newFace->index[1] = indexes[1]; - newFace->index[2] = indexes[0]; - - _ase_add_face( &faces,&facesTail,newFace ); + if ( !_pico_parse( p,0 ) ) { /* EOL */ break; } + if ( !_pico_stricmp( p->token,"*MESH_SMOOTHING" ) ) { + _pico_parse_int( p, &faces[index].smoothingGroup ); + } + if ( !_pico_stricmp( p->token,"*MESH_MTLID" ) ) { + _pico_parse_int( p, &faces[index].subMaterialId ); + } } + faces[index].materialId = 0; + faces[index].indices[0] = indexes[2]; + faces[index].indices[1] = indexes[1]; + faces[index].indices[2] = indexes[0]; } /* model texture vertex */ else if ( !_pico_stricmp( p->token,"*mesh_tvert" ) ) { - picoVec2_t uv; int index; - /* we must have a valid surface */ - if ( surface == NULL ) { - continue; + if ( numVertices == 0 ) { + _ase_error_return( "Texture Vertex parse error" ); } /* get uv vertex index */ - if ( !_pico_parse_int( p,&index ) ) { - _ase_error_return( "UV vertex parse error" ); + if ( !_pico_parse_int( p,&index ) || index >= numTextureVertices ) { + _ase_error_return( "Texture vertex parse error" ); } /* get uv vertex s */ - if ( !_pico_parse_float( p,&uv[0] ) ) { - _ase_error_return( "UV vertex parse error" ); + if ( !_pico_parse_float( p,&texcoords[index].texcoord[0] ) ) { + _ase_error_return( "Texture vertex parse error" ); } /* get uv vertex t */ - if ( !_pico_parse_float( p,&uv[1] ) ) { - _ase_error_return( "UV vertex parse error" ); + if ( !_pico_parse_float( p,&texcoords[index].texcoord[1] ) ) { + _ase_error_return( "Texture vertex parse error" ); } /* ydnar: invert t */ - uv[ 1 ] = 1.0f - uv[ 1 ]; - - /* set texture vertex */ - PicoSetSurfaceST( surface,0,index,uv ); + texcoords[index].texcoord[ 1 ] = 1.0f - texcoords[index].texcoord[ 1 ]; } /* ydnar: model mesh texture face */ else if ( !_pico_stricmp( p->token, "*mesh_tface" ) ) { picoIndex_t indexes[3]; int index; - aseFace_t* face; - /* we must have a valid surface */ - if ( surface == NULL ) { - continue; + if ( numFaces == 0 ) { + _ase_error_return( "Texture face parse error" ); } /* get face index */ @@ -617,65 +773,52 @@ static picoModel_t *_ase_load( PM_PARAMS_LOAD ){ _ase_error_return( "Texture face parse error" ); } - /* set face indexes (note interleaved offset!) */ - PicoSetSurfaceIndex( surface, ( index * 9 + 3 ), indexes[2] ); - PicoSetSurfaceIndex( surface, ( index * 9 + 4 ), indexes[1] ); - PicoSetSurfaceIndex( surface, ( index * 9 + 5 ), indexes[0] ); - - face = _ase_get_face_for_index( faces,index ); - face->index[3] = indexes[2]; - face->index[4] = indexes[1]; - face->index[5] = indexes[0]; + faces[index].indices[3] = indexes[2]; + faces[index].indices[4] = indexes[1]; + faces[index].indices[5] = indexes[0]; } /* model color vertex */ else if ( !_pico_stricmp( p->token,"*mesh_vertcol" ) ) { - picoColor_t color; int index; float colorInput; - /* we must have a valid surface */ - if ( surface == NULL ) { - continue; + if ( numVertices == 0 ) { + _ase_error_return( "Color Vertex parse error" ); } /* get color vertex index */ if ( !_pico_parse_int( p,&index ) ) { - _ase_error_return( "UV vertex parse error" ); + _ase_error_return( "Color vertex parse error" ); } /* get R component */ if ( !_pico_parse_float( p,&colorInput ) ) { - _ase_error_return( "color vertex parse error" ); + _ase_error_return( "Color vertex parse error" ); } - color[0] = (picoByte_t)( colorInput * 255 ); + colors[index].color[0] = (picoByte_t)( colorInput * 255 ); /* get G component */ if ( !_pico_parse_float( p,&colorInput ) ) { - _ase_error_return( "color vertex parse error" ); + _ase_error_return( "Color vertex parse error" ); } - color[1] = (picoByte_t)( colorInput * 255 ); + colors[index].color[1] = (picoByte_t)( colorInput * 255 ); /* get B component */ if ( !_pico_parse_float( p,&colorInput ) ) { - _ase_error_return( "color vertex parse error" ); + _ase_error_return( "Color vertex parse error" ); } - color[2] = (picoByte_t)( colorInput * 255 ); + colors[index].color[2] = (picoByte_t)( colorInput * 255 ); /* leave alpha alone since we don't get any data from the ASE format */ - color[3] = 255; - - /* set texture vertex */ - PicoSetSurfaceColor( surface,0,index,color ); + colors[index].color[3] = 255; } /* model color face */ else if ( !_pico_stricmp( p->token,"*mesh_cface" ) ) { picoIndex_t indexes[3]; int index; - aseFace_t* face; - /* we must have a valid surface */ - if ( surface == NULL ) { - continue; + if ( numFaces == 0 ) { + _ase_error_return( "Face parse error" ); } /* get face index */ @@ -701,20 +844,14 @@ static picoModel_t *_ase_load( PM_PARAMS_LOAD ){ _ase_error_return( "Face parse error" ); } - /* set face indexes (note interleaved offset!) */ - PicoSetSurfaceIndex( surface, ( index * 9 + 6 ), indexes[2] ); - PicoSetSurfaceIndex( surface, ( index * 9 + 7 ), indexes[1] ); - PicoSetSurfaceIndex( surface, ( index * 9 + 8 ), indexes[0] ); - - face = _ase_get_face_for_index( faces,index ); - face->index[6] = indexes[2]; - face->index[7] = indexes[1]; - face->index[8] = indexes[0]; + faces[index].indices[6] = indexes[2]; + faces[index].indices[7] = indexes[1]; + faces[index].indices[8] = indexes[0]; } /* model material */ else if ( !_pico_stricmp( p->token, "*material" ) ) { aseSubMaterial_t* subMaterial = NULL; - picoShader_t *shader = NULL; + picoShader_t *shader; int level = 1, index; char materialName[ 1024 ]; float transValue = 0.0f, shineValue = 1.0f; @@ -755,6 +892,8 @@ static picoModel_t *_ase_load( PM_PARAMS_LOAD ){ if ( level == subMaterialLevel ) { /* set material name */ + _pico_first_token( materialName ); + shadername_convert( materialName ); PicoSetShaderName( shader, materialName ); /* set shader's transparency */ @@ -944,6 +1083,7 @@ static picoModel_t *_ase_load( PM_PARAMS_LOAD ){ } /* set material name */ + shadername_convert( materialName ); PicoSetShaderName( shader,materialName ); /* set shader's transparency */ @@ -967,6 +1107,34 @@ static picoModel_t *_ase_load( PM_PARAMS_LOAD ){ /* set material map name */ PicoSetShaderMapName( shader, mapname ); + /* extract shadername from bitmap path */ + if ( mapname != NULL ) { + char* p = mapname; + + /* convert to shader-name format */ + shadername_convert( mapname ); + { + /* remove extension */ + char* last_period = strrchr( p, '.' ); + if ( last_period != NULL ) { + *last_period = '\0'; + } + } + + /* find shader path */ + for (; *p != '\0'; ++p ) + { + if ( _pico_strnicmp( p, "models/", 7 ) == 0 || _pico_strnicmp( p, "textures/", 9 ) == 0 ) { + break; + } + } + + if ( *p != '\0' ) { + /* set material name */ + PicoSetShaderName( shader,p ); + } + } + /* this is just a material with 1 submaterial */ subMaterial = _ase_add_submaterial( &materials, index, 0, shader ); } @@ -982,9 +1150,11 @@ static picoModel_t *_ase_load( PM_PARAMS_LOAD ){ } /* ydnar: finish existing surface */ -// _ase_make_surface( model, &surface ); - _ase_submit_triangles( surface, model,materials,faces ); - _ase_free_faces( &faces,&facesTail ); + _ase_submit_triangles( model, materials, vertices, texcoords, colors, faces, numFaces ); + _pico_free( faces ); + _pico_free( vertices ); + _pico_free( texcoords ); + _pico_free( colors ); #ifdef DEBUG_PM_ASE _ase_print_materials( materials ); @@ -995,6 +1165,8 @@ static picoModel_t *_ase_load( PM_PARAMS_LOAD ){ _ase_free_materials( &materials ); + _pico_free_parser( p ); + /* return allocated pico model */ return model; } diff --git a/libs/picomodel/pm_fm.c b/libs/picomodel/pm_fm.c index a4251337..9201e27e 100644 --- a/libs/picomodel/pm_fm.c +++ b/libs/picomodel/pm_fm.c @@ -350,7 +350,7 @@ static picoModel_t *_fm_load( PM_PARAMS_LOAD ){ texCoord->t = _pico_little_short( texCoord[i].t ); } // set Skin Name - strncpy( skinname, (char *) fm.fm_skin, FM_SKINPATHSIZE ); + strncpy( skinname, (unsigned char *) fm.fm_skin, FM_SKINPATHSIZE ); #ifdef FM_VERBOSE_DBG // Print out md2 values diff --git a/libs/picomodel/pm_lwo.c b/libs/picomodel/pm_lwo.c index f1d82dd9..5b4f2d60 100644 --- a/libs/picomodel/pm_lwo.c +++ b/libs/picomodel/pm_lwo.c @@ -103,7 +103,7 @@ static picoModel_t *_lwo_load( PM_PARAMS_LOAD ){ lwPolygon *pol; lwPolVert *v; lwVMapPt *vm; - char name[ 64 ]; + char name[ 256 ]; int i, j, k, numverts; picoModel_t *picoModel; @@ -235,6 +235,7 @@ static picoModel_t *_lwo_load( PM_PARAMS_LOAD ){ /* detox and set shader name */ strncpy( name, surface->name, sizeof( name ) ); + _pico_first_token( name ); _pico_setfext( name, "" ); _pico_unixify( name ); PicoSetShaderName( picoShader, name ); @@ -282,9 +283,22 @@ static picoModel_t *_lwo_load( PM_PARAMS_LOAD ){ xyz[ 1 ] = pt->pos[ 2 ]; xyz[ 2 ] = pt->pos[ 1 ]; +///* doom3 lwo data doesn't seem to have smoothing-angle information */ +//#if 0 +// if ( surface->smooth <= 0 ) { +// /* use face normals */ normal[ 0 ] = v->norm[ 0 ]; normal[ 1 ] = v->norm[ 2 ]; normal[ 2 ] = v->norm[ 1 ]; + } + else +//#endif + { + /* smooth normals later */ + normal[ 0 ] = 0; + normal[ 1 ] = 0; + normal[ 2 ] = 0; + } st[ 0 ] = xyz[ defaultSTAxis[ 0 ] ] * defaultXYZtoSTScale[ 0 ]; st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ]; diff --git a/libs/picomodel/pm_obj.c b/libs/picomodel/pm_obj.c index 2f6e93c4..a8b1dfed 100644 --- a/libs/picomodel/pm_obj.c +++ b/libs/picomodel/pm_obj.c @@ -241,7 +241,7 @@ static int _obj_mtl_load( picoModel_t *model ){ return 0; \ } /* alloc copy of model file name */ - fileName = _pico_clone_alloc( model->fileName,-1 ); + fileName = _pico_clone_alloc( model->fileName ); if ( fileName == NULL ) { return 0; } diff --git a/libs/picomodel/pm_terrain.c b/libs/picomodel/pm_terrain.c new file mode 100644 index 00000000..d0d54653 --- /dev/null +++ b/libs/picomodel/pm_terrain.c @@ -0,0 +1,607 @@ +/* ----------------------------------------------------------------------------- + + PicoModel Library + + Copyright (c) 2003, Randy Reddig & seaw0lf + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + Neither the names of the copyright holders nor the names of its contributors may + be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ----------------------------------------------------------------------------- */ + + + +/* marker */ +#define PM_TERRAIN_C + + + +/* dependencies */ +#include "picointernal.h" + + + +typedef struct tga_s +{ + unsigned char id_length, colormap_type, image_type; + unsigned short colormap_index, colormap_length; + unsigned char colormap_size; + unsigned short x_origin, y_origin, width, height; + unsigned char pixel_size, attributes; +} +tga_t; + + + +/* + _terrain_load_tga_buffer() + loads a tga image into a newly allocated image buffer + fixme: replace/clean this function + */ + +void _terrain_load_tga_buffer( unsigned char *buffer, unsigned char **pic, int *width, int *height ) { + int row, column; + int columns, rows, numPixels; + unsigned char *pixbuf; + unsigned char *buf_p; + tga_t targa_header; + unsigned char *targa_rgba; + + + *pic = NULL; + + if ( buffer == NULL ) { + return; + } + + buf_p = buffer; + + targa_header.id_length = *buf_p++; + targa_header.colormap_type = *buf_p++; + targa_header.image_type = *buf_p++; + + targa_header.colormap_index = _pico_little_short( *(short*)buf_p ); + buf_p += 2; + targa_header.colormap_length = _pico_little_short( *(short*) buf_p ); + buf_p += 2; + targa_header.colormap_size = *buf_p++; + targa_header.x_origin = _pico_little_short( *(short*) buf_p ); + buf_p += 2; + targa_header.y_origin = _pico_little_short( *(short*) buf_p ); + buf_p += 2; + targa_header.width = _pico_little_short( *(short*) buf_p ); + buf_p += 2; + targa_header.height = _pico_little_short( *(short*) buf_p ); + buf_p += 2; + targa_header.pixel_size = *buf_p++; + targa_header.attributes = *buf_p++; + + if ( targa_header.image_type != 2 && targa_header.image_type != 10 && targa_header.image_type != 3 ) { + _pico_printf( PICO_ERROR, "Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n" ); + pic = NULL; + return; + } + + if ( targa_header.colormap_type != 0 ) { + _pico_printf( PICO_ERROR, "Indexed color TGA images not supported\n" ); + return; + } + + if ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 && targa_header.image_type != 3 ) { + _pico_printf( PICO_ERROR, "Only 32 or 24 bit TGA images supported (not indexed color)\n" ); + pic = NULL; + return; + } + + columns = targa_header.width; + rows = targa_header.height; + numPixels = columns * rows; + + if ( width ) { + *width = columns; + } + if ( height ) { + *height = rows; + } + + targa_rgba = _pico_alloc( numPixels * 4 ); + *pic = targa_rgba; + + if ( targa_header.id_length != 0 ) { + buf_p += targa_header.id_length; // skip TARGA image comment + + } + if ( targa_header.image_type == 2 || targa_header.image_type == 3 ) { + // Uncompressed RGB or gray scale image + for ( row = rows - 1; row >= 0; row-- ) + { + pixbuf = targa_rgba + row * columns * 4; + for ( column = 0; column < columns; column++ ) + { + unsigned char red,green,blue,alphabyte; + switch ( targa_header.pixel_size ) + { + + case 8: + blue = *buf_p++; + green = blue; + red = blue; + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = 255; + break; + + case 24: + blue = *buf_p++; + green = *buf_p++; + red = *buf_p++; + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = 255; + break; + case 32: + blue = *buf_p++; + green = *buf_p++; + red = *buf_p++; + alphabyte = *buf_p++; + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = alphabyte; + break; + default: + break; + } + } + } + } + + /* rle encoded pixels */ + else if ( targa_header.image_type == 10 ) { + unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j; + + red = 0; + green = 0; + blue = 0; + alphabyte = 0xff; + + for ( row = rows - 1; row >= 0; row-- ) { + pixbuf = targa_rgba + row * columns * 4; + for ( column = 0; column < columns; ) { + packetHeader = *buf_p++; + packetSize = 1 + ( packetHeader & 0x7f ); + if ( packetHeader & 0x80 ) { // run-length packet + switch ( targa_header.pixel_size ) { + case 24: + blue = *buf_p++; + green = *buf_p++; + red = *buf_p++; + alphabyte = 255; + break; + case 32: + blue = *buf_p++; + green = *buf_p++; + red = *buf_p++; + alphabyte = *buf_p++; + break; + default: + //Error("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name ); + break; + } + + for ( j = 0; j < packetSize; j++ ) { + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = alphabyte; + column++; + if ( column == columns ) { // run spans across rows + column = 0; + if ( row > 0 ) { + row--; + } + else{ + goto breakOut; + } + pixbuf = targa_rgba + row * columns * 4; + } + } + } + else { // non run-length packet + for ( j = 0; j < packetSize; j++ ) { + switch ( targa_header.pixel_size ) { + case 24: + blue = *buf_p++; + green = *buf_p++; + red = *buf_p++; + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = 255; + break; + case 32: + blue = *buf_p++; + green = *buf_p++; + red = *buf_p++; + alphabyte = *buf_p++; + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = alphabyte; + break; + default: + //Sysprintf("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name ); + break; + } + column++; + if ( column == columns ) { // pixel packet run spans across rows + column = 0; + if ( row > 0 ) { + row--; + } + else{ + goto breakOut; + } + pixbuf = targa_rgba + row * columns * 4; + } + } + } + } +breakOut:; + } + } + + /* fix vertically flipped image */ + if ( ( targa_header.attributes & ( 1 << 5 ) ) ) { + int flip; + for ( row = 0; row < .5f * rows; row++ ) + { + for ( column = 0; column < columns; column++ ) + { + flip = *( (int*)targa_rgba + row * columns + column ); + *( (int*)targa_rgba + row * columns + column ) = *( (int*)targa_rgba + ( ( rows - 1 ) - row ) * columns + column ); + *( (int*)targa_rgba + ( ( rows - 1 ) - row ) * columns + column ) = flip; + } + } + } +} + + + +/* + _terrain_canload() + validates a picoterrain file + */ + +static int _terrain_canload( PM_PARAMS_CANLOAD ) { + picoParser_t *p; + + + /* keep the friggin compiler happy */ + *fileName = *fileName; + + /* create pico parser */ + p = _pico_new_parser( (picoByte_t*) buffer, bufSize ); + if ( p == NULL ) { + return PICO_PMV_ERROR_MEMORY; + } + + /* get first token */ + if ( _pico_parse_first( p ) == NULL ) { + return PICO_PMV_ERROR_IDENT; + } + + /* check first token */ + if ( _pico_stricmp( p->token, "picoterrain" ) ) { + _pico_free_parser( p ); + return PICO_PMV_ERROR_IDENT; + } + + /* free the pico parser object */ + _pico_free_parser( p ); + + /* file seems to be a valid picoterrain file */ + return PICO_PMV_OK; +} + + + +/* + _terrain_load() + loads a picoterrain file + */ + +static picoModel_t *_terrain_load( PM_PARAMS_LOAD ) { + int i, j, v, pw[ 5 ], r; + picoParser_t *p; + + char *shader, *heightmapFile, *colormapFile; + picoVec3_t scale, origin; + + unsigned char *imageBuffer; + int imageBufSize, w, h, cw, ch; + unsigned char *heightmap, *colormap, *heightPixel, *colorPixel; + + picoModel_t *picoModel; + picoSurface_t *picoSurface; + picoShader_t *picoShader; + picoVec3_t xyz, normal; + picoVec2_t st; + picoColor_t color; + + + /* keep the friggin compiler happy */ + *fileName = *fileName; + + /* create pico parser */ + p = _pico_new_parser( (picoByte_t*) buffer, bufSize ); + if ( p == NULL ) { + return NULL; + } + + /* get first token */ + if ( _pico_parse_first( p ) == NULL ) { + return NULL; + } + + /* check first token */ + if ( _pico_stricmp( p->token, "picoterrain" ) ) { + _pico_printf( PICO_ERROR, "Invalid PicoTerrain model" ); + _pico_free_parser( p ); + return NULL; + } + + /* setup */ + shader = heightmapFile = colormapFile = NULL; + _pico_set_vec( scale, 512, 512, 32 ); + + /* parse ase model file */ + while ( 1 ) + { + /* get first token on line */ + if ( !_pico_parse_first( p ) ) { + break; + } + + /* skip empty lines */ + if ( !p->token || !p->token[ 0 ] ) { + continue; + } + + /* shader */ + if ( !_pico_stricmp( p->token, "shader" ) ) { + if ( _pico_parse( p, 0 ) && p->token[ 0 ] ) { + if ( shader != NULL ) { + _pico_free( shader ); + } + shader = _pico_clone_alloc( p->token ); + } + } + + /* heightmap */ + else if ( !_pico_stricmp( p->token, "heightmap" ) ) { + if ( _pico_parse( p, 0 ) && p->token[ 0 ] ) { + if ( heightmapFile != NULL ) { + _pico_free( heightmapFile ); + } + heightmapFile = _pico_clone_alloc( p->token ); + } + } + + /* colormap */ + else if ( !_pico_stricmp( p->token, "colormap" ) ) { + if ( _pico_parse( p, 0 ) && p->token[ 0 ] ) { + if ( colormapFile != NULL ) { + _pico_free( colormapFile ); + } + colormapFile = _pico_clone_alloc( p->token ); + } + } + + /* scale */ + else if ( !_pico_stricmp( p->token, "scale" ) ) { + _pico_parse_vec( p, scale ); + } + + /* skip unparsed rest of line and continue */ + _pico_parse_skip_rest( p ); + } + + /* ----------------------------------------------------------------- */ + + /* load heightmap */ + heightmap = imageBuffer = NULL; + _pico_load_file( heightmapFile, &imageBuffer, &imageBufSize ); + _terrain_load_tga_buffer( imageBuffer, &heightmap, &w, &h ); + _pico_free( heightmapFile ); + _pico_free_file( imageBuffer ); + + if ( heightmap == NULL || w < 2 || h < 2 ) { + _pico_printf( PICO_ERROR, "PicoTerrain model with invalid heightmap" ); + if ( shader != NULL ) { + _pico_free( shader ); + } + if ( colormapFile != NULL ) { + _pico_free( colormapFile ); + } + _pico_free_parser( p ); + return NULL; + } + + /* set origin (bottom lowest corner of terrain mesh) */ + _pico_set_vec( origin, ( w / -2 ) * scale[ 0 ], ( h / -2 ) * scale[ 1 ], -128 * scale[ 2 ] ); + + /* load colormap */ + colormap = imageBuffer = NULL; + _pico_load_file( colormapFile, &imageBuffer, &imageBufSize ); + _terrain_load_tga_buffer( imageBuffer, &colormap, &cw, &ch ); + _pico_free( colormapFile ); + _pico_free_file( imageBuffer ); + + if ( cw != w || ch != h ) { + _pico_printf( PICO_WARNING, "PicoTerrain colormap/heightmap size mismatch" ); + _pico_free( colormap ); + colormap = NULL; + } + + /* ----------------------------------------------------------------- */ + + /* create new pico model */ + picoModel = PicoNewModel(); + if ( picoModel == NULL ) { + _pico_printf( PICO_ERROR, "Unable to allocate a new model" ); + return NULL; + } + + /* do model setup */ + PicoSetModelFrameNum( picoModel, frameNum ); + PicoSetModelNumFrames( picoModel, 1 ); /* sea */ + PicoSetModelName( picoModel, fileName ); + PicoSetModelFileName( picoModel, fileName ); + + /* allocate new pico surface */ + picoSurface = PicoNewSurface( picoModel ); + if ( picoSurface == NULL ) { + _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" ); + PicoFreeModel( picoModel ); /* sea */ + return NULL; + } + + /* terrain surfaces are triangle meshes */ + PicoSetSurfaceType( picoSurface, PICO_TRIANGLES ); + + /* set surface name */ + PicoSetSurfaceName( picoSurface, "picoterrain" ); + + /* create new pico shader */ + picoShader = PicoNewShader( picoModel ); + if ( picoShader == NULL ) { + _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" ); + PicoFreeModel( picoModel ); + _pico_free( shader ); + return NULL; + } + + /* detox and set shader name */ + _pico_setfext( shader, "" ); + _pico_unixify( shader ); + PicoSetShaderName( picoShader, shader ); + _pico_free( shader ); + + /* associate current surface with newly created shader */ + PicoSetSurfaceShader( picoSurface, picoShader ); + + /* make bogus normal */ + _pico_set_vec( normal, 0.0f, 0.0f, 0.0f ); + + /* create mesh */ + for ( j = 0; j < h; j++ ) + { + for ( i = 0; i < w; i++ ) + { + /* get pointers */ + v = i + ( j * w ); + heightPixel = heightmap + v * 4; + colorPixel = colormap + ? colormap + v * 4 + : NULL; + + /* set xyz */ + _pico_set_vec( xyz, origin[ 0 ] + scale[ 0 ] * i, + origin[ 1 ] + scale[ 1 ] * j, + origin[ 2 ] + scale[ 2 ] * heightPixel[ 0 ] ); + PicoSetSurfaceXYZ( picoSurface, v, xyz ); + + /* set normal */ + PicoSetSurfaceNormal( picoSurface, v, normal ); + + /* set st */ + st[ 0 ] = (float) i; + st[ 1 ] = (float) j; + PicoSetSurfaceST( picoSurface, 0, v, st ); + + /* set color */ + if ( colorPixel != NULL ) { + _pico_set_color( color, colorPixel[ 0 ], colorPixel[ 1 ], colorPixel[ 2 ], colorPixel[ 3 ] ); + } + else{ + _pico_set_color( color, 255, 255, 255, 255 ); + } + PicoSetSurfaceColor( picoSurface, 0, v, color ); + + /* set triangles (zero alpha in heightmap suppresses this quad) */ + if ( i < ( w - 1 ) && j < ( h - 1 ) && heightPixel[ 3 ] >= 128 ) { + /* set indexes */ + pw[ 0 ] = i + ( j * w ); + pw[ 1 ] = i + ( ( j + 1 ) * w ); + pw[ 2 ] = i + 1 + ( ( j + 1 ) * w ); + pw[ 3 ] = i + 1 + ( j * w ); + pw[ 4 ] = i + ( j * w ); /* same as pw[ 0 ] */ + + /* set radix */ + r = ( i + j ) & 1; + + /* make first triangle */ + PicoSetSurfaceIndex( picoSurface, ( v * 6 + 0 ), (picoIndex_t) pw[ r + 0 ] ); + PicoSetSurfaceIndex( picoSurface, ( v * 6 + 1 ), (picoIndex_t) pw[ r + 1 ] ); + PicoSetSurfaceIndex( picoSurface, ( v * 6 + 2 ), (picoIndex_t) pw[ r + 2 ] ); + + /* make second triangle */ + PicoSetSurfaceIndex( picoSurface, ( v * 6 + 3 ), (picoIndex_t) pw[ r + 0 ] ); + PicoSetSurfaceIndex( picoSurface, ( v * 6 + 4 ), (picoIndex_t) pw[ r + 2 ] ); + PicoSetSurfaceIndex( picoSurface, ( v * 6 + 5 ), (picoIndex_t) pw[ r + 3 ] ); + } + } + } + + /* free stuff */ + _pico_free_parser( p ); + _pico_free( heightmap ); + _pico_free( colormap ); + + /* return the new pico model */ + return picoModel; +} + + + +/* pico file format module definition */ +const picoModule_t picoModuleTerrain = +{ + "1.3", /* module version string */ + "PicoTerrain", /* module display name */ + "Randy Reddig", /* author's name */ + "2003 Randy Reddig", /* module copyright */ + { + "picoterrain", NULL, NULL, NULL /* default extensions to use */ + }, + _terrain_canload, /* validation routine */ + _terrain_load, /* load routine */ + NULL, /* save validation routine */ + NULL /* save routine */ +};