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