]> git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_fm.c
picomodel: white default color of fm, md2 (was one white and rest black)
[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
216
217         bb0 = bb = (picoByte_t*) _pico_alloc( bufSize );
218         memcpy( bb, buffer, bufSize );
219
220         // Header Header
221         fm.fm_header_hdr = (fm_chunk_header_t *) bb;
222         fm_file_pos = sizeof( fm_chunk_header_t ) + fm.fm_header_hdr->size;
223         if ( ( strcmp( fm.fm_header_hdr->ident, FM_HEADERCHUNKNAME ) )  ) {
224                 _pico_printf( PICO_WARNING, "FM Header Ident incorrect\n" );
225                 _pico_free( bb0 );
226                 return NULL;
227         }
228
229         if ( _pico_little_long( fm.fm_header_hdr->version ) != FM_HEADERCHUNKVER ) {
230                 _pico_printf( PICO_WARNING, "FM Header Version incorrect\n" );
231                 _pico_free( bb0 );
232                 return NULL;
233         }
234
235         // Skin Header
236         fm.fm_skin_hdr = (fm_chunk_header_t *) ( bb + fm_file_pos );
237         fm_file_pos += sizeof( fm_chunk_header_t ) + fm.fm_skin_hdr->size;
238         if ( ( strcmp( fm.fm_skin_hdr->ident, FM_SKINCHUNKNAME ) ) ) {
239                 _pico_printf( PICO_WARNING, "FM Skin Ident incorrect\n" );
240                 _pico_free( bb0 );
241                 return NULL;
242         }
243
244         if ( _pico_little_long( fm.fm_skin_hdr->version ) != FM_SKINCHUNKVER ) {
245                 _pico_printf( PICO_WARNING, "FM Skin Version incorrect\n" );
246                 _pico_free( bb0 );
247                 return NULL;
248         }
249
250         // ST Header
251         fm.fm_st_hdr = (fm_chunk_header_t *) ( bb + fm_file_pos );
252         fm_file_pos += sizeof( fm_chunk_header_t ) + fm.fm_st_hdr->size;
253         if ( ( strcmp( fm.fm_st_hdr->ident, FM_STCOORDCHUNKNAME ) ) ) {
254                 _pico_printf( PICO_WARNING, "FM ST Ident incorrect\n" );
255                 _pico_free( bb0 );
256                 return NULL;
257         }
258
259         if ( _pico_little_long( fm.fm_st_hdr->version ) != FM_STCOORDCHUNKVER ) {
260                 _pico_printf( PICO_WARNING, "FM ST Version incorrect\n" );
261                 _pico_free( bb0 );
262                 return NULL;
263         }
264
265         // Tris Header
266         fm.fm_tri_hdr = (fm_chunk_header_t *) ( bb + fm_file_pos );
267         fm_file_pos += sizeof( fm_chunk_header_t ) + fm.fm_tri_hdr->size;
268         if ( ( strcmp( fm.fm_tri_hdr->ident, FM_TRISCHUNKNAME ) ) ) {
269                 _pico_printf( PICO_WARNING, "FM Tri Ident incorrect\n" );
270                 _pico_free( bb0 );
271                 return NULL;
272         }
273
274         if ( _pico_little_long( fm.fm_tri_hdr->version ) != FM_TRISCHUNKVER ) {
275                 _pico_printf( PICO_WARNING, "FM Tri Version incorrect\n" );
276                 _pico_free( bb0 );
277                 return NULL;
278         }
279
280         // Frame Header
281         fm.fm_frame_hdr = (fm_chunk_header_t *) ( bb + fm_file_pos );
282         fm_file_pos += sizeof( fm_chunk_header_t );
283         if ( ( strcmp( fm.fm_frame_hdr->ident, FM_FRAMESCHUNKNAME ) ) ) {
284                 _pico_printf( PICO_WARNING, "FM Frame Ident incorrect\n" );
285                 _pico_free( bb0 );
286                 return NULL;
287         }
288
289         if ( _pico_little_long( fm.fm_frame_hdr->version ) != FM_FRAMESCHUNKVER ) {
290                 _pico_printf( PICO_WARNING, "FM Frame Version incorrect\n" );
291                 _pico_free( bb0 );
292                 return NULL;
293         }
294
295         // Header
296         fm_file_pos = sizeof( fm_chunk_header_t );
297         fm_head = fm.fm_header = (fm_header_t *) ( bb + fm_file_pos );
298         fm_file_pos += fm.fm_header_hdr->size;
299
300         // Skin
301         fm_file_pos += sizeof( fm_chunk_header_t );
302         fm.fm_skin = (fm_skinpath_t *) ( bb + fm_file_pos );
303         fm_file_pos += fm.fm_skin_hdr->size;
304
305         // ST
306         fm_file_pos += sizeof( fm_chunk_header_t );
307         texCoord = fm.fm_st = (fm_st_t *) ( bb + fm_file_pos );
308         fm_file_pos += fm.fm_st_hdr->size;
309
310         // Tri
311         fm_file_pos += sizeof( fm_chunk_header_t );
312         tri_verts = fm.fm_tri = (fm_xyz_st_t *) ( bb + fm_file_pos );
313         fm_file_pos += fm.fm_tri_hdr->size;
314
315         // Frame
316         fm_file_pos += sizeof( fm_chunk_header_t );
317         frame = fm.fm_frame = (fm_frame_t *) ( bb + fm_file_pos );
318
319         // do frame check
320         if ( fm_head->numFrames < 1 ) {
321                 _pico_printf( PICO_ERROR, "%s has 0 frames!", fileName );
322                 _pico_free( bb0 );
323                 return NULL;
324         }
325
326         if ( frameNum < 0 || frameNum >= fm_head->numFrames ) {
327                 _pico_printf( PICO_ERROR, "Invalid or out-of-range FM frame specified" );
328                 _pico_free( bb0 );
329                 return NULL;
330         }
331
332         // swap fm
333         fm_head->skinWidth = _pico_little_long( fm_head->skinWidth );
334         fm_head->skinHeight = _pico_little_long( fm_head->skinHeight );
335         fm_head->frameSize = _pico_little_long( fm_head->frameSize );
336
337         fm_head->numSkins = _pico_little_long( fm_head->numSkins );
338         fm_head->numXYZ = _pico_little_long( fm_head->numXYZ );
339         fm_head->numST = _pico_little_long( fm_head->numST );
340         fm_head->numTris = _pico_little_long( fm_head->numTris );
341         fm_head->numGLCmds = _pico_little_long( fm_head->numGLCmds );
342         fm_head->numFrames = _pico_little_long( fm_head->numFrames );
343
344         // swap frame scale and translation
345         for ( i = 0; i < 3; i++ )
346         {
347                 frame->header.scale[ i ] = _pico_little_float( frame->header.scale[ i ] );
348                 frame->header.translate[ i ] = _pico_little_float( frame->header.translate[ i ] );
349         }
350
351         // swap triangles
352         triangle = tri_verts;
353         for ( i = 0; i < fm_head->numTris; i++, triangle++ )
354         {
355                 for ( j = 0; j < 3; j++ )
356                 {
357                         triangle->index_xyz[ j ] = _pico_little_short( triangle->index_xyz[ j ] );
358                         triangle->index_st[ j ] = _pico_little_short( triangle->index_st[ j ] );
359                 }
360         }
361
362         // swap st coords
363         for ( i = 0; i < fm_head->numST; i++ )
364         {
365                 texCoord->s = _pico_little_short( texCoord[i].s );
366                 texCoord->t = _pico_little_short( texCoord[i].t );
367         }
368         // set Skin Name
369         strncpy( skinname, (const char *) fm.fm_skin, FM_SKINPATHSIZE );
370
371 #ifdef FM_VERBOSE_DBG
372         // Print out md2 values
373         _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 );
374 #endif
375
376         // detox Skin name
377         _pico_setfext( skinname, "" );
378         _pico_unixify( skinname );
379
380         /* create new pico model */
381         picoModel = PicoNewModel();
382         if ( picoModel == NULL ) {
383                 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
384                 _pico_free( bb0 );
385                 return NULL;
386         }
387
388         /* do model setup */
389         PicoSetModelFrameNum( picoModel, frameNum );
390         PicoSetModelNumFrames( picoModel, fm_head->numFrames ); /* sea */
391         PicoSetModelName( picoModel, fileName );
392         PicoSetModelFileName( picoModel, fileName );
393
394         // allocate new pico surface
395         picoSurface = PicoNewSurface( picoModel );
396         if ( picoSurface == NULL ) {
397                 _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
398                 PicoFreeModel( picoModel );
399                 _pico_free( bb0 );
400                 return NULL;
401         }
402
403
404         PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
405         PicoSetSurfaceName( picoSurface, frame->header.name );
406         picoShader = PicoNewShader( picoModel );
407         if ( picoShader == NULL ) {
408                 _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
409                 PicoFreeModel( picoModel );
410                 _pico_free( bb0 );
411                 return NULL;
412         }
413
414         PicoSetShaderName( picoShader, skinname );
415
416         // associate current surface with newly created shader
417         PicoSetSurfaceShader( picoSurface, picoShader );
418
419         // Init LUT for Verts
420         p_index_LUT = (index_LUT_t *)_pico_alloc( sizeof( index_LUT_t ) * fm_head->numXYZ );
421         for ( i = 0; i < fm_head->numXYZ; i++ )
422         {
423                 p_index_LUT[i].Vert = -1;
424                 p_index_LUT[i].ST = -1;
425                 p_index_LUT[i].next = NULL;
426         }
427
428         // Fill in Look Up Table, and allocate/fill Linked List from vert array as needed for dup STs per Vert.
429         dups = 0;
430         triangle = tri_verts;
431
432         for ( i = 0; i < fm_head->numTris; i++ )
433         {
434                 for ( j = 0; j < 3; j++ )
435                 {
436                         if ( p_index_LUT[triangle->index_xyz[j]].ST == -1 ) { // No Main Entry
437                                 p_index_LUT[triangle->index_xyz[j]].ST = triangle->index_st[j];
438                         }
439
440                         else if ( triangle->index_st[j] == p_index_LUT[triangle->index_xyz[j]].ST ) { // Equal to Main Entry
441 #ifdef FM_VERBOSE_DBG
442                                 _pico_printf( PICO_NORMAL, "-> Tri #%d, Vert %d:\t XYZ:%d   ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j] );
443 #endif
444                                 continue;
445                         }
446                         else if ( ( p_index_LUT[triangle->index_xyz[j]].next == NULL ) ) { // Not equal to Main entry, and no LL entry
447                                 // Add first entry of LL from Main
448                                 p_index_LUT2 = (index_LUT_t *)_pico_alloc( sizeof( index_LUT_t ) );
449                                 if ( p_index_LUT2 == NULL ) {
450                                         _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n" );
451                                 }
452                                 p_index_LUT[triangle->index_xyz[j]].next = (index_LUT_t *)p_index_LUT2;
453                                 p_index_LUT2->Vert = dups;
454                                 p_index_LUT2->ST = triangle->index_st[j];
455                                 p_index_LUT2->next = NULL;
456 #ifdef FM_VERBOSE_DBG
457                                 _pico_printf( PICO_NORMAL, " ADDING first LL XYZ:%d DUP:%d ST:%d\n", triangle->index_xyz[j], dups, triangle->index_st[j] );
458 #endif
459                                 triangle->index_xyz[j] = dups + fm_head->numXYZ; // Make change in Tri hunk
460                                 dups++;
461                         }
462                         else // Try to find in LL from Main Entry
463                         {
464                                 p_index_LUT3 = p_index_LUT2 = p_index_LUT[triangle->index_xyz[j]].next;
465                                 while ( ( p_index_LUT2 != NULL ) && ( triangle->index_xyz[j] != p_index_LUT2->Vert ) ) // Walk down LL
466                                 {
467                                         p_index_LUT3 = p_index_LUT2;
468                                         p_index_LUT2 = p_index_LUT2->next;
469                                 }
470                                 p_index_LUT2 = p_index_LUT3;
471
472                                 if ( triangle->index_st[j] == p_index_LUT2->ST ) { // Found it
473                                         triangle->index_xyz[j] = p_index_LUT2->Vert + fm_head->numXYZ; // Make change in Tri hunk
474 #ifdef FM_VERBOSE_DBG
475                                         _pico_printf( PICO_NORMAL, "--> Tri #%d, Vert %d:\t XYZ:%d   ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j] );
476 #endif
477                                         continue;
478                                 }
479
480                                 if ( p_index_LUT2->next == NULL ) { // Didn't find it. Add entry to LL.
481                                         // Add the Entry
482                                         p_index_LUT3 = (index_LUT_t *)_pico_alloc( sizeof( index_LUT_t ) );
483                                         if ( p_index_LUT3 == NULL ) {
484                                                 _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n" );
485                                         }
486                                         p_index_LUT2->next = (index_LUT_t *)p_index_LUT3;
487                                         p_index_LUT3->Vert = dups;
488                                         p_index_LUT3->ST = triangle->index_st[j];
489                                         p_index_LUT3->next = NULL;
490 #ifdef FM_VERBOSE_DBG
491                                         _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] );
492 #endif
493                                         triangle->index_xyz[j] = dups + fm_head->numXYZ; // Make change in Tri hunk
494                                         dups++;
495                                 }
496                         }
497 #ifdef FM_VERBOSE_DBG
498                         _pico_printf( PICO_NORMAL, "---> Tri #%d, Vert %d:\t XYZ:%d   ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j] );
499 #endif
500                 }
501                 triangle++;
502         }
503
504         // malloc and build array for Dup STs
505         p_index_LUT_DUPS = (index_DUP_LUT_t *)_pico_alloc( sizeof( index_DUP_LUT_t ) * dups );
506         if ( p_index_LUT_DUPS == NULL ) {
507                 _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n" );
508         }
509
510         dup_index = 0;
511         for ( i = 0; i < fm_head->numXYZ; i++ )
512         {
513                 p_index_LUT2 = p_index_LUT[i].next;
514                 while ( p_index_LUT2 != NULL )
515                 {
516                         p_index_LUT_DUPS[p_index_LUT2->Vert].OldVert = i;
517                         p_index_LUT_DUPS[p_index_LUT2->Vert].ST = p_index_LUT2->ST;
518                         dup_index++;
519                         p_index_LUT2 = p_index_LUT2->next;
520                 }
521         }
522 #ifdef FM_VERBOSE_DBG
523         _pico_printf( PICO_NORMAL, " Dups = %d\n", dups );
524         _pico_printf( PICO_NORMAL, " Dup Index = %d\n", dup_index );
525 #endif
526         for ( i = 0; i < fm_head->numXYZ; i++ )
527         {
528 #ifdef FM_VERBOSE_DBG
529                 _pico_printf( PICO_NORMAL, "Vert: %4d\t%4d",i, p_index_LUT[i].ST );
530 #endif
531                 if ( p_index_LUT[i].next != NULL ) {
532
533                         p_index_LUT2 = p_index_LUT[i].next;
534                         do {
535 #ifdef FM_VERBOSE_DBG
536                                 _pico_printf( PICO_NORMAL, " %4d %4d", p_index_LUT2->Vert, p_index_LUT2->ST );
537 #endif
538                                 p_index_LUT2 = p_index_LUT2->next;
539                         } while ( p_index_LUT2 != NULL );
540
541                 }
542 #ifdef FM_VERBOSE_DBG
543                 _pico_printf( PICO_NORMAL, "\n" );
544 #endif
545         }
546
547
548 #ifdef FM_VERBOSE_DBG
549         for ( i = 0; i < dup_index; i++ )
550                 _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 );
551
552         triangle = tri_verts;
553         for ( i = 0; i < fm_head->numTris; i++ )
554         {
555                 for ( j = 0; j < 3; j++ )
556                         _pico_printf( PICO_NORMAL, "Tri #%d, Vert %d:\t XYZ:%d   ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j] );
557                 _pico_printf( PICO_NORMAL, "\n" );
558                 triangle++;
559         }
560 #endif
561         // Build Picomodel
562         triangle = tri_verts;
563         for ( j = 0; j < fm_head->numTris; j++, triangle++ )
564         {
565                 PicoSetSurfaceIndex( picoSurface, j * 3, triangle->index_xyz[0] );
566                 PicoSetSurfaceIndex( picoSurface, j * 3 + 1, triangle->index_xyz[1] );
567                 PicoSetSurfaceIndex( picoSurface, j * 3 + 2, triangle->index_xyz[2] );
568         }
569
570         vert = (fm_vert_normal_t*) ( (picoByte_t*) ( frame->verts ) );
571         for ( i = 0; i < fm_head->numXYZ; i++, vert++ )
572         {
573                 /* set vertex origin */
574                 xyz[ 0 ] = vert->v[0] * frame->header.scale[0] + frame->header.translate[0];
575                 xyz[ 1 ] = vert->v[1] * frame->header.scale[1] + frame->header.translate[1];
576                 xyz[ 2 ] = vert->v[2] * frame->header.scale[2] + frame->header.translate[2];
577                 PicoSetSurfaceXYZ( picoSurface, i, xyz );
578
579                 /* set normal */
580                 normal[ 0 ] = fm_normals[vert->lightnormalindex][0];
581                 normal[ 1 ] = fm_normals[vert->lightnormalindex][1];
582                 normal[ 2 ] = fm_normals[vert->lightnormalindex][2];
583                 PicoSetSurfaceNormal( picoSurface, i, normal );
584
585                 /* set st coords */
586                 st[ 0 ] =  ( ( texCoord[p_index_LUT[i].ST].s ) / ( (float)fm_head->skinWidth ) );
587                 st[ 1 ] =  ( texCoord[p_index_LUT[i].ST].t / ( (float)fm_head->skinHeight ) );
588                 PicoSetSurfaceST( picoSurface, 0, i, st );
589
590                 /* set color */
591                 PicoSetSurfaceColor( picoSurface, 0, i, picoColor_white );
592         }
593
594         if ( dups ) {
595                 for ( i = 0; i < dups; i++ )
596                 {
597                         j = p_index_LUT_DUPS[i].OldVert;
598                         /* set vertex origin */
599                         xyz[ 0 ] = frame->verts[j].v[0] * frame->header.scale[0] + frame->header.translate[0];
600                         xyz[ 1 ] = frame->verts[j].v[1] * frame->header.scale[1] + frame->header.translate[1];
601                         xyz[ 2 ] = frame->verts[j].v[2] * frame->header.scale[2] + frame->header.translate[2];
602                         PicoSetSurfaceXYZ( picoSurface, i + fm_head->numXYZ, xyz );
603
604                         /* set normal */
605                         normal[ 0 ] = fm_normals[frame->verts[j].lightnormalindex][0];
606                         normal[ 1 ] = fm_normals[frame->verts[j].lightnormalindex][1];
607                         normal[ 2 ] = fm_normals[frame->verts[j].lightnormalindex][2];
608                         PicoSetSurfaceNormal( picoSurface, i + fm_head->numXYZ, normal );
609
610                         /* set st coords */
611                         st[ 0 ] =  ( ( texCoord[p_index_LUT_DUPS[i].ST].s ) / ( (float)fm_head->skinWidth ) );
612                         st[ 1 ] =  ( texCoord[p_index_LUT_DUPS[i].ST].t / ( (float)fm_head->skinHeight ) );
613                         PicoSetSurfaceST( picoSurface, 0, i + fm_head->numXYZ, st );
614
615                         /* set color */
616                         PicoSetSurfaceColor( picoSurface, 0, i + fm_head->numXYZ, picoColor_white );
617                 }
618         }
619
620         // Free up malloc'ed LL entries
621         for ( i = 0; i < fm_head->numXYZ; i++ )
622         {
623                 if ( p_index_LUT[i].next != NULL ) {
624                         p_index_LUT2 = p_index_LUT[i].next;
625                         do {
626                                 p_index_LUT3 = p_index_LUT2->next;
627                                 _pico_free( p_index_LUT2 );
628                                 p_index_LUT2 = p_index_LUT3;
629                                 dups--;
630                         } while ( p_index_LUT2 != NULL );
631                 }
632         }
633
634         if ( dups ) {
635                 _pico_printf( PICO_WARNING, " Not all LL mallocs freed\n" );
636         }
637
638         // Free malloc'ed LUTs
639         _pico_free( p_index_LUT );
640         _pico_free( p_index_LUT_DUPS );
641
642         /* return the new pico model */
643         _pico_free( bb0 );
644         return picoModel;
645
646 }
647
648
649
650 /* pico file format module definition */
651 const picoModule_t picoModuleFM =
652 {
653         "0.85",                     /* module version string */
654         "Heretic 2 FM",             /* module display name */
655         "Nurail",                   /* author's name */
656         "2003 Nurail",              /* module copyright */
657         {
658                 "fm", NULL, NULL, NULL  /* default extensions to use */
659         },
660         _fm_canload,                /* validation routine */
661         _fm_load,                   /* load routine */
662         NULL,                       /* save validation routine */
663         NULL                        /* save routine */
664 };