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