]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_md3.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / libs / picomodel / pm_md3.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_MD3_C\r
39 \r
40 \r
41 \r
42 /* dependencies */\r
43 #include "picointernal.h"\r
44 \r
45 \r
46 \r
47 /* md3 model format */\r
48 #define MD3_MAGIC                       "IDP3"\r
49 #define MD3_VERSION                     15\r
50 \r
51 /* md3 vertex scale */\r
52 #define MD3_SCALE                 (1.0f / 64.0f)\r
53 \r
54 /* md3 model frame information */\r
55 typedef struct md3Frame_s\r
56 {\r
57         float           bounds[ 2 ][ 3 ];\r
58         float           localOrigin[ 3 ];\r
59         float           radius;\r
60         char            creator[ 16 ];\r
61 }\r
62 md3Frame_t;\r
63 \r
64 /* md3 model tag information */\r
65 typedef struct md3Tag_s\r
66 {\r
67         char            name[ 64 ];\r
68         float           origin[ 3 ];\r
69         float           axis[ 3 ][ 3 ];\r
70 }\r
71 md3Tag_t;\r
72 \r
73 /* md3 surface md3 (one object mesh) */\r
74 typedef struct md3Surface_s\r
75 {\r
76         char            magic[ 4 ];\r
77         char            name[ 64 ];             /* polyset name */\r
78         int                     flags;\r
79         int                     numFrames;              /* all model surfaces should have the same */\r
80         int                     numShaders;             /* all model surfaces should have the same */\r
81         int                     numVerts;\r
82         int                     numTriangles;\r
83         int                     ofsTriangles;\r
84         int                     ofsShaders;             /* offset from start of md3Surface_t */\r
85         int                     ofsSt;                  /* texture coords are common for all frames */\r
86         int                     ofsVertexes;    /* numVerts * numFrames */\r
87         int                     ofsEnd;                 /* next surface follows */\r
88 }\r
89 md3Surface_t;\r
90 \r
91 typedef struct md3Shader_s\r
92 {\r
93         char            name[ 64 ];\r
94         int                     shaderIndex;    /* for ingame use */\r
95 }\r
96 md3Shader_t;\r
97 \r
98 typedef struct md3Triangle_s\r
99 {\r
100         int                     indexes[ 3 ];\r
101 }\r
102 md3Triangle_t;\r
103 \r
104 typedef struct md3TexCoord_s\r
105 {\r
106         float           st[ 2 ];\r
107 }\r
108 md3TexCoord_t;\r
109 \r
110 typedef struct md3Vertex_s\r
111 {\r
112         short           xyz[ 3 ];\r
113         short           normal;\r
114 }\r
115 md3Vertex_t;\r
116 \r
117 \r
118 /* md3 model file md3 structure */\r
119 typedef struct md3_s\r
120 {\r
121         char            magic[ 4 ];             /* MD3_MAGIC */\r
122         int                     version;\r
123         char            name[ 64 ];             /* model name */\r
124         int                     flags;\r
125         int                     numFrames;\r
126         int                     numTags;\r
127         int                     numSurfaces;\r
128         int                     numSkins;               /* number of skins for the mesh */\r
129         int                     ofsFrames;              /* offset for first frame */\r
130         int                     ofsTags;                /* numFrames * numTags */\r
131         int                     ofsSurfaces;    /* first surface, others follow */\r
132         int                     ofsEnd;                 /* end of file */\r
133 }\r
134 md3_t;\r
135 \r
136 \r
137 \r
138 \r
139 /*\r
140 _md3_canload()\r
141 validates a quake3 arena md3 model file. btw, i use the\r
142 preceding underscore cause it's a static func referenced\r
143 by one structure only.\r
144 */\r
145 \r
146 static int _md3_canload( PM_PARAMS_CANLOAD )\r
147 {\r
148         md3_t   *md3;\r
149         \r
150 \r
151         /* to keep the compiler happy */\r
152         *fileName = *fileName;\r
153         \r
154         /* sanity check */\r
155         if( bufSize < ( sizeof( *md3 ) * 2) )\r
156                 return PICO_PMV_ERROR_SIZE;\r
157         \r
158         /* set as md3 */\r
159         md3     = (md3_t*) buffer;\r
160         \r
161         /* check md3 magic */\r
162         if( *((int*) md3->magic) != *((int*) MD3_MAGIC) ) \r
163                 return PICO_PMV_ERROR_IDENT;\r
164         \r
165         /* check md3 version */\r
166         if( _pico_little_long( md3->version ) != MD3_VERSION )\r
167                 return PICO_PMV_ERROR_VERSION;\r
168         \r
169         /* file seems to be a valid md3 */\r
170         return PICO_PMV_OK;\r
171 }\r
172 \r
173 \r
174 \r
175 /*\r
176 _md3_load()\r
177 loads a quake3 arena md3 model file.\r
178 */\r
179 \r
180 static picoModel_t *_md3_load( PM_PARAMS_LOAD )\r
181 {\r
182         int                             i, j;\r
183         picoByte_t              *bb;\r
184         md3_t                   *md3;\r
185         md3Surface_t    *surface;\r
186         md3Shader_t             *shader;\r
187         md3TexCoord_t   *texCoord;\r
188         md3Frame_t              *frame;\r
189         md3Triangle_t   *triangle;\r
190         md3Vertex_t             *vertex;\r
191         double                  lat, lng;\r
192         \r
193         picoModel_t             *picoModel;\r
194         picoSurface_t   *picoSurface;\r
195         picoShader_t    *picoShader;\r
196         picoVec3_t              xyz, normal;\r
197         picoVec2_t              st;\r
198         picoColor_t             color;\r
199         \r
200         \r
201         /* -------------------------------------------------\r
202         md3 loading\r
203         ------------------------------------------------- */\r
204 \r
205 \r
206         /* set as md3 */\r
207         bb = (picoByte_t*) buffer;\r
208         md3     = (md3_t*) buffer;\r
209         \r
210         /* check ident and version */\r
211         if( *((int*) md3->magic) != *((int*) MD3_MAGIC) || _pico_little_long( md3->version ) != MD3_VERSION )\r
212         {\r
213                 /* not an md3 file (todo: set error) */\r
214                 return NULL;\r
215         }\r
216         \r
217         /* swap md3; sea: swaps fixed */\r
218         md3->version = _pico_little_long( md3->version );\r
219         md3->numFrames = _pico_little_long( md3->numFrames );\r
220         md3->numTags = _pico_little_long( md3->numTags );\r
221         md3->numSurfaces = _pico_little_long( md3->numSurfaces );\r
222         md3->numSkins = _pico_little_long( md3->numSkins );\r
223         md3->ofsFrames = _pico_little_long( md3->ofsFrames );\r
224         md3->ofsTags = _pico_little_long( md3->ofsTags );\r
225         md3->ofsSurfaces = _pico_little_long( md3->ofsSurfaces );\r
226         md3->ofsEnd = _pico_little_long( md3->ofsEnd );\r
227         \r
228         /* do frame check */\r
229         if( md3->numFrames < 1 )\r
230         {\r
231                 _pico_printf( PICO_ERROR, "MD3 with 0 frames" );\r
232                 return NULL;\r
233         }\r
234         \r
235         if( frameNum < 0 || frameNum >= md3->numFrames )\r
236         {\r
237                 _pico_printf( PICO_ERROR, "Invalid or out-of-range MD3 frame specified" );\r
238                 return NULL;\r
239         }\r
240         \r
241         /* swap frames */\r
242         frame = (md3Frame_t*) (bb + md3->ofsFrames );\r
243         for( i = 0; i < md3->numFrames; i++, frame++ )\r
244         {\r
245                 frame->radius = _pico_little_float( frame->radius );\r
246                 for( j = 0; j < 3; j++ )\r
247                 {\r
248                         frame->bounds[ 0 ][ j ] = _pico_little_float( frame->bounds[ 0 ][ j ] );\r
249                         frame->bounds[ 1 ][ j ] = _pico_little_float( frame->bounds[ 1 ][ j ] );\r
250                         frame->localOrigin[ j ] = _pico_little_float( frame->localOrigin[ j ] );\r
251                 }\r
252         }\r
253         \r
254         /* swap surfaces */\r
255         surface = (md3Surface_t*) (bb + md3->ofsSurfaces);\r
256         for( i = 0; i < md3->numSurfaces; i++ )\r
257         {\r
258                 /* swap surface md3; sea: swaps fixed */\r
259                 surface->flags = _pico_little_long( surface->flags );\r
260                 surface->numFrames = _pico_little_long( surface->numFrames );\r
261                 surface->numShaders = _pico_little_long( surface->numShaders );\r
262                 surface->numTriangles = _pico_little_long( surface->numTriangles );\r
263                 surface->ofsTriangles = _pico_little_long( surface->ofsTriangles );\r
264                 surface->numVerts = _pico_little_long( surface->numVerts );\r
265                 surface->ofsShaders = _pico_little_long( surface->ofsShaders );\r
266                 surface->ofsSt = _pico_little_long( surface->ofsSt );\r
267                 surface->ofsVertexes = _pico_little_long( surface->ofsVertexes );\r
268                 surface->ofsEnd = _pico_little_long( surface->ofsEnd );\r
269                 \r
270                 /* swap triangles */\r
271                 triangle = (md3Triangle_t*) ((picoByte_t*) surface + surface->ofsTriangles);\r
272                 for( j = 0; j < surface->numTriangles; j++, triangle++ )\r
273                 {\r
274                         /* sea: swaps fixed */\r
275                         triangle->indexes[ 0 ] = _pico_little_long( triangle->indexes[ 0 ] );\r
276                         triangle->indexes[ 1 ] = _pico_little_long( triangle->indexes[ 1 ] );\r
277                         triangle->indexes[ 2 ] = _pico_little_long( triangle->indexes[ 2 ] );\r
278                 }\r
279                 \r
280                 /* swap st coords */\r
281                 texCoord = (md3TexCoord_t*) ((picoByte_t*) surface + surface->ofsSt);\r
282                 for( j = 0; j < surface->numVerts; j++, texCoord++ )\r
283                 {\r
284                         texCoord->st[ 0 ] = _pico_little_float( texCoord->st[ 0 ] );\r
285                         texCoord->st[ 1 ] = _pico_little_float( texCoord->st[ 1 ] );\r
286                 }\r
287                 \r
288                 /* swap xyz/normals */\r
289                 vertex = (md3Vertex_t*) ((picoByte_t*) surface + surface->ofsVertexes);\r
290                 for( j = 0; j < (surface->numVerts * surface->numFrames); j++, vertex++)\r
291                 {\r
292                         vertex->xyz[ 0 ] = _pico_little_short( vertex->xyz[ 0 ] );\r
293                         vertex->xyz[ 1 ] = _pico_little_short( vertex->xyz[ 1 ] );\r
294                         vertex->xyz[ 2 ] = _pico_little_short( vertex->xyz[ 2 ] );\r
295                         vertex->normal   = _pico_little_short( vertex->normal );\r
296                 }\r
297                 \r
298                 /* get next surface */\r
299                 surface = (md3Surface_t*) ((picoByte_t*) surface + surface->ofsEnd);\r
300         }\r
301         \r
302         /* -------------------------------------------------\r
303         pico model creation\r
304         ------------------------------------------------- */\r
305         \r
306         /* create new pico model */\r
307         picoModel = PicoNewModel();\r
308         if( picoModel == NULL )\r
309         {\r
310                 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );\r
311                 return NULL;\r
312         }\r
313         \r
314         /* do model setup */\r
315         PicoSetModelFrameNum( picoModel, frameNum );\r
316         PicoSetModelNumFrames( picoModel, md3->numFrames ); /* sea */\r
317         PicoSetModelName( picoModel, fileName );\r
318         PicoSetModelFileName( picoModel, fileName );\r
319         \r
320         /* md3 surfaces become picomodel surfaces */\r
321         surface = (md3Surface_t*) (bb + md3->ofsSurfaces);\r
322         \r
323         /* run through md3 surfaces */\r
324         for( i = 0; i < md3->numSurfaces; i++ )\r
325         {\r
326                 /* allocate new pico surface */\r
327                 picoSurface = PicoNewSurface( picoModel );\r
328                 if( picoSurface == NULL )\r
329                 {\r
330                         _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );\r
331                         PicoFreeModel( picoModel ); /* sea */\r
332                         return NULL;\r
333                 }\r
334                 \r
335                 /* md3 model surfaces are all triangle meshes */\r
336                 PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );\r
337                 \r
338                 /* set surface name */\r
339                 PicoSetSurfaceName( picoSurface, surface->name );\r
340                 \r
341                 /* create new pico shader -sea */\r
342                 picoShader = PicoNewShader( picoModel );\r
343                 if( picoShader == NULL )\r
344                 {\r
345                         _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );\r
346                         PicoFreeModel( picoModel );\r
347                         return NULL;\r
348                 }\r
349                 \r
350                 /* detox and set shader name */\r
351                 shader = (md3Shader_t*) ((picoByte_t*) surface + surface->ofsShaders);\r
352                 _pico_setfext( shader->name, "" );\r
353                 _pico_unixify( shader->name );\r
354                 PicoSetShaderName( picoShader, shader->name );\r
355                 \r
356                 /* associate current surface with newly created shader */\r
357                 PicoSetSurfaceShader( picoSurface, picoShader );\r
358                 \r
359                 /* copy indexes */\r
360                 triangle = (md3Triangle_t *) ((picoByte_t*) surface + surface->ofsTriangles);\r
361                 \r
362                 for( j = 0; j < surface->numTriangles; j++, triangle++ )\r
363                 {\r
364                         PicoSetSurfaceIndex( picoSurface, (j * 3 + 0), (picoIndex_t) triangle->indexes[ 0 ] );\r
365                         PicoSetSurfaceIndex( picoSurface, (j * 3 + 1), (picoIndex_t) triangle->indexes[ 1 ] );\r
366                         PicoSetSurfaceIndex( picoSurface, (j * 3 + 2), (picoIndex_t) triangle->indexes[ 2 ] );\r
367                 }\r
368                 \r
369                 /* copy vertexes */\r
370                 texCoord = (md3TexCoord_t*) ((picoByte_t *) surface + surface->ofsSt);\r
371                 vertex = (md3Vertex_t*) ((picoByte_t*) surface + surface->ofsVertexes + surface->numVerts * frameNum * sizeof( md3Vertex_t ) );\r
372                 _pico_set_color( color, 255, 255, 255, 255 );\r
373                 \r
374                 for( j = 0; j < surface->numVerts; j++, texCoord++, vertex++ )\r
375                 {\r
376                         /* set vertex origin */\r
377                         xyz[ 0 ] = MD3_SCALE * vertex->xyz[ 0 ];\r
378                         xyz[ 1 ] = MD3_SCALE * vertex->xyz[ 1 ];\r
379                         xyz[ 2 ] = MD3_SCALE * vertex->xyz[ 2 ];\r
380                         PicoSetSurfaceXYZ( picoSurface, j, xyz );\r
381                         \r
382                         /* decode lat/lng normal to 3 float normal */\r
383                         lat = (float) ((vertex->normal >> 8) & 0xff);\r
384                         lng = (float) (vertex->normal & 0xff);\r
385                         lat *= PICO_PI / 128;\r
386                         lng *= PICO_PI / 128;\r
387                         normal[ 0 ] = (picoVec_t) cos( lat ) * (picoVec_t) sin( lng );\r
388                         normal[ 1 ] = (picoVec_t) sin( lat ) * (picoVec_t) sin( lng );\r
389                         normal[ 2 ] = (picoVec_t) cos( lng );\r
390                         PicoSetSurfaceNormal( picoSurface, j, normal );\r
391                         \r
392                         /* set st coords */\r
393                         st[ 0 ] = texCoord->st[ 0 ];\r
394                         st[ 1 ] = texCoord->st[ 1 ];\r
395                         PicoSetSurfaceST( picoSurface, 0, j, st );\r
396 \r
397                         /* set color */\r
398                         PicoSetSurfaceColor( picoSurface, 0, j, color );\r
399                 }\r
400                 \r
401                 /* get next surface */\r
402                 surface = (md3Surface_t*) ((picoByte_t*) surface + surface->ofsEnd);\r
403         }\r
404         \r
405         /* return the new pico model */\r
406         return picoModel;\r
407 }\r
408 \r
409 \r
410 \r
411 /* pico file format module definition */\r
412 const picoModule_t picoModuleMD3 =\r
413 {\r
414         "1.3",                                          /* module version string */\r
415         "Quake 3 Arena",                        /* module display name */\r
416         "Randy Reddig",                         /* author's name */\r
417         "2002 Randy Reddig",            /* module copyright */\r
418         {\r
419                 "md3", NULL, NULL, NULL /* default extensions to use */\r
420         },\r
421         _md3_canload,                           /* validation routine */\r
422         _md3_load,                                      /* load routine */\r
423          NULL,                                          /* save validation routine */\r
424          NULL                                           /* save routine */\r
425 };\r