1 /* -----------------------------------------------------------------------------
\r
5 Copyright (c) 2002, Randy Reddig & seaw0lf
\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
11 Redistributions of source code must retain the above copyright notice, this list
\r
12 of conditions and the following disclaimer.
\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
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
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
33 ----------------------------------------------------------------------------- */
\r
41 #include "picointernal.h"
\r
43 /* disable warnings */
\r
45 #pragma warning( disable:4100 ) /* unref param */
\r
49 * - loader seems stable
\r
51 * - fix uv coordinate problem
\r
52 * - check for buffer overflows ('bufptr' accesses)
\r
54 /* uncomment when debugging this module */
\r
55 #define DEBUG_PM_MS3D
\r
56 #define DEBUG_PM_MS3D_EX
\r
59 static picoColor_t white = { 255,255,255,255 };
\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
70 #define MS3D_SELECTED 1
\r
71 #define MS3D_HIDDEN 2
\r
72 #define MS3D_SELECTED2 4
\r
73 #define MS3D_DIRTY 8
\r
75 /* this freaky loader needs byte alignment */
\r
76 #pragma pack(push, 1)
\r
79 typedef struct SMsHeader
\r
87 typedef struct SMsVertex
\r
89 unsigned char flags; /* sel, sel2, or hidden */
\r
91 char boneID; /* -1 means 'no bone' */
\r
92 unsigned char refCount;
\r
97 typedef struct SMsTriangle
\r
99 unsigned short flags; /* sel, sel2, or hidden */
\r
100 unsigned short vertexIndices[3];
\r
101 float vertexNormals[3][3];
\r
104 unsigned char smoothingGroup; /* 1 - 32 */
\r
105 unsigned char groupIndex;
\r
109 /* ms3d material */
\r
110 typedef struct SMsMaterial
\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
125 // ms3d group (static part)
\r
126 // followed by a variable size block (see below)
\r
127 typedef struct SMsGroup
\r
129 unsigned char flags; // sel, hidden
\r
131 unsigned short numTriangles;
\r
133 unsigned short triangleIndices[ numTriangles ];
\r
134 char materialIndex; // -1 means 'no material'
\r
140 typedef struct SMsJoint
\r
142 unsigned char flags;
\r
144 char parentName[32];
\r
146 float translation[3];
\r
147 unsigned short numRotationKeyframes;
\r
148 unsigned short numTranslationKeyframes;
\r
153 typedef struct SMsKeyframe
\r
156 float parameter[3];
\r
160 /* restore previous data alignment */
\r
164 * validates a milkshape3d model file.
\r
166 static int _ms3d_canload( PM_PARAMS_CANLOAD )
\r
171 /* to keep the compiler happy */
\r
172 *fileName = *fileName;
\r
175 if (bufSize < sizeof(TMsHeader))
\r
176 return PICO_PMV_ERROR_SIZE;
\r
178 /* get ms3d header */
\r
179 hdr = (TMsHeader *)buffer;
\r
181 /* check ms3d magic */
\r
182 if (strncmp(hdr->magic,"MS3D000000",10) != 0)
\r
183 return PICO_PMV_ERROR_IDENT;
\r
185 /* check ms3d version */
\r
186 if (_pico_little_long(hdr->version) < 3 ||
\r
187 _pico_little_long(hdr->version) > 4)
\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
192 /* file seems to be a valid ms3d */
\r
193 return PICO_PMV_OK;
\r
196 static unsigned char *GetWord( unsigned char *bufptr, int *out )
\r
198 if (bufptr == NULL) return NULL;
\r
199 *out = _pico_little_short( *(unsigned short *)bufptr );
\r
200 return( bufptr + 2 );
\r
204 * loads a milkshape3d model file.
\r
206 static picoModel_t *_ms3d_load( PM_PARAMS_LOAD )
\r
208 picoModel_t *model;
\r
209 unsigned char *bufptr;
\r
210 int shaderRefs[ MS3D_MAX_GROUPS ];
\r
213 // unsigned char *ptrToGroups;
\r
215 unsigned char *ptrToVerts;
\r
217 unsigned char *ptrToTris;
\r
220 /* create new pico model */
\r
221 model = PicoNewModel();
\r
222 if (model == NULL) return NULL;
\r
224 /* do model setup */
\r
225 PicoSetModelFrameNum( model, frameNum );
\r
226 PicoSetModelName( model, fileName );
\r
227 PicoSetModelFileName( model, fileName );
\r
230 bufptr = (unsigned char *)buffer + sizeof(TMsHeader);
\r
232 /* get number of vertices */
\r
233 bufptr = GetWord( bufptr,&numVerts );
\r
234 ptrToVerts = bufptr;
\r
236 #ifdef DEBUG_PM_MS3D
\r
237 printf("NumVertices: %d\n",numVerts);
\r
240 for (i=0; i<numVerts; i++)
\r
243 vertex = (TMsVertex *)bufptr;
\r
244 bufptr += sizeof( TMsVertex );
\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
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
257 /* get number of triangles */
\r
258 bufptr = GetWord( bufptr,&numTris );
\r
259 ptrToTris = bufptr;
\r
261 #ifdef DEBUG_PM_MS3D
\r
262 printf("NumTriangles: %d\n",numTris);
\r
265 for (i=0; i<numTris; i++)
\r
267 TMsTriangle *triangle;
\r
268 triangle = (TMsTriangle *)bufptr;
\r
269 bufptr += sizeof( TMsTriangle );
\r
271 triangle->flags = _pico_little_short( triangle->flags );
\r
273 /* run through all tri verts */
\r
274 for (k=0; k<3; k++)
\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
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
286 /* check for out of range indices */
\r
287 if (triangle->vertexIndices[ k ] >= numVerts)
\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
295 /* get number of groups */
\r
296 bufptr = GetWord( bufptr,&numGroups );
\r
297 // ptrToGroups = bufptr;
\r
299 #ifdef DEBUG_PM_MS3D
\r
300 printf("NumGroups: %d\n",numGroups);
\r
302 /* run through all groups in model */
\r
303 for (i=0; i<numGroups && i<MS3D_MAX_GROUPS; i++)
\r
305 picoSurface_t *surface;
\r
308 group = (TMsGroup *)bufptr;
\r
309 bufptr += sizeof( TMsGroup );
\r
311 /* we ignore hidden groups */
\r
312 if (group->flags & MS3D_HIDDEN)
\r
314 bufptr += (group->numTriangles * 2) + 1;
\r
317 /* forced null term of group name */
\r
318 group->name[ 31 ] = '\0';
\r
320 /* create new pico surface */
\r
321 surface = PicoNewSurface( model );
\r
322 if (surface == NULL)
\r
324 PicoFreeModel( model );
\r
327 /* do surface setup */
\r
328 PicoSetSurfaceType( surface,PICO_TRIANGLES );
\r
329 PicoSetSurfaceName( surface,group->name );
\r
331 /* process triangle indices */
\r
332 for (k=0; k<group->numTriangles; k++)
\r
334 TMsTriangle *triangle;
\r
335 unsigned int triangleIndex;
\r
337 /* get triangle index */
\r
338 bufptr = GetWord( bufptr,(int *)&triangleIndex );
\r
340 /* get ptr to triangle data */
\r
341 triangle = (TMsTriangle *)(ptrToTris + (sizeof(TMsTriangle) * triangleIndex));
\r
343 /* run through triangle vertices */
\r
344 for (m=0; m<3; m++)
\r
347 unsigned int vertexIndex;
\r
348 picoVec2_t texCoord;
\r
350 /* get ptr to vertex data */
\r
351 vertexIndex = triangle->vertexIndices[ m ];
\r
352 vertex = (TMsVertex *)(ptrToVerts + (sizeof(TMsVertex) * vertexIndex));
\r
354 /* store vertex origin */
\r
355 PicoSetSurfaceXYZ( surface,vertexIndex,vertex->xyz );
\r
357 /* store vertex color */
\r
358 PicoSetSurfaceColor( surface,0,vertexIndex,white );
\r
360 /* store vertex normal */
\r
361 PicoSetSurfaceNormal( surface,vertexIndex,triangle->vertexNormals[ m ] );
\r
363 /* store current face vertex index */
\r
364 PicoSetSurfaceIndex( surface,(k * 3 + (2 - m)),(picoIndex_t)vertexIndex );
\r
366 /* get texture vertex coord */
\r
367 texCoord[ 0 ] = triangle->s[ m ];
\r
368 texCoord[ 1 ] = -triangle->t[ m ]; /* flip t */
\r
370 /* store texture vertex coord */
\r
371 PicoSetSurfaceST( surface,0,vertexIndex,texCoord );
\r
374 /* store material */
\r
375 shaderRefs[ i ] = *bufptr++;
\r
377 #ifdef DEBUG_PM_MS3D
\r
378 printf("Group %d: '%s' (%d tris)\n",i,group->name,group->numTriangles);
\r
381 /* get number of materials */
\r
382 bufptr = GetWord( bufptr,&numMaterials );
\r
384 #ifdef DEBUG_PM_MS3D
\r
385 printf("NumMaterials: %d\n",numMaterials);
\r
387 /* run through all materials in model */
\r
388 for (i=0; i<numMaterials; i++)
\r
390 picoShader_t *shader;
\r
391 picoColor_t ambient,diffuse,specular;
\r
392 TMsMaterial *material;
\r
395 material = (TMsMaterial *)bufptr;
\r
396 bufptr += sizeof( TMsMaterial );
\r
398 /* null term strings */
\r
399 material->name [ 31 ] = '\0';
\r
400 material->texture [ 127 ] = '\0';
\r
401 material->alphamap[ 127 ] = '\0';
\r
403 /* ltrim strings */
\r
404 _pico_strltrim( material->name );
\r
405 _pico_strltrim( material->texture );
\r
406 _pico_strltrim( material->alphamap );
\r
408 /* rtrim strings */
\r
409 _pico_strrtrim( material->name );
\r
410 _pico_strrtrim( material->texture );
\r
411 _pico_strrtrim( material->alphamap );
\r
413 /* create new pico shader */
\r
414 shader = PicoNewShader( model );
\r
415 if (shader == NULL)
\r
417 PicoFreeModel( model );
\r
420 /* scale shader colors */
\r
421 for (k=0; k<4; k++)
\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
427 /* set shader colors */
\r
428 PicoSetShaderAmbientColor( shader,ambient );
\r
429 PicoSetShaderDiffuseColor( shader,diffuse );
\r
430 PicoSetShaderSpecularColor( shader,specular );
\r
432 /* set shader transparency */
\r
433 PicoSetShaderTransparency( shader,material->transparency );
\r
435 /* set shader shininess (0..127) */
\r
436 PicoSetShaderShininess( shader,material->shininess );
\r
438 /* set shader name */
\r
439 PicoSetShaderName( shader,material->name );
\r
441 /* set shader texture map name */
\r
442 PicoSetShaderMapName( shader,material->texture );
\r
444 #ifdef DEBUG_PM_MS3D
\r
445 printf("Material %d: '%s' ('%s','%s')\n",i,material->name,material->texture,material->alphamap);
\r
448 /* assign shaders to surfaces */
\r
449 for (i=0; i<numGroups && i<MS3D_MAX_GROUPS; i++)
\r
451 picoSurface_t *surface;
\r
452 picoShader_t *shader;
\r
455 if (shaderRefs[ i ] >= MS3D_MAX_MATERIALS ||
\r
456 shaderRefs[ i ] < 0)
\r
460 surface = PicoGetModelSurface( model,i );
\r
461 if (surface == NULL) continue;
\r
464 shader = PicoGetModelShader( model,shaderRefs[ i ] );
\r
465 if (shader == NULL) continue;
\r
467 /* assign shader */
\r
468 PicoSetSurfaceShader( surface,shader );
\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
475 /* return allocated pico model */
\r
480 /* pico file format module definition */
\r
481 const picoModule_t picoModuleMS3D =
\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
488 "ms3d",NULL,NULL,NULL /* default extensions to use */
\r
490 _ms3d_canload, /* validation routine */
\r
491 _ms3d_load, /* load routine */
\r
492 NULL, /* save validation routine */
\r
493 NULL /* save routine */
\r