]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_fm.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / libs / picomodel / pm_fm.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 Nurail: Used pm_md3.c (Randy Reddig) as a template.\r
37 */\r
38 \r
39 /* marker */\r
40 #define PM_FM_C\r
41 \r
42 /* dependencies */\r
43 #include "pm_fm.h"\r
44 \r
45 //#define FM_VERBOSE_DBG        0\r
46 #undef FM_VERBOSE_DBG\r
47 #undef FM_DBG\r
48 \r
49 typedef struct index_LUT_s\r
50 {\r
51         short   Vert;\r
52         short   ST;\r
53         struct  index_LUT_s     *next;\r
54 \r
55 } index_LUT_t;\r
56 \r
57 typedef struct index_DUP_LUT_s\r
58 {\r
59         short                   ST;\r
60         short                   OldVert;\r
61 \r
62 } index_DUP_LUT_t;\r
63 \r
64 \r
65 // _fm_canload()\r
66 static int _fm_canload( PM_PARAMS_CANLOAD )\r
67 {\r
68         fm_t            fm;\r
69         unsigned char   *bb;\r
70         int             fm_file_pos;\r
71 \r
72         bb = (unsigned char *) buffer;\r
73 \r
74         // Header\r
75         fm.fm_header_hdr = (fm_chunk_header_t *) bb;\r
76         fm_file_pos = sizeof(fm_chunk_header_t) + fm.fm_header_hdr->size;\r
77 #ifdef FM_VERBOSE_DBG\r
78         _pico_printf( PICO_VERBOSE, "IDENT: %s\n", (unsigned char *) fm.fm_header_hdr->ident );\r
79 #endif\r
80         if( (strcmp(fm.fm_header_hdr->ident, FM_HEADERCHUNKNAME))  )\r
81         {\r
82 #ifdef FM_DBG\r
83                 _pico_printf( PICO_WARNING, "FM Header Ident incorrect\n");\r
84 #endif\r
85                 return PICO_PMV_ERROR_IDENT;\r
86         }\r
87 \r
88         // check fm\r
89         if( _pico_little_long( fm.fm_header_hdr->version ) != FM_HEADERCHUNKVER )\r
90         {\r
91 #ifdef FM_DBG\r
92                 _pico_printf( PICO_WARNING, "FM Header Version incorrect\n");\r
93 #endif\r
94                 return PICO_PMV_ERROR_VERSION;\r
95         }\r
96 \r
97         // Skin\r
98         fm.fm_skin_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);\r
99         fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_skin_hdr->size;\r
100 #ifdef FM_VERBOSE_DBG\r
101         _pico_printf( PICO_VERBOSE, "SKIN: %s\n", (unsigned char *) fm.fm_skin_hdr->ident );\r
102 #endif\r
103         if( (strcmp(fm.fm_skin_hdr->ident, FM_SKINCHUNKNAME)) )\r
104         {\r
105 #ifdef FM_DBG\r
106                 _pico_printf( PICO_WARNING, "FM Skin Ident incorrect\n");\r
107 #endif\r
108                 return PICO_PMV_ERROR_IDENT;\r
109         }\r
110 \r
111         // check fm\r
112         if( _pico_little_long( fm.fm_skin_hdr->version ) != FM_SKINCHUNKVER )\r
113         {\r
114 #ifdef FM_DBG\r
115                 _pico_printf( PICO_WARNING, "FM Skin Version incorrect\n");\r
116 #endif\r
117                 return PICO_PMV_ERROR_VERSION;\r
118         }\r
119 \r
120         // st\r
121         fm.fm_st_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);\r
122         fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_st_hdr->size;\r
123 #ifdef FM_VERBOSE_DBG\r
124         _pico_printf( PICO_VERBOSE, "ST: %s\n", (unsigned char *) fm.fm_st_hdr->ident );\r
125 #endif\r
126         if( (strcmp(fm.fm_st_hdr->ident, FM_STCOORDCHUNKNAME)) )\r
127         {\r
128 #ifdef FM_DBG\r
129                 _pico_printf( PICO_WARNING, "FM ST Ident incorrect\n");\r
130 #endif\r
131                 return PICO_PMV_ERROR_IDENT;\r
132         }\r
133 \r
134         // check fm\r
135         if( _pico_little_long( fm.fm_st_hdr->version ) != FM_STCOORDCHUNKVER )\r
136         {\r
137 #ifdef FM_DBG\r
138                 _pico_printf( PICO_WARNING, "FM ST Version incorrect\n");\r
139 #endif\r
140                 return PICO_PMV_ERROR_VERSION;\r
141         }\r
142 \r
143         // tri\r
144         fm.fm_tri_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);\r
145         fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_tri_hdr->size;\r
146 #ifdef FM_VERBOSE_DBG\r
147         _pico_printf( PICO_VERBOSE, "TRI: %s\n", (unsigned char *) fm.fm_tri_hdr->ident );\r
148 #endif\r
149         if( (strcmp(fm.fm_tri_hdr->ident, FM_TRISCHUNKNAME)) )\r
150         {\r
151 #ifdef FM_DBG\r
152                 _pico_printf( PICO_WARNING, "FM Tri Ident incorrect\n");\r
153 #endif\r
154                 return PICO_PMV_ERROR_IDENT;\r
155         }\r
156 \r
157         // check fm\r
158         if( _pico_little_long( fm.fm_tri_hdr->version ) != FM_TRISCHUNKVER )\r
159         {\r
160 #ifdef FM_DBG\r
161                 _pico_printf( PICO_WARNING, "FM Tri Version incorrect\n");\r
162 #endif\r
163                 return PICO_PMV_ERROR_VERSION;\r
164         }\r
165 \r
166         // frame\r
167         fm.fm_frame_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);\r
168         fm_file_pos += sizeof(fm_chunk_header_t);\r
169 #ifdef FM_VERBOSE_DBG\r
170         _pico_printf( PICO_VERBOSE, "FRAME: %s\n", (unsigned char *) fm.fm_frame_hdr->ident );\r
171 #endif\r
172         if( (strcmp(fm.fm_frame_hdr->ident, FM_FRAMESCHUNKNAME)) )\r
173         {\r
174 #ifdef FM_DBG\r
175                 _pico_printf( PICO_WARNING, "FM Frame Ident incorrect\n");\r
176 #endif\r
177                 return PICO_PMV_ERROR_IDENT;\r
178         }\r
179 \r
180         // check fm\r
181         if( _pico_little_long( fm.fm_frame_hdr->version ) != FM_FRAMESCHUNKVER )\r
182         {\r
183 #ifdef FM_DBG\r
184                 _pico_printf( PICO_WARNING, "FM Frame Version incorrect\n");\r
185 #endif\r
186                 return PICO_PMV_ERROR_VERSION;\r
187         }\r
188 \r
189         // file seems to be a valid fm\r
190         return PICO_PMV_OK;\r
191 }\r
192 \r
193 \r
194 \r
195 // _fm_load() loads a Heretic 2 model file.\r
196 static picoModel_t *_fm_load( PM_PARAMS_LOAD )\r
197 {\r
198         int                             i, j, dups, dup_index;\r
199         int                             fm_file_pos;\r
200         short                   tot_numVerts;\r
201         index_LUT_t             *p_index_LUT, *p_index_LUT2, *p_index_LUT3;\r
202         index_DUP_LUT_t *p_index_LUT_DUPS;\r
203 \r
204         fm_vert_normal_t        *vert;\r
205 \r
206         char                    skinname[FM_SKINPATHSIZE];\r
207         fm_t                    fm;\r
208         fm_header_t             *fm_head;\r
209         fm_st_t                 *texCoord;\r
210         fm_xyz_st_t             *tri_verts;\r
211         fm_xyz_st_t             *triangle;\r
212         fm_frame_t              *frame;\r
213 \r
214         picoByte_t      *bb;\r
215         picoModel_t     *picoModel;\r
216         picoSurface_t   *picoSurface;\r
217         picoShader_t    *picoShader;\r
218         picoVec3_t      xyz, normal;\r
219         picoVec2_t      st;\r
220         picoColor_t     color;\r
221         \r
222 \r
223         // fm loading\r
224         _pico_printf( PICO_NORMAL, "Loading \"%s\"", fileName );\r
225 \r
226         bb = (picoByte_t*) buffer;\r
227 \r
228         // Header Header\r
229         fm.fm_header_hdr = (fm_chunk_header_t *) bb;\r
230         fm_file_pos = sizeof(fm_chunk_header_t) + fm.fm_header_hdr->size;\r
231         if( (strcmp(fm.fm_header_hdr->ident, FM_HEADERCHUNKNAME))  )\r
232         {\r
233                 _pico_printf( PICO_WARNING, "FM Header Ident incorrect\n");\r
234                 return NULL;\r
235         }\r
236 \r
237         if( _pico_little_long( fm.fm_header_hdr->version ) != FM_HEADERCHUNKVER )\r
238         {\r
239                 _pico_printf( PICO_WARNING, "FM Header Version incorrect\n");\r
240                 return NULL;\r
241         }\r
242 \r
243         // Skin Header\r
244         fm.fm_skin_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);\r
245         fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_skin_hdr->size;\r
246         if( (strcmp(fm.fm_skin_hdr->ident, FM_SKINCHUNKNAME)) )\r
247         {\r
248                 _pico_printf( PICO_WARNING, "FM Skin Ident incorrect\n");\r
249                 return NULL;\r
250         }\r
251 \r
252         if( _pico_little_long( fm.fm_skin_hdr->version ) != FM_SKINCHUNKVER )\r
253         {\r
254                 _pico_printf( PICO_WARNING, "FM Skin Version incorrect\n");\r
255                 return NULL;\r
256         }\r
257 \r
258         // ST Header\r
259         fm.fm_st_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);\r
260         fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_st_hdr->size;\r
261         if( (strcmp(fm.fm_st_hdr->ident, FM_STCOORDCHUNKNAME)) )\r
262         {\r
263                 _pico_printf( PICO_WARNING, "FM ST Ident incorrect\n");\r
264                 return NULL;\r
265         }\r
266 \r
267         if( _pico_little_long( fm.fm_st_hdr->version ) != FM_STCOORDCHUNKVER )\r
268         {\r
269                 _pico_printf( PICO_WARNING, "FM ST Version incorrect\n");\r
270                 return NULL;\r
271         }\r
272 \r
273         // Tris Header\r
274         fm.fm_tri_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);\r
275         fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_tri_hdr->size;\r
276         if( (strcmp(fm.fm_tri_hdr->ident, FM_TRISCHUNKNAME)) )\r
277         {\r
278                 _pico_printf( PICO_WARNING, "FM Tri Ident incorrect\n");\r
279                 return NULL;\r
280         }\r
281 \r
282         if( _pico_little_long( fm.fm_tri_hdr->version ) != FM_TRISCHUNKVER )\r
283         {\r
284                 _pico_printf( PICO_WARNING, "FM Tri Version incorrect\n");\r
285                 return NULL;\r
286         }\r
287 \r
288         // Frame Header\r
289         fm.fm_frame_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);\r
290         fm_file_pos += sizeof(fm_chunk_header_t);\r
291         if( (strcmp(fm.fm_frame_hdr->ident, FM_FRAMESCHUNKNAME)) )\r
292         {\r
293                 _pico_printf( PICO_WARNING, "FM Frame Ident incorrect\n");\r
294                 return NULL;\r
295         }\r
296 \r
297         if( _pico_little_long( fm.fm_frame_hdr->version ) != FM_FRAMESCHUNKVER )\r
298         {\r
299                 _pico_printf( PICO_WARNING, "FM Frame Version incorrect\n");\r
300                 return NULL;\r
301         }\r
302 \r
303         // Header\r
304         fm_file_pos = sizeof(fm_chunk_header_t);\r
305         fm_head = fm.fm_header = (fm_header_t *) (bb + fm_file_pos);\r
306         fm_file_pos += fm.fm_header_hdr->size;\r
307 \r
308         // Skin\r
309         fm_file_pos += sizeof(fm_chunk_header_t);\r
310         fm.fm_skin = (fm_skinpath_t *) (bb + fm_file_pos);\r
311         fm_file_pos += fm.fm_skin_hdr->size;\r
312 \r
313         // ST\r
314         fm_file_pos += sizeof(fm_chunk_header_t);\r
315         texCoord = fm.fm_st = (fm_st_t *) (bb + fm_file_pos);\r
316         fm_file_pos += fm.fm_st_hdr->size;\r
317 \r
318         // Tri\r
319         fm_file_pos += sizeof(fm_chunk_header_t);\r
320         tri_verts = fm.fm_tri = (fm_xyz_st_t *) (bb + fm_file_pos);\r
321         fm_file_pos += fm.fm_tri_hdr->size;\r
322 \r
323         // Frame\r
324         fm_file_pos += sizeof(fm_chunk_header_t);\r
325         frame = fm.fm_frame = (fm_frame_t *) (bb + fm_file_pos);\r
326 \r
327         // do frame check\r
328         if( fm_head->numFrames < 1 )\r
329         {\r
330                 _pico_printf( PICO_ERROR, "%s has 0 frames!", fileName );\r
331                 return NULL;\r
332         }\r
333         \r
334         if( frameNum < 0 || frameNum >= fm_head->numFrames )\r
335         {\r
336                 _pico_printf( PICO_ERROR, "Invalid or out-of-range FM frame specified" );\r
337                 return NULL;\r
338         }\r
339 \r
340         // swap fm\r
341         fm_head->skinWidth = _pico_little_long( fm_head->skinWidth );\r
342         fm_head->skinHeight = _pico_little_long( fm_head->skinHeight );\r
343         fm_head->frameSize = _pico_little_long( fm_head->frameSize );\r
344 \r
345         fm_head->numSkins = _pico_little_long( fm_head->numSkins );\r
346         fm_head->numXYZ = _pico_little_long( fm_head->numXYZ );\r
347         fm_head->numST = _pico_little_long( fm_head->numST );\r
348         fm_head->numTris = _pico_little_long( fm_head->numTris );\r
349         fm_head->numGLCmds = _pico_little_long( fm_head->numGLCmds );\r
350         fm_head->numFrames = _pico_little_long( fm_head->numFrames );\r
351 \r
352         // swap frame scale and translation\r
353         for( i = 0; i < 3; i++ )\r
354         {\r
355                 frame->header.scale[ i ] = _pico_little_float( frame->header.scale[ i ] );\r
356                 frame->header.translate[ i ] = _pico_little_float( frame->header.translate[ i ] );\r
357         }\r
358 \r
359         // swap triangles\r
360         triangle = tri_verts;\r
361         for( i = 0; i < fm_head->numTris; i++, triangle++ )\r
362         {\r
363                 for( j = 0; j < 3; j++ )\r
364                 {\r
365                         triangle->index_xyz[ j ] = _pico_little_short( triangle->index_xyz[ j ] );\r
366                         triangle->index_st[ j ] = _pico_little_short( triangle->index_st[ j ] );\r
367                 }\r
368         }\r
369 \r
370         // swap st coords\r
371         for( i = 0; i < fm_head->numST; i++ )\r
372         {\r
373                 texCoord->s = _pico_little_short( texCoord[i].s );\r
374                 texCoord->t = _pico_little_short( texCoord[i].t );\r
375         }\r
376         // set Skin Name\r
377         strncpy(skinname, (unsigned char *) fm.fm_skin, FM_SKINPATHSIZE );\r
378 \r
379 #ifdef FM_VERBOSE_DBG\r
380         // Print out md2 values\r
381         _pico_printf(PICO_VERBOSE,"numSkins->%d  numXYZ->%d  numST->%d  numTris->%d  numFrames->%d\nSkin Name \"%s\"\n", fm_head->numSkins, fm_head->numXYZ, fm_head->numST, fm_head->numTris, fm_head->numFrames, &skinname );\r
382 #endif\r
383 \r
384         // detox Skin name\r
385         _pico_setfext( skinname, "" );\r
386         _pico_unixify( skinname );\r
387 \r
388         /* create new pico model */\r
389         picoModel = PicoNewModel();\r
390         if( picoModel == NULL )\r
391         {\r
392                 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );\r
393                 return NULL;\r
394         }\r
395 \r
396         /* do model setup */\r
397         PicoSetModelFrameNum( picoModel, frameNum );\r
398         PicoSetModelNumFrames( picoModel, fm_head->numFrames ); /* sea */\r
399         PicoSetModelName( picoModel, fileName );\r
400         PicoSetModelFileName( picoModel, fileName );\r
401 \r
402         // allocate new pico surface\r
403         picoSurface = PicoNewSurface( picoModel );\r
404         if( picoSurface == NULL )\r
405         {\r
406                 _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );\r
407                 PicoFreeModel( picoModel );\r
408                 return NULL;\r
409         }\r
410 \r
411 \r
412         PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );\r
413         PicoSetSurfaceName( picoSurface, frame->header.name );\r
414         picoShader = PicoNewShader( picoModel );\r
415         if( picoShader == NULL )\r
416         {\r
417                 _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );\r
418                 PicoFreeModel( picoModel );\r
419                 return NULL;\r
420         }\r
421 \r
422         PicoSetShaderName( picoShader, skinname );\r
423 \r
424         // associate current surface with newly created shader\r
425         PicoSetSurfaceShader( picoSurface, picoShader );\r
426 \r
427         // Init LUT for Verts\r
428         p_index_LUT = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t) * fm_head->numXYZ);\r
429         for(i=0; i<fm_head->numXYZ; i++)\r
430         {\r
431                 p_index_LUT[i].Vert = -1;\r
432                 p_index_LUT[i].ST = -1;\r
433                 p_index_LUT[i].next = NULL;\r
434         }\r
435 \r
436         // Fill in Look Up Table, and allocate/fill Linked List from vert array as needed for dup STs per Vert.\r
437         tot_numVerts = fm_head->numXYZ;\r
438         dups = 0;\r
439         triangle = tri_verts;\r
440 \r
441         for(i=0; i<fm_head->numTris; i++)\r
442         {\r
443                 for(j=0; j<3; j++)\r
444                 {\r
445                         if (p_index_LUT[triangle->index_xyz[j]].ST == -1) // No Main Entry\r
446                                 p_index_LUT[triangle->index_xyz[j]].ST = triangle->index_st[j];\r
447 \r
448                         else if (triangle->index_st[j] == p_index_LUT[triangle->index_xyz[j]].ST ) // Equal to Main Entry\r
449                         {\r
450 #ifdef FM_VERBOSE_DBG\r
451                                 _pico_printf( PICO_NORMAL, "-> Tri #%d, Vert %d:\t XYZ:%d   ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);\r
452 #endif\r
453                                 continue;\r
454                         }\r
455                         else if ( (p_index_LUT[triangle->index_xyz[j]].next == NULL) )  // Not equal to Main entry, and no LL entry\r
456                         {       // Add first entry of LL from Main\r
457                                 p_index_LUT2 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t));\r
458                                 if (p_index_LUT2 == NULL)\r
459                                         _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n");\r
460                                 p_index_LUT[triangle->index_xyz[j]].next = (index_LUT_t *)p_index_LUT2;\r
461                                 p_index_LUT2->Vert = dups;\r
462                                 p_index_LUT2->ST = triangle->index_st[j];\r
463                                 p_index_LUT2->next = NULL;\r
464 #ifdef FM_VERBOSE_DBG\r
465                                 _pico_printf( PICO_NORMAL, " ADDING first LL XYZ:%d DUP:%d ST:%d\n", triangle->index_xyz[j], dups, triangle->index_st[j]);\r
466 #endif\r
467                                 triangle->index_xyz[j] = dups + fm_head->numXYZ; // Make change in Tri hunk\r
468                                 dups++;\r
469                         }\r
470                         else // Try to find in LL from Main Entry\r
471                         {\r
472                                 p_index_LUT3 = p_index_LUT2 = p_index_LUT[triangle->index_xyz[j]].next;\r
473                                 while ( (p_index_LUT2 != NULL) && (triangle->index_xyz[j] != p_index_LUT2->Vert) ) // Walk down LL\r
474                                 {\r
475                                         p_index_LUT3 = p_index_LUT2;\r
476                                         p_index_LUT2 = p_index_LUT2->next;\r
477                                 }\r
478                                 p_index_LUT2 = p_index_LUT3;\r
479 \r
480                                 if ( triangle->index_st[j] == p_index_LUT2->ST ) // Found it\r
481                                 {\r
482                                         triangle->index_xyz[j] = p_index_LUT2->Vert + fm_head->numXYZ; // Make change in Tri hunk\r
483 #ifdef FM_VERBOSE_DBG\r
484                                         _pico_printf( PICO_NORMAL, "--> Tri #%d, Vert %d:\t XYZ:%d   ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);\r
485 #endif\r
486                                         continue;\r
487                                 }\r
488 \r
489                                 if ( p_index_LUT2->next == NULL)  // Didn't find it. Add entry to LL.\r
490                                 {\r
491                                         // Add the Entry\r
492                                         p_index_LUT3 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t));\r
493                                         if (p_index_LUT3 == NULL)\r
494                                                 _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n");\r
495                                         p_index_LUT2->next = (index_LUT_t *)p_index_LUT3;\r
496                                         p_index_LUT3->Vert = dups;\r
497                                         p_index_LUT3->ST = triangle->index_st[j];\r
498                                         p_index_LUT3->next = NULL;\r
499 #ifdef FM_VERBOSE_DBG\r
500                                         _pico_printf( PICO_NORMAL, " ADDING additional LL XYZ:%d DUP:%d NewXYZ:%d ST:%d\n", triangle->index_xyz[j], dups, dups + (fm_head->numXYZ), triangle->index_st[j]);\r
501 #endif\r
502                                         triangle->index_xyz[j] = dups + fm_head->numXYZ; // Make change in Tri hunk\r
503                                         dups++;\r
504                                 }\r
505                         }\r
506 #ifdef FM_VERBOSE_DBG\r
507                         _pico_printf( PICO_NORMAL, "---> Tri #%d, Vert %d:\t XYZ:%d   ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);\r
508 #endif\r
509                 }\r
510                 triangle++;\r
511         }\r
512 \r
513         // malloc and build array for Dup STs\r
514         p_index_LUT_DUPS = (index_DUP_LUT_t *)_pico_alloc(sizeof(index_DUP_LUT_t) * dups);\r
515         if (p_index_LUT_DUPS == NULL)\r
516                 _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n");\r
517 \r
518         dup_index = 0;\r
519         for(i=0; i<fm_head->numXYZ; i++)\r
520         {\r
521                 p_index_LUT2 = p_index_LUT[i].next;\r
522                 while (p_index_LUT2 != NULL)\r
523                 {\r
524                         p_index_LUT_DUPS[p_index_LUT2->Vert].OldVert = i;\r
525                         p_index_LUT_DUPS[p_index_LUT2->Vert].ST = p_index_LUT2->ST;\r
526                         dup_index++;\r
527                         p_index_LUT2 = p_index_LUT2->next;\r
528                 }\r
529         }\r
530 #ifdef FM_VERBOSE_DBG\r
531         _pico_printf( PICO_NORMAL, " Dups = %d\n", dups);\r
532         _pico_printf( PICO_NORMAL, " Dup Index = %d\n", dup_index);\r
533 #endif\r
534         for(i=0; i<fm_head->numXYZ; i++)\r
535         {\r
536 #ifdef FM_VERBOSE_DBG\r
537                 _pico_printf( PICO_NORMAL, "Vert: %4d\t%4d",i, p_index_LUT[i].ST);\r
538 #endif\r
539                 if (p_index_LUT[i].next != NULL)\r
540                 {\r
541 \r
542                         p_index_LUT2 = p_index_LUT[i].next;\r
543                         do {\r
544 #ifdef FM_VERBOSE_DBG\r
545                                 _pico_printf( PICO_NORMAL, " %4d %4d", p_index_LUT2->Vert, p_index_LUT2->ST);\r
546 #endif\r
547                                 p_index_LUT2 = p_index_LUT2->next;\r
548                         } while ( p_index_LUT2 != NULL);\r
549 \r
550                 }\r
551 #ifdef FM_VERBOSE_DBG\r
552                 _pico_printf( PICO_NORMAL, "\n");\r
553 #endif\r
554         }\r
555 \r
556 \r
557 #ifdef FM_VERBOSE_DBG\r
558         for(i=0; i<dup_index; i++)\r
559                 _pico_printf( PICO_NORMAL, " Dup Index #%d  OldVert: %d  ST: %d\n", i, p_index_LUT_DUPS[i].OldVert, p_index_LUT_DUPS[i].ST);\r
560 \r
561         triangle = tri_verts;\r
562         for(i=0; i<fm_head->numTris; i++)\r
563         {\r
564                 for(j=0; j<3; j++)\r
565                         _pico_printf( PICO_NORMAL, "Tri #%d, Vert %d:\t XYZ:%d   ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);\r
566                 _pico_printf( PICO_NORMAL, "\n");\r
567                 triangle++;\r
568         }\r
569 #endif\r
570         // Build Picomodel\r
571         triangle = tri_verts;\r
572         for( j = 0; j < fm_head->numTris; j++, triangle++ )\r
573         {\r
574                 PicoSetSurfaceIndex( picoSurface, j*3   , triangle->index_xyz[0] );\r
575                 PicoSetSurfaceIndex( picoSurface, j*3+1 , triangle->index_xyz[1] );\r
576                 PicoSetSurfaceIndex( picoSurface, j*3+2 , triangle->index_xyz[2] );\r
577         }\r
578 \r
579         vert = (fm_vert_normal_t*) ((picoByte_t*) (frame->verts) );\r
580         for(i=0; i< fm_head->numXYZ; i++, vert++)\r
581         {\r
582                 /* set vertex origin */\r
583                 xyz[ 0 ] = vert->v[0] * frame->header.scale[0] + frame->header.translate[0];\r
584                 xyz[ 1 ] = vert->v[1] * frame->header.scale[1] + frame->header.translate[1];\r
585                 xyz[ 2 ] = vert->v[2] * frame->header.scale[2] + frame->header.translate[2];\r
586                 PicoSetSurfaceXYZ( picoSurface, i , xyz );\r
587 \r
588                 /* set normal */\r
589                 normal[ 0 ] = fm_normals[vert->lightnormalindex][0];\r
590                 normal[ 1 ] = fm_normals[vert->lightnormalindex][1];\r
591                 normal[ 2 ] = fm_normals[vert->lightnormalindex][2];\r
592                 PicoSetSurfaceNormal( picoSurface, i , normal );\r
593 \r
594                 /* set st coords */\r
595                 st[ 0 ] =  ((texCoord[p_index_LUT[i].ST].s) / ((float)fm_head->skinWidth));\r
596                 st[ 1 ] =  (texCoord[p_index_LUT[i].ST].t / ((float)fm_head->skinHeight));\r
597                 PicoSetSurfaceST( picoSurface, 0, i , st );\r
598         }\r
599 \r
600         if (dups)\r
601         {\r
602                 for(i=0; i<dups; i++)\r
603                 {\r
604                         j = p_index_LUT_DUPS[i].OldVert;\r
605                         /* set vertex origin */\r
606                         xyz[ 0 ] = frame->verts[j].v[0] * frame->header.scale[0] + frame->header.translate[0];\r
607                         xyz[ 1 ] = frame->verts[j].v[1] * frame->header.scale[1] + frame->header.translate[1];\r
608                         xyz[ 2 ] = frame->verts[j].v[2] * frame->header.scale[2] + frame->header.translate[2];\r
609                         PicoSetSurfaceXYZ( picoSurface, i + fm_head->numXYZ , xyz );\r
610 \r
611                         /* set normal */\r
612                         normal[ 0 ] = fm_normals[frame->verts[j].lightnormalindex][0];\r
613                         normal[ 1 ] = fm_normals[frame->verts[j].lightnormalindex][1];\r
614                         normal[ 2 ] = fm_normals[frame->verts[j].lightnormalindex][2];\r
615                         PicoSetSurfaceNormal( picoSurface, i + fm_head->numXYZ , normal );\r
616 \r
617                         /* set st coords */\r
618                         st[ 0 ] =  ((texCoord[p_index_LUT_DUPS[i].ST].s) / ((float)fm_head->skinWidth));\r
619                         st[ 1 ] =  (texCoord[p_index_LUT_DUPS[i].ST].t / ((float)fm_head->skinHeight));\r
620                         PicoSetSurfaceST( picoSurface, 0, i + fm_head->numXYZ , st );\r
621                 }\r
622         }\r
623 \r
624         /* set color */\r
625         PicoSetSurfaceColor( picoSurface, 0, 0, color );\r
626 \r
627         // Free up malloc'ed LL entries\r
628         for(i=0; i<fm_head->numXYZ; i++)\r
629         {\r
630                 if(p_index_LUT[i].next != NULL)\r
631                 {\r
632                         p_index_LUT2 = p_index_LUT[i].next;\r
633                         do {\r
634                                 p_index_LUT3 = p_index_LUT2->next;\r
635                                 _pico_free(p_index_LUT2);\r
636                                 p_index_LUT2 = p_index_LUT3;\r
637                                 dups--;\r
638                         } while (p_index_LUT2 != NULL);\r
639                 }\r
640         }\r
641 \r
642         if (dups)\r
643                 _pico_printf(PICO_WARNING, " Not all LL mallocs freed\n");\r
644 \r
645         // Free malloc'ed LUTs\r
646         _pico_free(p_index_LUT);\r
647         _pico_free(p_index_LUT_DUPS);\r
648 \r
649         /* return the new pico model */\r
650         return picoModel;\r
651 \r
652 }\r
653 \r
654 \r
655 \r
656 /* pico file format module definition */\r
657 const picoModule_t picoModuleFM =\r
658 {\r
659         "0.85",                                         /* module version string */\r
660         "Heretic 2 FM",                         /* module display name */\r
661         "Nurail",                                       /* author's name */\r
662         "2003 Nurail",                          /* module copyright */\r
663         {\r
664                 "fm", NULL, NULL, NULL  /* default extensions to use */\r
665         },\r
666         _fm_canload,                            /* validation routine */\r
667         _fm_load,                                       /* load routine */\r
668          NULL,                                          /* save validation routine */\r
669          NULL                                           /* save routine */\r
670 };\r