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