]> git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_lwo.c
uncrustify! now the code is only ugly on the *inside*
[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         static char lwIDStr[5];
52
53         if ( !lwID ) {
54                 return "n/a";
55         }
56
57         lwIDStr[ 0 ] = (char)( ( lwID ) >> 24 );
58         lwIDStr[ 1 ] = (char)( ( lwID ) >> 16 );
59         lwIDStr[ 2 ] = (char)( ( lwID ) >> 8 );
60         lwIDStr[ 3 ] = (char)( ( lwID ) );
61         lwIDStr[ 4 ] = '\0';
62
63         return lwIDStr;
64 }
65
66 /*
67    _lwo_canload()
68    validates a LightWave Object model file. btw, i use the
69    preceding underscore cause it's a static func referenced
70    by one structure only.
71  */
72 static int _lwo_canload( PM_PARAMS_CANLOAD ){
73         picoMemStream_t *s;
74         unsigned int failID = 0;
75         int failpos = -1;
76         int ret;
77
78         /* create a new pico memorystream */
79         s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );
80         if ( s == NULL ) {
81                 return PICO_PMV_ERROR_MEMORY;
82         }
83
84         ret = lwValidateObject( fileName, s, &failID, &failpos );
85
86         _pico_free_memstream( s );
87
88         return ret;
89 }
90
91 /*
92    _lwo_load()
93    loads a LightWave Object model file.
94  */
95 static picoModel_t *_lwo_load( PM_PARAMS_LOAD ){
96         picoMemStream_t *s;
97         unsigned int failID = 0;
98         int failpos = -1;
99         lwObject        *obj;
100         lwSurface       *surface;
101         lwLayer         *layer;
102         lwPoint         *pt;
103         lwPolygon       *pol;
104         lwPolVert       *v;
105         lwVMapPt        *vm;
106         char name[ 64 ];
107         int i, j, k, numverts;
108
109         picoModel_t     *picoModel;
110         picoSurface_t   *picoSurface;
111         picoShader_t    *picoShader;
112         picoVec3_t xyz, normal;
113         picoVec2_t st;
114         picoColor_t color;
115
116         int defaultSTAxis[ 2 ];
117         picoVec2_t defaultXYZtoSTScale;
118
119         picoVertexCombinationHash_t **hashTable;
120         picoVertexCombinationHash_t *vertexCombinationHash;
121
122 #ifdef DEBUG_PM_LWO
123         clock_t load_start, load_finish, convert_start, convert_finish;
124         double load_elapsed, convert_elapsed;
125
126         load_start = clock();
127 #endif
128
129         /* do frame check */
130         if ( frameNum < 0 || frameNum >= 1 ) {
131                 _pico_printf( PICO_ERROR, "Invalid or out-of-range LWO frame specified" );
132                 return NULL;
133         }
134
135         /* create a new pico memorystream */
136         s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );
137         if ( s == NULL ) {
138                 return NULL;
139         }
140
141         obj = lwGetObject( fileName, s, &failID, &failpos );
142
143         _pico_free_memstream( s );
144
145         if ( !obj ) {
146                 _pico_printf( PICO_ERROR, "Couldn't load LWO file, failed on ID '%s', position %d", lwo_lwIDToStr( failID ), failpos );
147                 return NULL;
148         }
149
150 #ifdef DEBUG_PM_LWO
151         convert_start = load_finish = clock();
152         load_elapsed = (double)( load_finish - load_start ) / CLOCKS_PER_SEC;
153 #endif
154
155         /* -------------------------------------------------
156            pico model creation
157            ------------------------------------------------- */
158
159         /* create a new pico model */
160         picoModel = PicoNewModel();
161         if ( picoModel == NULL ) {
162                 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
163                 return NULL;
164         }
165
166         /* do model setup */
167         PicoSetModelFrameNum( picoModel, frameNum );
168         PicoSetModelNumFrames( picoModel, 1 );
169         PicoSetModelName( picoModel, fileName );
170         PicoSetModelFileName( picoModel, fileName );
171
172         /* create all polygons from layer[ 0 ] that belong to this surface */
173         layer = &obj->layer[0];
174
175         /* warn the user that other layers are discarded */
176         if ( obj->nlayers > 1 ) {
177                 _pico_printf( PICO_WARNING, "LWO loader discards any geometry data not in Layer 1 (%d layers found)", obj->nlayers );
178         }
179
180         /* initialize dummy normal */
181         normal[ 0 ] = normal[ 1 ] = normal[ 2 ] = 0.f;
182
183         /* setup default st map */
184         st[ 0 ] = st[ 1 ] = 0.f;    /* st[0] holds max, st[1] holds max par one */
185         defaultSTAxis[ 0 ] = 0;
186         defaultSTAxis[ 1 ] = 1;
187         for ( i = 0; i < 3; i++ )
188         {
189                 float min = layer->bbox[ i ];
190                 float max = layer->bbox[ i + 3 ];
191                 float size = max - min;
192
193                 if ( size > st[ 0 ] ) {
194                         defaultSTAxis[ 1 ] = defaultSTAxis[ 0 ];
195                         defaultSTAxis[ 0 ] = i;
196
197                         st[ 1 ] = st[ 0 ];
198                         st[ 0 ] = size;
199                 }
200                 else if ( size > st[ 1 ] ) {
201                         defaultSTAxis[ 1 ] = i;
202                         st[ 1 ] = size;
203                 }
204         }
205         defaultXYZtoSTScale[ 0 ] = 4.f / st[ 0 ];
206         defaultXYZtoSTScale[ 1 ] = 4.f / st[ 1 ];
207
208         /* LWO surfaces become pico surfaces */
209         surface = obj->surf;
210         while ( surface )
211         {
212                 /* allocate new pico surface */
213                 picoSurface = PicoNewSurface( picoModel );
214                 if ( picoSurface == NULL ) {
215                         _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
216                         PicoFreeModel( picoModel );
217                         lwFreeObject( obj );
218                         return NULL;
219                 }
220
221                 /* LWO model surfaces are all triangle meshes */
222                 PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
223
224                 /* set surface name */
225                 PicoSetSurfaceName( picoSurface, surface->name );
226
227                 /* create new pico shader */
228                 picoShader = PicoNewShader( picoModel );
229                 if ( picoShader == NULL ) {
230                         _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
231                         PicoFreeModel( picoModel );
232                         lwFreeObject( obj );
233                         return NULL;
234                 }
235
236                 /* detox and set shader name */
237                 strncpy( name, surface->name, sizeof( name ) );
238                 _pico_setfext( name, "" );
239                 _pico_unixify( name );
240                 PicoSetShaderName( picoShader, name );
241
242                 /* associate current surface with newly created shader */
243                 PicoSetSurfaceShader( picoSurface, picoShader );
244
245                 /* copy indices and vertex data */
246                 numverts = 0;
247
248                 hashTable = PicoNewVertexCombinationHashTable();
249
250                 if ( hashTable == NULL ) {
251                         _pico_printf( PICO_ERROR, "Unable to allocate hash table" );
252                         PicoFreeModel( picoModel );
253                         lwFreeObject( obj );
254                         return NULL;
255                 }
256
257                 for ( i = 0, pol = layer->polygon.pol; i < layer->polygon.count; i++, pol++ )
258                 {
259                         /* does this polygon belong to this surface? */
260                         if ( pol->surf != surface ) {
261                                 continue;
262                         }
263
264                         /* we only support polygons of the FACE type */
265                         if ( pol->type != ID_FACE ) {
266                                 _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it's type != FACE (%s)", lwo_lwIDToStr( pol->type ) );
267                                 continue;
268                         }
269
270                         /* NOTE: LWO has support for non-convex polygons, do we want to store them as well? */
271                         if ( pol->nverts != 3 ) {
272                                 _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it has != 3 verts (%d)", pol->nverts );
273                                 continue;
274                         }
275
276                         for ( j = 0, v = pol->v; j < 3; j++, v++ )
277                         {
278                                 pt = &layer->point.pt[ v->index ];
279
280                                 /* setup data */
281                                 xyz[ 0 ] = pt->pos[ 0 ];
282                                 xyz[ 1 ] = pt->pos[ 2 ];
283                                 xyz[ 2 ] = pt->pos[ 1 ];
284
285                                 normal[ 0 ] = v->norm[ 0 ];
286                                 normal[ 1 ] = v->norm[ 2 ];
287                                 normal[ 2 ] = v->norm[ 1 ];
288
289                                 st[ 0 ] = xyz[ defaultSTAxis[ 0 ] ] * defaultXYZtoSTScale[ 0 ];
290                                 st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ];
291
292                                 color[ 0 ] = (picoByte_t)( surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF );
293                                 color[ 1 ] = (picoByte_t)( surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF );
294                                 color[ 2 ] = (picoByte_t)( surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF );
295                                 color[ 3 ] = 0xFF;
296
297                                 /* set from points */
298                                 for ( k = 0, vm = pt->vm; k < pt->nvmaps; k++, vm++ )
299                                 {
300                                         if ( vm->vmap->type == LWID_( 'T','X','U','V' ) ) {
301                                                 /* set st coords */
302                                                 st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
303                                                 st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
304                                         }
305                                         else if ( vm->vmap->type == LWID_( 'R','G','B','A' ) ) {
306                                                 /* set rgba */
307                                                 color[ 0 ] = (picoByte_t)( vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF );
308                                                 color[ 1 ] = (picoByte_t)( vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF );
309                                                 color[ 2 ] = (picoByte_t)( vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF );
310                                                 color[ 3 ] = (picoByte_t)( vm->vmap->val[ vm->index ][ 3 ] * 0xFF );
311                                         }
312                                 }
313
314                                 /* override with polygon data */
315                                 for ( k = 0, vm = v->vm; k < v->nvmaps; k++, vm++ )
316                                 {
317                                         if ( vm->vmap->type == LWID_( 'T','X','U','V' ) ) {
318                                                 /* set st coords */
319                                                 st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
320                                                 st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
321                                         }
322                                         else if ( vm->vmap->type == LWID_( 'R','G','B','A' ) ) {
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                                 /* find vertex in this surface and if we can't find it there create it */
332                                 vertexCombinationHash = PicoFindVertexCombinationInHashTable( hashTable, xyz, normal, st, color );
333
334                                 if ( vertexCombinationHash ) {
335                                         /* found an existing one */
336                                         PicoSetSurfaceIndex( picoSurface, ( i * 3 + j ), vertexCombinationHash->index );
337                                 }
338                                 else
339                                 {
340                                         /* it is a new one */
341                                         vertexCombinationHash = PicoAddVertexCombinationToHashTable( hashTable, xyz, normal, st, color, (picoIndex_t) numverts );
342
343                                         if ( vertexCombinationHash == NULL ) {
344                                                 _pico_printf( PICO_ERROR, "Unable to allocate hash bucket entry table" );
345                                                 PicoFreeVertexCombinationHashTable( hashTable );
346                                                 PicoFreeModel( picoModel );
347                                                 lwFreeObject( obj );
348                                                 return NULL;
349                                         }
350
351                                         /* add the vertex to this surface */
352                                         PicoSetSurfaceXYZ( picoSurface, numverts, xyz );
353
354                                         /* set dummy normal */
355                                         PicoSetSurfaceNormal( picoSurface, numverts, normal );
356
357                                         /* set color */
358                                         PicoSetSurfaceColor( picoSurface, 0, numverts, color );
359
360                                         /* set st coords */
361                                         PicoSetSurfaceST( picoSurface, 0, numverts, st );
362
363                                         /* set index */
364                                         PicoSetSurfaceIndex( picoSurface, ( i * 3 + j ), (picoIndex_t) numverts );
365
366                                         numverts++;
367                                 }
368                         }
369                 }
370
371                 /* free the hashtable */
372                 PicoFreeVertexCombinationHashTable( hashTable );
373
374                 /* get next surface */
375                 surface = surface->next;
376         }
377
378 #ifdef DEBUG_PM_LWO
379         load_start = convert_finish = clock();
380 #endif
381
382         lwFreeObject( obj );
383
384 #ifdef DEBUG_PM_LWO
385         load_finish = clock();
386         load_elapsed += (double)( load_finish - load_start ) / CLOCKS_PER_SEC;
387         convert_elapsed = (double)( convert_finish - convert_start ) / CLOCKS_PER_SEC;
388         _pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s) (loading: %-.2fs converting: %-.2fs)\n", load_elapsed + convert_elapsed, load_elapsed, convert_elapsed  );
389 #endif
390
391         /* return the new pico model */
392         return picoModel;
393 }
394
395 /* pico file format module definition */
396 const picoModule_t picoModuleLWO =
397 {
398         "1.0",                      /* module version string */
399         "LightWave Object",         /* module display name */
400         "Arnout van Meer",          /* author's name */
401         "2003 Arnout van Meer, 2000 Ernie Wright",      /* module copyright */
402         {
403                 "lwo", NULL, NULL, NULL /* default extensions to use */
404         },
405         _lwo_canload,               /* validation routine */
406         _lwo_load,                  /* load routine */
407         NULL,                       /* save validation routine */
408         NULL                        /* save routine */
409 };