]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_ms3d.c
eol style
[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
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         TMsHeader *hdr;
169         
170         
171         /* to keep the compiler happy */
172         *fileName = *fileName;
173
174         /* sanity check */
175         if (bufSize < sizeof(TMsHeader))
176                 return PICO_PMV_ERROR_SIZE;
177
178         /* get ms3d header */
179         hdr = (TMsHeader *)buffer;
180
181         /* check ms3d magic */
182         if (strncmp(hdr->magic,"MS3D000000",10) != 0)
183                 return PICO_PMV_ERROR_IDENT;
184
185         /* check ms3d version */
186         if (_pico_little_long(hdr->version) < 3 ||
187                 _pico_little_long(hdr->version) > 4)
188         {
189                 _pico_printf( PICO_ERROR,"MS3D file ignored. Only MS3D 1.3 and 1.4 is supported." );
190                 return PICO_PMV_ERROR_VERSION;
191         }
192         /* file seems to be a valid ms3d */
193         return PICO_PMV_OK;
194 }
195
196 static unsigned char *GetWord( unsigned char *bufptr, int *out )
197 {
198         if (bufptr == NULL) return NULL;
199         *out = _pico_little_short( *(unsigned short *)bufptr );
200         return( bufptr + 2 );
201 }
202
203 /* _ms3d_load:
204  *      loads a milkshape3d model file.
205 */
206 static picoModel_t *_ms3d_load( PM_PARAMS_LOAD )
207 {
208         picoModel_t        *model;
209         unsigned char  *bufptr;
210         int                             shaderRefs[ MS3D_MAX_GROUPS ];
211         int                             numGroups;
212         int                             numMaterials;
213 //      unsigned char  *ptrToGroups;
214         int                             numVerts;
215         unsigned char  *ptrToVerts;
216         int                             numTris;
217         unsigned char  *ptrToTris;
218         int                             i,k,m;
219
220         /* create new pico model */
221         model = PicoNewModel();
222         if (model == NULL) return NULL;
223
224         /* do model setup */
225         PicoSetModelFrameNum( model, frameNum );
226         PicoSetModelName( model, fileName );
227         PicoSetModelFileName( model, fileName );
228
229         /* skip header */
230         bufptr = (unsigned char *)buffer + sizeof(TMsHeader);
231
232         /* get number of vertices */
233         bufptr = GetWord( bufptr,&numVerts );
234         ptrToVerts = bufptr;
235
236 #ifdef DEBUG_PM_MS3D
237         printf("NumVertices: %d\n",numVerts);
238 #endif
239         /* swap verts */
240         for (i=0; i<numVerts; i++)
241         {
242                 TMsVertex *vertex;
243                 vertex = (TMsVertex *)bufptr;
244                 bufptr += sizeof( TMsVertex );
245
246                 vertex->xyz[ 0 ] = _pico_little_float( vertex->xyz[ 0 ] );
247                 vertex->xyz[ 1 ] = _pico_little_float( vertex->xyz[ 1 ] );
248                 vertex->xyz[ 2 ] = _pico_little_float( vertex->xyz[ 2 ] );
249
250 #ifdef DEBUG_PM_MS3D_EX_
251                 printf("Vertex: x: %f y: %f z: %f\n",
252                         msvd[i]->vertex[0],
253                         msvd[i]->vertex[1],
254                         msvd[i]->vertex[2]);
255 #endif
256         }
257         /* get number of triangles */
258         bufptr = GetWord( bufptr,&numTris );
259         ptrToTris = bufptr;
260
261 #ifdef DEBUG_PM_MS3D
262         printf("NumTriangles: %d\n",numTris);
263 #endif
264         /* swap tris */
265         for (i=0; i<numTris; i++)
266         {
267                 TMsTriangle *triangle;
268                 triangle = (TMsTriangle *)bufptr;
269                 bufptr += sizeof( TMsTriangle );
270
271                 triangle->flags = _pico_little_short( triangle->flags );
272
273                 /* run through all tri verts */
274                 for (k=0; k<3; k++)
275                 {
276                         /* swap tex coords */
277                         triangle->s[ k ] = _pico_little_float( triangle->s[ k ] );
278                         triangle->t[ k ] = _pico_little_float( triangle->t[ k ] );
279
280                         /* swap fields */
281                         triangle->vertexIndices[ k ]      = _pico_little_short( triangle->vertexIndices[ k ] );
282                         triangle->vertexNormals[ 0 ][ k ] = _pico_little_float( triangle->vertexNormals[ 0 ][ k ] );
283                         triangle->vertexNormals[ 1 ][ k ] = _pico_little_float( triangle->vertexNormals[ 1 ][ k ] );
284                         triangle->vertexNormals[ 2 ][ k ] = _pico_little_float( triangle->vertexNormals[ 2 ][ k ] );
285
286                         /* check for out of range indices */
287                         if (triangle->vertexIndices[ k ] >= numVerts)
288                         {
289                                 _pico_printf( PICO_ERROR,"Vertex %d index %d out of range (%d, max %d)",i,k,triangle->vertexIndices[k],numVerts-1);
290                                 PicoFreeModel( model );
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                         return NULL;
326                 }
327                 /* do surface setup */
328                 PicoSetSurfaceType( surface,PICO_TRIANGLES );
329                 PicoSetSurfaceName( surface,group->name );
330
331                 /* process triangle indices */
332                 for (k=0; k<group->numTriangles; k++)
333                 {
334                         TMsTriangle *triangle;
335                         unsigned int triangleIndex;
336
337                         /* get triangle index */
338                         bufptr = GetWord( bufptr,(int *)&triangleIndex );
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
384 #ifdef DEBUG_PM_MS3D
385         printf("NumMaterials: %d\n",numMaterials);
386 #endif
387         /* run through all materials in model */
388         for (i=0; i<numMaterials; i++)
389         {
390                 picoShader_t *shader;
391                 picoColor_t   ambient,diffuse,specular;
392                 TMsMaterial  *material;
393                 int           k;
394
395                 material = (TMsMaterial *)bufptr;
396                 bufptr += sizeof( TMsMaterial );
397
398                 /* null term strings */
399                 material->name    [  31 ] = '\0';
400                 material->texture [ 127 ] = '\0';
401                 material->alphamap[ 127 ] = '\0';
402
403                 /* ltrim strings */
404                 _pico_strltrim( material->name );
405                 _pico_strltrim( material->texture );
406                 _pico_strltrim( material->alphamap );
407
408                 /* rtrim strings */
409                 _pico_strrtrim( material->name );
410                 _pico_strrtrim( material->texture );
411                 _pico_strrtrim( material->alphamap );
412
413                 /* create new pico shader */
414                 shader = PicoNewShader( model );
415                 if (shader == NULL)
416                 {
417                         PicoFreeModel( model );
418                         return NULL;
419                 }
420                 /* scale shader colors */
421                 for (k=0; k<4; k++)
422                 {
423                         ambient [ k ] = (picoByte_t) (material->ambient[ k ] * 255);
424                         diffuse [ k ] = (picoByte_t) (material->diffuse[ k ] * 255);
425                         specular[ k ] = (picoByte_t) (material->specular[ k ] * 255);
426                 }
427                 /* set shader colors */
428                 PicoSetShaderAmbientColor( shader,ambient );
429                 PicoSetShaderDiffuseColor( shader,diffuse );
430                 PicoSetShaderSpecularColor( shader,specular );
431
432                 /* set shader transparency */
433                 PicoSetShaderTransparency( shader,material->transparency );
434
435                 /* set shader shininess (0..127) */
436                 PicoSetShaderShininess( shader,material->shininess );
437
438                 /* set shader name */
439                 PicoSetShaderName( shader,material->name );
440
441                 /* set shader texture map name */
442                 PicoSetShaderMapName( shader,material->texture );
443
444 #ifdef DEBUG_PM_MS3D
445                 printf("Material %d: '%s' ('%s','%s')\n",i,material->name,material->texture,material->alphamap);
446 #endif
447         }
448         /* assign shaders to surfaces */
449         for (i=0; i<numGroups && i<MS3D_MAX_GROUPS; i++)
450         {
451                 picoSurface_t *surface;
452                 picoShader_t  *shader;
453
454                 /* sanity check */
455                 if (shaderRefs[ i ] >= MS3D_MAX_MATERIALS ||
456                         shaderRefs[ i ] < 0)
457                         continue;
458
459                 /* get surface */
460                 surface = PicoGetModelSurface( model,i );
461                 if (surface == NULL) continue;
462
463                 /* get shader */
464                 shader = PicoGetModelShader( model,shaderRefs[ i ] );
465                 if (shader == NULL) continue;
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         return model;
477 //      return NULL;
478 }
479
480 /* pico file format module definition */
481 const picoModule_t picoModuleMS3D =
482 {
483         "0.4-a",                                        /* module version string */
484         "Milkshape 3D",                         /* module display name */
485         "seaw0lf",                                      /* author's name */
486         "2002 seaw0lf",                         /* module copyright */
487         {
488                 "ms3d",NULL,NULL,NULL   /* default extensions to use */
489         },
490         _ms3d_canload,                          /* validation routine */
491         _ms3d_load,                                     /* load routine */
492          NULL,                                          /* save validation routine */
493          NULL                                           /* save routine */
494 };