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