1 /* -----------------------------------------------------------------------------
2
3 PicoModel Library
4
5 Copyright (c) 2002, Randy Reddig & seaw0lf
6 All rights reserved.
7
8 Redistribution and use in source and binary forms, with or without modification,
9 are permitted provided that the following conditions are met:
10
11 Redistributions of source code must retain the above copyright notice, this list
12 of conditions and the following disclaimer.
13
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.
17
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.
21
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.
32
33 ----------------------------------------------------------------------------- */
34
35 /* marker */
36 #define PM_LWO_C
37
38 /* dependencies */
39 #include "picointernal.h"
40 #include "lwo/lwo2.h"
41
42 /* uncomment when debugging this module */
43 /*#define DEBUG_PM_LWO*/
44
45 #ifdef DEBUG_PM_LWO
46 #include "time.h"
47 #endif
48
49 /* helper functions */
lwo_lwIDToStr(unsigned int lwID)50 static const char *lwo_lwIDToStr( unsigned int lwID )
51 {
52 static char lwIDStr[5];
53
54 if (!lwID)
55 {
56 return "n/a";
57 }
58
59 lwIDStr[ 0 ] = (char)((lwID) >> 24);
60 lwIDStr[ 1 ] = (char)((lwID) >> 16);
61 lwIDStr[ 2 ] = (char)((lwID) >> 8);
62 lwIDStr[ 3 ] = (char)((lwID));
63 lwIDStr[ 4 ] = '\0';
64
65 return lwIDStr;
66 }
67
68 /*
69 _lwo_canload()
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.
73 */
_lwo_canload(PM_PARAMS_CANLOAD)74 static int _lwo_canload( PM_PARAMS_CANLOAD )
75 {
76 picoMemStream_t *s;
77 unsigned int failID = 0;
78 int failpos = -1;
79 int ret;
80
81 /* create a new pico memorystream */
82 s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );
83 if (s == NULL)
84 {
85 return PICO_PMV_ERROR_MEMORY;
86 }
87
88 ret = lwValidateObject( fileName, s, &failID, &failpos );
89
90 _pico_free_memstream( s );
91
92 return ret;
93 }
94
95 /*
96 _lwo_load()
97 loads a LightWave Object model file.
98 */
_lwo_load(PM_PARAMS_LOAD)99 static picoModel_t *_lwo_load( PM_PARAMS_LOAD )
100 {
101 picoMemStream_t *s;
102 unsigned int failID = 0;
103 int failpos = -1;
104 lwObject *obj;
105 lwSurface *surface;
106 lwLayer *layer;
107 lwPoint *pt;
108 lwPolygon *pol;
109 lwPolVert *v;
110 lwVMapPt *vm;
111 char name[ 256 ];
112 int i, j, k, numverts;
113
114 picoModel_t *picoModel;
115 picoSurface_t *picoSurface;
116 picoShader_t *picoShader;
117 picoVec3_t xyz, normal;
118 picoVec2_t st;
119 picoColor_t color;
120
121 int defaultSTAxis[ 2 ];
122 picoVec2_t defaultXYZtoSTScale;
123
124 picoVertexCombinationHash_t **hashTable;
125 picoVertexCombinationHash_t *vertexCombinationHash;
126
127 #ifdef DEBUG_PM_LWO
128 clock_t load_start, load_finish, convert_start, convert_finish;
129 double load_elapsed, convert_elapsed;
130
131 load_start = clock();
132 #endif
133
134 /* do frame check */
135 if( frameNum < 0 || frameNum >= 1 )
136 {
137 _pico_printf( PICO_ERROR, "Invalid or out-of-range LWO frame specified" );
138 return NULL;
139 }
140
141 /* create a new pico memorystream */
142 s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );
143 if (s == NULL)
144 {
145 return NULL;
146 }
147
148 obj = lwGetObject( fileName, s, &failID, &failpos );
149
150 _pico_free_memstream( s );
151
152 if( !obj ) {
153 _pico_printf( PICO_ERROR, "Couldn't load LWO file, failed on ID '%s', position %d", lwo_lwIDToStr( failID ), failpos );
154 return NULL;
155 }
156
157 #ifdef DEBUG_PM_LWO
158 convert_start = load_finish = clock();
159 load_elapsed = (double)(load_finish - load_start) / CLOCKS_PER_SEC;
160 #endif
161
162 /* -------------------------------------------------
163 pico model creation
164 ------------------------------------------------- */
165
166 /* create a new pico model */
167 picoModel = PicoNewModel();
168 if (picoModel == NULL)
169 {
170 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
171 return NULL;
172 }
173
174 /* do model setup */
175 PicoSetModelFrameNum( picoModel, frameNum );
176 PicoSetModelNumFrames( picoModel, 1 );
177 PicoSetModelName( picoModel, fileName );
178 PicoSetModelFileName( picoModel, fileName );
179
180 /* create all polygons from layer[ 0 ] that belong to this surface */
181 layer = &obj->layer[0];
182
183 /* warn the user that other layers are discarded */
184 if (obj->nlayers > 1)
185 {
186 _pico_printf( PICO_WARNING, "LWO loader discards any geometry data not in Layer 1 (%d layers found)", obj->nlayers );
187 }
188
189 /* initialize dummy normal */
190 normal[ 0 ] = normal[ 1 ] = normal[ 2 ] = 0.f;
191
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++ )
197 {
198 float min = layer->bbox[ i ];
199 float max = layer->bbox[ i + 3 ];
200 float size = max - min;
201
202 if (size > st[ 0 ])
203 {
204 defaultSTAxis[ 1 ] = defaultSTAxis[ 0 ];
205 defaultSTAxis[ 0 ] = i;
206
207 st[ 1 ] = st[ 0 ];
208 st[ 0 ] = size;
209 }
210 else if (size > st[ 1 ])
211 {
212 defaultSTAxis[ 1 ] = i;
213 st[ 1 ] = size;
214 }
215 }
216 defaultXYZtoSTScale[ 0 ] = 4.f / st[ 0 ];
217 defaultXYZtoSTScale[ 1 ] = 4.f / st[ 1 ];
218
219 /* LWO surfaces become pico surfaces */
220 surface = obj->surf;
221 while (surface)
222 {
223 /* allocate new pico surface */
224 picoSurface = PicoNewSurface( picoModel );
225 if (picoSurface == NULL)
226 {
227 _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
228 PicoFreeModel( picoModel );
229 lwFreeObject( obj );
230 return NULL;
231 }
232
233 /* LWO model surfaces are all triangle meshes */
234 PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
235
236 /* set surface name */
237 PicoSetSurfaceName( picoSurface, surface->name );
238
239 /* create new pico shader */
240 picoShader = PicoNewShader( picoModel );
241 if (picoShader == NULL)
242 {
243 _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
244 PicoFreeModel( picoModel );
245 lwFreeObject( obj );
246 return NULL;
247 }
248
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 );
255
256 /* associate current surface with newly created shader */
257 PicoSetSurfaceShader( picoSurface, picoShader );
258
259 /* copy indices and vertex data */
260 numverts = 0;
261
262 hashTable = PicoNewVertexCombinationHashTable();
263
264 if (hashTable == NULL)
265 {
266 _pico_printf( PICO_ERROR, "Unable to allocate hash table" );
267 PicoFreeModel( picoModel );
268 lwFreeObject( obj );
269 return NULL;
270 }
271
272 for( i = 0, pol = layer->polygon.pol; i < layer->polygon.count; i++, pol++ )
273 {
274 /* does this polygon belong to this surface? */
275 if (pol->surf != surface)
276 continue;
277
278 /* we only support polygons of the FACE type */
279 if (pol->type != ID_FACE)
280 {
281 _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it's type != FACE (%s)", lwo_lwIDToStr( pol->type ) );
282 continue;
283 }
284
285 /* NOTE: LWO has support for non-convex polygons, do we want to store them as well? */
286 if (pol->nverts != 3)
287 {
288 _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it has != 3 verts (%d)", pol->nverts );
289 continue;
290 }
291
292 for( j = 0, v = pol->v; j < 3; j++, v++ )
293 {
294 pt = &layer->point.pt[ v->index ];
295
296 /* setup data */
297 xyz[ 0 ] = pt->pos[ 0 ];
298 xyz[ 1 ] = pt->pos[ 2 ];
299 xyz[ 2 ] = pt->pos[ 1 ];
300
301 /* doom3 lwo data doesn't seem to have smoothing-angle information */
302 #if 0
303 if(surface->smooth <= 0)
304 {
305 /* use face normals */
306 normal[ 0 ] = v->norm[ 0 ];
307 normal[ 1 ] = v->norm[ 2 ];
308 normal[ 2 ] = v->norm[ 1 ];
309 }
310 else
311 #endif
312 {
313 /* smooth normals later */
314 normal[ 0 ] = 0;
315 normal[ 1 ] = 0;
316 normal[ 2 ] = 0;
317 }
318
319 st[ 0 ] = xyz[ defaultSTAxis[ 0 ] ] * defaultXYZtoSTScale[ 0 ];
320 st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ];
321
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);
325 color[ 3 ] = 0xFF;
326
327 /* set from points */
328 for( k = 0, vm = pt->vm; k < pt->nvmaps; k++, vm++ )
329 {
330 if (vm->vmap->type == LWID_('T','X','U','V'))
331 {
332 /* set st coords */
333 st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
334 st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
335 }
336 else if (vm->vmap->type == LWID_('R','G','B','A'))
337 {
338 /* set rgba */
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);
343 }
344 }
345
346 /* override with polygon data */
347 for( k = 0, vm = v->vm; k < v->nvmaps; k++, vm++ )
348 {
349 if (vm->vmap->type == LWID_('T','X','U','V'))
350 {
351 /* set st coords */
352 st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
353 st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
354 }
355 else if (vm->vmap->type == LWID_('R','G','B','A'))
356 {
357 /* set rgba */
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);
362 }
363 }
364
365 /* find vertex in this surface and if we can't find it there create it */
366 vertexCombinationHash = PicoFindVertexCombinationInHashTable( hashTable, xyz, normal, st, color );
367
368 if (vertexCombinationHash)
369 {
370 /* found an existing one */
371 PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), vertexCombinationHash->index );
372 }
373 else
374 {
375 /* it is a new one */
376 vertexCombinationHash = PicoAddVertexCombinationToHashTable( hashTable, xyz, normal, st, color, (picoIndex_t) numverts );
377
378 if (vertexCombinationHash == NULL)
379 {
380 _pico_printf( PICO_ERROR, "Unable to allocate hash bucket entry table" );
381 PicoFreeVertexCombinationHashTable( hashTable );
382 PicoFreeModel( picoModel );
383 lwFreeObject( obj );
384 return NULL;
385 }
386
387 /* add the vertex to this surface */
388 PicoSetSurfaceXYZ( picoSurface, numverts, xyz );
389
390 /* set dummy normal */
391 PicoSetSurfaceNormal( picoSurface, numverts, normal );
392
393 /* set color */
394 PicoSetSurfaceColor( picoSurface, 0, numverts, color );
395
396 /* set st coords */
397 PicoSetSurfaceST( picoSurface, 0, numverts, st );
398
399 /* set index */
400 PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), (picoIndex_t) numverts );
401
402 numverts++;
403 }
404 }
405 }
406
407 /* free the hashtable */
408 PicoFreeVertexCombinationHashTable( hashTable );
409
410 /* get next surface */
411 surface = surface->next;
412 }
413
414 #ifdef DEBUG_PM_LWO
415 load_start = convert_finish = clock();
416 #endif
417
418 lwFreeObject( obj );
419
420 #ifdef DEBUG_PM_LWO
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 );
425 #endif
426
427 /* return the new pico model */
428 return picoModel;
429 }
430
431 /* pico file format module definition */
432 const picoModule_t picoModuleLWO =
433 {
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 */
438 {
439 "lwo", NULL, NULL, NULL /* default extensions to use */
440 },
441 _lwo_canload, /* validation routine */
442 _lwo_load, /* load routine */
443 NULL, /* save validation routine */
444 NULL /* save routine */
445 };
446