]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_lwo.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / libs / picomodel / pm_lwo.c
1 /* -----------------------------------------------------------------------------\r
2 \r
3 PicoModel Library \r
4 \r
5 Copyright (c) 2002, Randy Reddig & seaw0lf\r
6 All rights reserved.\r
7 \r
8 Redistribution and use in source and binary forms, with or without modification,\r
9 are permitted provided that the following conditions are met:\r
10 \r
11 Redistributions of source code must retain the above copyright notice, this list\r
12 of conditions and the following disclaimer.\r
13 \r
14 Redistributions in binary form must reproduce the above copyright notice, this\r
15 list of conditions and the following disclaimer in the documentation and/or\r
16 other materials provided with the distribution.\r
17 \r
18 Neither the names of the copyright holders nor the names of its contributors may\r
19 be used to endorse or promote products derived from this software without\r
20 specific prior written permission. \r
21 \r
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND\r
23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
24 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
25 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\r
26 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\r
29 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
32 \r
33 ----------------------------------------------------------------------------- */\r
34 \r
35 /* marker */\r
36 #define PM_LWO_C\r
37 \r
38 /* dependencies */\r
39 #include "picointernal.h"\r
40 #include "lwo/lwo2.h"\r
41 \r
42 /* uncomment when debugging this module */\r
43 /*#define DEBUG_PM_LWO*/\r
44 \r
45 #ifdef DEBUG_PM_LWO\r
46 #include "time.h"\r
47 #endif\r
48 \r
49 /* helper functions */\r
50 static const char *lwo_lwIDToStr( unsigned int lwID )\r
51 {\r
52         static char lwIDStr[5];\r
53 \r
54         if (!lwID)\r
55         {\r
56                 return "n/a";\r
57         }\r
58 \r
59         lwIDStr[ 0 ] = (char)((lwID) >> 24);\r
60         lwIDStr[ 1 ] = (char)((lwID) >> 16);\r
61         lwIDStr[ 2 ] = (char)((lwID) >> 8);\r
62         lwIDStr[ 3 ] = (char)((lwID));\r
63         lwIDStr[ 4 ] = '\0';\r
64 \r
65         return lwIDStr;\r
66 }\r
67 \r
68 /*\r
69 _lwo_canload()\r
70 validates a LightWave Object model file. btw, i use the\r
71 preceding underscore cause it's a static func referenced\r
72 by one structure only.\r
73 */\r
74 static int _lwo_canload( PM_PARAMS_CANLOAD )\r
75 {\r
76         picoMemStream_t *s;\r
77         unsigned int failID = 0;\r
78         int failpos = -1;\r
79         int ret;\r
80 \r
81         /* create a new pico memorystream */\r
82         s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );\r
83         if (s == NULL)\r
84         {\r
85                 return PICO_PMV_ERROR_MEMORY;\r
86         }\r
87 \r
88         ret = lwValidateObject( fileName, s, &failID, &failpos );\r
89 \r
90         _pico_free_memstream( s );\r
91 \r
92         return ret;\r
93 }\r
94 \r
95 /*\r
96 _lwo_load()\r
97 loads a LightWave Object model file.\r
98 */\r
99 static picoModel_t *_lwo_load( PM_PARAMS_LOAD )\r
100 {\r
101         picoMemStream_t *s;\r
102         unsigned int    failID = 0;\r
103         int                             failpos = -1;\r
104         lwObject                *obj;\r
105         lwSurface               *surface;\r
106         lwLayer                 *layer;\r
107         lwPoint                 *pt;\r
108         lwPolygon               *pol;\r
109         lwPolVert               *v;\r
110         lwVMapPt                *vm;\r
111         char                    name[ 64 ];\r
112         int                             i, j, k, numverts;\r
113 \r
114         picoModel_t             *picoModel;\r
115         picoSurface_t   *picoSurface;\r
116         picoShader_t    *picoShader;\r
117         picoVec3_t              xyz, normal;\r
118         picoVec2_t              st;\r
119         picoColor_t             color;\r
120 \r
121         int                             defaultSTAxis[ 2 ];\r
122         picoVec2_t              defaultXYZtoSTScale;\r
123 \r
124         picoVertexCombinationHash_t **hashTable;\r
125         picoVertexCombinationHash_t     *vertexCombinationHash;\r
126 \r
127 #ifdef DEBUG_PM_LWO\r
128         clock_t load_start, load_finish, convert_start, convert_finish;\r
129         double load_elapsed, convert_elapsed;\r
130 \r
131         load_start = clock();\r
132 #endif\r
133 \r
134         /* do frame check */\r
135         if( frameNum < 0 || frameNum >= 1 )\r
136         {\r
137                 _pico_printf( PICO_ERROR, "Invalid or out-of-range LWO frame specified" );\r
138                 return NULL;\r
139         }\r
140 \r
141         /* create a new pico memorystream */\r
142         s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );\r
143         if (s == NULL)\r
144         {\r
145                 return NULL;\r
146         }\r
147 \r
148         obj = lwGetObject( fileName, s, &failID, &failpos );\r
149 \r
150         _pico_free_memstream( s );\r
151 \r
152         if( !obj ) {\r
153                 _pico_printf( PICO_ERROR, "Couldn't load LWO file, failed on ID '%s', position %d", lwo_lwIDToStr( failID ), failpos );\r
154                 return NULL;\r
155         }\r
156 \r
157 #ifdef DEBUG_PM_LWO\r
158         convert_start = load_finish = clock();\r
159         load_elapsed = (double)(load_finish - load_start) / CLOCKS_PER_SEC;\r
160 #endif\r
161 \r
162         /* -------------------------------------------------\r
163         pico model creation\r
164         ------------------------------------------------- */\r
165         \r
166         /* create a new pico model */\r
167         picoModel = PicoNewModel();\r
168         if (picoModel == NULL)\r
169         {\r
170                 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );\r
171                 return NULL;\r
172         }\r
173 \r
174         /* do model setup */\r
175         PicoSetModelFrameNum( picoModel, frameNum );\r
176         PicoSetModelNumFrames( picoModel, 1 );\r
177         PicoSetModelName( picoModel, fileName );\r
178         PicoSetModelFileName( picoModel, fileName );\r
179 \r
180         /* create all polygons from layer[ 0 ] that belong to this surface */\r
181         layer = &obj->layer[0];\r
182 \r
183         /* warn the user that other layers are discarded */\r
184         if (obj->nlayers > 1)\r
185         {\r
186                 _pico_printf( PICO_WARNING, "LWO loader discards any geometry data not in Layer 1 (%d layers found)", obj->nlayers );\r
187         }\r
188 \r
189         /* initialize dummy normal */\r
190         normal[ 0 ] = normal[ 1 ] = normal[ 2 ] = 0.f;\r
191 \r
192         /* setup default st map */\r
193         st[ 0 ] = st[ 1 ] = 0.f;        /* st[0] holds max, st[1] holds max par one */\r
194         defaultSTAxis[ 0 ] = 0;\r
195         defaultSTAxis[ 1 ] = 1;\r
196         for( i = 0; i < 3; i++ )\r
197         {\r
198                 float min = layer->bbox[ i ];\r
199                 float max = layer->bbox[ i + 3 ];\r
200                 float size = max - min;\r
201                 \r
202                 if (size > st[ 0 ])\r
203                 {\r
204                         defaultSTAxis[ 1 ] = defaultSTAxis[ 0 ];\r
205                         defaultSTAxis[ 0 ] = i;\r
206 \r
207                         st[ 1 ] = st[ 0 ];\r
208                         st[ 0 ] = size;\r
209                 }\r
210                 else if (size > st[ 1 ])\r
211                 {\r
212                         defaultSTAxis[ 1 ] = i;\r
213                         st[ 1 ] = size;\r
214                 }\r
215         }\r
216         defaultXYZtoSTScale[ 0 ] = 4.f / st[ 0 ];\r
217         defaultXYZtoSTScale[ 1 ] = 4.f / st[ 1 ];\r
218 \r
219         /* LWO surfaces become pico surfaces */\r
220         surface = obj->surf;\r
221         while (surface)\r
222         {\r
223                 /* allocate new pico surface */\r
224                 picoSurface = PicoNewSurface( picoModel );\r
225                 if (picoSurface == NULL)\r
226                 {\r
227                         _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );\r
228                         PicoFreeModel( picoModel );\r
229                         lwFreeObject( obj );\r
230                         return NULL;\r
231                 }\r
232 \r
233                 /* LWO model surfaces are all triangle meshes */\r
234                 PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );\r
235 \r
236                 /* set surface name */\r
237                 PicoSetSurfaceName( picoSurface, surface->name );\r
238 \r
239                 /* create new pico shader */\r
240                 picoShader = PicoNewShader( picoModel );\r
241                 if (picoShader == NULL)\r
242                 {\r
243                         _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );\r
244                         PicoFreeModel( picoModel );\r
245                         lwFreeObject( obj );\r
246                         return NULL;\r
247                 }\r
248 \r
249                 /* detox and set shader name */\r
250                 strncpy( name, surface->name, sizeof(name) );\r
251                 _pico_setfext( name, "" );\r
252                 _pico_unixify( name );\r
253                 PicoSetShaderName( picoShader, name );\r
254 \r
255                 /* associate current surface with newly created shader */\r
256                 PicoSetSurfaceShader( picoSurface, picoShader );\r
257 \r
258                 /* copy indices and vertex data */\r
259                 numverts = 0;\r
260 \r
261                 hashTable = PicoNewVertexCombinationHashTable();\r
262 \r
263                 if (hashTable == NULL)\r
264                 {\r
265                         _pico_printf( PICO_ERROR, "Unable to allocate hash table" );\r
266                         PicoFreeModel( picoModel );\r
267                         lwFreeObject( obj );\r
268                         return NULL;\r
269                 }\r
270 \r
271                 for( i = 0, pol = layer->polygon.pol; i < layer->polygon.count; i++, pol++ )\r
272                 {\r
273                         /* does this polygon belong to this surface? */\r
274                         if (pol->surf != surface)\r
275                                 continue;\r
276 \r
277                         /* we only support polygons of the FACE type */\r
278                         if (pol->type != ID_FACE)\r
279                         {\r
280                                 _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it's type != FACE (%s)", lwo_lwIDToStr( pol->type ) );\r
281                                 continue;\r
282                         }\r
283 \r
284                         /* NOTE: LWO has support for non-convex polygons, do we want to store them as well? */\r
285                         if (pol->nverts != 3)\r
286                         {\r
287                                 _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it has != 3 verts (%d)", pol->nverts );\r
288                                 continue;\r
289                         }\r
290 \r
291                         for( j = 0, v = pol->v; j < 3; j++, v++ )\r
292                         {\r
293                                 pt = &layer->point.pt[ v->index ];\r
294 \r
295                                 /* setup data */\r
296                                 xyz[ 0 ] = pt->pos[ 0 ];\r
297                                 xyz[ 1 ] = pt->pos[ 2 ];\r
298                                 xyz[ 2 ] = pt->pos[ 1 ];\r
299 \r
300                                 normal[ 0 ] = v->norm[ 0 ];\r
301                                 normal[ 1 ] = v->norm[ 2 ];\r
302                                 normal[ 2 ] = v->norm[ 1 ];\r
303 \r
304                                 st[ 0 ] = xyz[ defaultSTAxis[ 0 ] ] * defaultXYZtoSTScale[ 0 ];\r
305                                 st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ];\r
306 \r
307                                 color[ 0 ] = (picoByte_t)(surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);\r
308                                 color[ 1 ] = (picoByte_t)(surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);\r
309                                 color[ 2 ] = (picoByte_t)(surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);\r
310                                 color[ 3 ] = 0xFF;\r
311 \r
312                                 /* set from points */\r
313                                 for( k = 0, vm = pt->vm; k < pt->nvmaps; k++, vm++ )\r
314                                 {\r
315                                         if (vm->vmap->type == LWID_('T','X','U','V'))\r
316                                         {\r
317                                                 /* set st coords */\r
318                                                 st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];\r
319                                                 st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];\r
320                                         }\r
321                                         else if (vm->vmap->type == LWID_('R','G','B','A'))\r
322                                         {\r
323                                                 /* set rgba */\r
324                                                 color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);\r
325                                                 color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);\r
326                                                 color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);\r
327                                                 color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);\r
328                                         }\r
329                                 }\r
330 \r
331                                 /* override with polygon data */\r
332                                 for( k = 0, vm = v->vm; k < v->nvmaps; k++, vm++ )\r
333                                 {\r
334                                         if (vm->vmap->type == LWID_('T','X','U','V'))\r
335                                         {\r
336                                                 /* set st coords */\r
337                                                 st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];\r
338                                                 st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];\r
339                                         }\r
340                                         else if (vm->vmap->type == LWID_('R','G','B','A'))\r
341                                         {\r
342                                                 /* set rgba */\r
343                                                 color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);\r
344                                                 color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);\r
345                                                 color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);\r
346                                                 color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);\r
347                                         }\r
348                                 }\r
349 \r
350                                 /* find vertex in this surface and if we can't find it there create it */\r
351                                 vertexCombinationHash = PicoFindVertexCombinationInHashTable( hashTable, xyz, normal, st, color );\r
352 \r
353                                 if (vertexCombinationHash)\r
354                                 {\r
355                                         /* found an existing one */\r
356                                         PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), vertexCombinationHash->index );\r
357                                 }\r
358                                 else\r
359                                 {\r
360                                         /* it is a new one */\r
361                                         vertexCombinationHash = PicoAddVertexCombinationToHashTable( hashTable, xyz, normal, st, color, (picoIndex_t) numverts );\r
362 \r
363                                         if (vertexCombinationHash == NULL)\r
364                                         {\r
365                                                 _pico_printf( PICO_ERROR, "Unable to allocate hash bucket entry table" );\r
366                                                 PicoFreeVertexCombinationHashTable( hashTable );\r
367                                                 PicoFreeModel( picoModel );\r
368                                                 lwFreeObject( obj );\r
369                                                 return NULL;\r
370                                         }\r
371 \r
372                                         /* add the vertex to this surface */\r
373                                         PicoSetSurfaceXYZ( picoSurface, numverts, xyz );\r
374 \r
375                                         /* set dummy normal */\r
376                                         PicoSetSurfaceNormal( picoSurface, numverts, normal );\r
377 \r
378                                         /* set color */\r
379                                         PicoSetSurfaceColor( picoSurface, 0, numverts, color );\r
380 \r
381                                         /* set st coords */\r
382                                         PicoSetSurfaceST( picoSurface, 0, numverts, st );\r
383 \r
384                                         /* set index */\r
385                                         PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), (picoIndex_t) numverts );\r
386 \r
387                                         numverts++;\r
388                                 }\r
389                         }\r
390                 }\r
391 \r
392                 /* free the hashtable */\r
393                 PicoFreeVertexCombinationHashTable( hashTable );\r
394 \r
395                 /* get next surface */          \r
396                 surface = surface->next;\r
397         }\r
398 \r
399 #ifdef DEBUG_PM_LWO\r
400         load_start = convert_finish = clock();\r
401 #endif\r
402 \r
403         lwFreeObject( obj );\r
404 \r
405 #ifdef DEBUG_PM_LWO\r
406         load_finish = clock();\r
407         load_elapsed += (double)(load_finish - load_start) / CLOCKS_PER_SEC;\r
408         convert_elapsed = (double)(convert_finish - convert_start) / CLOCKS_PER_SEC;\r
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  );\r
410 #endif\r
411 \r
412         /* return the new pico model */\r
413         return picoModel;\r
414 }\r
415 \r
416 /* pico file format module definition */\r
417 const picoModule_t picoModuleLWO =\r
418 {\r
419         "1.0",                                          /* module version string */\r
420         "LightWave Object",                     /* module display name */\r
421         "Arnout van Meer",                      /* author's name */\r
422         "2003 Arnout van Meer, 2000 Ernie Wright",              /* module copyright */\r
423         {\r
424                 "lwo", NULL, NULL, NULL /* default extensions to use */\r
425         },\r
426         _lwo_canload,                           /* validation routine */\r
427         _lwo_load,                                      /* load routine */\r
428          NULL,                                          /* save validation routine */\r
429          NULL                                           /* save routine */\r
430 };\r