From 14c2c858b996d1d746d061697bdc6e3b58461e14 Mon Sep 17 00:00:00 2001 From: Christophe Mateos Date: Fri, 9 Dec 2016 02:57:39 +0100 Subject: [PATCH] PicoModel - End of 1.5 backporting lwo subfolder already done by @jdolan: https://github.com/TTimo/GtkRadiant/commit/a269593afe5fe37b3522a27bfe8eba1717f6db7b#diff-584b574ac260b5150f42f60544e9266e --- libs/picomodel/lwo/clip.c | 29 ++ libs/picomodel/picointernal.c | 31 +- libs/picomodel/picomodel.c | 21 +- libs/picomodel/picomodules.c | 4 +- libs/picomodel/pm_3ds.c | 15 +- libs/picomodel/pm_ase.c | 119 +++++++ libs/picomodel/pm_fm.c | 2 +- libs/picomodel/pm_lwo.c | 16 +- libs/picomodel/pm_terrain.c | 607 ++++++++++++++++++++++++++++++++++ 9 files changed, 803 insertions(+), 41 deletions(-) create mode 100644 libs/picomodel/pm_terrain.c 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 e98b4531..c6f396ce 100644 --- a/libs/picomodel/picointernal.c +++ b/libs/picomodel/picointernal.c @@ -161,18 +161,19 @@ 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(const char *str) -{ +char *_pico_clone_alloc( const char *str ) { char* cloned; /* sanity check */ - if (str == NULL) + if ( str == NULL ) { return NULL; + } /* allocate memory */ cloned = _pico_alloc(strlen(str) + 1); - if (cloned == NULL) + if ( cloned == NULL ) { return NULL; + } /* copy input string to cloned string */ strcpy(cloned, str); @@ -266,10 +267,10 @@ void _pico_printf( int level, const char *format, ... ){ * trims everything after the first whitespace-delimited token */ -void _pico_first_token(char *str) -{ - if (!str || !*str) +void _pico_first_token( char *str ) { + if ( !str || ! * str ) { return; + } while (*str && !isspace(*str)) *str++; *str = '\0'; @@ -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/picomodel.c b/libs/picomodel/picomodel.c index 29e810a5..b8f185cd 100644 --- a/libs/picomodel/picomodel.c +++ b/libs/picomodel/picomodel.c @@ -251,8 +251,7 @@ picoModel_t *PicoLoadModel( char *fileName, int frameNum ){ FIXME: From 1.5; Unused yet */ -picoModel_t *PicoModuleLoadModelStream( const picoModule_t* module, void* inputStream, PicoInputStreamReadFunc inputStreamRead, size_t streamLength, int frameNum ) -{ +picoModel_t *PicoModuleLoadModelStream( const picoModule_t* module, void* inputStream, PicoInputStreamReadFunc inputStreamRead, size_t streamLength, int frameNum ) { picoModel_t *model; picoByte_t *buffer; int bufSize; @@ -260,14 +259,12 @@ picoModel_t *PicoModuleLoadModelStream( const picoModule_t* module, void* inputS /* init */ model = NULL; - if ( inputStream == NULL ) - { + if ( inputStream == NULL ) { _pico_printf( PICO_ERROR, "PicoLoadModel: invalid input stream (inputStream == NULL)" ); return NULL; } - if ( inputStreamRead == NULL ) - { + if ( inputStreamRead == NULL ) { _pico_printf( PICO_ERROR, "PicoLoadModel: invalid input stream (inputStreamRead == NULL) "); return NULL; } @@ -328,7 +325,6 @@ picoModel_t *PicoNewModel( void ){ } - /* PicoFreeModel() frees a model and all associated data @@ -366,7 +362,6 @@ void PicoFreeModel( picoModel_t *model ){ } - /* PicoAdjustModel() adjusts a models's memory allocations to handle the requested sizes. @@ -421,7 +416,6 @@ int PicoAdjustModel( picoModel_t *model, int numShaders, int numSurfaces ){ } - /* ---------------------------------------------------------------------------- shaders ---------------------------------------------------------------------------- */ @@ -434,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 ) { @@ -467,7 +460,6 @@ picoShader_t *PicoNewShader( picoModel_t *model ){ } - /* PicoFreeShader() frees a shader and all associated data -sea @@ -492,7 +484,6 @@ void PicoFreeShader( picoShader_t *shader ){ } - /* PicoFindShader() finds a named shader in a model @@ -531,7 +522,6 @@ picoShader_t *PicoFindShader( picoModel_t *model, char *name, int caseSensitive } - /* ---------------------------------------------------------------------------- surfaces ---------------------------------------------------------------------------- */ @@ -574,7 +564,6 @@ picoSurface_t *PicoNewSurface( picoModel_t *model ){ } - /* PicoFreeSurface() frees a surface and all associated data @@ -582,7 +571,6 @@ picoSurface_t *PicoNewSurface( picoModel_t *model ){ void PicoFreeSurface( picoSurface_t *surface ){ int i; - /* dummy check */ if ( surface == NULL ) { return; @@ -612,7 +600,6 @@ void PicoFreeSurface( picoSurface_t *surface ){ } - /* PicoAdjustSurface() adjusts a surface's memory allocations to handle the requested sizes. @@ -622,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; @@ -2169,7 +2155,6 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){ picoShader_t *shader; char *materialName; - /* get material name */ if ( _pico_parse( p, 1 ) == NULL ) { break; 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 194f7381..67f40d41 100644 --- a/libs/picomodel/pm_ase.c +++ b/libs/picomodel/pm_ase.c @@ -305,6 +305,125 @@ picoSurface_t* PicoModelFindOrAddSurface( picoModel_t *model, picoShader_t* shad indexes 6 7 8 = color indexes (new) */ +#if 0 +typedef picoIndex_t* picoIndexIter_t; + +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 */ + aseSubMaterial_t* subMtl = _ase_get_submaterial_or_default( materials, ( *i ).materialId, ( *i ).subMaterialId ); + if ( subMtl == NULL ) { + return; + } + + { + 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++ ) + { + 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 ) 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_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 */ +};