PicoModel - End of 1.5 backporting

lwo subfolder already done by @jdolan:
a269593afe (diff-584b574ac260b5150f42f60544e9266e)
This commit is contained in:
Christophe Mateos 2016-12-09 02:57:39 +01:00
parent 054cfcd15c
commit 14c2c858b9
9 changed files with 803 additions and 41 deletions

View file

@ -22,6 +22,35 @@ void lwFreeClip( lwClip *clip ){
if ( clip ) { if ( clip ) {
lwListFree( (void*) clip->ifilter, (ListFreeFunc) lwFreePlugin ); lwListFree( (void*) clip->ifilter, (ListFreeFunc) lwFreePlugin );
lwListFree( (void*) clip->pfilter, (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 ); _pico_free( clip );
} }
} }

View file

@ -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 * as custom clone size (the string is cropped to fit into mem
* if needed). -sea * if needed). -sea
*/ */
char *_pico_clone_alloc(const char *str) char *_pico_clone_alloc( const char *str ) {
{
char* cloned; char* cloned;
/* sanity check */ /* sanity check */
if (str == NULL) if ( str == NULL ) {
return NULL; return NULL;
}
/* allocate memory */ /* allocate memory */
cloned = _pico_alloc(strlen(str) + 1); cloned = _pico_alloc(strlen(str) + 1);
if (cloned == NULL) if ( cloned == NULL ) {
return NULL; return NULL;
}
/* copy input string to cloned string */ /* copy input string to cloned string */
strcpy(cloned, str); strcpy(cloned, str);
@ -266,10 +267,10 @@ void _pico_printf( int level, const char *format, ... ){
* trims everything after the first whitespace-delimited token * trims everything after the first whitespace-delimited token
*/ */
void _pico_first_token(char *str) void _pico_first_token( char *str ) {
{ if ( !str || ! * str ) {
if (!str || !*str)
return; return;
}
while (*str && !isspace(*str)) while (*str && !isspace(*str))
*str++; *str++;
*str = '\0'; *str = '\0';
@ -542,7 +543,7 @@ float _pico_big_float( float src ){
* case-insensitive strstr. -sea * case-insensitive strstr. -sea
*/ */
char *_pico_stristr( char *str, const char *substr ){ char *_pico_stristr( char *str, const char *substr ){
const int sublen = strlen( substr ); const size_t sublen = strlen( substr );
while ( *str ) while ( *str )
{ {
if ( !_pico_strnicmp( str,substr,sublen ) ) { 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 * returns ptr to filename portion in given path or an empty
* string otherwise. given 'path' is not altered. -sea * string otherwise. given 'path' is not altered. -sea
*/ */
char *_pico_nopath( const char *path ){ const char *_pico_nopath( const char *path ){
char *src; const char *src;
src = (char *)path + ( strlen( path ) - 1 );
if ( path == NULL ) { if ( path == NULL ) {
return (char *)""; return "";
} }
if ( !strchr( (char *)path,'/' ) && !strchr( (char *)path,'\\' ) ) { if ( !strchr( path,'/' ) && !strchr( path,'\\' ) ) {
return ( (char *)path ); return ( path );
} }
src = path + ( strlen( path ) - 1 );
while ( ( src-- ) != path ) while ( ( src-- ) != path )
{ {
if ( *src == '/' || *src == '\\' ) { if ( *src == '/' || *src == '\\' ) {
return ( ++src ); return ( ++src );
} }
} }
return (char *)""; return "";
} }
/* _pico_setfext: /* _pico_setfext:

View file

@ -251,8 +251,7 @@ picoModel_t *PicoLoadModel( char *fileName, int frameNum ){
FIXME: From 1.5; Unused yet 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; picoModel_t *model;
picoByte_t *buffer; picoByte_t *buffer;
int bufSize; int bufSize;
@ -260,14 +259,12 @@ picoModel_t *PicoModuleLoadModelStream( const picoModule_t* module, void* inputS
/* init */ /* init */
model = NULL; model = NULL;
if ( inputStream == NULL ) if ( inputStream == NULL ) {
{
_pico_printf( PICO_ERROR, "PicoLoadModel: invalid input stream (inputStream == NULL)" ); _pico_printf( PICO_ERROR, "PicoLoadModel: invalid input stream (inputStream == NULL)" );
return NULL; return NULL;
} }
if ( inputStreamRead == NULL ) if ( inputStreamRead == NULL ) {
{
_pico_printf( PICO_ERROR, "PicoLoadModel: invalid input stream (inputStreamRead == NULL) "); _pico_printf( PICO_ERROR, "PicoLoadModel: invalid input stream (inputStreamRead == NULL) ");
return NULL; return NULL;
} }
@ -328,7 +325,6 @@ picoModel_t *PicoNewModel( void ){
} }
/* /*
PicoFreeModel() PicoFreeModel()
frees a model and all associated data frees a model and all associated data
@ -366,7 +362,6 @@ void PicoFreeModel( picoModel_t *model ){
} }
/* /*
PicoAdjustModel() PicoAdjustModel()
adjusts a models's memory allocations to handle the requested sizes. 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 shaders
---------------------------------------------------------------------------- */ ---------------------------------------------------------------------------- */
@ -434,7 +428,6 @@ int PicoAdjustModel( picoModel_t *model, int numShaders, int numSurfaces ){
picoShader_t *PicoNewShader( picoModel_t *model ){ picoShader_t *PicoNewShader( picoModel_t *model ){
picoShader_t *shader; picoShader_t *shader;
/* allocate and clear */ /* allocate and clear */
shader = _pico_alloc( sizeof( picoShader_t ) ); shader = _pico_alloc( sizeof( picoShader_t ) );
if ( shader == NULL ) { if ( shader == NULL ) {
@ -467,7 +460,6 @@ picoShader_t *PicoNewShader( picoModel_t *model ){
} }
/* /*
PicoFreeShader() PicoFreeShader()
frees a shader and all associated data -sea frees a shader and all associated data -sea
@ -492,7 +484,6 @@ void PicoFreeShader( picoShader_t *shader ){
} }
/* /*
PicoFindShader() PicoFindShader()
finds a named shader in a model finds a named shader in a model
@ -531,7 +522,6 @@ picoShader_t *PicoFindShader( picoModel_t *model, char *name, int caseSensitive
} }
/* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------
surfaces surfaces
---------------------------------------------------------------------------- */ ---------------------------------------------------------------------------- */
@ -574,7 +564,6 @@ picoSurface_t *PicoNewSurface( picoModel_t *model ){
} }
/* /*
PicoFreeSurface() PicoFreeSurface()
frees a surface and all associated data frees a surface and all associated data
@ -582,7 +571,6 @@ picoSurface_t *PicoNewSurface( picoModel_t *model ){
void PicoFreeSurface( picoSurface_t *surface ){ void PicoFreeSurface( picoSurface_t *surface ){
int i; int i;
/* dummy check */ /* dummy check */
if ( surface == NULL ) { if ( surface == NULL ) {
return; return;
@ -612,7 +600,6 @@ void PicoFreeSurface( picoSurface_t *surface ){
} }
/* /*
PicoAdjustSurface() PicoAdjustSurface()
adjusts a surface's memory allocations to handle the requested sizes. 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 PicoAdjustSurface( picoSurface_t *surface, int numVertexes, int numSTArrays, int numColorArrays, int numIndexes, int numFaceNormals ){
int i; int i;
/* dummy check */ /* dummy check */
if ( surface == NULL ) { if ( surface == NULL ) {
return 0; return 0;
@ -2169,7 +2155,6 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){
picoShader_t *shader; picoShader_t *shader;
char *materialName; char *materialName;
/* get material name */ /* get material name */
if ( _pico_parse( p, 1 ) == NULL ) { if ( _pico_parse( p, 1 ) == NULL ) {
break; break;

View file

@ -54,6 +54,7 @@ extern const picoModule_t picoModuleMDC;
extern const picoModule_t picoModuleMD2; extern const picoModule_t picoModuleMD2;
extern const picoModule_t picoModuleFM; extern const picoModule_t picoModuleFM;
extern const picoModule_t picoModuleLWO; extern const picoModule_t picoModuleLWO;
extern const picoModule_t picoModuleTerrain;
@ -68,6 +69,7 @@ const picoModule_t *picoModules[] =
&picoModuleMD2, /* quake2 md2 */ &picoModuleMD2, /* quake2 md2 */
&picoModuleFM, /* heretic2 fm */ &picoModuleFM, /* heretic2 fm */
&picoModuleLWO, /* lightwave object */ &picoModuleLWO, /* lightwave object */
&picoModuleTerrain, /* picoterrain object */
&picoModuleOBJ, /* wavefront object */ &picoModuleOBJ, /* wavefront object */
NULL /* arnold */ NULL /* arnold */
}; };
@ -81,7 +83,7 @@ const picoModule_t *picoModules[] =
this param can be NULL when the count is not needed. 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 */ /* get module count */
if ( numModules != NULL ) { if ( numModules != NULL ) {
for ( ( *numModules ) = 0; picoModules[ *numModules ] != NULL; ( *numModules )++ ) ; for ( ( *numModules ) = 0; picoModules[ *numModules ] != NULL; ( *numModules )++ ) ;

View file

@ -124,7 +124,7 @@ debugChunkNames[] =
{ CHUNK_OBJECT_UV, "CHUNK_OBJECT_UV" }, { CHUNK_OBJECT_UV, "CHUNK_OBJECT_UV" },
{ 0, NULL } { 0, NULL }
}; };
static char *DebugGetChunkName( int id ){ static char *DebugGetChunkName( int id ) {
int i,max; /* imax? ;) */ int i,max; /* imax? ;) */
max = sizeof( debugChunkNames ) / sizeof( debugChunkNames[0] ); max = sizeof( debugChunkNames ) / sizeof( debugChunkNames[0] );
@ -397,6 +397,9 @@ static int GetMeshShader( T3dsLoaderPers *pers ){
return 0; 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 */ /* 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 */ /* the shaders and check the name against each shader. when we */
/* find a shader in our shader list that matches this name 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 */ /* we have a valid map name ptr */
if ( mapNamePtr != NULL ) { if ( mapNamePtr != NULL ) {
char temp[128]; char temp[128];
char *name; const char *name;
/* copy map name to local buffer */ /* copy map name to local buffer */
strcpy( mapName,mapNamePtr ); strcpy( mapName,mapNamePtr );
@ -523,7 +526,6 @@ static int DoNextEditorDataChunk( T3dsLoaderPers *pers, long endofs ){
/* read in surface name */ /* read in surface name */
if ( !GetASCIIZ( pers,surfaceName,sizeof( surfaceName ) ) ) { if ( !GetASCIIZ( pers,surfaceName,sizeof( surfaceName ) ) ) {
return 0; /* this is bad */ return 0; /* this is bad */
} }
//PicoGetSurfaceName //PicoGetSurfaceName
/* ignore NULL name surfaces */ /* 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 ... */ /* but for now we skip the new material's name ... */
if ( pers->shader ) { if ( pers->shader ) {
char *name = (char *)( pers->bufptr + pers->cofs ); 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 #ifdef DEBUG_PM_3DS
printf( "NewShader: '%s'\n",name ); printf( "NewShader: '%s'\n", cleanedName );
#endif #endif
_pico_free( cleanedName );
} }
} }
if ( chunk->id == CHUNK_MATDIFFUSE ) { if ( chunk->id == CHUNK_MATDIFFUSE ) {

View file

@ -305,6 +305,125 @@ picoSurface_t* PicoModelFindOrAddSurface( picoModel_t *model, picoShader_t* shad
indexes 6 7 8 = color indexes (new) 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 ){ 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; aseFacesIter_t i = faces, end = faces + numFaces;
for (; i != end; ++i ) for (; i != end; ++i )

View file

@ -350,7 +350,7 @@ static picoModel_t *_fm_load( PM_PARAMS_LOAD ){
texCoord->t = _pico_little_short( texCoord[i].t ); texCoord->t = _pico_little_short( texCoord[i].t );
} }
// set Skin Name // set Skin Name
strncpy( skinname, (char *) fm.fm_skin, FM_SKINPATHSIZE ); strncpy( skinname, (unsigned char *) fm.fm_skin, FM_SKINPATHSIZE );
#ifdef FM_VERBOSE_DBG #ifdef FM_VERBOSE_DBG
// Print out md2 values // Print out md2 values

View file

@ -103,7 +103,7 @@ static picoModel_t *_lwo_load( PM_PARAMS_LOAD ){
lwPolygon *pol; lwPolygon *pol;
lwPolVert *v; lwPolVert *v;
lwVMapPt *vm; lwVMapPt *vm;
char name[ 64 ]; char name[ 256 ];
int i, j, k, numverts; int i, j, k, numverts;
picoModel_t *picoModel; picoModel_t *picoModel;
@ -235,6 +235,7 @@ static picoModel_t *_lwo_load( PM_PARAMS_LOAD ){
/* detox and set shader name */ /* detox and set shader name */
strncpy( name, surface->name, sizeof( name ) ); strncpy( name, surface->name, sizeof( name ) );
_pico_first_token( name );
_pico_setfext( name, "" ); _pico_setfext( name, "" );
_pico_unixify( name ); _pico_unixify( name );
PicoSetShaderName( picoShader, name ); PicoSetShaderName( picoShader, name );
@ -282,9 +283,22 @@ static picoModel_t *_lwo_load( PM_PARAMS_LOAD ){
xyz[ 1 ] = pt->pos[ 2 ]; xyz[ 1 ] = pt->pos[ 2 ];
xyz[ 2 ] = pt->pos[ 1 ]; 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[ 0 ] = v->norm[ 0 ];
normal[ 1 ] = v->norm[ 2 ]; normal[ 1 ] = v->norm[ 2 ];
normal[ 2 ] = v->norm[ 1 ]; 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[ 0 ] = xyz[ defaultSTAxis[ 0 ] ] * defaultXYZtoSTScale[ 0 ];
st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ]; st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ];

607
libs/picomodel/pm_terrain.c Normal file
View file

@ -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 */
};