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