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