]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_ms3d.c
Merge branch 'master' into divVerent/farplanedist-sky-fix
[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         const TMsHeader *hdr;
168
169
170         /* sanity check */
171         if ( (size_t) bufSize < sizeof( TMsHeader ) ) {
172                 return PICO_PMV_ERROR_SIZE;
173         }
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
183         /* check ms3d version */
184         if ( _pico_little_long( hdr->version ) < 3 ||
185                  _pico_little_long( hdr->version ) > 4 ) {
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         if ( bufptr == NULL ) {
195                 return NULL;
196         }
197         *out = _pico_little_short( *(unsigned short *)bufptr );
198         return( bufptr + 2 );
199 }
200
201 /* _ms3d_load:
202  *      loads a milkshape3d model file.
203  */
204 static picoModel_t *_ms3d_load( PM_PARAMS_LOAD ){
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 ) {
220                 return NULL;
221         }
222
223         /* do model setup */
224         PicoSetModelFrameNum( model, frameNum );
225         PicoSetModelName( model, fileName );
226         PicoSetModelFileName( model, fileName );
227
228         bufptr0 = bufptr = (picoByte_t*) _pico_alloc( bufSize );
229         memcpy( bufptr, buffer, bufSize );
230         /* skip header */
231         bufptr += sizeof( TMsHeader );
232
233         /* get number of vertices */
234         bufptr = GetWord( bufptr,&numVerts );
235         ptrToVerts = bufptr;
236
237 #ifdef DEBUG_PM_MS3D
238         printf( "NumVertices: %d\n",numVerts );
239 #endif
240         /* swap verts */
241         for ( i = 0; i < numVerts; i++ )
242         {
243                 TMsVertex *vertex;
244                 vertex = (TMsVertex *)bufptr;
245                 bufptr += sizeof( TMsVertex );
246
247                 vertex->xyz[ 0 ] = _pico_little_float( vertex->xyz[ 0 ] );
248                 vertex->xyz[ 1 ] = _pico_little_float( vertex->xyz[ 1 ] );
249                 vertex->xyz[ 2 ] = _pico_little_float( vertex->xyz[ 2 ] );
250
251 #ifdef DEBUG_PM_MS3D_EX_
252                 printf( "Vertex: x: %f y: %f z: %f\n",
253                                 msvd[i]->vertex[0],
254                                 msvd[i]->vertex[1],
255                                 msvd[i]->vertex[2] );
256 #endif
257         }
258         /* get number of triangles */
259         bufptr = GetWord( bufptr,&numTris );
260         ptrToTris = bufptr;
261
262 #ifdef DEBUG_PM_MS3D
263         printf( "NumTriangles: %d\n",numTris );
264 #endif
265         /* swap tris */
266         for ( i = 0; i < numTris; i++ )
267         {
268                 TMsTriangle *triangle;
269                 triangle = (TMsTriangle *)bufptr;
270                 bufptr += sizeof( TMsTriangle );
271
272                 triangle->flags = _pico_little_short( triangle->flags );
273
274                 /* run through all tri verts */
275                 for ( k = 0; k < 3; k++ )
276                 {
277                         /* swap tex coords */
278                         triangle->s[ k ] = _pico_little_float( triangle->s[ k ] );
279                         triangle->t[ k ] = _pico_little_float( triangle->t[ k ] );
280
281                         /* swap fields */
282                         triangle->vertexIndices[ k ]      = _pico_little_short( triangle->vertexIndices[ k ] );
283                         triangle->vertexNormals[ 0 ][ k ] = _pico_little_float( triangle->vertexNormals[ 0 ][ k ] );
284                         triangle->vertexNormals[ 1 ][ k ] = _pico_little_float( triangle->vertexNormals[ 1 ][ k ] );
285                         triangle->vertexNormals[ 2 ][ k ] = _pico_little_float( triangle->vertexNormals[ 2 ][ k ] );
286
287                         /* check for out of range indices */
288                         if ( triangle->vertexIndices[ k ] >= numVerts ) {
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                                 _pico_free( bufptr0 );
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                         _pico_free( bufptr0 );
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                         PicoFreeModel( model );
417                         _pico_free( bufptr0 );
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
460                 /* get surface */
461                 surface = PicoGetModelSurface( model,i );
462                 if ( surface == NULL ) {
463                         continue;
464                 }
465
466                 /* get shader */
467                 shader = PicoGetModelShader( model,shaderRefs[ i ] );
468                 if ( shader == NULL ) {
469                         continue;
470                 }
471
472                 /* assign shader */
473                 PicoSetSurfaceShader( surface,shader );
474
475 #ifdef DEBUG_PM_MS3D
476                 printf( "Mapped: %d ('%s') to %d (%s)\n",
477                                 shaderRefs[i],shader->name,i,surface->name );
478 #endif
479         }
480         /* return allocated pico model */
481         _pico_free( bufptr0 );
482         return model;
483 //      return NULL;
484 }
485
486 /* pico file format module definition */
487 const picoModule_t picoModuleMS3D =
488 {
489         "0.4-a",                    /* module version string */
490         "Milkshape 3D",             /* module display name */
491         "seaw0lf",                  /* author's name */
492         "2002 seaw0lf",             /* module copyright */
493         {
494                 "ms3d",NULL,NULL,NULL   /* default extensions to use */
495         },
496         _ms3d_canload,              /* validation routine */
497         _ms3d_load,                 /* load routine */
498         NULL,                       /* save validation routine */
499         NULL                        /* save routine */
500 };