1 /* -----------------------------------------------------------------------------
5 Copyright (c) 2002, Randy Reddig & seaw0lf
8 Redistribution and use in source and binary forms, with or without modification,
9 are permitted provided that the following conditions are met:
11 Redistributions of source code must retain the above copyright notice, this list
12 of conditions and the following disclaimer.
14 Redistributions in binary form must reproduce the above copyright notice, this
15 list of conditions and the following disclaimer in the documentation and/or
16 other materials provided with the distribution.
18 Neither the names of the copyright holders nor the names of its contributors may
19 be used to endorse or promote products derived from this software without
20 specific prior written permission.
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
26 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 ----------------------------------------------------------------------------- */
39 #include "picointernal.h"
42 /* uncomment when debugging this module */
43 /*#define DEBUG_PM_LWO*/
49 /* helper functions */
50 static const char *lwo_lwIDToStr( unsigned int lwID )
52 static char lwIDStr[5];
59 lwIDStr[ 0 ] = (char)((lwID) >> 24);
60 lwIDStr[ 1 ] = (char)((lwID) >> 16);
61 lwIDStr[ 2 ] = (char)((lwID) >> 8);
62 lwIDStr[ 3 ] = (char)((lwID));
70 validates a LightWave Object model file. btw, i use the
71 preceding underscore cause it's a static func referenced
72 by one structure only.
74 static int _lwo_canload( PM_PARAMS_CANLOAD )
77 unsigned int failID = 0;
81 /* create a new pico memorystream */
82 s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );
85 return PICO_PMV_ERROR_MEMORY;
88 ret = lwValidateObject( fileName, s, &failID, &failpos );
90 _pico_free_memstream( s );
97 loads a LightWave Object model file.
99 static picoModel_t *_lwo_load( PM_PARAMS_LOAD )
102 unsigned int failID = 0;
112 int i, j, k, numverts;
114 picoModel_t *picoModel;
115 picoSurface_t *picoSurface;
116 picoShader_t *picoShader;
117 picoVec3_t xyz, normal;
121 int defaultSTAxis[ 2 ];
122 picoVec2_t defaultXYZtoSTScale;
124 picoVertexCombinationHash_t **hashTable;
125 picoVertexCombinationHash_t *vertexCombinationHash;
128 clock_t load_start, load_finish, convert_start, convert_finish;
129 double load_elapsed, convert_elapsed;
131 load_start = clock();
135 if( frameNum < 0 || frameNum >= 1 )
137 _pico_printf( PICO_ERROR, "Invalid or out-of-range LWO frame specified" );
141 /* create a new pico memorystream */
142 s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );
148 obj = lwGetObject( fileName, s, &failID, &failpos );
150 _pico_free_memstream( s );
153 _pico_printf( PICO_ERROR, "Couldn't load LWO file, failed on ID '%s', position %d", lwo_lwIDToStr( failID ), failpos );
158 convert_start = load_finish = clock();
159 load_elapsed = (double)(load_finish - load_start) / CLOCKS_PER_SEC;
162 /* -------------------------------------------------
164 ------------------------------------------------- */
166 /* create a new pico model */
167 picoModel = PicoNewModel();
168 if (picoModel == NULL)
170 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
175 PicoSetModelFrameNum( picoModel, frameNum );
176 PicoSetModelNumFrames( picoModel, 1 );
177 PicoSetModelName( picoModel, fileName );
178 PicoSetModelFileName( picoModel, fileName );
180 /* create all polygons from layer[ 0 ] that belong to this surface */
181 layer = &obj->layer[0];
183 /* warn the user that other layers are discarded */
184 if (obj->nlayers > 1)
186 _pico_printf( PICO_WARNING, "LWO loader discards any geometry data not in Layer 1 (%d layers found)", obj->nlayers );
189 /* initialize dummy normal */
190 normal[ 0 ] = normal[ 1 ] = normal[ 2 ] = 0.f;
192 /* setup default st map */
193 st[ 0 ] = st[ 1 ] = 0.f; /* st[0] holds max, st[1] holds max par one */
194 defaultSTAxis[ 0 ] = 0;
195 defaultSTAxis[ 1 ] = 1;
196 for( i = 0; i < 3; i++ )
198 float min = layer->bbox[ i ];
199 float max = layer->bbox[ i + 3 ];
200 float size = max - min;
204 defaultSTAxis[ 1 ] = defaultSTAxis[ 0 ];
205 defaultSTAxis[ 0 ] = i;
210 else if (size > st[ 1 ])
212 defaultSTAxis[ 1 ] = i;
216 defaultXYZtoSTScale[ 0 ] = 4.f / st[ 0 ];
217 defaultXYZtoSTScale[ 1 ] = 4.f / st[ 1 ];
219 /* LWO surfaces become pico surfaces */
223 /* allocate new pico surface */
224 picoSurface = PicoNewSurface( picoModel );
225 if (picoSurface == NULL)
227 _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
228 PicoFreeModel( picoModel );
233 /* LWO model surfaces are all triangle meshes */
234 PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
236 /* set surface name */
237 PicoSetSurfaceName( picoSurface, surface->name );
239 /* create new pico shader */
240 picoShader = PicoNewShader( picoModel );
241 if (picoShader == NULL)
243 _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
244 PicoFreeModel( picoModel );
249 /* detox and set shader name */
250 strncpy( name, surface->name, sizeof(name) );
251 _pico_first_token( name );
252 _pico_setfext( name, "" );
253 _pico_unixify( name );
254 PicoSetShaderName( picoShader, name );
256 /* associate current surface with newly created shader */
257 PicoSetSurfaceShader( picoSurface, picoShader );
259 /* copy indices and vertex data */
262 hashTable = PicoNewVertexCombinationHashTable();
264 if (hashTable == NULL)
266 _pico_printf( PICO_ERROR, "Unable to allocate hash table" );
267 PicoFreeModel( picoModel );
272 for( i = 0, pol = layer->polygon.pol; i < layer->polygon.count; i++, pol++ )
274 /* does this polygon belong to this surface? */
275 if (pol->surf != surface)
278 /* we only support polygons of the FACE type */
279 if (pol->type != ID_FACE)
281 _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it's type != FACE (%s)", lwo_lwIDToStr( pol->type ) );
285 /* NOTE: LWO has support for non-convex polygons, do we want to store them as well? */
286 if (pol->nverts != 3)
288 _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it has != 3 verts (%d)", pol->nverts );
292 for( j = 0, v = pol->v; j < 3; j++, v++ )
294 pt = &layer->point.pt[ v->index ];
297 xyz[ 0 ] = pt->pos[ 0 ];
298 xyz[ 1 ] = pt->pos[ 2 ];
299 xyz[ 2 ] = pt->pos[ 1 ];
301 /* doom3 lwo data doesn't seem to have smoothing-angle information */
303 if(surface->smooth <= 0)
305 /* use face normals */
306 normal[ 0 ] = v->norm[ 0 ];
307 normal[ 1 ] = v->norm[ 2 ];
308 normal[ 2 ] = v->norm[ 1 ];
313 /* smooth normals later */
319 st[ 0 ] = xyz[ defaultSTAxis[ 0 ] ] * defaultXYZtoSTScale[ 0 ];
320 st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ];
322 color[ 0 ] = (picoByte_t)(surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
323 color[ 1 ] = (picoByte_t)(surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
324 color[ 2 ] = (picoByte_t)(surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
327 /* set from points */
328 for( k = 0, vm = pt->vm; k < pt->nvmaps; k++, vm++ )
330 if (vm->vmap->type == LWID_('T','X','U','V'))
333 st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
334 st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
336 else if (vm->vmap->type == LWID_('R','G','B','A'))
339 color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
340 color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
341 color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
342 color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);
346 /* override with polygon data */
347 for( k = 0, vm = v->vm; k < v->nvmaps; k++, vm++ )
349 if (vm->vmap->type == LWID_('T','X','U','V'))
352 st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
353 st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
355 else if (vm->vmap->type == LWID_('R','G','B','A'))
358 color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
359 color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
360 color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
361 color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);
365 /* find vertex in this surface and if we can't find it there create it */
366 vertexCombinationHash = PicoFindVertexCombinationInHashTable( hashTable, xyz, normal, st, color );
368 if (vertexCombinationHash)
370 /* found an existing one */
371 PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), vertexCombinationHash->index );
375 /* it is a new one */
376 vertexCombinationHash = PicoAddVertexCombinationToHashTable( hashTable, xyz, normal, st, color, (picoIndex_t) numverts );
378 if (vertexCombinationHash == NULL)
380 _pico_printf( PICO_ERROR, "Unable to allocate hash bucket entry table" );
381 PicoFreeVertexCombinationHashTable( hashTable );
382 PicoFreeModel( picoModel );
387 /* add the vertex to this surface */
388 PicoSetSurfaceXYZ( picoSurface, numverts, xyz );
390 /* set dummy normal */
391 PicoSetSurfaceNormal( picoSurface, numverts, normal );
394 PicoSetSurfaceColor( picoSurface, 0, numverts, color );
397 PicoSetSurfaceST( picoSurface, 0, numverts, st );
400 PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), (picoIndex_t) numverts );
407 /* free the hashtable */
408 PicoFreeVertexCombinationHashTable( hashTable );
410 /* get next surface */
411 surface = surface->next;
415 load_start = convert_finish = clock();
421 load_finish = clock();
422 load_elapsed += (double)(load_finish - load_start) / CLOCKS_PER_SEC;
423 convert_elapsed = (double)(convert_finish - convert_start) / CLOCKS_PER_SEC;
424 _pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s) (loading: %-.2fs converting: %-.2fs)\n", load_elapsed + convert_elapsed, load_elapsed, convert_elapsed );
427 /* return the new pico model */
431 /* pico file format module definition */
432 const picoModule_t picoModuleLWO =
434 "1.0", /* module version string */
435 "LightWave Object", /* module display name */
436 "Arnout van Meer", /* author's name */
437 "2003 Arnout van Meer, 2000 Ernie Wright", /* module copyright */
439 "lwo", NULL, NULL, NULL /* default extensions to use */
441 _lwo_canload, /* validation routine */
442 _lwo_load, /* load routine */
443 NULL, /* save validation routine */
444 NULL /* save routine */