]> git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_ms3d.c
gcc: appease the hardening warnings
[xonotic/netradiant.git] / libs / picomodel / pm_ms3d.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 <assert.h>
37 #include "picointernal.h"
38 #include "globaldefs.h"
39
40 /* disable warnings */
41 #if GDEF_COMPILER_MSVC
42 #pragma warning( disable:4100 )         /* unref param */
43 #endif
44
45 /* remarks:
46  * - loader seems stable
47  * todo:
48  * - fix uv coordinate problem
49  * - check for buffer overflows ('bufptr' accesses)
50  */
51 /* uncomment when debugging this module */
52  #define DEBUG_PM_MS3D
53  #define DEBUG_PM_MS3D_EX
54
55 /* plain white */
56 static picoColor_t white = { 255,255,255,255 };
57
58 /* ms3d limits */
59 const int MS3D_MAX_VERTS      = 8192;
60 const int MS3D_MAX_TRIS       = 16384;
61 #define MS3D_MAX_GROUPS 128
62 const int MS3D_MAX_MATERIALS  = 128;
63 const int MS3D_MAX_JOINTS     = 128;
64 const int MS3D_MAX_KEYFRAMES  = 216;
65
66 /* ms3d flags */
67 const int MS3D_SELECTED       = 1;
68 const int MS3D_HIDDEN         = 2;
69 const int MS3D_SELECTED2      = 4;
70 const int MS3D_DIRTY          = 8;
71
72 /* this freaky loader needs byte alignment */
73 #pragma pack(push, 1)
74
75 /* ms3d header */
76 typedef struct SMsHeader
77 {
78         char magic[10];
79         int version;
80 }
81 TMsHeader;
82
83 /* ms3d vertex */
84 typedef struct SMsVertex
85 {
86         unsigned char flags;                /* sel, sel2, or hidden */
87         float xyz[3];
88         char boneID;                        /* -1 means 'no bone' */
89         unsigned char refCount;
90 }
91 TMsVertex;
92
93 /* ms3d triangle */
94 typedef struct SMsTriangle
95 {
96         unsigned short flags;               /* sel, sel2, or hidden */
97         unsigned short vertexIndices[3];
98         float vertexNormals[3][3];
99         float s[3];
100         float t[3];
101         unsigned char smoothingGroup;       /* 1 - 32 */
102         unsigned char groupIndex;
103 }
104 TMsTriangle;
105
106 /* ms3d material */
107 typedef struct SMsMaterial
108 {
109         char name[32];
110         float ambient[4];
111         float diffuse[4];
112         float specular[4];
113         float emissive[4];
114         float shininess;                    /* range 0..128 */
115         float transparency;                 /* range 0..1 */
116         unsigned char mode;
117         char texture [128];                 /* texture.bmp */
118         char alphamap[128];                 /* alpha.bmp */
119 }
120 TMsMaterial;
121
122 // ms3d group (static part)
123 // followed by a variable size block (see below)
124 typedef struct SMsGroup
125 {
126         unsigned char flags;                // sel, hidden
127         char name[32];
128         unsigned short numTriangles;
129 /*
130     unsigned short      triangleIndices[ numTriangles ];
131     char                        materialIndex;          // -1 means 'no material'
132  */
133 }
134 TMsGroup;
135
136 // ms3d joint
137 typedef struct SMsJoint
138 {
139         unsigned char flags;
140         char name[32];
141         char parentName[32];
142         float rotation[3];
143         float translation[3];
144         unsigned short numRotationKeyframes;
145         unsigned short numTranslationKeyframes;
146 }
147 TMsJoint;
148
149 // ms3d keyframe
150 typedef struct SMsKeyframe
151 {
152         float time;
153         float parameter[3];
154 }
155 TMsKeyframe;
156
157 /* restore previous data alignment */
158 #pragma pack(pop)
159
160 /* _ms3d_canload:
161  *      validates a milkshape3d model file.
162  */
163 static int _ms3d_canload( PM_PARAMS_CANLOAD ){
164         const TMsHeader *hdr;
165
166
167         /* sanity check */
168         if ( (size_t) bufSize < sizeof( TMsHeader ) ) {
169                 return PICO_PMV_ERROR_SIZE;
170         }
171
172         /* get ms3d header */
173         hdr = (const TMsHeader *)buffer;
174
175         /* check ms3d magic */
176         if ( strncmp( hdr->magic,"MS3D000000",10 ) != 0 ) {
177                 return PICO_PMV_ERROR_IDENT;
178         }
179
180         /* check ms3d version */
181         if ( _pico_little_long( hdr->version ) < 3 ||
182                  _pico_little_long( hdr->version ) > 4 ) {
183                 _pico_printf( PICO_ERROR,"MS3D file ignored. Only MS3D 1.3 and 1.4 is supported." );
184                 return PICO_PMV_ERROR_VERSION;
185         }
186         /* file seems to be a valid ms3d */
187         return PICO_PMV_OK;
188 }
189
190 static unsigned char *GetWord( unsigned char *bufptr, int *out ){
191         if ( bufptr == NULL ) {
192                 return NULL;
193         }
194         *out = _pico_little_short( *(unsigned short *)bufptr );
195         return( bufptr + 2 );
196 }
197
198 /* _ms3d_load:
199  *      loads a milkshape3d model file.
200  */
201 static picoModel_t *_ms3d_load( PM_PARAMS_LOAD ){
202         picoModel_t    *model;
203         unsigned char  *bufptr, *bufptr0;
204         int shaderRefs[ MS3D_MAX_GROUPS ];
205         int numGroups;
206         int numMaterials;
207 //      unsigned char  *ptrToGroups;
208         int numVerts;
209         unsigned char  *ptrToVerts;
210         int numTris;
211         unsigned char  *ptrToTris;
212         int i,k,m;
213
214         /* create new pico model */
215         model = PicoNewModel();
216         if ( model == NULL ) {
217                 return NULL;
218         }
219
220         /* do model setup */
221         PicoSetModelFrameNum( model, frameNum );
222         PicoSetModelName( model, fileName );
223         PicoSetModelFileName( model, fileName );
224
225         bufptr0 = bufptr = (picoByte_t*) _pico_alloc( bufSize );
226         memcpy( bufptr, buffer, bufSize );
227         /* skip header */
228         bufptr += sizeof( TMsHeader );
229
230         /* get number of vertices */
231         bufptr = GetWord( bufptr,&numVerts );
232         assert(bufptr);
233         ptrToVerts = bufptr;
234
235 #ifdef DEBUG_PM_MS3D
236         printf( "NumVertices: %d\n",numVerts );
237 #endif
238         /* swap verts */
239         for ( i = 0; i < numVerts; i++ )
240         {
241                 TMsVertex *vertex;
242                 vertex = (TMsVertex *)bufptr;
243                 bufptr += sizeof( TMsVertex );
244
245                 vertex->xyz[ 0 ] = _pico_little_float( vertex->xyz[ 0 ] );
246                 vertex->xyz[ 1 ] = _pico_little_float( vertex->xyz[ 1 ] );
247                 vertex->xyz[ 2 ] = _pico_little_float( vertex->xyz[ 2 ] );
248
249 #ifdef DEBUG_PM_MS3D_EX_
250                 printf( "Vertex: x: %f y: %f z: %f\n",
251                                 msvd[i]->vertex[0],
252                                 msvd[i]->vertex[1],
253                                 msvd[i]->vertex[2] );
254 #endif
255         }
256         /* get number of triangles */
257         bufptr = GetWord( bufptr,&numTris );
258         ptrToTris = bufptr;
259
260 #ifdef DEBUG_PM_MS3D
261         printf( "NumTriangles: %d\n",numTris );
262 #endif
263         /* swap tris */
264         for ( i = 0; i < numTris; i++ )
265         {
266                 TMsTriangle *triangle;
267                 triangle = (TMsTriangle *)bufptr;
268                 bufptr += sizeof( TMsTriangle );
269
270                 triangle->flags = _pico_little_short( triangle->flags );
271
272                 /* run through all tri verts */
273                 for ( k = 0; k < 3; k++ )
274                 {
275                         /* swap tex coords */
276                         triangle->s[ k ] = _pico_little_float( triangle->s[ k ] );
277                         triangle->t[ k ] = _pico_little_float( triangle->t[ k ] );
278
279                         /* swap fields */
280                         triangle->vertexIndices[ k ]      = _pico_little_short( triangle->vertexIndices[ k ] );
281                         triangle->vertexNormals[ 0 ][ k ] = _pico_little_float( triangle->vertexNormals[ 0 ][ k ] );
282                         triangle->vertexNormals[ 1 ][ k ] = _pico_little_float( triangle->vertexNormals[ 1 ][ k ] );
283                         triangle->vertexNormals[ 2 ][ k ] = _pico_little_float( triangle->vertexNormals[ 2 ][ k ] );
284
285                         /* check for out of range indices */
286                         if ( triangle->vertexIndices[ k ] >= numVerts ) {
287                                 _pico_printf( PICO_ERROR,"Vertex %d index %d out of range (%d, max %d)",i,k,triangle->vertexIndices[k],numVerts - 1 );
288                                 PicoFreeModel( model );
289                                 _pico_free( bufptr0 );
290                                 return NULL; /* yuck */
291                         }
292                 }
293         }
294         /* get number of groups */
295         bufptr = GetWord( bufptr,&numGroups );
296         assert(bufptr);
297 //      ptrToGroups = bufptr;
298
299 #ifdef DEBUG_PM_MS3D
300         printf( "NumGroups: %d\n",numGroups );
301 #endif
302         /* run through all groups in model */
303         for ( i = 0; i < numGroups && i < MS3D_MAX_GROUPS; i++ )
304         {
305                 picoSurface_t *surface;
306                 TMsGroup      *group;
307
308                 group = (TMsGroup *)bufptr;
309                 bufptr += sizeof( TMsGroup );
310
311                 /* we ignore hidden groups */
312                 if ( group->flags & MS3D_HIDDEN ) {
313                         bufptr += ( group->numTriangles * 2 ) + 1;
314                         continue;
315                 }
316                 /* forced null term of group name */
317                 group->name[ 31 ] = '\0';
318
319                 /* create new pico surface */
320                 surface = PicoNewSurface( model );
321                 if ( surface == NULL ) {
322                         PicoFreeModel( model );
323                         _pico_free( bufptr0 );
324                         return NULL;
325                 }
326                 /* do surface setup */
327                 PicoSetSurfaceType( surface,PICO_TRIANGLES );
328                 PicoSetSurfaceName( surface,group->name );
329
330                 /* process triangle indices */
331                 for ( k = 0; k < group->numTriangles; k++ )
332                 {
333                         TMsTriangle *triangle;
334                         unsigned int triangleIndex;
335
336                         /* get triangle index */
337                         bufptr = GetWord( bufptr,(int *)&triangleIndex );
338                         assert(bufptr);
339
340                         /* get ptr to triangle data */
341                         triangle = (TMsTriangle *)( ptrToTris + ( sizeof( TMsTriangle ) * triangleIndex ) );
342
343                         /* run through triangle vertices */
344                         for ( m = 0; m < 3; m++ )
345                         {
346                                 TMsVertex   *vertex;
347                                 unsigned int vertexIndex;
348                                 picoVec2_t texCoord;
349
350                                 /* get ptr to vertex data */
351                                 vertexIndex = triangle->vertexIndices[ m ];
352                                 vertex = (TMsVertex *)( ptrToVerts + ( sizeof( TMsVertex ) * vertexIndex ) );
353
354                                 /* store vertex origin */
355                                 PicoSetSurfaceXYZ( surface,vertexIndex,vertex->xyz );
356
357                                 /* store vertex color */
358                                 PicoSetSurfaceColor( surface,0,vertexIndex,white );
359
360                                 /* store vertex normal */
361                                 PicoSetSurfaceNormal( surface,vertexIndex,triangle->vertexNormals[ m ] );
362
363                                 /* store current face vertex index */
364                                 PicoSetSurfaceIndex( surface,( k * 3 + ( 2 - m ) ),(picoIndex_t)vertexIndex );
365
366                                 /* get texture vertex coord */
367                                 texCoord[ 0 ] = triangle->s[ m ];
368                                 texCoord[ 1 ] = -triangle->t[ m ];  /* flip t */
369
370                                 /* store texture vertex coord */
371                                 PicoSetSurfaceST( surface,0,vertexIndex,texCoord );
372                         }
373                 }
374                 /* store material */
375                 shaderRefs[ i ] = *bufptr++;
376
377 #ifdef DEBUG_PM_MS3D
378                 printf( "Group %d: '%s' (%d tris)\n",i,group->name,group->numTriangles );
379 #endif
380         }
381         /* get number of materials */
382         bufptr = GetWord( bufptr,&numMaterials );
383         assert(bufptr);
384
385 #ifdef DEBUG_PM_MS3D
386         printf( "NumMaterials: %d\n",numMaterials );
387 #endif
388         /* run through all materials in model */
389         for ( i = 0; i < numMaterials; i++ )
390         {
391                 picoShader_t *shader;
392                 picoColor_t ambient,diffuse,specular;
393                 TMsMaterial  *material;
394                 int k;
395
396                 material = (TMsMaterial *)bufptr;
397                 bufptr += sizeof( TMsMaterial );
398
399                 /* null term strings */
400                 material->name    [  31 ] = '\0';
401                 material->texture [ 127 ] = '\0';
402                 material->alphamap[ 127 ] = '\0';
403
404                 /* ltrim strings */
405                 _pico_strltrim( material->name );
406                 _pico_strltrim( material->texture );
407                 _pico_strltrim( material->alphamap );
408
409                 /* rtrim strings */
410                 _pico_strrtrim( material->name );
411                 _pico_strrtrim( material->texture );
412                 _pico_strrtrim( material->alphamap );
413
414                 /* create new pico shader */
415                 shader = PicoNewShader( model );
416                 if ( shader == NULL ) {
417                         PicoFreeModel( model );
418                         _pico_free( bufptr0 );
419                         return NULL;
420                 }
421                 /* scale shader colors */
422                 for ( k = 0; k < 4; k++ )
423                 {
424                         ambient [ k ] = (picoByte_t) ( material->ambient[ k ] * 255 );
425                         diffuse [ k ] = (picoByte_t) ( material->diffuse[ k ] * 255 );
426                         specular[ k ] = (picoByte_t) ( material->specular[ k ] * 255 );
427                 }
428                 /* set shader colors */
429                 PicoSetShaderAmbientColor( shader,ambient );
430                 PicoSetShaderDiffuseColor( shader,diffuse );
431                 PicoSetShaderSpecularColor( shader,specular );
432
433                 /* set shader transparency */
434                 PicoSetShaderTransparency( shader,material->transparency );
435
436                 /* set shader shininess (0..127) */
437                 PicoSetShaderShininess( shader,material->shininess );
438
439                 /* set shader name */
440                 PicoSetShaderName( shader,material->name );
441
442                 /* set shader texture map name */
443                 PicoSetShaderMapName( shader,material->texture );
444
445 #ifdef DEBUG_PM_MS3D
446                 printf( "Material %d: '%s' ('%s','%s')\n",i,material->name,material->texture,material->alphamap );
447 #endif
448         }
449         /* assign shaders to surfaces */
450         for ( i = 0; i < numGroups && i < MS3D_MAX_GROUPS; i++ )
451         {
452                 picoSurface_t *surface;
453                 picoShader_t  *shader;
454
455                 /* sanity check */
456                 if ( shaderRefs[ i ] >= MS3D_MAX_MATERIALS ||
457                          shaderRefs[ i ] < 0 ) {
458                         continue;
459                 }
460
461                 /* get surface */
462                 surface = PicoGetModelSurface( model,i );
463                 if ( surface == NULL ) {
464                         continue;
465                 }
466
467                 /* get shader */
468                 shader = PicoGetModelShader( model,shaderRefs[ i ] );
469                 if ( shader == NULL ) {
470                         continue;
471                 }
472
473                 /* assign shader */
474                 PicoSetSurfaceShader( surface,shader );
475
476 #ifdef DEBUG_PM_MS3D
477                 printf( "Mapped: %d ('%s') to %d (%s)\n",
478                                 shaderRefs[i],shader->name,i,surface->name );
479 #endif
480         }
481         /* return allocated pico model */
482         _pico_free( bufptr0 );
483         return model;
484 //      return NULL;
485 }
486
487 /* pico file format module definition */
488 const picoModule_t picoModuleMS3D =
489 {
490         "0.4-a",                    /* module version string */
491         "Milkshape 3D",             /* module display name */
492         "seaw0lf",                  /* author's name */
493         "2002 seaw0lf",             /* module copyright */
494         {
495                 "ms3d",NULL,NULL,NULL   /* default extensions to use */
496         },
497         _ms3d_canload,              /* validation routine */
498         _ms3d_load,                 /* load routine */
499         NULL,                       /* save validation routine */
500         NULL                        /* save routine */
501 };