]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_lwo.c
ok
[xonotic/netradiant.git] / libs / picomodel / pm_lwo.c
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 */
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 */
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 */
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 };