mirror of
https://github.com/TTimo/GtkRadiant.git
synced 2024-11-10 07:11:54 +00:00
PicoModel - End of 1.5 backporting
lwo subfolder already done by @jdolan:
a269593afe (diff-584b574ac260b5150f42f60544e9266e)
This commit is contained in:
parent
054cfcd15c
commit
14c2c858b9
9 changed files with 803 additions and 41 deletions
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 )++ ) ;
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ];
|
||||
|
|
607
libs/picomodel/pm_terrain.c
Normal file
607
libs/picomodel/pm_terrain.c
Normal 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 */
|
||||
};
|
Loading…
Reference in a new issue