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