-/* -----------------------------------------------------------------------------\r
-\r
-PicoModel Library \r
-\r
-Copyright (c) 2002, Randy Reddig & seaw0lf\r
-All rights reserved.\r
-\r
-Redistribution and use in source and binary forms, with or without modification,\r
-are permitted provided that the following conditions are met:\r
-\r
-Redistributions of source code must retain the above copyright notice, this list\r
-of conditions and the following disclaimer.\r
-\r
-Redistributions in binary form must reproduce the above copyright notice, this\r
-list of conditions and the following disclaimer in the documentation and/or\r
-other materials provided with the distribution.\r
-\r
-Neither the names of the copyright holders nor the names of its contributors may\r
-be used to endorse or promote products derived from this software without\r
-specific prior written permission. \r
-\r
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND\r
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\r
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\r
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
-\r
------------------------------------------------------------------------------ */\r
-\r
-/* marker */\r
-#define PM_LWO_C\r
-\r
-/* dependencies */\r
-#include "picointernal.h"\r
-#include "lwo/lwo2.h"\r
-\r
-/* uncomment when debugging this module */\r
-/*#define DEBUG_PM_LWO*/\r
-\r
-#ifdef DEBUG_PM_LWO\r
-#include "time.h"\r
-#endif\r
-\r
-/* helper functions */\r
-static const char *lwo_lwIDToStr( unsigned int lwID )\r
-{\r
- static char lwIDStr[5];\r
-\r
- if (!lwID)\r
- {\r
- return "n/a";\r
- }\r
-\r
- lwIDStr[ 0 ] = (char)((lwID) >> 24);\r
- lwIDStr[ 1 ] = (char)((lwID) >> 16);\r
- lwIDStr[ 2 ] = (char)((lwID) >> 8);\r
- lwIDStr[ 3 ] = (char)((lwID));\r
- lwIDStr[ 4 ] = '\0';\r
-\r
- return lwIDStr;\r
-}\r
-\r
-/*\r
-_lwo_canload()\r
-validates a LightWave Object model file. btw, i use the\r
-preceding underscore cause it's a static func referenced\r
-by one structure only.\r
-*/\r
-static int _lwo_canload( PM_PARAMS_CANLOAD )\r
-{\r
- picoMemStream_t *s;\r
- unsigned int failID = 0;\r
- int failpos = -1;\r
- int ret;\r
-\r
- /* create a new pico memorystream */\r
- s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );\r
- if (s == NULL)\r
- {\r
- return PICO_PMV_ERROR_MEMORY;\r
- }\r
-\r
- ret = lwValidateObject( fileName, s, &failID, &failpos );\r
-\r
- _pico_free_memstream( s );\r
-\r
- return ret;\r
-}\r
-\r
-/*\r
-_lwo_load()\r
-loads a LightWave Object model file.\r
-*/\r
-static picoModel_t *_lwo_load( PM_PARAMS_LOAD )\r
-{\r
- picoMemStream_t *s;\r
- unsigned int failID = 0;\r
- int failpos = -1;\r
- lwObject *obj;\r
- lwSurface *surface;\r
- lwLayer *layer;\r
- lwPoint *pt;\r
- lwPolygon *pol;\r
- lwPolVert *v;\r
- lwVMapPt *vm;\r
- char name[ 64 ];\r
- int i, j, k, numverts;\r
-\r
- picoModel_t *picoModel;\r
- picoSurface_t *picoSurface;\r
- picoShader_t *picoShader;\r
- picoVec3_t xyz, normal;\r
- picoVec2_t st;\r
- picoColor_t color;\r
-\r
- int defaultSTAxis[ 2 ];\r
- picoVec2_t defaultXYZtoSTScale;\r
-\r
- picoVertexCombinationHash_t **hashTable;\r
- picoVertexCombinationHash_t *vertexCombinationHash;\r
-\r
-#ifdef DEBUG_PM_LWO\r
- clock_t load_start, load_finish, convert_start, convert_finish;\r
- double load_elapsed, convert_elapsed;\r
-\r
- load_start = clock();\r
-#endif\r
-\r
- /* do frame check */\r
- if( frameNum < 0 || frameNum >= 1 )\r
- {\r
- _pico_printf( PICO_ERROR, "Invalid or out-of-range LWO frame specified" );\r
- return NULL;\r
- }\r
-\r
- /* create a new pico memorystream */\r
- s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );\r
- if (s == NULL)\r
- {\r
- return NULL;\r
- }\r
-\r
- obj = lwGetObject( fileName, s, &failID, &failpos );\r
-\r
- _pico_free_memstream( s );\r
-\r
- if( !obj ) {\r
- _pico_printf( PICO_ERROR, "Couldn't load LWO file, failed on ID '%s', position %d", lwo_lwIDToStr( failID ), failpos );\r
- return NULL;\r
- }\r
-\r
-#ifdef DEBUG_PM_LWO\r
- convert_start = load_finish = clock();\r
- load_elapsed = (double)(load_finish - load_start) / CLOCKS_PER_SEC;\r
-#endif\r
-\r
- /* -------------------------------------------------\r
- pico model creation\r
- ------------------------------------------------- */\r
- \r
- /* create a new pico model */\r
- picoModel = PicoNewModel();\r
- if (picoModel == NULL)\r
- {\r
- _pico_printf( PICO_ERROR, "Unable to allocate a new model" );\r
- return NULL;\r
- }\r
-\r
- /* do model setup */\r
- PicoSetModelFrameNum( picoModel, frameNum );\r
- PicoSetModelNumFrames( picoModel, 1 );\r
- PicoSetModelName( picoModel, fileName );\r
- PicoSetModelFileName( picoModel, fileName );\r
-\r
- /* create all polygons from layer[ 0 ] that belong to this surface */\r
- layer = &obj->layer[0];\r
-\r
- /* warn the user that other layers are discarded */\r
- if (obj->nlayers > 1)\r
- {\r
- _pico_printf( PICO_WARNING, "LWO loader discards any geometry data not in Layer 1 (%d layers found)", obj->nlayers );\r
- }\r
-\r
- /* initialize dummy normal */\r
- normal[ 0 ] = normal[ 1 ] = normal[ 2 ] = 0.f;\r
-\r
- /* setup default st map */\r
- st[ 0 ] = st[ 1 ] = 0.f; /* st[0] holds max, st[1] holds max par one */\r
- defaultSTAxis[ 0 ] = 0;\r
- defaultSTAxis[ 1 ] = 1;\r
- for( i = 0; i < 3; i++ )\r
- {\r
- float min = layer->bbox[ i ];\r
- float max = layer->bbox[ i + 3 ];\r
- float size = max - min;\r
- \r
- if (size > st[ 0 ])\r
- {\r
- defaultSTAxis[ 1 ] = defaultSTAxis[ 0 ];\r
- defaultSTAxis[ 0 ] = i;\r
-\r
- st[ 1 ] = st[ 0 ];\r
- st[ 0 ] = size;\r
- }\r
- else if (size > st[ 1 ])\r
- {\r
- defaultSTAxis[ 1 ] = i;\r
- st[ 1 ] = size;\r
- }\r
- }\r
- defaultXYZtoSTScale[ 0 ] = 4.f / st[ 0 ];\r
- defaultXYZtoSTScale[ 1 ] = 4.f / st[ 1 ];\r
-\r
- /* LWO surfaces become pico surfaces */\r
- surface = obj->surf;\r
- while (surface)\r
- {\r
- /* allocate new pico surface */\r
- picoSurface = PicoNewSurface( picoModel );\r
- if (picoSurface == NULL)\r
- {\r
- _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );\r
- PicoFreeModel( picoModel );\r
- lwFreeObject( obj );\r
- return NULL;\r
- }\r
-\r
- /* LWO model surfaces are all triangle meshes */\r
- PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );\r
-\r
- /* set surface name */\r
- PicoSetSurfaceName( picoSurface, surface->name );\r
-\r
- /* create new pico shader */\r
- picoShader = PicoNewShader( picoModel );\r
- if (picoShader == NULL)\r
- {\r
- _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );\r
- PicoFreeModel( picoModel );\r
- lwFreeObject( obj );\r
- return NULL;\r
- }\r
-\r
- /* detox and set shader name */\r
- strncpy( name, surface->name, sizeof(name) );\r
- _pico_setfext( name, "" );\r
- _pico_unixify( name );\r
- PicoSetShaderName( picoShader, name );\r
-\r
- /* associate current surface with newly created shader */\r
- PicoSetSurfaceShader( picoSurface, picoShader );\r
-\r
- /* copy indices and vertex data */\r
- numverts = 0;\r
-\r
- hashTable = PicoNewVertexCombinationHashTable();\r
-\r
- if (hashTable == NULL)\r
- {\r
- _pico_printf( PICO_ERROR, "Unable to allocate hash table" );\r
- PicoFreeModel( picoModel );\r
- lwFreeObject( obj );\r
- return NULL;\r
- }\r
-\r
- for( i = 0, pol = layer->polygon.pol; i < layer->polygon.count; i++, pol++ )\r
- {\r
- /* does this polygon belong to this surface? */\r
- if (pol->surf != surface)\r
- continue;\r
-\r
- /* we only support polygons of the FACE type */\r
- if (pol->type != ID_FACE)\r
- {\r
- _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it's type != FACE (%s)", lwo_lwIDToStr( pol->type ) );\r
- continue;\r
- }\r
-\r
- /* NOTE: LWO has support for non-convex polygons, do we want to store them as well? */\r
- if (pol->nverts != 3)\r
- {\r
- _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it has != 3 verts (%d)", pol->nverts );\r
- continue;\r
- }\r
-\r
- for( j = 0, v = pol->v; j < 3; j++, v++ )\r
- {\r
- pt = &layer->point.pt[ v->index ];\r
-\r
- /* setup data */\r
- xyz[ 0 ] = pt->pos[ 0 ];\r
- xyz[ 1 ] = pt->pos[ 2 ];\r
- xyz[ 2 ] = pt->pos[ 1 ];\r
-\r
- normal[ 0 ] = v->norm[ 0 ];\r
- normal[ 1 ] = v->norm[ 2 ];\r
- normal[ 2 ] = v->norm[ 1 ];\r
-\r
- st[ 0 ] = xyz[ defaultSTAxis[ 0 ] ] * defaultXYZtoSTScale[ 0 ];\r
- st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ];\r
-\r
- color[ 0 ] = (picoByte_t)(surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);\r
- color[ 1 ] = (picoByte_t)(surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);\r
- color[ 2 ] = (picoByte_t)(surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);\r
- color[ 3 ] = 0xFF;\r
-\r
- /* set from points */\r
- for( k = 0, vm = pt->vm; k < pt->nvmaps; k++, vm++ )\r
- {\r
- if (vm->vmap->type == LWID_('T','X','U','V'))\r
- {\r
- /* set st coords */\r
- st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];\r
- st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];\r
- }\r
- else if (vm->vmap->type == LWID_('R','G','B','A'))\r
- {\r
- /* set rgba */\r
- color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);\r
- color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);\r
- color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);\r
- color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);\r
- }\r
- }\r
-\r
- /* override with polygon data */\r
- for( k = 0, vm = v->vm; k < v->nvmaps; k++, vm++ )\r
- {\r
- if (vm->vmap->type == LWID_('T','X','U','V'))\r
- {\r
- /* set st coords */\r
- st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];\r
- st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];\r
- }\r
- else if (vm->vmap->type == LWID_('R','G','B','A'))\r
- {\r
- /* set rgba */\r
- color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);\r
- color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);\r
- color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);\r
- color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);\r
- }\r
- }\r
-\r
- /* find vertex in this surface and if we can't find it there create it */\r
- vertexCombinationHash = PicoFindVertexCombinationInHashTable( hashTable, xyz, normal, st, color );\r
-\r
- if (vertexCombinationHash)\r
- {\r
- /* found an existing one */\r
- PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), vertexCombinationHash->index );\r
- }\r
- else\r
- {\r
- /* it is a new one */\r
- vertexCombinationHash = PicoAddVertexCombinationToHashTable( hashTable, xyz, normal, st, color, (picoIndex_t) numverts );\r
-\r
- if (vertexCombinationHash == NULL)\r
- {\r
- _pico_printf( PICO_ERROR, "Unable to allocate hash bucket entry table" );\r
- PicoFreeVertexCombinationHashTable( hashTable );\r
- PicoFreeModel( picoModel );\r
- lwFreeObject( obj );\r
- return NULL;\r
- }\r
-\r
- /* add the vertex to this surface */\r
- PicoSetSurfaceXYZ( picoSurface, numverts, xyz );\r
-\r
- /* set dummy normal */\r
- PicoSetSurfaceNormal( picoSurface, numverts, normal );\r
-\r
- /* set color */\r
- PicoSetSurfaceColor( picoSurface, 0, numverts, color );\r
-\r
- /* set st coords */\r
- PicoSetSurfaceST( picoSurface, 0, numverts, st );\r
-\r
- /* set index */\r
- PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), (picoIndex_t) numverts );\r
-\r
- numverts++;\r
- }\r
- }\r
- }\r
-\r
- /* free the hashtable */\r
- PicoFreeVertexCombinationHashTable( hashTable );\r
-\r
- /* get next surface */ \r
- surface = surface->next;\r
- }\r
-\r
-#ifdef DEBUG_PM_LWO\r
- load_start = convert_finish = clock();\r
-#endif\r
-\r
- lwFreeObject( obj );\r
-\r
-#ifdef DEBUG_PM_LWO\r
- load_finish = clock();\r
- load_elapsed += (double)(load_finish - load_start) / CLOCKS_PER_SEC;\r
- convert_elapsed = (double)(convert_finish - convert_start) / CLOCKS_PER_SEC;\r
- _pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s) (loading: %-.2fs converting: %-.2fs)\n", load_elapsed + convert_elapsed, load_elapsed, convert_elapsed );\r
-#endif\r
-\r
- /* return the new pico model */\r
- return picoModel;\r
-}\r
-\r
-/* pico file format module definition */\r
-const picoModule_t picoModuleLWO =\r
-{\r
- "1.0", /* module version string */\r
- "LightWave Object", /* module display name */\r
- "Arnout van Meer", /* author's name */\r
- "2003 Arnout van Meer, 2000 Ernie Wright", /* module copyright */\r
- {\r
- "lwo", NULL, NULL, NULL /* default extensions to use */\r
- },\r
- _lwo_canload, /* validation routine */\r
- _lwo_load, /* load routine */\r
- NULL, /* save validation routine */\r
- NULL /* save routine */\r
-};\r
+/* -----------------------------------------------------------------------------
+
+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_LWO_C
+
+/* dependencies */
+#include "picointernal.h"
+#include "lwo/lwo2.h"
+
+/* uncomment when debugging this module */
+/*#define DEBUG_PM_LWO*/
+
+#ifdef DEBUG_PM_LWO
+#include "time.h"
+#endif
+
+/* helper functions */
+static const char *lwo_lwIDToStr( unsigned int lwID )
+{
+ static char lwIDStr[5];
+
+ if (!lwID)
+ {
+ return "n/a";
+ }
+
+ lwIDStr[ 0 ] = (char)((lwID) >> 24);
+ lwIDStr[ 1 ] = (char)((lwID) >> 16);
+ lwIDStr[ 2 ] = (char)((lwID) >> 8);
+ lwIDStr[ 3 ] = (char)((lwID));
+ lwIDStr[ 4 ] = '\0';
+
+ return lwIDStr;
+}
+
+/*
+_lwo_canload()
+validates a LightWave Object model file. btw, i use the
+preceding underscore cause it's a static func referenced
+by one structure only.
+*/
+static int _lwo_canload( PM_PARAMS_CANLOAD )
+{
+ picoMemStream_t *s;
+ unsigned int failID = 0;
+ int failpos = -1;
+ int ret;
+
+ /* create a new pico memorystream */
+ s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );
+ if (s == NULL)
+ {
+ return PICO_PMV_ERROR_MEMORY;
+ }
+
+ ret = lwValidateObject( fileName, s, &failID, &failpos );
+
+ _pico_free_memstream( s );
+
+ return ret;
+}
+
+/*
+_lwo_load()
+loads a LightWave Object model file.
+*/
+static picoModel_t *_lwo_load( PM_PARAMS_LOAD )
+{
+ picoMemStream_t *s;
+ unsigned int failID = 0;
+ int failpos = -1;
+ lwObject *obj;
+ lwSurface *surface;
+ lwLayer *layer;
+ lwPoint *pt;
+ lwPolygon *pol;
+ lwPolVert *v;
+ lwVMapPt *vm;
+ char name[ 64 ];
+ int i, j, k, numverts;
+
+ picoModel_t *picoModel;
+ picoSurface_t *picoSurface;
+ picoShader_t *picoShader;
+ picoVec3_t xyz, normal;
+ picoVec2_t st;
+ picoColor_t color;
+
+ int defaultSTAxis[ 2 ];
+ picoVec2_t defaultXYZtoSTScale;
+
+ picoVertexCombinationHash_t **hashTable;
+ picoVertexCombinationHash_t *vertexCombinationHash;
+
+#ifdef DEBUG_PM_LWO
+ clock_t load_start, load_finish, convert_start, convert_finish;
+ double load_elapsed, convert_elapsed;
+
+ load_start = clock();
+#endif
+
+ /* do frame check */
+ if( frameNum < 0 || frameNum >= 1 )
+ {
+ _pico_printf( PICO_ERROR, "Invalid or out-of-range LWO frame specified" );
+ return NULL;
+ }
+
+ /* create a new pico memorystream */
+ s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );
+ if (s == NULL)
+ {
+ return NULL;
+ }
+
+ obj = lwGetObject( fileName, s, &failID, &failpos );
+
+ _pico_free_memstream( s );
+
+ if( !obj ) {
+ _pico_printf( PICO_ERROR, "Couldn't load LWO file, failed on ID '%s', position %d", lwo_lwIDToStr( failID ), failpos );
+ return NULL;
+ }
+
+#ifdef DEBUG_PM_LWO
+ convert_start = load_finish = clock();
+ load_elapsed = (double)(load_finish - load_start) / CLOCKS_PER_SEC;
+#endif
+
+ /* -------------------------------------------------
+ pico model creation
+ ------------------------------------------------- */
+
+ /* create a 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 );
+ PicoSetModelName( picoModel, fileName );
+ PicoSetModelFileName( picoModel, fileName );
+
+ /* create all polygons from layer[ 0 ] that belong to this surface */
+ layer = &obj->layer[0];
+
+ /* warn the user that other layers are discarded */
+ if (obj->nlayers > 1)
+ {
+ _pico_printf( PICO_WARNING, "LWO loader discards any geometry data not in Layer 1 (%d layers found)", obj->nlayers );
+ }
+
+ /* initialize dummy normal */
+ normal[ 0 ] = normal[ 1 ] = normal[ 2 ] = 0.f;
+
+ /* setup default st map */
+ st[ 0 ] = st[ 1 ] = 0.f; /* st[0] holds max, st[1] holds max par one */
+ defaultSTAxis[ 0 ] = 0;
+ defaultSTAxis[ 1 ] = 1;
+ for( i = 0; i < 3; i++ )
+ {
+ float min = layer->bbox[ i ];
+ float max = layer->bbox[ i + 3 ];
+ float size = max - min;
+
+ if (size > st[ 0 ])
+ {
+ defaultSTAxis[ 1 ] = defaultSTAxis[ 0 ];
+ defaultSTAxis[ 0 ] = i;
+
+ st[ 1 ] = st[ 0 ];
+ st[ 0 ] = size;
+ }
+ else if (size > st[ 1 ])
+ {
+ defaultSTAxis[ 1 ] = i;
+ st[ 1 ] = size;
+ }
+ }
+ defaultXYZtoSTScale[ 0 ] = 4.f / st[ 0 ];
+ defaultXYZtoSTScale[ 1 ] = 4.f / st[ 1 ];
+
+ /* LWO surfaces become pico surfaces */
+ surface = obj->surf;
+ while (surface)
+ {
+ /* allocate new pico surface */
+ picoSurface = PicoNewSurface( picoModel );
+ if (picoSurface == NULL)
+ {
+ _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
+ PicoFreeModel( picoModel );
+ lwFreeObject( obj );
+ return NULL;
+ }
+
+ /* LWO model surfaces are all triangle meshes */
+ PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
+
+ /* set surface name */
+ PicoSetSurfaceName( picoSurface, surface->name );
+
+ /* create new pico shader */
+ picoShader = PicoNewShader( picoModel );
+ if (picoShader == NULL)
+ {
+ _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
+ PicoFreeModel( picoModel );
+ lwFreeObject( obj );
+ return NULL;
+ }
+
+ /* detox and set shader name */
+ strncpy( name, surface->name, sizeof(name) );
+ _pico_setfext( name, "" );
+ _pico_unixify( name );
+ PicoSetShaderName( picoShader, name );
+
+ /* associate current surface with newly created shader */
+ PicoSetSurfaceShader( picoSurface, picoShader );
+
+ /* copy indices and vertex data */
+ numverts = 0;
+
+ hashTable = PicoNewVertexCombinationHashTable();
+
+ if (hashTable == NULL)
+ {
+ _pico_printf( PICO_ERROR, "Unable to allocate hash table" );
+ PicoFreeModel( picoModel );
+ lwFreeObject( obj );
+ return NULL;
+ }
+
+ for( i = 0, pol = layer->polygon.pol; i < layer->polygon.count; i++, pol++ )
+ {
+ /* does this polygon belong to this surface? */
+ if (pol->surf != surface)
+ continue;
+
+ /* we only support polygons of the FACE type */
+ if (pol->type != ID_FACE)
+ {
+ _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it's type != FACE (%s)", lwo_lwIDToStr( pol->type ) );
+ continue;
+ }
+
+ /* NOTE: LWO has support for non-convex polygons, do we want to store them as well? */
+ if (pol->nverts != 3)
+ {
+ _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it has != 3 verts (%d)", pol->nverts );
+ continue;
+ }
+
+ for( j = 0, v = pol->v; j < 3; j++, v++ )
+ {
+ pt = &layer->point.pt[ v->index ];
+
+ /* setup data */
+ xyz[ 0 ] = pt->pos[ 0 ];
+ xyz[ 1 ] = pt->pos[ 2 ];
+ xyz[ 2 ] = pt->pos[ 1 ];
+
+ normal[ 0 ] = v->norm[ 0 ];
+ normal[ 1 ] = v->norm[ 2 ];
+ normal[ 2 ] = v->norm[ 1 ];
+
+ st[ 0 ] = xyz[ defaultSTAxis[ 0 ] ] * defaultXYZtoSTScale[ 0 ];
+ st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ];
+
+ color[ 0 ] = (picoByte_t)(surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
+ color[ 1 ] = (picoByte_t)(surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
+ color[ 2 ] = (picoByte_t)(surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
+ color[ 3 ] = 0xFF;
+
+ /* set from points */
+ for( k = 0, vm = pt->vm; k < pt->nvmaps; k++, vm++ )
+ {
+ if (vm->vmap->type == LWID_('T','X','U','V'))
+ {
+ /* set st coords */
+ st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
+ st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
+ }
+ else if (vm->vmap->type == LWID_('R','G','B','A'))
+ {
+ /* set rgba */
+ color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
+ color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
+ color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
+ color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);
+ }
+ }
+
+ /* override with polygon data */
+ for( k = 0, vm = v->vm; k < v->nvmaps; k++, vm++ )
+ {
+ if (vm->vmap->type == LWID_('T','X','U','V'))
+ {
+ /* set st coords */
+ st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
+ st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
+ }
+ else if (vm->vmap->type == LWID_('R','G','B','A'))
+ {
+ /* set rgba */
+ color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
+ color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
+ color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
+ color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);
+ }
+ }
+
+ /* find vertex in this surface and if we can't find it there create it */
+ vertexCombinationHash = PicoFindVertexCombinationInHashTable( hashTable, xyz, normal, st, color );
+
+ if (vertexCombinationHash)
+ {
+ /* found an existing one */
+ PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), vertexCombinationHash->index );
+ }
+ else
+ {
+ /* it is a new one */
+ vertexCombinationHash = PicoAddVertexCombinationToHashTable( hashTable, xyz, normal, st, color, (picoIndex_t) numverts );
+
+ if (vertexCombinationHash == NULL)
+ {
+ _pico_printf( PICO_ERROR, "Unable to allocate hash bucket entry table" );
+ PicoFreeVertexCombinationHashTable( hashTable );
+ PicoFreeModel( picoModel );
+ lwFreeObject( obj );
+ return NULL;
+ }
+
+ /* add the vertex to this surface */
+ PicoSetSurfaceXYZ( picoSurface, numverts, xyz );
+
+ /* set dummy normal */
+ PicoSetSurfaceNormal( picoSurface, numverts, normal );
+
+ /* set color */
+ PicoSetSurfaceColor( picoSurface, 0, numverts, color );
+
+ /* set st coords */
+ PicoSetSurfaceST( picoSurface, 0, numverts, st );
+
+ /* set index */
+ PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), (picoIndex_t) numverts );
+
+ numverts++;
+ }
+ }
+ }
+
+ /* free the hashtable */
+ PicoFreeVertexCombinationHashTable( hashTable );
+
+ /* get next surface */
+ surface = surface->next;
+ }
+
+#ifdef DEBUG_PM_LWO
+ load_start = convert_finish = clock();
+#endif
+
+ lwFreeObject( obj );
+
+#ifdef DEBUG_PM_LWO
+ load_finish = clock();
+ load_elapsed += (double)(load_finish - load_start) / CLOCKS_PER_SEC;
+ convert_elapsed = (double)(convert_finish - convert_start) / CLOCKS_PER_SEC;
+ _pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s) (loading: %-.2fs converting: %-.2fs)\n", load_elapsed + convert_elapsed, load_elapsed, convert_elapsed );
+#endif
+
+ /* return the new pico model */
+ return picoModel;
+}
+
+/* pico file format module definition */
+const picoModule_t picoModuleLWO =
+{
+ "1.0", /* module version string */
+ "LightWave Object", /* module display name */
+ "Arnout van Meer", /* author's name */
+ "2003 Arnout van Meer, 2000 Ernie Wright", /* module copyright */
+ {
+ "lwo", NULL, NULL, NULL /* default extensions to use */
+ },
+ _lwo_canload, /* validation routine */
+ _lwo_load, /* load routine */
+ NULL, /* save validation routine */
+ NULL /* save routine */
+};