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