]> git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_lwo.c
* fixed warning
[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[ 64 ];
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_setfext( name, "" );
252                 _pico_unixify( name );
253                 PicoSetShaderName( picoShader, name );
254
255                 /* associate current surface with newly created shader */
256                 PicoSetSurfaceShader( picoSurface, picoShader );
257
258                 /* copy indices and vertex data */
259                 numverts = 0;
260
261                 hashTable = PicoNewVertexCombinationHashTable();
262
263                 if (hashTable == NULL)
264                 {
265                         _pico_printf( PICO_ERROR, "Unable to allocate hash table" );
266                         PicoFreeModel( picoModel );
267                         lwFreeObject( obj );
268                         return NULL;
269                 }
270
271                 for( i = 0, pol = layer->polygon.pol; i < layer->polygon.count; i++, pol++ )
272                 {
273                         /* does this polygon belong to this surface? */
274                         if (pol->surf != surface)
275                                 continue;
276
277                         /* we only support polygons of the FACE type */
278                         if (pol->type != ID_FACE)
279                         {
280                                 _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it's type != FACE (%s)", lwo_lwIDToStr( pol->type ) );
281                                 continue;
282                         }
283
284                         /* NOTE: LWO has support for non-convex polygons, do we want to store them as well? */
285                         if (pol->nverts != 3)
286                         {
287                                 _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it has != 3 verts (%d)", pol->nverts );
288                                 continue;
289                         }
290
291                         for( j = 0, v = pol->v; j < 3; j++, v++ )
292                         {
293                                 pt = &layer->point.pt[ v->index ];
294
295                                 /* setup data */
296                                 xyz[ 0 ] = pt->pos[ 0 ];
297                                 xyz[ 1 ] = pt->pos[ 2 ];
298                                 xyz[ 2 ] = pt->pos[ 1 ];
299
300                                 normal[ 0 ] = v->norm[ 0 ];
301                                 normal[ 1 ] = v->norm[ 2 ];
302                                 normal[ 2 ] = v->norm[ 1 ];
303
304                                 st[ 0 ] = xyz[ defaultSTAxis[ 0 ] ] * defaultXYZtoSTScale[ 0 ];
305                                 st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ];
306
307                                 color[ 0 ] = (picoByte_t)(surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
308                                 color[ 1 ] = (picoByte_t)(surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
309                                 color[ 2 ] = (picoByte_t)(surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
310                                 color[ 3 ] = 0xFF;
311
312                                 /* set from points */
313                                 for( k = 0, vm = pt->vm; k < pt->nvmaps; k++, vm++ )
314                                 {
315                                         if (vm->vmap->type == LWID_('T','X','U','V'))
316                                         {
317                                                 /* set st coords */
318                                                 st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
319                                                 st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
320                                         }
321                                         else if (vm->vmap->type == LWID_('R','G','B','A'))
322                                         {
323                                                 /* set rgba */
324                                                 color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
325                                                 color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
326                                                 color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
327                                                 color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);
328                                         }
329                                 }
330
331                                 /* override with polygon data */
332                                 for( k = 0, vm = v->vm; k < v->nvmaps; k++, vm++ )
333                                 {
334                                         if (vm->vmap->type == LWID_('T','X','U','V'))
335                                         {
336                                                 /* set st coords */
337                                                 st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
338                                                 st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
339                                         }
340                                         else if (vm->vmap->type == LWID_('R','G','B','A'))
341                                         {
342                                                 /* set rgba */
343                                                 color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
344                                                 color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
345                                                 color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
346                                                 color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);
347                                         }
348                                 }
349
350                                 /* find vertex in this surface and if we can't find it there create it */
351                                 vertexCombinationHash = PicoFindVertexCombinationInHashTable( hashTable, xyz, normal, st, color );
352
353                                 if (vertexCombinationHash)
354                                 {
355                                         /* found an existing one */
356                                         PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), vertexCombinationHash->index );
357                                 }
358                                 else
359                                 {
360                                         /* it is a new one */
361                                         vertexCombinationHash = PicoAddVertexCombinationToHashTable( hashTable, xyz, normal, st, color, (picoIndex_t) numverts );
362
363                                         if (vertexCombinationHash == NULL)
364                                         {
365                                                 _pico_printf( PICO_ERROR, "Unable to allocate hash bucket entry table" );
366                                                 PicoFreeVertexCombinationHashTable( hashTable );
367                                                 PicoFreeModel( picoModel );
368                                                 lwFreeObject( obj );
369                                                 return NULL;
370                                         }
371
372                                         /* add the vertex to this surface */
373                                         PicoSetSurfaceXYZ( picoSurface, numverts, xyz );
374
375                                         /* set dummy normal */
376                                         PicoSetSurfaceNormal( picoSurface, numverts, normal );
377
378                                         /* set color */
379                                         PicoSetSurfaceColor( picoSurface, 0, numverts, color );
380
381                                         /* set st coords */
382                                         PicoSetSurfaceST( picoSurface, 0, numverts, st );
383
384                                         /* set index */
385                                         PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), (picoIndex_t) numverts );
386
387                                         numverts++;
388                                 }
389                         }
390                 }
391
392                 /* free the hashtable */
393                 PicoFreeVertexCombinationHashTable( hashTable );
394
395                 /* get next surface */          
396                 surface = surface->next;
397         }
398
399 #ifdef DEBUG_PM_LWO
400         load_start = convert_finish = clock();
401 #endif
402
403         lwFreeObject( obj );
404
405 #ifdef DEBUG_PM_LWO
406         load_finish = clock();
407         load_elapsed += (double)(load_finish - load_start) / CLOCKS_PER_SEC;
408         convert_elapsed = (double)(convert_finish - convert_start) / CLOCKS_PER_SEC;
409         _pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s) (loading: %-.2fs converting: %-.2fs)\n", load_elapsed + convert_elapsed, load_elapsed, convert_elapsed  );
410 #endif
411
412         /* return the new pico model */
413         return picoModel;
414 }
415
416 /* pico file format module definition */
417 const picoModule_t picoModuleLWO =
418 {
419         "1.0",                                          /* module version string */
420         "LightWave Object",                     /* module display name */
421         "Arnout van Meer",                      /* author's name */
422         "2003 Arnout van Meer, 2000 Ernie Wright",              /* module copyright */
423         {
424                 "lwo", NULL, NULL, NULL /* default extensions to use */
425         },
426         _lwo_canload,                           /* validation routine */
427         _lwo_load,                                      /* load routine */
428          NULL,                                          /* save validation routine */
429          NULL                                           /* save routine */
430 };