]> git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_ms3d.c
Merge branch 'master' of github.com:TTimo/GtkRadiant
[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         TMsHeader *hdr;
168
169
170         /* to keep the compiler happy */
171         *fileName = *fileName;
172
173         /* sanity check */
174         if ( bufSize < sizeof( TMsHeader ) ) {
175                 return PICO_PMV_ERROR_SIZE;
176         }
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
186         /* check ms3d version */
187         if ( _pico_little_long( hdr->version ) < 3 ||
188                  _pico_little_long( hdr->version ) > 4 ) {
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         if ( bufptr == NULL ) {
198                 return NULL;
199         }
200         *out = _pico_little_short( *(unsigned short *)bufptr );
201         return( bufptr + 2 );
202 }
203
204 /* _ms3d_load:
205  *      loads a milkshape3d model file.
206  */
207 static picoModel_t *_ms3d_load( PM_PARAMS_LOAD ){
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 ) {
223                 return NULL;
224         }
225
226         /* do model setup */
227         PicoSetModelFrameNum( model, frameNum );
228         PicoSetModelName( model, fileName );
229         PicoSetModelFileName( model, fileName );
230
231         /* skip header */
232         bufptr = (unsigned char *)buffer + sizeof( TMsHeader );
233
234         /* get number of vertices */
235         bufptr = GetWord( bufptr,&numVerts );
236         ptrToVerts = bufptr;
237
238 #ifdef DEBUG_PM_MS3D
239         printf( "NumVertices: %d\n",numVerts );
240 #endif
241         /* swap verts */
242         for ( i = 0; i < numVerts; i++ )
243         {
244                 TMsVertex *vertex;
245                 vertex = (TMsVertex *)bufptr;
246                 bufptr += sizeof( TMsVertex );
247
248                 vertex->xyz[ 0 ] = _pico_little_float( vertex->xyz[ 0 ] );
249                 vertex->xyz[ 1 ] = _pico_little_float( vertex->xyz[ 1 ] );
250                 vertex->xyz[ 2 ] = _pico_little_float( vertex->xyz[ 2 ] );
251
252 #ifdef DEBUG_PM_MS3D_EX_
253                 printf( "Vertex: x: %f y: %f z: %f\n",
254                                 msvd[i]->vertex[0],
255                                 msvd[i]->vertex[1],
256                                 msvd[i]->vertex[2] );
257 #endif
258         }
259         /* get number of triangles */
260         bufptr = GetWord( bufptr,&numTris );
261         ptrToTris = bufptr;
262
263 #ifdef DEBUG_PM_MS3D
264         printf( "NumTriangles: %d\n",numTris );
265 #endif
266         /* swap tris */
267         for ( i = 0; i < numTris; i++ )
268         {
269                 TMsTriangle *triangle;
270                 triangle = (TMsTriangle *)bufptr;
271                 bufptr += sizeof( TMsTriangle );
272
273                 triangle->flags = _pico_little_short( triangle->flags );
274
275                 /* run through all tri verts */
276                 for ( k = 0; k < 3; k++ )
277                 {
278                         /* swap tex coords */
279                         triangle->s[ k ] = _pico_little_float( triangle->s[ k ] );
280                         triangle->t[ k ] = _pico_little_float( triangle->t[ k ] );
281
282                         /* swap fields */
283                         triangle->vertexIndices[ k ]      = _pico_little_short( triangle->vertexIndices[ k ] );
284                         triangle->vertexNormals[ 0 ][ k ] = _pico_little_float( triangle->vertexNormals[ 0 ][ k ] );
285                         triangle->vertexNormals[ 1 ][ k ] = _pico_little_float( triangle->vertexNormals[ 1 ][ k ] );
286                         triangle->vertexNormals[ 2 ][ k ] = _pico_little_float( triangle->vertexNormals[ 2 ][ k ] );
287
288                         /* check for out of range indices */
289                         if ( triangle->vertexIndices[ k ] >= numVerts ) {
290                                 _pico_printf( PICO_ERROR,"Vertex %d index %d out of range (%d, max %d)",i,k,triangle->vertexIndices[k],numVerts - 1 );
291                                 PicoFreeModel( model );
292                                 return NULL; /* yuck */
293                         }
294                 }
295         }
296         /* get number of groups */
297         bufptr = GetWord( bufptr,&numGroups );
298 //      ptrToGroups = bufptr;
299
300 #ifdef DEBUG_PM_MS3D
301         printf( "NumGroups: %d\n",numGroups );
302 #endif
303         /* run through all groups in model */
304         for ( i = 0; i < numGroups && i < MS3D_MAX_GROUPS; i++ )
305         {
306                 picoSurface_t *surface;
307                 TMsGroup      *group;
308
309                 group = (TMsGroup *)bufptr;
310                 bufptr += sizeof( TMsGroup );
311
312                 /* we ignore hidden groups */
313                 if ( group->flags & MS3D_HIDDEN ) {
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                         PicoFreeModel( model );
324                         return NULL;
325                 }
326                 /* do surface setup */
327                 PicoSetSurfaceType( surface,PICO_TRIANGLES );
328                 PicoSetSurfaceName( surface,group->name );
329
330                 /* process triangle indices */
331                 for ( k = 0; k < group->numTriangles; k++ )
332                 {
333                         TMsTriangle *triangle;
334                         unsigned int triangleIndex;
335
336                         /* get triangle index */
337                         bufptr = GetWord( bufptr,(int *)&triangleIndex );
338
339                         /* get ptr to triangle data */
340                         triangle = (TMsTriangle *)( ptrToTris + ( sizeof( TMsTriangle ) * triangleIndex ) );
341
342                         /* run through triangle vertices */
343                         for ( m = 0; m < 3; m++ )
344                         {
345                                 TMsVertex   *vertex;
346                                 unsigned int vertexIndex;
347                                 picoVec2_t texCoord;
348
349                                 /* get ptr to vertex data */
350                                 vertexIndex = triangle->vertexIndices[ m ];
351                                 vertex = (TMsVertex *)( ptrToVerts + ( sizeof( TMsVertex ) * vertexIndex ) );
352
353                                 /* store vertex origin */
354                                 PicoSetSurfaceXYZ( surface,vertexIndex,vertex->xyz );
355
356                                 /* store vertex color */
357                                 PicoSetSurfaceColor( surface,0,vertexIndex,white );
358
359                                 /* store vertex normal */
360                                 PicoSetSurfaceNormal( surface,vertexIndex,triangle->vertexNormals[ m ] );
361
362                                 /* store current face vertex index */
363                                 PicoSetSurfaceIndex( surface,( k * 3 + ( 2 - m ) ),(picoIndex_t)vertexIndex );
364
365                                 /* get texture vertex coord */
366                                 texCoord[ 0 ] = triangle->s[ m ];
367                                 texCoord[ 1 ] = -triangle->t[ m ];  /* flip t */
368
369                                 /* store texture vertex coord */
370                                 PicoSetSurfaceST( surface,0,vertexIndex,texCoord );
371                         }
372                 }
373                 /* store material */
374                 shaderRefs[ i ] = *bufptr++;
375
376 #ifdef DEBUG_PM_MS3D
377                 printf( "Group %d: '%s' (%d tris)\n",i,group->name,group->numTriangles );
378 #endif
379         }
380         /* get number of materials */
381         bufptr = GetWord( bufptr,&numMaterials );
382
383 #ifdef DEBUG_PM_MS3D
384         printf( "NumMaterials: %d\n",numMaterials );
385 #endif
386         /* run through all materials in model */
387         for ( i = 0; i < numMaterials; i++ )
388         {
389                 picoShader_t *shader;
390                 picoColor_t ambient,diffuse,specular;
391                 TMsMaterial  *material;
392                 int k;
393
394                 material = (TMsMaterial *)bufptr;
395                 bufptr += sizeof( TMsMaterial );
396
397                 /* null term strings */
398                 material->name    [  31 ] = '\0';
399                 material->texture [ 127 ] = '\0';
400                 material->alphamap[ 127 ] = '\0';
401
402                 /* ltrim strings */
403                 _pico_strltrim( material->name );
404                 _pico_strltrim( material->texture );
405                 _pico_strltrim( material->alphamap );
406
407                 /* rtrim strings */
408                 _pico_strrtrim( material->name );
409                 _pico_strrtrim( material->texture );
410                 _pico_strrtrim( material->alphamap );
411
412                 /* create new pico shader */
413                 shader = PicoNewShader( model );
414                 if ( shader == NULL ) {
415                         PicoFreeModel( model );
416                         return NULL;
417                 }
418                 /* scale shader colors */
419                 for ( k = 0; k < 4; k++ )
420                 {
421                         ambient [ k ] = (picoByte_t) ( material->ambient[ k ] * 255 );
422                         diffuse [ k ] = (picoByte_t) ( material->diffuse[ k ] * 255 );
423                         specular[ k ] = (picoByte_t) ( material->specular[ k ] * 255 );
424                 }
425                 /* set shader colors */
426                 PicoSetShaderAmbientColor( shader,ambient );
427                 PicoSetShaderDiffuseColor( shader,diffuse );
428                 PicoSetShaderSpecularColor( shader,specular );
429
430                 /* set shader transparency */
431                 PicoSetShaderTransparency( shader,material->transparency );
432
433                 /* set shader shininess (0..127) */
434                 PicoSetShaderShininess( shader,material->shininess );
435
436                 /* set shader name */
437                 PicoSetShaderName( shader,material->name );
438
439                 /* set shader texture map name */
440                 PicoSetShaderMapName( shader,material->texture );
441
442 #ifdef DEBUG_PM_MS3D
443                 printf( "Material %d: '%s' ('%s','%s')\n",i,material->name,material->texture,material->alphamap );
444 #endif
445         }
446         /* assign shaders to surfaces */
447         for ( i = 0; i < numGroups && i < MS3D_MAX_GROUPS; i++ )
448         {
449                 picoSurface_t *surface;
450                 picoShader_t  *shader;
451
452                 /* sanity check */
453                 if ( shaderRefs[ i ] >= MS3D_MAX_MATERIALS ||
454                          shaderRefs[ i ] < 0 ) {
455                         continue;
456                 }
457
458                 /* get surface */
459                 surface = PicoGetModelSurface( model,i );
460                 if ( surface == NULL ) {
461                         continue;
462                 }
463
464                 /* get shader */
465                 shader = PicoGetModelShader( model,shaderRefs[ i ] );
466                 if ( shader == NULL ) {
467                         continue;
468                 }
469
470                 /* assign shader */
471                 PicoSetSurfaceShader( surface,shader );
472
473 #ifdef DEBUG_PM_MS3D
474                 printf( "Mapped: %d ('%s') to %d (%s)\n",
475                                 shaderRefs[i],shader->name,i,surface->name );
476 #endif
477         }
478         /* return allocated pico model */
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 };