mirror of
https://github.com/id-Software/GtkRadiant.git
synced 2026-03-20 00:49:29 +01:00
The GtkRadiant sources as originally released under the GPL license.
This commit is contained in:
858
libs/picomodel/pm_obj.c
Normal file
858
libs/picomodel/pm_obj.c
Normal file
@@ -0,0 +1,858 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
|
||||
PicoModel Library
|
||||
|
||||
Copyright (c) 2002, 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_OBJ_C
|
||||
|
||||
/* dependencies */
|
||||
#include "picointernal.h"
|
||||
|
||||
/* disable warnings */
|
||||
#ifdef WIN32
|
||||
#pragma warning( disable:4100 ) /* unref param */
|
||||
#endif
|
||||
|
||||
/* todo:
|
||||
* - '_obj_load' code crashes in a weird way after
|
||||
* '_obj_mtl_load' for a few .mtl files
|
||||
* - process 'mtllib' rather than using <model>.mtl
|
||||
* - handle 'usemtl' statements
|
||||
*/
|
||||
/* uncomment when debugging this module */
|
||||
/* #define DEBUG_PM_OBJ */
|
||||
/* #define DEBUG_PM_OBJ_EX */
|
||||
|
||||
/* this holds temporary vertex data read by parser */
|
||||
typedef struct SObjVertexData
|
||||
{
|
||||
picoVec3_t v; /* geometric vertices */
|
||||
picoVec2_t vt; /* texture vertices */
|
||||
picoVec3_t vn; /* vertex normals (optional) */
|
||||
}
|
||||
TObjVertexData;
|
||||
|
||||
/* _obj_canload:
|
||||
* validates a wavefront obj model file.
|
||||
*/
|
||||
static int _obj_canload( PM_PARAMS_CANLOAD )
|
||||
{
|
||||
picoParser_t *p;
|
||||
|
||||
/* check data length */
|
||||
if (bufSize < 30)
|
||||
return PICO_PMV_ERROR_SIZE;
|
||||
|
||||
/* first check file extension. we have to do this for objs */
|
||||
/* cause there is no good way to identify the contents */
|
||||
if (_pico_stristr(fileName,".obj") != NULL ||
|
||||
_pico_stristr(fileName,".wf" ) != NULL)
|
||||
{
|
||||
return PICO_PMV_OK;
|
||||
}
|
||||
/* if the extension check failed we parse through the first */
|
||||
/* few lines in file and look for common keywords often */
|
||||
/* appearing at the beginning of wavefront objects */
|
||||
|
||||
/* alllocate a new pico parser */
|
||||
p = _pico_new_parser( (picoByte_t *)buffer,bufSize );
|
||||
if (p == NULL)
|
||||
return PICO_PMV_ERROR_MEMORY;
|
||||
|
||||
/* parse obj head line by line for type check */
|
||||
while( 1 )
|
||||
{
|
||||
/* get first token on line */
|
||||
if (_pico_parse_first( p ) == NULL)
|
||||
break;
|
||||
|
||||
/* we only parse the first few lines, say 80 */
|
||||
if (p->curLine > 80)
|
||||
break;
|
||||
|
||||
/* skip empty lines */
|
||||
if (p->token == NULL || !strlen( p->token ))
|
||||
continue;
|
||||
|
||||
/* material library keywords are teh good */
|
||||
if (!_pico_stricmp(p->token,"usemtl") ||
|
||||
!_pico_stricmp(p->token,"mtllib") ||
|
||||
!_pico_stricmp(p->token,"g") ||
|
||||
!_pico_stricmp(p->token,"v")) /* v,g bit fishy, but uh... */
|
||||
{
|
||||
/* free the pico parser thing */
|
||||
_pico_free_parser( p );
|
||||
|
||||
/* seems to be a valid wavefront obj */
|
||||
return PICO_PMV_OK;
|
||||
}
|
||||
/* skip rest of line */
|
||||
_pico_parse_skip_rest( p );
|
||||
}
|
||||
/* free the pico parser thing */
|
||||
_pico_free_parser( p );
|
||||
|
||||
/* doesn't really look like an obj to us */
|
||||
return PICO_PMV_ERROR;
|
||||
}
|
||||
|
||||
/* SizeObjVertexData:
|
||||
* This pretty piece of 'alloc ahead' code dynamically
|
||||
* allocates - and reallocates as soon as required -
|
||||
* my vertex data array in even steps.
|
||||
*/
|
||||
#define SIZE_OBJ_STEP 4096
|
||||
|
||||
static TObjVertexData *SizeObjVertexData(
|
||||
TObjVertexData *vertexData, int reqEntries,
|
||||
int *entries, int *allocated)
|
||||
{
|
||||
int newAllocated;
|
||||
|
||||
/* sanity checks */
|
||||
if (reqEntries < 1)
|
||||
return NULL;
|
||||
if (entries == NULL || allocated == NULL)
|
||||
return NULL; /* must have */
|
||||
|
||||
/* no need to grow yet */
|
||||
if (vertexData && (reqEntries < *allocated))
|
||||
{
|
||||
*entries = reqEntries;
|
||||
return vertexData;
|
||||
}
|
||||
/* given vertex data ptr not allocated yet */
|
||||
if (vertexData == NULL)
|
||||
{
|
||||
/* how many entries to allocate */
|
||||
newAllocated = (reqEntries > SIZE_OBJ_STEP) ?
|
||||
reqEntries : SIZE_OBJ_STEP;
|
||||
|
||||
/* throw out an extended debug message */
|
||||
#ifdef DEBUG_PM_OBJ_EX
|
||||
printf("SizeObjVertexData: allocate (%d entries)\n",
|
||||
newAllocated);
|
||||
#endif
|
||||
/* first time allocation */
|
||||
vertexData = (TObjVertexData *)
|
||||
_pico_alloc( sizeof(TObjVertexData) * newAllocated );
|
||||
|
||||
/* allocation failed */
|
||||
if (vertexData == NULL)
|
||||
return NULL;
|
||||
|
||||
/* allocation succeeded */
|
||||
*allocated = newAllocated;
|
||||
*entries = reqEntries;
|
||||
return vertexData;
|
||||
}
|
||||
/* given vertex data ptr needs to be resized */
|
||||
if (reqEntries == *allocated)
|
||||
{
|
||||
newAllocated = (*allocated + SIZE_OBJ_STEP);
|
||||
|
||||
/* throw out an extended debug message */
|
||||
#ifdef DEBUG_PM_OBJ_EX
|
||||
printf("SizeObjVertexData: reallocate (%d entries)\n",
|
||||
newAllocated);
|
||||
#endif
|
||||
/* try to reallocate */
|
||||
vertexData = (TObjVertexData *)
|
||||
_pico_realloc( (void *)&vertexData,
|
||||
sizeof(TObjVertexData) * (*allocated),
|
||||
sizeof(TObjVertexData) * (newAllocated));
|
||||
|
||||
/* reallocation failed */
|
||||
if (vertexData == NULL)
|
||||
return NULL;
|
||||
|
||||
/* reallocation succeeded */
|
||||
*allocated = newAllocated;
|
||||
*entries = reqEntries;
|
||||
return vertexData;
|
||||
}
|
||||
/* we're b0rked when we reach this */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void FreeObjVertexData( TObjVertexData *vertexData )
|
||||
{
|
||||
if (vertexData != NULL)
|
||||
{
|
||||
free( (TObjVertexData *)vertexData );
|
||||
}
|
||||
}
|
||||
|
||||
static int _obj_mtl_load( picoModel_t *model )
|
||||
{
|
||||
picoShader_t *curShader = NULL;
|
||||
picoParser_t *p;
|
||||
picoByte_t *mtlBuffer;
|
||||
int mtlBufSize;
|
||||
char *fileName;
|
||||
|
||||
/* sanity checks */
|
||||
if( model == NULL || model->fileName == NULL )
|
||||
return 0;
|
||||
|
||||
/* skip if we have a zero length model file name */
|
||||
if (!strlen( model->fileName ))
|
||||
return 0;
|
||||
|
||||
/* helper */
|
||||
#define _obj_mtl_error_return \
|
||||
{ \
|
||||
_pico_free_parser( p ); \
|
||||
_pico_free_file( mtlBuffer ); \
|
||||
_pico_free( fileName ); \
|
||||
return 0; \
|
||||
}
|
||||
/* alloc copy of model file name */
|
||||
fileName = _pico_clone_alloc( model->fileName );
|
||||
if (fileName == NULL)
|
||||
return 0;
|
||||
|
||||
/* change extension of model file to .mtl */
|
||||
_pico_setfext( fileName, "mtl" );
|
||||
|
||||
/* load .mtl file contents */
|
||||
_pico_load_file( fileName,&mtlBuffer,&mtlBufSize );
|
||||
|
||||
/* check result */
|
||||
if (mtlBufSize == 0) return 1; /* file is empty: no error */
|
||||
if (mtlBufSize < 0) return 0; /* load failed: error */
|
||||
|
||||
/* create a new pico parser */
|
||||
p = _pico_new_parser( mtlBuffer, mtlBufSize );
|
||||
if (p == NULL)
|
||||
_obj_mtl_error_return;
|
||||
|
||||
/* doo teh .mtl parse */
|
||||
while( 1 )
|
||||
{
|
||||
/* get next token in material file */
|
||||
if (_pico_parse( p,1 ) == NULL)
|
||||
break;
|
||||
#if 0
|
||||
|
||||
/* skip empty lines */
|
||||
if (p->token == NULL || !strlen( p->token ))
|
||||
continue;
|
||||
|
||||
/* skip comment lines */
|
||||
if (p->token[0] == '#')
|
||||
{
|
||||
_pico_parse_skip_rest( p );
|
||||
continue;
|
||||
}
|
||||
/* new material */
|
||||
if (!_pico_stricmp(p->token,"newmtl"))
|
||||
{
|
||||
picoShader_t *shader;
|
||||
char *name;
|
||||
|
||||
/* get material name */
|
||||
name = _pico_parse( p,0 );
|
||||
|
||||
/* validate material name */
|
||||
if (name == NULL || !strlen(name))
|
||||
{
|
||||
_pico_printf( PICO_ERROR,"Missing material name in MTL, line %d.",p->curLine);
|
||||
_obj_mtl_error_return;
|
||||
}
|
||||
/* create a new pico shader */
|
||||
shader = PicoNewShader( model );
|
||||
if (shader == NULL)
|
||||
_obj_mtl_error_return;
|
||||
|
||||
/* set shader name */
|
||||
PicoSetShaderName( shader,name );
|
||||
|
||||
/* assign pointer to current shader */
|
||||
curShader = shader;
|
||||
}
|
||||
/* diffuse map name */
|
||||
else if (!_pico_stricmp(p->token,"map_kd"))
|
||||
{
|
||||
char *mapName;
|
||||
|
||||
/* pointer to current shader must be valid */
|
||||
if (curShader == NULL)
|
||||
_obj_mtl_error_return;
|
||||
|
||||
/* get material's diffuse map name */
|
||||
mapName = _pico_parse( p,0 );
|
||||
|
||||
/* validate map name */
|
||||
if (mapName == NULL || !strlen(mapName))
|
||||
{
|
||||
_pico_printf( PICO_ERROR,"Missing material map name in MTL, line %d.",p->curLine);
|
||||
_obj_mtl_error_return;
|
||||
}
|
||||
/* set shader map name */
|
||||
PicoSetShaderMapName( shader,mapName );
|
||||
}
|
||||
/* dissolve factor (pseudo transparency 0..1) */
|
||||
/* where 0 means 100% transparent and 1 means opaque */
|
||||
else if (!_pico_stricmp(p->token,"d"))
|
||||
{
|
||||
picoByte_t *diffuse;
|
||||
float value;
|
||||
|
||||
|
||||
/* get dissolve factor */
|
||||
if (!_pico_parse_float( p,&value ))
|
||||
_obj_mtl_error_return;
|
||||
|
||||
/* set shader transparency */
|
||||
PicoSetShaderTransparency( curShader,value );
|
||||
|
||||
/* get shader's diffuse color */
|
||||
diffuse = PicoGetShaderDiffuseColor( curShader );
|
||||
|
||||
/* set diffuse alpha to transparency */
|
||||
diffuse[ 3 ] = (picoByte_t)( value * 255.0 );
|
||||
|
||||
/* set shader's new diffuse color */
|
||||
PicoSetShaderDiffuseColor( curShader,diffuse );
|
||||
}
|
||||
/* shininess (phong specular component) */
|
||||
else if (!_pico_stricmp(p->token,"ns"))
|
||||
{
|
||||
/* remark:
|
||||
* - well, this is some major obj spec fuckup once again. some
|
||||
* apps store this in 0..1 range, others use 0..100 range,
|
||||
* even others use 0..2048 range, and again others use the
|
||||
* range 0..128, some even use 0..1000, 0..200, 400..700,
|
||||
* honestly, what's up with the 3d app coders? happens when
|
||||
* you smoke too much weed i guess. -sea
|
||||
*/
|
||||
float value;
|
||||
|
||||
/* pointer to current shader must be valid */
|
||||
if (curShader == NULL)
|
||||
_obj_mtl_error_return;
|
||||
|
||||
/* get totally screwed up shininess (a random value in fact ;) */
|
||||
if (!_pico_parse_float( p,&value ))
|
||||
_obj_mtl_error_return;
|
||||
|
||||
/* okay, there is no way to set this correctly, so we simply */
|
||||
/* try to guess a few ranges (most common ones i have seen) */
|
||||
|
||||
/* assume 0..2048 range */
|
||||
if (value > 1000)
|
||||
value = 128.0 * (value / 2048.0);
|
||||
/* assume 0..1000 range */
|
||||
else if (value > 200)
|
||||
value = 128.0 * (value / 1000.0);
|
||||
/* assume 0..200 range */
|
||||
else if (value > 100)
|
||||
value = 128.0 * (value / 200.0);
|
||||
/* assume 0..100 range */
|
||||
else if (value > 1)
|
||||
value = 128.0 * (value / 100.0);
|
||||
/* assume 0..1 range */
|
||||
else {
|
||||
value *= 128.0;
|
||||
}
|
||||
/* negative shininess is bad (yes, i have seen it...) */
|
||||
if (value < 0.0) value = 0.0;
|
||||
|
||||
/* set the pico shininess value in range 0..127 */
|
||||
/* geez, .obj is such a mess... */
|
||||
PicoSetShaderShininess( curShader,value );
|
||||
}
|
||||
/* kol0r ambient (wut teh fuk does "ka" stand for?) */
|
||||
else if (!_pico_stricmp(p->token,"ka"))
|
||||
{
|
||||
picoColor_t color;
|
||||
picoVec3_t v;
|
||||
|
||||
/* pointer to current shader must be valid */
|
||||
if (curShader == NULL)
|
||||
_obj_mtl_error_return;
|
||||
|
||||
/* get color vector */
|
||||
if (!_pico_parse_vec( p,v ))
|
||||
_obj_mtl_error_return;
|
||||
|
||||
/* scale to byte range */
|
||||
color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
|
||||
color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
|
||||
color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
|
||||
color[ 3 ] = (picoByte_t)( 255 );
|
||||
|
||||
/* set ambient color */
|
||||
PicoSetShaderAmbientColor( curShader,color );
|
||||
}
|
||||
/* kol0r diffuse */
|
||||
else if (!_pico_stricmp(p->token,"kd"))
|
||||
{
|
||||
picoColor_t color;
|
||||
picoVec3_t v;
|
||||
|
||||
/* pointer to current shader must be valid */
|
||||
if (curShader == NULL)
|
||||
_obj_mtl_error_return;
|
||||
|
||||
/* get color vector */
|
||||
if (!_pico_parse_vec( p,v ))
|
||||
_obj_mtl_error_return;
|
||||
|
||||
/* scale to byte range */
|
||||
color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
|
||||
color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
|
||||
color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
|
||||
color[ 3 ] = (picoByte_t)( 255 );
|
||||
|
||||
/* set diffuse color */
|
||||
PicoSetShaderDiffuseColor( curShader,color );
|
||||
}
|
||||
/* kol0r specular */
|
||||
else if (!_pico_stricmp(p->token,"ks"))
|
||||
{
|
||||
picoColor_t color;
|
||||
picoVec3_t v;
|
||||
|
||||
/* pointer to current shader must be valid */
|
||||
if (curShader == NULL)
|
||||
_obj_mtl_error_return;
|
||||
|
||||
/* get color vector */
|
||||
if (!_pico_parse_vec( p,v ))
|
||||
_obj_mtl_error_return;
|
||||
|
||||
/* scale to byte range */
|
||||
color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
|
||||
color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
|
||||
color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
|
||||
color[ 3 ] = (picoByte_t)( 255 );
|
||||
|
||||
/* set specular color */
|
||||
PicoSetShaderSpecularColor( curShader,color );
|
||||
}
|
||||
#endif
|
||||
/* skip rest of line */
|
||||
_pico_parse_skip_rest( p );
|
||||
}
|
||||
|
||||
/* free parser, file buffer, and file name */
|
||||
_pico_free_parser( p );
|
||||
_pico_free_file( mtlBuffer );
|
||||
_pico_free( fileName );
|
||||
|
||||
/* return with success */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* _obj_load:
|
||||
* loads a wavefront obj model file.
|
||||
*/
|
||||
static picoModel_t *_obj_load( PM_PARAMS_LOAD )
|
||||
{
|
||||
TObjVertexData *vertexData = NULL;
|
||||
picoModel_t *model;
|
||||
picoSurface_t *curSurface = NULL;
|
||||
picoParser_t *p;
|
||||
int allocated;
|
||||
int entries;
|
||||
int numVerts = 0;
|
||||
int numNormals = 0;
|
||||
int numUVs = 0;
|
||||
int curVertex = 0;
|
||||
int curFace = 0;
|
||||
|
||||
/* helper */
|
||||
#define _obj_error_return(m) \
|
||||
{ \
|
||||
_pico_printf( PICO_ERROR,"%s in OBJ, line %d.",m,p->curLine); \
|
||||
_pico_free_parser( p ); \
|
||||
FreeObjVertexData( vertexData ); \
|
||||
PicoFreeModel( model ); \
|
||||
return NULL; \
|
||||
}
|
||||
/* alllocate a new pico parser */
|
||||
p = _pico_new_parser( (picoByte_t *)buffer,bufSize );
|
||||
if (p == NULL) return NULL;
|
||||
|
||||
/* create a new pico model */
|
||||
model = PicoNewModel();
|
||||
if (model == NULL)
|
||||
{
|
||||
_pico_free_parser( p );
|
||||
return NULL;
|
||||
}
|
||||
/* do model setup */
|
||||
PicoSetModelFrameNum( model,frameNum );
|
||||
PicoSetModelName( model,fileName );
|
||||
PicoSetModelFileName( model,fileName );
|
||||
|
||||
/* try loading the materials; we don't handle the result */
|
||||
#if 0
|
||||
_obj_mtl_load( model );
|
||||
#endif
|
||||
|
||||
/* parse obj line by line */
|
||||
while( 1 )
|
||||
{
|
||||
/* get first token on line */
|
||||
if (_pico_parse_first( p ) == NULL)
|
||||
break;
|
||||
|
||||
/* skip empty lines */
|
||||
if (p->token == NULL || !strlen( p->token ))
|
||||
continue;
|
||||
|
||||
/* skip comment lines */
|
||||
if (p->token[0] == '#')
|
||||
{
|
||||
_pico_parse_skip_rest( p );
|
||||
continue;
|
||||
}
|
||||
/* vertex */
|
||||
if (!_pico_stricmp(p->token,"v"))
|
||||
{
|
||||
TObjVertexData *data;
|
||||
picoVec3_t v;
|
||||
|
||||
vertexData = SizeObjVertexData( vertexData,numVerts+1,&entries,&allocated );
|
||||
if (vertexData == NULL)
|
||||
_obj_error_return("Realloc of vertex data failed (1)");
|
||||
|
||||
data = &vertexData[ numVerts++ ];
|
||||
|
||||
/* get and copy vertex */
|
||||
if (!_pico_parse_vec( p,v ))
|
||||
_obj_error_return("Vertex parse error");
|
||||
|
||||
_pico_copy_vec( v,data->v );
|
||||
|
||||
#ifdef DEBUG_PM_OBJ_EX
|
||||
printf("Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2]);
|
||||
#endif
|
||||
}
|
||||
/* uv coord */
|
||||
else if (!_pico_stricmp(p->token,"vt"))
|
||||
{
|
||||
TObjVertexData *data;
|
||||
picoVec2_t coord;
|
||||
|
||||
vertexData = SizeObjVertexData( vertexData,numUVs+1,&entries,&allocated );
|
||||
if (vertexData == NULL)
|
||||
_obj_error_return("Realloc of vertex data failed (2)");
|
||||
|
||||
data = &vertexData[ numUVs++ ];
|
||||
|
||||
/* get and copy tex coord */
|
||||
if (!_pico_parse_vec2( p,coord ))
|
||||
_obj_error_return("UV coord parse error");
|
||||
|
||||
_pico_copy_vec2( coord,data->vt );
|
||||
|
||||
#ifdef DEBUG_PM_OBJ_EX
|
||||
printf("TexCoord: u: %f v: %f\n",coord[0],coord[1]);
|
||||
#endif
|
||||
}
|
||||
/* vertex normal */
|
||||
else if (!_pico_stricmp(p->token,"vn"))
|
||||
{
|
||||
TObjVertexData *data;
|
||||
picoVec3_t n;
|
||||
|
||||
vertexData = SizeObjVertexData( vertexData,numNormals+1,&entries,&allocated );
|
||||
if (vertexData == NULL)
|
||||
_obj_error_return("Realloc of vertex data failed (3)");
|
||||
|
||||
data = &vertexData[ numNormals++ ];
|
||||
|
||||
/* get and copy vertex normal */
|
||||
if (!_pico_parse_vec( p,n ))
|
||||
_obj_error_return("Vertex normal parse error");
|
||||
|
||||
_pico_copy_vec( n,data->vn );
|
||||
|
||||
#ifdef DEBUG_PM_OBJ_EX
|
||||
printf("Normal: x: %f y: %f z: %f\n",n[0],n[1],n[2]);
|
||||
#endif
|
||||
}
|
||||
/* new group (for us this means a new surface) */
|
||||
else if (!_pico_stricmp(p->token,"g"))
|
||||
{
|
||||
picoSurface_t *newSurface;
|
||||
char *groupName;
|
||||
|
||||
/* get first group name (ignore 2nd,3rd,etc.) */
|
||||
groupName = _pico_parse( p,0 );
|
||||
if (groupName == NULL || !strlen(groupName))
|
||||
{
|
||||
/* some obj exporters feel like they don't need to */
|
||||
/* supply a group name. so we gotta handle it here */
|
||||
#if 1
|
||||
strcpy( p->token,"default" );
|
||||
groupName = p->token;
|
||||
#else
|
||||
_obj_error_return("Invalid or missing group name");
|
||||
#endif
|
||||
}
|
||||
/* allocate a pico surface */
|
||||
newSurface = PicoNewSurface( model );
|
||||
if (newSurface == NULL)
|
||||
_obj_error_return("Error allocating surface");
|
||||
|
||||
/* reset face index for surface */
|
||||
curFace = 0;
|
||||
|
||||
/* set ptr to current surface */
|
||||
curSurface = newSurface;
|
||||
|
||||
/* we use triangle meshes */
|
||||
PicoSetSurfaceType( newSurface,PICO_TRIANGLES );
|
||||
|
||||
/* set surface name */
|
||||
PicoSetSurfaceName( newSurface,groupName );
|
||||
|
||||
#ifdef DEBUG_PM_OBJ_EX
|
||||
printf("Group: '%s'\n",groupName);
|
||||
#endif
|
||||
}
|
||||
/* face (oh jesus, hopefully this will do the job right ;) */
|
||||
else if (!_pico_stricmp(p->token,"f"))
|
||||
{
|
||||
/* okay, this is a mess. some 3d apps seem to try being unique, */
|
||||
/* hello cinema4d & 3d exploration, feel good today?, and save */
|
||||
/* this crap in tons of different formats. gah, those screwed */
|
||||
/* coders. tho the wavefront obj standard defines exactly two */
|
||||
/* ways of storing face information. so, i really won't support */
|
||||
/* such stupid extravaganza here! */
|
||||
|
||||
picoVec3_t verts [ 4 ];
|
||||
picoVec3_t normals[ 4 ];
|
||||
picoVec2_t coords [ 4 ];
|
||||
|
||||
int iv [ 4 ], has_v;
|
||||
int ivt[ 4 ], has_vt = 0;
|
||||
int ivn[ 4 ], has_vn = 0;
|
||||
int have_quad = 0;
|
||||
int slashcount;
|
||||
int doubleslash;
|
||||
int i;
|
||||
|
||||
/* group defs *must* come before faces */
|
||||
if (curSurface == NULL)
|
||||
_obj_error_return("No group defined for faces");
|
||||
|
||||
#ifdef DEBUG_PM_OBJ_EX
|
||||
printf("Face: ");
|
||||
#endif
|
||||
/* read vertex/uv/normal indices for the first three face */
|
||||
/* vertices (cause we only support triangles) into 'i*[]' */
|
||||
/* store the actual vertex/uv/normal data in three arrays */
|
||||
/* called 'verts','coords' and 'normals'. */
|
||||
for (i=0; i<4; i++)
|
||||
{
|
||||
char *str;
|
||||
|
||||
/* get next vertex index string (different */
|
||||
/* formats are handled below) */
|
||||
str = _pico_parse( p,0 );
|
||||
if (str == NULL)
|
||||
{
|
||||
/* just break for quads */
|
||||
if (i == 3) break;
|
||||
|
||||
/* error otherwise */
|
||||
_obj_error_return("Face parse error");
|
||||
}
|
||||
/* if this is the fourth index string we're */
|
||||
/* parsing we assume that we have a quad */
|
||||
if (i == 3)
|
||||
have_quad = 1;
|
||||
|
||||
/* get slash count once */
|
||||
if (i == 0)
|
||||
{
|
||||
slashcount = _pico_strchcount( str,'/' );
|
||||
doubleslash = strstr(str,"//") != NULL;
|
||||
}
|
||||
/* handle format 'v//vn' */
|
||||
if (doubleslash && (slashcount == 2))
|
||||
{
|
||||
has_v = has_vn = 1;
|
||||
sscanf( str,"%d//%d",&iv[ i ],&ivn[ i ] );
|
||||
}
|
||||
/* handle format 'v/vt/vn' */
|
||||
else if (!doubleslash && (slashcount == 2))
|
||||
{
|
||||
has_v = has_vt = has_vn = 1;
|
||||
sscanf( str,"%d/%d/%d",&iv[ i ],&ivt[ i ],&ivn[ i ] );
|
||||
}
|
||||
/* handle format 'v/vt' (non-standard fuckage) */
|
||||
else if (!doubleslash && (slashcount == 1))
|
||||
{
|
||||
has_v = has_vt = 1;
|
||||
sscanf( str,"%d/%d",&iv[ i ],&ivt[ i ] );
|
||||
}
|
||||
/* else assume face format 'v' */
|
||||
/* (must have been invented by some bored granny) */
|
||||
else {
|
||||
/* get single vertex index */
|
||||
has_v = 1;
|
||||
iv[ i ] = atoi( str );
|
||||
|
||||
/* either invalid face format or out of range */
|
||||
if (iv[ i ] == 0)
|
||||
_obj_error_return("Invalid face format");
|
||||
}
|
||||
/* fix useless back references */
|
||||
/* todo: check if this works as it is supposed to */
|
||||
|
||||
/* assign new indices */
|
||||
if (iv [ i ] < 0) iv [ i ] = (numVerts - iv [ i ]);
|
||||
if (ivt[ i ] < 0) ivt[ i ] = (numUVs - ivt[ i ]);
|
||||
if (ivn[ i ] < 0) ivn[ i ] = (numNormals - ivn[ i ]);
|
||||
|
||||
/* validate indices */
|
||||
/* - commented out. index range checks will trigger
|
||||
if (iv [ i ] < 1) iv [ i ] = 1;
|
||||
if (ivt[ i ] < 1) ivt[ i ] = 1;
|
||||
if (ivn[ i ] < 1) ivn[ i ] = 1;
|
||||
*/
|
||||
/* set vertex origin */
|
||||
if (has_v)
|
||||
{
|
||||
/* check vertex index range */
|
||||
if (iv[ i ] < 1 || iv[ i ] > numVerts)
|
||||
_obj_error_return("Vertex index out of range");
|
||||
|
||||
/* get vertex data */
|
||||
verts[ i ][ 0 ] = vertexData[ iv[ i ] - 1 ].v[ 0 ];
|
||||
verts[ i ][ 1 ] = vertexData[ iv[ i ] - 1 ].v[ 1 ];
|
||||
verts[ i ][ 2 ] = vertexData[ iv[ i ] - 1 ].v[ 2 ];
|
||||
}
|
||||
/* set vertex normal */
|
||||
if (has_vn)
|
||||
{
|
||||
/* check normal index range */
|
||||
if (ivn[ i ] < 1 || ivn[ i ] > numNormals)
|
||||
_obj_error_return("Normal index out of range");
|
||||
|
||||
/* get normal data */
|
||||
normals[ i ][ 0 ] = vertexData[ ivn[ i ] - 1 ].vn[ 0 ];
|
||||
normals[ i ][ 1 ] = vertexData[ ivn[ i ] - 1 ].vn[ 1 ];
|
||||
normals[ i ][ 2 ] = vertexData[ ivn[ i ] - 1 ].vn[ 2 ];
|
||||
}
|
||||
/* set texture coordinate */
|
||||
if (has_vt)
|
||||
{
|
||||
/* check uv index range */
|
||||
if (ivt[ i ] < 1 || ivt[ i ] > numUVs)
|
||||
_obj_error_return("UV coord index out of range");
|
||||
|
||||
/* get uv coord data */
|
||||
coords[ i ][ 0 ] = vertexData[ ivt[ i ] - 1 ].vt[ 0 ];
|
||||
coords[ i ][ 1 ] = vertexData[ ivt[ i ] - 1 ].vt[ 1 ];
|
||||
coords[ i ][ 1 ] = -coords[ i ][ 1 ];
|
||||
}
|
||||
#ifdef DEBUG_PM_OBJ_EX
|
||||
printf("(%4d",iv[ i ]);
|
||||
if (has_vt) printf(" %4d",ivt[ i ]);
|
||||
if (has_vn) printf(" %4d",ivn[ i ]);
|
||||
printf(") ");
|
||||
#endif
|
||||
}
|
||||
#ifdef DEBUG_PM_OBJ_EX
|
||||
printf("\n");
|
||||
#endif
|
||||
/* now that we have extracted all the indices and have */
|
||||
/* read the actual data we need to assign all the crap */
|
||||
/* to our current pico surface */
|
||||
if (has_v)
|
||||
{
|
||||
int max = 3;
|
||||
if (have_quad) max = 4;
|
||||
|
||||
/* assign all surface information */
|
||||
for (i=0; i<max; i++)
|
||||
{
|
||||
/*if( has_v )*/ PicoSetSurfaceXYZ ( curSurface, (curVertex + i), verts [ i ] );
|
||||
/*if( has_vt )*/ PicoSetSurfaceST ( curSurface,0,(curVertex + i), coords [ i ] );
|
||||
/*if( has_vn )*/ PicoSetSurfaceNormal( curSurface, (curVertex + i), normals[ i ] );
|
||||
}
|
||||
/* add our triangle (A B C) */
|
||||
PicoSetSurfaceIndex( curSurface,(curFace * 3 + 2),(picoIndex_t)( curVertex + 0 ) );
|
||||
PicoSetSurfaceIndex( curSurface,(curFace * 3 + 1),(picoIndex_t)( curVertex + 1 ) );
|
||||
PicoSetSurfaceIndex( curSurface,(curFace * 3 + 0),(picoIndex_t)( curVertex + 2 ) );
|
||||
curFace++;
|
||||
|
||||
/* if we don't have a simple triangle, but a quad... */
|
||||
if (have_quad)
|
||||
{
|
||||
/* we have to add another triangle (2nd half of quad which is A C D) */
|
||||
PicoSetSurfaceIndex( curSurface,(curFace * 3 + 2),(picoIndex_t)( curVertex + 0 ) );
|
||||
PicoSetSurfaceIndex( curSurface,(curFace * 3 + 1),(picoIndex_t)( curVertex + 2 ) );
|
||||
PicoSetSurfaceIndex( curSurface,(curFace * 3 + 0),(picoIndex_t)( curVertex + 3 ) );
|
||||
curFace++;
|
||||
}
|
||||
/* increase vertex count */
|
||||
curVertex += max;
|
||||
}
|
||||
}
|
||||
/* skip unparsed rest of line and continue */
|
||||
_pico_parse_skip_rest( p );
|
||||
}
|
||||
/* free memory used by temporary vertexdata */
|
||||
FreeObjVertexData( vertexData );
|
||||
|
||||
/* return allocated pico model */
|
||||
return model;
|
||||
// return NULL;
|
||||
}
|
||||
|
||||
/* pico file format module definition */
|
||||
const picoModule_t picoModuleOBJ =
|
||||
{
|
||||
"0.6-b", /* module version string */
|
||||
"Wavefront ASCII", /* module display name */
|
||||
"seaw0lf", /* author's name */
|
||||
"2002 seaw0lf", /* module copyright */
|
||||
{
|
||||
"obj",NULL,NULL,NULL /* default extensions to use */
|
||||
},
|
||||
_obj_canload, /* validation routine */
|
||||
_obj_load, /* load routine */
|
||||
NULL, /* save validation routine */
|
||||
NULL /* save routine */
|
||||
};
|
||||
Reference in New Issue
Block a user