1 /*
2      PLIB - A Suite of Portable Game Libraries
3      Copyright (C) 1998,2002  Steve Baker
4 
5      This library is free software; you can redistribute it and/or
6      modify it under the terms of the GNU Library General Public
7      License as published by the Free Software Foundation; either
8      version 2 of the License, or (at your option) any later version.
9 
10      This library is distributed in the hope that it will be useful,
11      but WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Library General Public License for more details.
14 
15      You should have received a copy of the GNU Library General Public
16      License along with this library; if not, write to the Free Software
17      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18 
19      For further information visit http://plib.sourceforge.net
20 
21      $Id: ssgLoadMDL.cxx 2012 2005-01-15 20:36:46Z sjbaker $
22 */
23 
24 //===========================================================================
25 //
26 // File: GngMsfsIO.cpp
27 //
28 // Created: Tue Feb 29 22:20:31 2000
29 //
30 // Author: Thomas Engh Sevaldrud <tse@math.sintef.no>
31 //
32 // Revision: $Id: ssgLoadMDL.cxx 2012 2005-01-15 20:36:46Z sjbaker $
33 //
34 // Description:
35 //
36 //===========================================================================
37 // Copyright (c) 2000 Thomas E. Sevaldrud <tse@math.sintef.no>
38 //===========================================================================
39 
40 #include "ssgLocal.h"
41 
42 // kludge: global
43 int g_noLoDs =1;
44 
45 #ifdef SSG_LOAD_MDL_SUPPORTED
46 
47 #include "ssgLoadMDL.h"
48 
49 #define DEF_SHININESS 50
50 
51 //#define DEBUG
52 
53 #ifdef DEBUG
54 #include <iostream>
55 #define DEBUGPRINT(x) std::cerr << x
56 #else
57 #define DEBUGPRINT(x)
58 #endif
59 
60 static ssgLoaderOptions *current_options;
61 
62 // Temporary vertex arrays
63 static ssgVertexArray     *curr_vtx_;
64 static ssgNormalArray     *curr_norm_;
65 static ssgIndexArray      *curr_index_;
66 
67 // Vertex arrays
68 static ssgVertexArray     *vertex_array_;
69 static ssgNormalArray     *normal_array_;
70 static ssgTexCoordArray   *tex_coords_;
71 
72 // Current part (index array)
73 static ssgLeaf        *curr_part_;
74 static ssgBranch      *model_;
75 
76 // Moving parts
77 static ssgBranch      *ailerons_grp_, *elevator_grp_, *rudder_grp_;
78 static ssgBranch      *gear_grp_, *spoilers_grp_, *flaps_grp_;
79 static ssgBranch          *prop_grp_;
80 
81 static sgMat4         curr_matrix_;
82 static sgVec3       curr_rot_pt_;
83 static sgVec4       curr_col_;
84 static char            *curr_tex_name_;
85 static ssgAxisTransform   *curr_xfm_;
86 
87 int noGT=0, noLT=0, no0=0;
88 
89 #define EXPERIMENTAL_CULL_FACE_CODE
90 #ifdef EXPERIMENTAL_CULL_FACE_CODE
91 static bool         curr_cull_face_;
92 #endif
93 
94 // File Address Stack
95 static const int          MAX_STACK_DEPTH = 128; // wk: 32 is too small
96 static long               stack_        [MAX_STACK_DEPTH]; // adress part
97 static short              lod_          [MAX_STACK_DEPTH]; // lod part of the stack
98 static int                stack_depth_;
99 static short              noLoDs;
100 static short              curr_lod;
101 
102 //  static sgMat4       matrix_stack_ [MAX_STACK_DEPTH];
103 //  static char         *textures_    [MAX_STACK_DEPTH];
104 //  static ssgBranch    *groups_      [MAX_STACK_DEPTH];
105 
106 static int          start_idx_, last_idx_;
107 static int              curr_var_;
108 
109 static bool         has_normals_, vtx_dirty_, tex_vtx_dirty_;
110 //static bool             join_children_, override_normals_;
111 
112 //static char             *tex_fmt_;
113 
114 //===========================================================================
115 
116 /*static void initLoader()
117 {
118   start_idx_        = 0;
119   join_children_    = true;
120   override_normals_ = true;
121   tex_fmt_          = "tif";
122   stack_depth_      = 0;
123 #ifdef EXPERIMENTAL_CULL_FACE_CODE
124   curr_cull_face_   = false ;
125 #endif
126 }*/
127 
128 #ifdef DEBUG
129 FILE *wkfp;
130 
131 #define PRINT_STRUCTURE(a, b) fprintf(wkfp, a, b);
132 #define PRINT_STRUCTURE1(a ) fprintf(wkfp, a);
133 
134 #else
135 
136 #define PRINT_STRUCTURE(a, b)
137 #define PRINT_STRUCTURE1(a )
138 
139 #endif
140 
141 //===========================================================================
142 
newPart()143 static void newPart()
144 {
145   vtx_dirty_     = true;
146   tex_vtx_dirty_ = true;
147   curr_tex_name_ = NULL;
148   sgSetVec4( curr_col_, 1.0f, 1.0f, 1.0f, 1.0f );
149 
150   delete curr_vtx_;
151   delete curr_norm_;
152   curr_vtx_  = new ssgVertexArray ;
153   curr_norm_ = new ssgNormalArray ;
154 }
155 
156 //===========================================================================
157 
push_stack(long entry,short lod)158 static void push_stack( long entry, short lod ) {
159   assert( stack_depth_ < MAX_STACK_DEPTH - 1 );
160 
161   lod_ [stack_depth_] = lod;
162   stack_[stack_depth_++] = entry;
163 }
164 
pop_stack(short & lod)165 static long pop_stack(short &lod) {
166   assert( stack_depth_ > 0 );
167 
168   lod = lod_[--stack_depth_];
169   return stack_[stack_depth_];
170 }
171 
172 //===========================================================================
173 
recalcNormals()174 static void recalcNormals() {
175   DEBUGPRINT( "Calculating normals." << std::endl);
176   sgVec3 n;
177 
178   for (int i = 0; i < curr_index_->getNum() - 2; i++) {
179     unsigned short ix0 = *curr_index_->get(i    );
180     unsigned short ix1 = *curr_index_->get(i + 1);
181     unsigned short ix2 = *curr_index_->get(i + 2);
182 
183     sgMakeNormal( n,
184       vertex_array_->get(ix0),
185       vertex_array_->get(ix1),
186       vertex_array_->get(ix2) );
187 
188     sgCopyVec3( normal_array_->get(ix0), n );
189     sgCopyVec3( normal_array_->get(ix1), n );
190     sgCopyVec3( normal_array_->get(ix2), n );
191   }
192 }
193 
194 //===========================================================================
195 
readPoint(FILE * fp,sgVec3 p)196 static void readPoint(FILE* fp, sgVec3 p)
197 {
198   short x_int, y_int, z_int;
199   y_int = ulEndianReadLittle16(fp);
200   z_int = ulEndianReadLittle16(fp);
201   x_int = ulEndianReadLittle16(fp);
202 
203   // Convert from .MDL units (ca 2mm) to meters
204   p[0] =  -x_int/512.0f;
205   p[1] =  y_int/512.0f;
206   p[2] =  z_int/512.0f;
207 }
208 
209 //===========================================================================
210 
211 //  MtkPoint3D readPoint(FILE* fp)
212 //  {
213 //   short x_int, y_int, z_int;
214 //   fread(&x_int, 2, 1, fp);
215 //   fread(&y_int, 2, 1, fp);
216 //   fread(&z_int, 2, 1, fp);
217 
218 //   // Convert from .MDL units (ca 2mm) to meters
219 //   MtkPoint3D p;
220 //   p.x() = (double)x_int/512.0;
221 //   p.y() = (double)y_int/512.0;
222 //   p.z() = (double)z_int/512.0;
223 
224 //   /*
225 //   for(list<MtkTransMatrix3D>::iterator i = matrix_stack_.begin();
226 //       i != matrix_stack_.end(); i++)
227 //      {
228 //      p = p*(*i);
229 //      p += (*i).getCol(3);
230 //      }
231 //   */
232 
233 //   if(matrix_stack_.size() > 0)
234 //      {
235 //      MtkTransMatrix3D m = matrix_stack_.front();
236 //      p -= m.getCol(3);
237 //      p = p*m;
238 //      }
239 
240 //   MtkPoint3D r;
241 //   r.y() =  p.x();
242 //   r.z() = -p.y();
243 //   r.x() =  p.z();
244 
245 //   return r;
246 //  }
247 
248 //===========================================================================
249 
readVector(FILE * fp,sgVec3 v)250 static void readVector(FILE* fp, sgVec3 v)
251 {
252   short x_int, y_int, z_int;
253   y_int = ulEndianReadLittle16(fp);
254   z_int = ulEndianReadLittle16(fp);
255   x_int = ulEndianReadLittle16(fp);
256 
257   v[0] = -(float)x_int;
258   v[1] = (float)y_int;
259   v[2] = (float)z_int;
260 
261   sgNormaliseVec3( v );
262 }
263 
264 //===========================================================================
265 // for new lists, especially vertex list:
266 
267 struct oneVertex {
268   sgVec3 p, n; // point position, normal
269   sgVec2 tc;    // texture coords
270 };
271 
272 struct oneVertex *TheVertexList; // array. Kludge: only one allowed
273 
274 struct oneTexture {
275   char fname[64];
276 };
277 
278 struct oneTexture *TheTextureList; // array. Kludge: only one allowed
279 //===========================================================================
280 
createTriangIndices(ssgIndexArray * ixarr,int numverts,const sgVec3 s_norm,long dist)281 static void createTriangIndices(ssgIndexArray *ixarr,
282                                 int numverts, const sgVec3 s_norm, long dist)
283 {
284   sgVec3 v1, v2, cross;
285 
286   if ( numverts > ixarr->getNum() ) {
287     ulSetError( UL_WARNING, "ssgLoadMDL: Index array with too few entries." );
288     return;
289   }
290 
291   // triangulate polygons
292   if(numverts == 1)
293   {
294     unsigned short ix0 = *ixarr->get(0);
295     if ( ix0 >= vertex_array_->getNum() ) {
296       ulSetError(UL_WARNING, "ssgLoadMDL: Index out of bounds (%d/%d).",
297         ix0, vertex_array_->getNum() );
298       return;
299     }
300 
301     curr_index_->add(ix0);
302     curr_index_->add(ix0);
303     curr_index_->add(ix0);
304   }
305 
306   else if(numverts == 2)
307   {
308     unsigned short ix0 = *ixarr->get(0);
309     unsigned short ix1 = *ixarr->get(1);
310     if ( ix0 >= vertex_array_->getNum() ||
311       ix1 >= vertex_array_->getNum() ) {
312       ulSetError(UL_WARNING, "ssgLoadMDL: Index out of bounds. (%d,%d / %d",
313         ix0, ix1, vertex_array_->getNum() );
314       return;
315     }
316 
317     curr_index_->add(ix0);
318     curr_index_->add(ix1);
319     curr_index_->add(ix0);
320   }
321 
322   else if(numverts == 3)
323   {
324     unsigned short ix0 = *ixarr->get(0);
325     unsigned short ix1 = *ixarr->get(1);
326     unsigned short ix2 = *ixarr->get(2);
327     if ( ix0 >= vertex_array_->getNum() ||
328       ix1 >= vertex_array_->getNum() ||
329       ix2 >= vertex_array_->getNum() ) {
330       ulSetError(UL_WARNING, "ssgLoadMDL: Index out of bounds. " \
331         "(%d,%d,%d / %d)", ix0, ix1, ix2, vertex_array_->getNum());
332       return;
333     }
334 
335     sgSubVec3(v1,
336       vertex_array_->get(ix1),
337       vertex_array_->get(ix0));
338     sgSubVec3(v2,
339       vertex_array_->get(ix2),
340       vertex_array_->get(ix0));
341 
342     sgVectorProductVec3(cross, v1, v2);
343 
344     if(sgScalarProductVec3(cross, s_norm) >= 0.0f)
345     {
346       curr_index_->add(ix0);
347       curr_index_->add(ix1);
348       curr_index_->add(ix2);
349     }
350     else
351     {
352       curr_index_->add(ix0);
353       curr_index_->add(ix2);
354       curr_index_->add(ix1);
355     }
356   }
357 
358   else
359   {
360     unsigned short ix0 = *ixarr->get(0);
361     unsigned short ix1 = *ixarr->get(1);
362     unsigned short ix2 = *ixarr->get(2);
363     if ( ix0 >= vertex_array_->getNum() ||
364       ix1 >= vertex_array_->getNum() ||
365       ix2 >= vertex_array_->getNum() ) {
366       ulSetError(UL_WARNING, "ssgLoadMDL: Index out of bounds. " \
367         "(%d,%d,%d / %d)", ix0, ix1, ix2, vertex_array_->getNum());
368       return;
369     }
370 
371     // Ensure counter-clockwise ordering
372     sgMakeNormal(cross,
373       vertex_array_->get(ix0),
374       vertex_array_->get(ix1),
375       vertex_array_->get(ix2));
376     bool flip = (sgScalarProductVec3(cross, s_norm) < 0.0);
377 
378     curr_index_->add(ix0);
379     char tsA[99999];
380     int bWrong=FALSE;
381     // a lot of debug output follows - sorry!
382     sprintf(tsA, "------- %ld %f\n%f, %f, %f\n",
383         dist, ((float)dist)/(512.0f*32767.0f), s_norm[0], s_norm[1], s_norm[2]);
384     for(int i = 1; i < numverts; i++)
385     {
386       ix1 = *ixarr->get( flip ? numverts-i : i);
387 //
388       SGfloat f = sgScalarProductVec3(s_norm, vertex_array_->get(ix1));
389       sprintf(tsA, "%s%f, ", tsA, f);
390       f = f - ((float)dist)/(512.0f*32767.0f);
391       if((f<-0.5f) || (f>0.5f))
392         bWrong = TRUE;
393 //
394       if ( ix1 >= vertex_array_->getNum() ) {
395         ulSetError(UL_WARNING, "ssgLoadMDL: Index out of bounds. (%d/%d)",
396           ix1, vertex_array_->getNum());
397         continue;
398       }
399 
400       curr_index_->add(ix1);
401     }
402     if (bWrong)
403       printf("%s\n-------\n", tsA);
404 
405   }
406 }
407 
408 //===========================================================================
409 
readTexIndices(FILE * fp,int numverts,const sgVec3 s_norm,bool flip_y,long dist)410 static bool readTexIndices(FILE *fp, int numverts, const sgVec3 s_norm, bool flip_y, long dist)
411 {
412   ssgIndexArray temp_index_;
413 
414   if(numverts <= 0)
415     return false;
416 
417   if(tex_coords_->getNum() <
418     vertex_array_->getNum())
419   {
420     sgVec2 dummy_pt;
421     sgSetVec2(dummy_pt, FLT_MAX, FLT_MAX);
422     for(int i = tex_coords_->getNum();
423     i < vertex_array_->getNum(); i++)
424       tex_coords_->add(dummy_pt);
425   }
426 
427   // Read index values and texture coordinates
428   for(int v = 0; v < numverts; v++)
429   {
430     unsigned short ix;
431     short tx_int, ty_int;
432 
433     ix     = ulEndianReadLittle16(fp);
434     tx_int = ulEndianReadLittle16(fp);
435     ty_int = ulEndianReadLittle16(fp);
436 
437     if (flip_y) {
438       ty_int = 255 - ty_int;
439     }
440 
441     int tex_idx = ix - start_idx_ + last_idx_;
442 
443     sgVec2 tc;
444     sgSetVec2(tc, tx_int/255.0f, ty_int/255.0f);
445 
446     sgVec2 curr_tc;
447 
448     if ( tex_idx >= 0 && tex_idx < tex_coords_->getNum() ) {
449       sgCopyVec2(curr_tc, tex_coords_->get(tex_idx));
450     } else {
451       ulSetError( UL_WARNING, "ssgLoadMDL: Texture coord out of range (%d).",
452         tex_idx );
453       continue;
454     }
455 
456     double dist = sgDistanceVec2(curr_tc, tc);
457 
458     if((curr_tc[0] >= FLT_MAX - 1 && curr_tc[1] >= FLT_MAX - 1))
459     {
460       //DEBUGPRINT( "." );
461       sgCopyVec2(tex_coords_->get(tex_idx), tc);
462     }
463 
464     else if(dist > 0.0001)
465     {
466       // We have a different texture coordinate for an existing vertex,
467       // so we have to copy this vertex and create a new index for it
468       // to get the correct texture mapping.
469 
470       //DEBUGPRINT( "duplicating texture coordinate!\n");
471 
472       int idx = ix - start_idx_ + last_idx_;
473       tex_idx = vertex_array_->getNum();
474 
475       ssgVertexArray* vtx_arr  = vertex_array_; //curr_vtx_;
476       ssgNormalArray* norm_arr = normal_array_; //curr_norm_;
477 
478       sgVec3 vtx, nrm;
479       sgCopyVec3( vtx, vtx_arr ->get(idx) );
480       sgCopyVec3( nrm, norm_arr->get(idx) );
481       vtx_arr ->add(vtx);
482       norm_arr->add(nrm);
483 
484       tex_coords_->add(tc);
485     }
486 
487     temp_index_.add(tex_idx);
488 
489 #ifdef DEBUG
490     int check_index = *temp_index_.get(v);
491     float *check_tc = tex_coords_->get(check_index);
492     DEBUGPRINT( "ix[" << v << "] = " << check_index <<
493       " (u=" << check_tc[0] << ", v=" <<
494       check_tc[1] << ")" << std::endl);
495 #endif
496 
497   }
498 
499   createTriangIndices(&temp_index_, numverts, s_norm, dist);
500 
501   return true;
502 }
503 
504 //===========================================================================
505 
readIndices(FILE * fp,int numverts,const sgVec3 s_norm,long dist)506 static bool readIndices(FILE* fp, int numverts, const sgVec3 s_norm, long dist)
507 {
508   ssgIndexArray temp_index_;
509 
510   if(numverts <= 0)
511     return false;
512 
513   // Read index values
514   for(int v = 0; v < numverts; v++)
515   {
516     unsigned short ix;
517     ix = ulEndianReadLittle16(fp);
518     temp_index_.add(ix - start_idx_ + last_idx_);
519     DEBUGPRINT( "ix[" << v << "] = " << *temp_index_.get(v) << std::endl);
520   }
521 
522   createTriangIndices(&temp_index_, numverts, s_norm, dist);
523 
524   return true;
525 }
526 
527 //===========================================================================
528 
setColor(int color,int pal_id)529 static void setColor(int color, int pal_id)
530 {
531   if(pal_id == 0x68)
532   {
533     curr_col_[0] = fsAltPalette[color].r / 255.0f;
534     curr_col_[1] = fsAltPalette[color].g / 255.0f;
535     curr_col_[2] = fsAltPalette[color].b / 255.0f;
536     curr_col_[3] = 0.2f;
537   }
538   else
539   {
540     curr_col_[0] = fsAcPalette[color].r / 255.0f;
541     curr_col_[1] = fsAcPalette[color].g / 255.0f;
542     curr_col_[2] = fsAcPalette[color].b / 255.0f;
543     curr_col_[3] = 1.0f;
544   }
545 }
546 
547 //===========================================================================
548 
setColor(int r,int g,int b,int attr)549 static void setColor(int r, int g, int b, int attr)
550 {
551   curr_col_[0] = r / 255.0f;
552   curr_col_[1] = g / 255.0f;
553   curr_col_[2] = b / 255.0f;
554   if(attr < 239)
555     curr_col_[3] = 0.2f;
556 }
557 
558 //===========================================================================
559 
setTexture(char * name)560 static bool setTexture(char* name)
561 {
562   curr_tex_name_ = name;
563 
564   return true;
565 }
566 
567 //===========================================================================
568 
createState(bool use_texture)569 static ssgSimpleState *createState(bool use_texture)
570 {
571   DEBUGPRINT("new State: col = " << curr_col_[0] << ", " << curr_col_[1] <<
572     ", " << curr_col_[2] << ", " << curr_col_[3]);
573   if ( curr_tex_name_  == NULL )
574     DEBUGPRINT(", tex = <NULL> " << std::endl);
575   else
576     DEBUGPRINT(", tex = " << curr_tex_name_ << std::endl);
577 
578   ssgSimpleState *state = new ssgSimpleState();
579 
580   state->setShininess(DEF_SHININESS);
581   state->setShadeModel(GL_SMOOTH);
582 
583   state->enable   (GL_LIGHTING);
584   state->enable   (GL_CULL_FACE);
585   state->disable  (GL_COLOR_MATERIAL);
586 
587   if(curr_col_[3] < 0.99f)
588   {
589     state->setTranslucent();
590     state->enable(GL_BLEND);
591     state->enable(GL_ALPHA_TEST);
592   }
593   else
594   {
595     state->setOpaque();
596     state->disable(GL_BLEND);
597     state->disable(GL_ALPHA_TEST);
598   }
599 
600   if(curr_tex_name_ != NULL && use_texture)
601   {
602     state->setMaterial( GL_AMBIENT, 1.0f, 1.0f, 1.0f, curr_col_[3]);
603     state->setMaterial( GL_DIFFUSE, 1.0f, 1.0f, 1.0f, curr_col_[3]);
604     state->enable(GL_TEXTURE_2D);
605     state->setTexture( current_options ->
606       createTexture(curr_tex_name_, FALSE, FALSE) ) ;
607   }
608   else
609   {
610     state->setMaterial( GL_AMBIENT, curr_col_);
611     state->setMaterial( GL_DIFFUSE, curr_col_);
612     state->disable(GL_TEXTURE_2D);
613   }
614 
615   state->setMaterial( GL_SPECULAR, 1.0f, 1.0f, 1.0f, curr_col_[3] );
616   state->setMaterial( GL_EMISSION, 0.0f, 0.0f, 0.0f, 1.0f );
617 
618   return state;
619 }
620 
getCurrGroup()621 static ssgBranch *getCurrGroup() {
622   //Find the correct parent for the new group
623   if(curr_xfm_)
624     return curr_xfm_;
625   else
626   {
627     return model_;
628   }
629 }
630 
631 //===========================================================================
632 
633 // wk : It's not completely clear to me whether the following two functions can not be merged; Oh well.
634 // CreateAndAddLeaf1 is called then faces are created and then CreateAndAddLeaf2 is called
635 
636 
CreateAndAddLeaf1(GLenum ty,ssgTexCoordArray * tex_coords_P,bool use_texture)637 static void CreateAndAddLeaf1(GLenum ty, ssgTexCoordArray *tex_coords_P, bool use_texture)
638 {
639   curr_index_ = new ssgIndexArray();
640   curr_part_ = new ssgVtxArray( ty, vertex_array_,
641     normal_array_,
642     tex_coords_P,
643     NULL,
644     curr_index_ );
645   curr_part_->setState( createState(true) );
646   char sName[10];
647   sprintf(sName, "lod %d", (int)curr_lod);
648   curr_part_->setName(sName);
649 }
650 
CreateAndAddLeaf2()651 static void CreateAndAddLeaf2()
652 {
653   ssgBranch* grp = getCurrGroup();
654   ((ssgVtxArray *)curr_part_)->removeUnusedVertices();
655   grp->addKid( current_options -> createLeaf(curr_part_, NULL) );
656 }
657 
658 
659 
660 //===========================================================================
661 
getMPGroup(int var)662 static ssgBranch *getMPGroup(int var)
663 {
664 
665   switch(var)
666   {
667   case 0x4c:    // Rudder
668     if(!rudder_grp_)
669     {
670       rudder_grp_ = new ssgBranch();
671       rudder_grp_->setName("rudder");
672       model_->addKid(rudder_grp_);
673     }
674     return rudder_grp_;
675     break;
676 
677   case 0x4e:    // Elevator
678     if(!elevator_grp_)
679     {
680       elevator_grp_ = new ssgBranch();
681       elevator_grp_->setName("elevator");
682       model_->addKid(elevator_grp_);
683     }
684     return elevator_grp_;
685     break;
686 
687   case 0x6a:    // Ailerons
688     if(!ailerons_grp_)
689     {
690       ailerons_grp_ = new ssgBranch();
691       ailerons_grp_->setName("ailerons");
692       model_->addKid(ailerons_grp_);
693     }
694     return ailerons_grp_;
695     break;
696 
697   case 0x6c:    // Flaps
698     if(!flaps_grp_)
699     {
700       flaps_grp_ = new ssgBranch();
701       flaps_grp_->setName("flaps");
702       model_->addKid(flaps_grp_);
703     }
704     return flaps_grp_;
705     break;
706 
707   case 0x6e:    // Gear
708     if(!gear_grp_)
709     {
710       gear_grp_ = new ssgBranch();
711       gear_grp_->setName("gear");
712       model_->addKid(gear_grp_);
713     }
714     return gear_grp_;
715     break;
716 
717   case 0x7c:    // Spoilers
718     if(!spoilers_grp_)
719     {
720       spoilers_grp_ = new ssgBranch();
721       spoilers_grp_->setName("spoilers");
722       model_->addKid(spoilers_grp_);
723     }
724     return spoilers_grp_;
725     break;
726 
727   case 0x58:
728   case 0x7a:    // Propeller
729     if(!prop_grp_)
730     {
731       prop_grp_ = new ssgBranch();
732       prop_grp_->setName("propeller");
733       model_->addKid(prop_grp_);
734     }
735     return prop_grp_;
736     break;
737 
738   default:
739     return model_;
740   }
741   return NULL;
742 }
743 
744 //===========================================================================
745 
getMPLimits(int var,float * min,float * max)746 static void getMPLimits(int var, float *min, float *max)
747 {
748   switch(var)
749   {
750   case 0x4c:    // Rudder
751     *min = -30.0;
752     *max =  30.0;
753     break;
754 
755   case 0x4e:    // Elevator
756     *min = -30.0;
757     *max =  30.0;
758     break;
759 
760   case 0x6a:    // Ailerons
761     *min = -30.0;
762     *max =  30.0;
763     break;
764 
765   case 0x6c:    // Flaps
766     *min = 0.0;
767     *max = 70.0;
768     break;
769 
770   case 0x6e:    // Gear
771     *min = 0.0;
772     *max = -90.0;
773     break;
774 
775   case 0x7c:    // Spoilers
776     *min = 0.0;
777     *max = 90.0;
778     break;
779 
780   case 0x58:
781   case 0x7a:    // Propeller
782     *min = 0.0;
783     *max = 360.0;
784     break;
785   }
786 }
787 
788 
789 
ParseBGL(FILE * fp)790 void ParseBGL(FILE *fp) // "traversing" through the file
791 {
792   bool done = false;
793   while(!feof(fp) && !done)
794   {
795     unsigned int   skip_offset = 0;
796 
797     PRINT_STRUCTURE( "offset %lx\n", (long)ftell(fp))
798     unsigned short opcode = ulEndianReadLittle16(fp);
799 
800     DEBUGPRINT( "opcode = " << std::hex << opcode << std::dec << " at address" << ftell(fp)-2 // -2 since opcode has already been read
801                    << std::endl );
802 
803     switch(opcode)
804     {
805     case 0x23:  // BGL_CALL
806       {
807         short offset;
808         offset = ulEndianReadLittle16(fp);
809         long addr = ftell(fp);
810         DEBUGPRINT( "BGL_CALL(" << offset << ")\n" );
811         push_stack(addr, curr_lod);
812         long dst = addr + offset - 4;
813         fseek(fp, dst, SEEK_SET);
814 
815 
816         PRINT_STRUCTURE( "call %lx\n", (long)offset)
817       }
818       break;
819 
820     case 0x8a:  // BGL_CALL32
821       {
822         int offset;
823         offset = ulEndianReadLittle32(fp);
824         long addr = ftell(fp);
825         DEBUGPRINT( "BGL_CALL32(" << offset << ")\n" );
826         PRINT_STRUCTURE( "call32 %lx\n", (long)offset)
827         push_stack(addr, curr_lod);
828         long dst = addr + offset - 6;
829         fseek(fp, dst, SEEK_SET);
830       }
831       break;
832 
833     case 0x0d:  // BGL_JUMP
834       {
835         short offset;
836         offset = ulEndianReadLittle16(fp);
837         PRINT_STRUCTURE( "jump %lx\n", (long)offset)
838         long addr = ftell(fp);
839         long dst = addr + offset - 4;
840         fseek(fp, dst, SEEK_SET);
841         DEBUGPRINT( "BGL_JUMP(" << offset << ")\n" );
842       }
843       break;
844 
845     case 0x88:  // BGL_JUMP32
846       {
847         int offset;
848         offset = ulEndianReadLittle32(fp);
849         long addr = ftell(fp);
850         DEBUGPRINT( "BGL_JUMP32(" << offset << ")\n" );
851         PRINT_STRUCTURE( "jump32 %lx\n", (long)offset)
852         long dst = addr + offset - 6;
853         fseek(fp, dst, SEEK_SET);
854       }
855       break;
856 
857     case 0x8e:  // BGL_VFILE_MARKER
858       {
859         short offset;
860         offset = ulEndianReadLittle16(fp);
861         DEBUGPRINT( "vars: " << offset << std::endl);
862         break;
863       }
864 
865     case 0x39:  // BGL_IFMSK
866       {
867         short offset, var, mask;
868         offset = ulEndianReadLittle16(fp);
869         var    = ulEndianReadLittle16(fp);
870         mask   = ulEndianReadLittle16(fp);
871         long addr = ftell(fp);
872         long dst = addr + offset - 8;
873         DEBUGPRINT( "BGL_IFMSK(" << offset << ", 0x" << std::hex << var <<
874           ", 0x" << mask << std::dec << ")\n" );
875         //          if(var & mask == 0)
876         PRINT_STRUCTURE( "if msk %lx\n", (long)offset)
877         switch(var)
878         {
879         case 0x7e:
880           fseek(fp, dst, SEEK_SET);
881           break;
882 
883         default:
884           break;
885         }
886       }
887       break;
888 
889     case 0x24:  // BGL_IFIN1
890       {
891         short offset, lo, hi;
892         unsigned short var;
893         offset = ulEndianReadLittle16(fp);
894         var    = ulEndianReadLittle16(fp);
895         lo     = ulEndianReadLittle16(fp);
896         hi     = ulEndianReadLittle16(fp);
897         DEBUGPRINT( "BGL_IFIN1(" << offset << ", 0x" << std::hex << var <<
898           ", " << std::dec << lo << ", " << hi << ")\n" );
899         PRINT_STRUCTURE( "ifin1 %lx\n", (long)offset)
900         curr_var_ = var;
901       }
902       break;
903 
904     case 0x46:  // BGL_POINT_VICALL
905       {
906         short offset, var_rot_x, var_rot_y, var_rot_z;
907         unsigned short int_rot_x, int_rot_y, int_rot_z;
908         offset = ulEndianReadLittle16(fp);
909         sgVec3 ctr;
910         readPoint(fp, ctr);
911 
912         int_rot_y = ulEndianReadLittle16(fp);
913         var_rot_y = ulEndianReadLittle16(fp);
914 
915         int_rot_x = ulEndianReadLittle16(fp);
916         var_rot_x = ulEndianReadLittle16(fp);
917 
918         int_rot_z = ulEndianReadLittle16(fp);
919         var_rot_z = ulEndianReadLittle16(fp);
920 
921         float rx =  360.0f*(float)int_rot_x/0xffff;
922         float ry =  360.0f*(float)int_rot_y/0xffff;
923         float rz =  360.0f*(float)int_rot_z/0xffff;
924 
925         // We build a rotation matrix by adding all constant
926         // rotations (int_rot_*) to current_matrix_. As soon as we reach
927         // the actual variable rotation, we multiply
928         // the axis of the variable rotation with our current matrix.
929         // This will be the axis of rotation in the original coordinate
930         // system. This can now be inserted into a GngLinearControl
931         // transform.
932         if(var_rot_x > 0 || var_rot_y > 0 || var_rot_z > 0)
933         {
934           ssgAxisTransform* tmp = NULL;
935           if(curr_xfm_)
936             tmp = curr_xfm_;
937           curr_xfm_ = new ssgAxisTransform();
938           curr_xfm_->setCenter(curr_rot_pt_);
939 
940           int var = 0;
941           if(var_rot_x > 0)
942             var = var_rot_x;
943           else if(var_rot_y > 0)
944             var = var_rot_y;
945           else if(var_rot_z > 0)
946             var = var_rot_z;
947 
948           float min_limit, max_limit;
949           getMPLimits(var, & min_limit, & max_limit);
950 
951           sgVec3 axis = { (float)var_rot_y, (float)var_rot_z,
952             (float)var_rot_x };
953           sgNormaliseVec3( axis ) ;
954           sgXformVec3( axis, curr_matrix_ ) ;
955           sgNegateVec3(axis);
956           curr_xfm_->setAxis(axis);
957           curr_xfm_->setRotationLimits(min_limit, max_limit);
958 
959           char name[256];
960           sprintf(name, "ssgAxisRotation(%x)", var);
961           curr_xfm_->setName(name);
962           if(tmp)
963             tmp->addKid(curr_xfm_);
964           else
965           {
966             ssgBranch* grp = getMPGroup(var);
967             grp->addKid(curr_xfm_);
968           }
969         }
970 
971         // Build up the constant rotations
972         sgMat4 rot_mat;
973         sgMakeRotMat4( rot_mat, ry, rz, rx );
974         sgPostMultMat4( curr_matrix_, rot_mat );
975         sgAddVec3( curr_rot_pt_, ctr );
976 
977         long addr = ftell(fp);
978         long dst = addr + offset - 22;
979         fseek(fp, dst, SEEK_SET);
980         push_stack(addr, curr_lod);
981 
982         break;
983       }
984 
985     case 0x5f:  // BGL_IFSIZEV
986       {
987         short offset;
988         unsigned short real_size, pixels_ref;
989         offset     = ulEndianReadLittle16(fp);
990         real_size  = ulEndianReadLittle16(fp);
991         pixels_ref = ulEndianReadLittle16(fp);
992         DEBUGPRINT("BGL_IFSIZEV: jmp = " << offset << ", sz = " <<
993           real_size << ", px = " << pixels_ref << std::endl);
994         PRINT_STRUCTURE( "ifsizev!! %lx\n", (long)offset)
995         long addr = ftell(fp);
996         long dst = addr + offset - 8;
997         fseek(fp, dst, SEEK_SET);
998         push_stack(addr, ++noLoDs);
999         break;
1000       }
1001 
1002     case 0x3b:  // BGL_VINSTANCE
1003       {
1004         short offset, var;
1005         offset = ulEndianReadLittle16(fp);
1006         var    = ulEndianReadLittle16(fp);
1007         long addr = ftell(fp);
1008         long var_abs = addr + var - 6;
1009         fseek(fp, var_abs, SEEK_SET);
1010         float p = 360.0f * (float)ulEndianReadLittle32(fp) / 0xffffffff;
1011         float r = 360.0f * (float)ulEndianReadLittle32(fp) / 0xffffffff;
1012         float h = 360.0f * (float)ulEndianReadLittle32(fp) / 0xffffffff;
1013         sgMat4 rot_mat;
1014         sgMakeRotMat4(rot_mat, h, p, r);
1015         sgPostMultMat4(curr_matrix_, rot_mat);
1016         DEBUGPRINT( "BGL_VINSTANCE(" << offset << ", h=" << h << ", p=" <<
1017           p << ", r=" << r << ")\n");
1018         long dst = addr + offset - 6;
1019         fseek(fp, dst, SEEK_SET);
1020       }
1021       break;
1022 
1023     case 0x0: // EOF
1024     case 0x22:  // BGL return
1025       {
1026         curr_xfm_    = NULL;
1027         sgMakeIdentMat4( curr_matrix_ );
1028         sgZeroVec3( curr_rot_pt_ );
1029         curr_var_    = 0;
1030         DEBUGPRINT( "BGL return\n\n");
1031         PRINT_STRUCTURE1("return\n");
1032         if(stack_depth_ == 0)
1033           done = true;
1034         else
1035         {
1036           long addr = pop_stack(curr_lod);
1037           fseek(fp, addr, SEEK_SET);
1038         }
1039       }
1040       break;
1041 
1042     case 0x1a:  // RESLIST (point list with no normals)
1043       {
1044         newPart();
1045         has_normals_ = false;
1046 
1047         start_idx_               = ulEndianReadLittle16(fp);
1048         unsigned short numpoints = ulEndianReadLittle16(fp);
1049 
1050         DEBUGPRINT( "New group (unlit): start_idx = " << start_idx_
1051           << ", num vertices = " << numpoints << std::endl);
1052 
1053         sgVec3 null_normal;
1054         sgZeroVec3( null_normal );
1055 
1056         for(int i = 0; i < numpoints; i++)
1057         {
1058           sgVec3 p;
1059           readPoint(fp, p);
1060           curr_vtx_->add(p);
1061           curr_norm_->add(null_normal);
1062         }
1063       }
1064       break;
1065 
1066     case 0x29:  // GORAUD RESLIST (point list with normals)
1067       {
1068         newPart();
1069         has_normals_ = true;
1070 
1071         start_idx_               = ulEndianReadLittle16(fp);
1072         unsigned short numpoints = ulEndianReadLittle16(fp);
1073 
1074         DEBUGPRINT( "New group (goraud): start_idx = " << start_idx_
1075           << ", num vertices = " << numpoints << std::endl);
1076 
1077         for(int i = 0; i < numpoints; i++)
1078         {
1079           sgVec3 p;
1080           readPoint(fp, p);
1081           sgVec3 v;
1082           readVector(fp, v);
1083           curr_vtx_->add(p);
1084           curr_norm_->add(v);
1085         }
1086       }
1087       break;
1088 
1089     case 0x0f:  // STRRES: Start line definition
1090       {
1091         unsigned short idx = ulEndianReadLittle16(fp);
1092         DEBUGPRINT( "Start line: idx = " << idx << std::endl);
1093         if(vtx_dirty_)
1094         {
1095           last_idx_ = vertex_array_->getNum();
1096           for(int i = 0; i < curr_vtx_->getNum(); i++)
1097           {
1098             vertex_array_->add(curr_vtx_ ->get(i));
1099             normal_array_->add(curr_norm_->get(i));
1100           }
1101           vtx_dirty_ = false;
1102         }
1103         CreateAndAddLeaf1(GL_LINES, NULL, false);
1104 #ifdef EXPERIMENTAL_CULL_FACE_CODE
1105         curr_part_->setCullFace ( curr_cull_face_ ) ;
1106 #endif
1107 
1108         curr_index_->add(idx - start_idx_ + last_idx_);
1109         ssgBranch *grp = getCurrGroup();
1110         grp->addKid(curr_part_);
1111 
1112         //assert(curr_part_->getState()->getTexture() == NULL);
1113       }
1114       break;
1115 
1116     case 0x10:  // CNTRES: Continue line definition
1117       {
1118         unsigned short idx = ulEndianReadLittle16(fp);
1119         DEBUGPRINT( "Cont. line: idx = " << idx << std::endl);
1120         curr_index_->add(idx - start_idx_ + last_idx_);
1121       }
1122       break;
1123 
1124     case 0x20:
1125     case 0x7a:  // Goraud shaded Texture-mapped ABCD Facet
1126       {
1127         if(tex_vtx_dirty_)
1128         {
1129           last_idx_ = vertex_array_->getNum();
1130           for(int i = 0; i < curr_vtx_->getNum(); i++)
1131           {
1132             vertex_array_->add(curr_vtx_ ->get(i));
1133             normal_array_->add(curr_norm_->get(i));
1134           }
1135           tex_vtx_dirty_ = false;
1136         }
1137         CreateAndAddLeaf1(GL_TRIANGLE_FAN, tex_coords_, true);
1138 
1139         //assert(curr_part_->getState()->getTexture() != NULL);
1140 
1141         unsigned short numverts = ulEndianReadLittle16(fp);
1142         DEBUGPRINT( "New part: (goraud/texture), num indices = " <<
1143           numverts << std::endl);
1144 
1145         // Normal vector
1146         sgVec3 v;
1147         readVector(fp, v);
1148 
1149         // Dot product reference
1150 #ifdef EXPERIMENTAL_CULL_FACE_CODE
1151         long l = ulEndianReadLittle32(fp) ;
1152         if(l>0)
1153           noGT++;
1154         else if (l<0)
1155           noLT++;
1156         if((v[0]==0) && (v[1]==0) && (v[2]==0))
1157           no0++;
1158         //rr_part_->setCullFace ( curr_cull_face_ ) ;
1159 
1160 #else
1161         ulEndianReadLittle32(fp);
1162 #endif
1163         // Read vertex indices and texture coordinates
1164         bool flip_y = FALSE;
1165         if(curr_tex_name_!=NULL)
1166         { char *texture_extension =
1167           curr_tex_name_ + strlen(curr_tex_name_) - 3;
1168           flip_y = ulStrEqual( texture_extension, "BMP" ) != 0 ;
1169         }
1170         /*old:
1171         char *texture_extension =
1172         curr_tex_name_ + strlen(curr_tex_name_) - 3;
1173         bool flip_y = ulStrEqual( texture_extension, "BMP" );
1174         */
1175         readTexIndices(fp, numverts, v, flip_y, l); // adds stuff to curr_index_
1176 
1177         if(!has_normals_)
1178         {
1179           while (normal_array_->getNum() < vertex_array_->getNum())
1180             normal_array_->add(v);
1181           recalcNormals();
1182         }
1183         CreateAndAddLeaf2();
1184       }
1185       break;
1186 
1187     case 0x60:  // BGL_FACE_TMAP
1188       {
1189         if(tex_vtx_dirty_)
1190         {
1191           last_idx_ = vertex_array_->getNum();
1192           for(int i = 0; i < curr_vtx_->getNum(); i++)
1193           {
1194             vertex_array_->add(curr_vtx_ ->get(i));
1195             normal_array_->add(curr_norm_->get(i));
1196           }
1197           tex_vtx_dirty_ = false;
1198         }
1199 
1200         CreateAndAddLeaf1(GL_TRIANGLE_FAN, tex_coords_, true);
1201 
1202 #ifdef EXPERIMENTAL_CULL_FACE_CODE
1203         curr_part_->setCullFace ( curr_cull_face_ ) ;
1204 #endif
1205         //assert(curr_part_->getState()->getTexture() != NULL);
1206 
1207         unsigned short numverts = ulEndianReadLittle16(fp);
1208         DEBUGPRINT( "New part: (goraud/texture), num indices = " <<
1209           numverts << std::endl);
1210 
1211         // Point in polygon
1212         sgVec3 p;
1213         readPoint(fp, p);
1214 
1215         // Normal vector
1216         sgVec3 v;
1217         readVector(fp, v);
1218 
1219         // Read vertex inidices and texture coordinates
1220         bool flip_y = FALSE;
1221         if(curr_tex_name_!=NULL)
1222         { char *texture_extension =
1223         curr_tex_name_ + strlen(curr_tex_name_) - 3;
1224         flip_y = ulStrEqual( texture_extension, "BMP" ) != 0 ;
1225         }
1226         /*
1227         char *texture_extension =
1228         curr_tex_name_ + strlen(curr_tex_name_) - 3;
1229         bool flip_y = ulStrEqual( texture_extension, "BMP" );
1230         */
1231         readTexIndices(fp, numverts, v, flip_y, -11);
1232 
1233         if(!has_normals_)
1234         {
1235           while (normal_array_->getNum() < vertex_array_->getNum())
1236             normal_array_->add(v);
1237           recalcNormals();
1238         }
1239 
1240         CreateAndAddLeaf2();
1241       }
1242       break;
1243 
1244     case 0x1d:  // BGL_FACE
1245       {
1246         if(vtx_dirty_)
1247         {
1248           last_idx_ = vertex_array_->getNum();
1249           for(int i = 0; i < curr_vtx_->getNum(); i++)
1250           {
1251             vertex_array_->add(curr_vtx_->get(i));
1252             normal_array_->add(curr_norm_->get(i));
1253           }
1254           vtx_dirty_ = false;
1255         }
1256 
1257         CreateAndAddLeaf1(GL_TRIANGLE_FAN, NULL, false);
1258 
1259 #ifdef EXPERIMENTAL_CULL_FACE_CODE
1260         curr_part_->setCullFace ( curr_cull_face_ ) ;
1261 #endif
1262         //assert(curr_part_->getState()->getTexture() == NULL);
1263 
1264         unsigned short numverts = ulEndianReadLittle16(fp);
1265         DEBUGPRINT( "BGL_FACE: num indices = " << numverts << std::endl);
1266 
1267         sgVec3 p;
1268         readPoint(fp, p);
1269         // Surface normal
1270         sgVec3 v;
1271         readVector(fp, v);
1272 
1273         // Read vertex indices
1274         readIndices(fp, numverts, v, -11);
1275 
1276         if(!has_normals_)
1277         {
1278           while (normal_array_->getNum() < vertex_array_->getNum())
1279             normal_array_->add(v);
1280           recalcNormals();
1281         }
1282 
1283         CreateAndAddLeaf2();
1284       }
1285       break;
1286 
1287     case 0x3e:  // FACETN (no texture)
1288     case 0x2a:  // Goraud shaded ABCD Facet
1289       {
1290         if(vtx_dirty_)
1291         {
1292           last_idx_ = vertex_array_->getNum();
1293           for(int i = 0; i < curr_vtx_->getNum(); i++)
1294           {
1295             vertex_array_->add(curr_vtx_->get(i));
1296             normal_array_->add(curr_norm_->get(i));
1297           }
1298           vtx_dirty_ = false;
1299         }
1300 
1301         CreateAndAddLeaf1(GL_TRIANGLE_FAN, NULL, false);
1302 
1303         //assert(curr_part_->getState()->getTexture() == NULL);
1304 
1305         unsigned short numverts = ulEndianReadLittle16(fp);
1306         DEBUGPRINT( "BGL_FACETN: num indices = " << numverts << std::endl);
1307 
1308         // Surface normal
1309         sgVec3 v;
1310         readVector(fp, v);
1311 
1312         // dot-ref
1313 #ifdef EXPERIMENTAL_CULL_FACE_CODE
1314         long l = ulEndianReadLittle32(fp) ;
1315         if(l>0)
1316           noGT++;
1317         else if (l<0)
1318           noLT++;
1319         if((v[0]==0) && (v[1]==0) && (v[2]==0))
1320           no0++;
1321           //rr_part_->setCullFace ( curr_cull_face_ ) ;
1322 
1323 #else
1324         ulEndianReadLittle32(fp);
1325 #endif
1326         // Read vertex indices
1327         readIndices(fp, numverts, v, l);
1328 
1329         if(!has_normals_)
1330         {
1331           while (normal_array_->getNum() < vertex_array_->getNum())
1332             normal_array_->add(v);
1333           recalcNormals();
1334         }
1335 
1336         CreateAndAddLeaf2();
1337       }
1338       break;
1339 
1340     case 0x18:  // Set texture
1341       {
1342         unsigned short id, dx, scale, dy;
1343         id    = ulEndianReadLittle16(fp);
1344         dx    = ulEndianReadLittle16(fp);
1345         scale = ulEndianReadLittle16(fp);
1346         dy    = ulEndianReadLittle16(fp);
1347         char tex_name[14];
1348         fread(tex_name, 1, 14, fp);
1349         static char tex_filename[14];
1350         int j = 0;
1351         for(int i = 0; i < 14; i++)
1352         {
1353           if(!isspace(tex_name[i]))
1354             tex_filename[j++] = tolower(tex_name[i]);
1355         }
1356         tex_filename[j] = '\0';
1357         DEBUGPRINT( "Set texture: name = " << tex_filename << ", id = " << id
1358           << ", dx = " << dx << ", dy = " << dy << ", scale = " <<
1359           scale << std::endl);
1360         setTexture(tex_filename);
1361       }
1362       break;
1363 
1364     case 0x43:  // TEXTURE2
1365       {
1366         unsigned short length, idx;
1367         unsigned char  flags, chksum;
1368         unsigned int   color;
1369         static char tex_filename[128];
1370         length = ulEndianReadLittle16(fp);
1371         idx    = ulEndianReadLittle16(fp);
1372         fread(&flags, 1, 1, fp);
1373         fread(&chksum, 1, 1, fp);
1374         color = ulEndianReadLittle32(fp);
1375         if(chksum != 0)
1376         {
1377           DEBUGPRINT( "warning: TEXTURE2 Checksum != 0\n");
1378         }
1379 
1380         char c;
1381         int i = 0;
1382         while((c = getc(fp)) != 0)
1383         {
1384           if(!isspace(c))
1385             tex_filename[i++] = tolower(c);
1386         }
1387         tex_filename[i] = '\0';
1388 
1389         // Padding byte
1390         if((strlen(tex_filename) + 1) % 2)
1391           c = getc(fp);
1392 
1393         DEBUGPRINT( "TEXTURE2: Set texture: name = " << tex_filename <<
1394           std::endl);
1395         setTexture(tex_filename);
1396         break;
1397       }
1398 
1399     case 0x50:  // GCOLOR (Goraud shaded color)
1400     case 0x51:  // LCOLOR (Line color)
1401     case 0x52:      // SCOLOR (Light source shaded surface color)
1402       {
1403         unsigned char color, param;
1404         fread(&color, 1, 1, fp);
1405         fread(&param, 1, 1, fp);
1406         DEBUGPRINT( "Set color = " << (int)color << " (" << std::hex <<
1407           (int)param << std::dec << ")\n");
1408         setColor((int)color, (int)param);
1409       }
1410       break;
1411 
1412     case 0x2D:  // BGL_SCOLOR24
1413       {
1414         unsigned char col[4];
1415         fread(col, 1, 4, fp);
1416         DEBUGPRINT( "color = " << (int)col[0] << ", " << (int)col[2] <<
1417           ", " << (int)col[3] << ", " << (int)col[1] << std::endl);
1418         setColor(col[0], col[2], col[3], col[1]);
1419         break;
1420       }
1421 
1422 
1423       //-------------------------------------------
1424       // The next code is either ignored
1425       // or for experimental use..
1426       //-------------------------------------------
1427 
1428     case 0x03:
1429       {
1430         //DEBUGPRINT( "BGL_CASE\n" );
1431         unsigned short number_cases = ulEndianReadLittle16(fp);
1432         skip_offset = 6 + 2 * number_cases;
1433         PRINT_STRUCTURE1("case :-( !!\n");
1434       }
1435       break;
1436 
1437     case 0xB0: //BGLOP_CRASH_OCTTREE
1438       {
1439 /* from scdis code by Takuya Murakami:
1440 
1441   2   Jump Offset
1442   2   Crash Type (See CRASH_FLAG_xxx in makemdl.exe)
1443   2   # of nodes
1444   4*3 box x/y/z
1445   4*3 box w/h/d
1446   1*8 ?
1447   1*8*(# of nodes) ?
1448 */
1449 
1450         ulEndianReadLittle32(fp); // ignore jump offset / crash type
1451         int count = ulEndianReadLittle16(fp);
1452         //fseek(fp, 4*3+4*3+1*8+1*8*count, SEEK_CUR);
1453         fseek(fp, 5*8+8*count, SEEK_CUR); // completely experimental
1454       }
1455       break;
1456 
1457     case 0xB2: //light
1458       {
1459 /*from scdis code by Takuya Murakami:
1460      Format:
1461   2   type
1462   4*3     offset (x,y,z, real4)
1463   4 intensity
1464   4 linear attenuation factor (real4)
1465   4 squared attenuation factor (real4)
1466   4 color (BGRA : strange byte order...)
1467   4*3 direction(x,y,z real4)
1468 */
1469         fseek(fp, 2+4*10, SEEK_CUR);
1470       }
1471       break;
1472 
1473     case 0xB3: //ifinf(1)
1474       {
1475         // long offset = ulEndianReadLittle32(fp);
1476         // unsigned short v = ulEndianReadLittle16(fp);
1477         // float low = ulEndianReadLittleFloat(fp);
1478         // float high = ulEndianReadLittleFloat(fp);
1479         ulEndianReadLittle32(fp);
1480         ulEndianReadLittle16(fp);
1481         ulEndianReadLittleFloat(fp);
1482         ulEndianReadLittleFloat(fp);
1483 
1484       }
1485       break;
1486     //-------------------------------------------
1487       // The next codes are CFS2 specific
1488       //-------------------------------------------
1489 
1490     case 0xB5: //, VertexList,  anaVertexList, 0 ) // VERTEX_LIST_BEGIN
1491       {
1492 
1493         newPart();
1494         int count = ulEndianReadLittle16(fp);
1495         ulEndianReadLittle32(fp); // dummy
1496 #ifdef UL_WIN32
1497         if(TheVertexList)
1498           ::MessageBox(0, "More than one vertexlist", "Warning:", 0);
1499 #endif
1500         TheVertexList = new oneVertex[count]; // kludge: TheVertexList is unused??
1501 //    4*8 x,y,z,nx,ny,nz,tu,tv (real4)
1502 
1503         for (int i = 0; i < count; i++)
1504         {
1505           TheVertexList[i].p[0] = ulEndianReadLittleFloat(fp); // / 1E6;
1506           TheVertexList[i].p[1] = ulEndianReadLittleFloat(fp); // / 1E6;
1507           TheVertexList[i].p[2] = ulEndianReadLittleFloat(fp); // / 1E6;
1508           TheVertexList[i].n[0] = ulEndianReadLittleFloat(fp);
1509           TheVertexList[i].n[1] = ulEndianReadLittleFloat(fp);
1510           TheVertexList[i].n[2] = ulEndianReadLittleFloat(fp);
1511           TheVertexList[i].tc[0]= ulEndianReadLittleFloat(fp);
1512           TheVertexList[i].tc[1]= ulEndianReadLittleFloat(fp);
1513 
1514           curr_vtx_->add(TheVertexList[i].p);
1515           curr_norm_->add(TheVertexList[i].n);
1516           tex_coords_->add(TheVertexList[i].tc);
1517 
1518         }
1519       }
1520       break;
1521 
1522     case 0xB6: //, MaterialList,  anaMaterialList, 0 ) // MATERIAL_LIST_BEGIN
1523       {
1524 
1525         int count = ulEndianReadLittle16(fp);
1526         ulEndianReadLittle32(fp); // dummy
1527         fseek(fp, count*4*17, SEEK_CUR);
1528       }
1529 
1530       break;
1531 
1532     case 0xB7: //, TextureList,   anaTextureList, 0 )   // TEXTURE_LIST_BEGIN/TEXTURE_DEF/TEXTURE_LIST_END
1533       {
1534 
1535         int count = ulEndianReadLittle16(fp);
1536         ulEndianReadLittle32(fp); // dummy
1537 #ifdef UL_WIN32
1538         if(TheTextureList)
1539           ::MessageBox(0, "More than one texturelist", "Warning:", 0);
1540 #endif
1541         TheTextureList = new oneTexture[count]; // kludge: TheVertexList is unused??
1542         for(int i=0;i<count;i++)
1543         {
1544           // u_int32 cls = get4(); u_int8 r = get1(); u_int8 a = get1(); u_int8 g = get1(); u_int8 b = get1(); u_int32 hint = get4(); float size = getreal4();
1545           fseek(fp, 16, SEEK_CUR);
1546           fread(TheTextureList[i].fname, 64, 1, fp);
1547         }
1548       }
1549       break;
1550 
1551     case 0xB9: //, DrawTriList,   anaDrawTriList, 0 )
1552       {
1553 
1554         if(tex_vtx_dirty_)
1555         {
1556           last_idx_ = vertex_array_->getNum();
1557           for(int i = 0; i < curr_vtx_->getNum(); i++)
1558           {
1559             vertex_array_->add(curr_vtx_ ->get(i));
1560             normal_array_->add(curr_norm_->get(i));
1561           }
1562           tex_vtx_dirty_ = false;
1563         }
1564 
1565 
1566 
1567         ssgVertexArray    *vertex_array_bu = (ssgVertexArray    *)vertex_array_->clone();
1568 
1569         for(int i=0;i<vertex_array_->getNum();i++)
1570           sgXformPnt3(vertex_array_->get(i), curr_matrix_);
1571 
1572         CreateAndAddLeaf1(GL_TRIANGLES, tex_coords_, true);
1573         //delete vertex_array_; kludge!!
1574         vertex_array_ = vertex_array_bu; // to undo the transformation
1575 
1576         //assert(curr_part_->getState()->getTexture() != NULL);
1577 
1578         int base = ulEndianReadLittle16(fp);
1579         // int vertexcount = ulEndianReadLittle16(fp);
1580         ulEndianReadLittle16(fp);
1581         int wkcount = ulEndianReadLittle16(fp);
1582         wkcount = wkcount / 3; // tri
1583         assert(wkcount>0);
1584 
1585         DEBUGPRINT( "New part: (DrawTriList), num tris = " << wkcount << std::endl);
1586         if(tex_coords_->getNum() != vertex_array_->getNum())
1587         {
1588           printf("tex_coords_->getNum() = %d,  vertex_array_->getNum() = %d\n", tex_coords_->getNum(), vertex_array_->getNum());
1589           assert(FALSE);
1590         }
1591         assert(tex_coords_->getNum() == normal_array_->getNum());
1592         for(int j=0;j<wkcount;j++)
1593         {
1594           int i1 = ulEndianReadLittle16(fp);
1595           int i2 = ulEndianReadLittle16(fp);
1596           int i3 = ulEndianReadLittle16(fp);
1597           if(i1+base>=tex_coords_->getNum())
1598           {
1599             printf("i1+base = %d, i1 = %d, base = %d, tex_coords_->getNum() = %d\n", i1+base, i1, base, tex_coords_->getNum());
1600             assert(FALSE);
1601           }
1602           assert(i2+base<tex_coords_->getNum());
1603           assert(i3+base<tex_coords_->getNum());
1604 
1605           curr_index_->add(i1+base);
1606           curr_index_->add(i2+base);
1607           curr_index_->add(i3+base);
1608         }
1609         CreateAndAddLeaf2();
1610       }
1611       break;
1612 
1613     case 0xBA: //, DrawLineList,  anaDrawLineList,  0 )
1614       {
1615         // int base = ulEndianReadLittle16(fp);
1616         // int dummycount = ulEndianReadLittle16(fp);
1617         ulEndianReadLittle16(fp);
1618         ulEndianReadLittle16(fp);
1619 
1620         int wkcount = ulEndianReadLittle16(fp);
1621         wkcount = wkcount / 2;
1622         fseek(fp, 2*2*wkcount, SEEK_CUR);
1623       }
1624       break;
1625 
1626     case 0xBB: //, DrawPointList, anaDrawPointList, 0 )
1627       {
1628         // int base = ulEndianReadLittle16(fp);
1629         // int dummycount = ulEndianReadLittle16(fp);
1630         ulEndianReadLittle16(fp);
1631         ulEndianReadLittle16(fp);
1632 
1633         int wkcount = ulEndianReadLittle16(fp);
1634         wkcount = wkcount / 1;
1635         fseek(fp, 2*1*wkcount, SEEK_CUR);
1636       }
1637       break;
1638 
1639     case 0xBC: // BGL_BEGIN / BGLVersion
1640       {
1641         // long v = ulEndianReadLittle32(fp);
1642         ulEndianReadLittle32(fp);
1643 
1644         PRINT_STRUCTURE("BGLVersion %lx\n", v)
1645       }
1646       break;
1647     case 0xB8: // SetMaterial
1648       {
1649         ulEndianReadLittle16(fp); // word
1650         short TheTextureIndex = ulEndianReadLittle16(fp); // word
1651         if (TheTextureIndex != -1)
1652         {
1653           char *s = TheTextureList[TheTextureIndex].fname;
1654           int j = 0;
1655           static char tex_filename[64];
1656 
1657           for(int i = 0; i < 64; i++)
1658           {
1659             if(!isspace(s[i]))
1660               tex_filename[j++] = tolower(s[i]);
1661           }
1662           tex_filename[j] = '\0';
1663           //DEBUGPRINT( "Set texture: name = " << tex_filename << std::endl);
1664           setTexture(tex_filename);
1665         }
1666         else
1667           curr_tex_name_ = NULL;
1668       }
1669       break;
1670     case 0xB4: // TextureSize
1671       ulEndianReadLittle32(fp); // float
1672       break;
1673     case 0xBD: // BGL_END / EndVersion
1674       break;
1675     case 0xAE: // BGL_TRANSFORM_END
1676       sgMakeIdentMat4( curr_matrix_ );
1677 
1678       break;
1679     case 0xAF: // BGL_TRANSFORM_MATRIX
1680       {
1681         sgMat4 this_mat;
1682         this_mat[3][0] = ulEndianReadLittleFloat(fp);
1683         this_mat[3][1] = ulEndianReadLittleFloat(fp);
1684         this_mat[3][2] = ulEndianReadLittleFloat(fp);
1685         this_mat[0][0] = ulEndianReadLittleFloat(fp);
1686         this_mat[0][1] = ulEndianReadLittleFloat(fp);
1687         this_mat[0][2] = ulEndianReadLittleFloat(fp);
1688         this_mat[1][0] = ulEndianReadLittleFloat(fp);
1689         this_mat[1][1] = ulEndianReadLittleFloat(fp);
1690         this_mat[1][2] = ulEndianReadLittleFloat(fp);
1691         this_mat[2][0] = ulEndianReadLittleFloat(fp);
1692         this_mat[2][1] = ulEndianReadLittleFloat(fp);
1693         this_mat[2][2] = ulEndianReadLittleFloat(fp);
1694         DEBUGPRINT( "***** Matrix: " << std::endl << "x, y, z = " <<
1695           this_mat[3][0] << ", " << this_mat[3][1] << ", " << this_mat[3][2] <<  std::endl << "3 x 3 matrix:" <<  std::endl <<
1696           this_mat[0][0] << ", " << this_mat[0][1] << ", " << this_mat[0][2]  <<  std::endl <<
1697           this_mat[1][0] << ", " << this_mat[1][1] << ", " << this_mat[1][2] <<  std::endl <<
1698           this_mat[2][0] << ", " << this_mat[2][1] << ", " << this_mat[2][2] <<  std::endl);
1699         this_mat[0][3] = SG_ZERO ;
1700         this_mat[1][3] = SG_ZERO ;
1701         this_mat[2][3] = SG_ZERO ;
1702         this_mat[3][3] = SG_ONE ;
1703         sgPostMultMat4( curr_matrix_, this_mat);
1704 
1705       }
1706       break;
1707 
1708     default: // Unknown opcode
1709       {
1710         if (opcode < 256)
1711         {
1712           if ( opcodes[opcode].size != -1)
1713           {
1714             DEBUGPRINT( "** " << opcodes[opcode].name << " (size " <<
1715               opcodes[opcode].size << ")" << std::endl );
1716             skip_offset = opcodes[opcode].size - 2; // opcode already read
1717           }
1718           else
1719           {
1720             DEBUGPRINT( "Unhandled opcode " << opcodes[opcode].name
1721               << " (" << std::hex << opcode << std::dec << ")" <<
1722               std::endl );
1723           }
1724         }
1725         else
1726         {
1727           DEBUGPRINT( "Op-code out of range: " << std::hex << opcode <<
1728             std::dec << std::endl );
1729         }
1730       } // default
1731       break;
1732     } // switch
1733 
1734     if (skip_offset > 0)
1735       fseek( fp, skip_offset, SEEK_CUR );
1736 
1737   } // while !feof...
1738 }
1739 //===========================================================================
1740 
1741 #define MYMAKEFOURCC(a, b, c, d) \
1742     ((unsigned long)(a) | ((unsigned long)(b) << 8) |  \
1743     ((unsigned long)(c) << 16) | ((unsigned long)(d) << 24 ))
1744 
1745 static unsigned long lRIFF = MYMAKEFOURCC('R', 'I', 'F', 'F');
1746 static unsigned long lMDL8 = MYMAKEFOURCC('M', 'D', 'L', '8');
1747 
FindBGLBeginRIFF(FILE * fp)1748 void FindBGLBeginRIFF(FILE *fp)
1749 // place file cursor on the first BGL command.
1750 // if none found, places it on the file end
1751 // This function is for RIFF format used in MSFS2k2 and 2k4 and CFS2 (and other MS sims?)
1752 {
1753   unsigned int l;
1754   while ((lRIFF != (l = ulEndianReadLittle32(fp))) && (!feof(fp)))
1755     ;
1756   if (lRIFF != l) // RIFF not found
1757   {
1758     assert(feof(fp));
1759     return;
1760   }
1761   ulEndianReadLittle32(fp); // ignore file length
1762   l = ulEndianReadLittle32(fp);
1763   if (l != MYMAKEFOURCC('M', 'D', 'L', '8'))
1764     printf("Warning: Not a 'MDL8' RIFF file\n");
1765   else
1766     printf("RIFF file, subtype 'MDL8' recognised\n");
1767   while(!feof(fp))
1768   {
1769     char buffer[5];
1770     buffer[4] = 0;
1771     fread(buffer, 4, 1, fp);
1772     unsigned long offset = ulEndianReadLittle32(fp);
1773     if (offset & 1L)
1774       offset++; // if offset is odd, add one pad byte
1775     printf("RIFF Chunk '%s' found, data length = %ld\n", buffer, offset);
1776     if (0==strcmp(buffer, "BGL "))
1777     {
1778       // Great!!
1779       return;
1780     }
1781     fseek(fp, offset, SEEK_CUR);
1782   }
1783 }
1784 
FindBGLBeginOldVersion(FILE * fp)1785 void FindBGLBeginOldVersion(FILE *fp)
1786 // place file cursor on the first BGL command.
1787 // if none found, places it on the file end
1788 // This function is for old MDL files (for FS98, for example)
1789 {
1790   unsigned short op1, op2;
1791   op1 = ulEndianReadLittle16(fp);
1792 
1793   while(!feof(fp))
1794   {
1795     op2 = ulEndianReadLittle16(fp);
1796     if(op1 == 0x76 && op2 == 0x3a)
1797     {
1798       fseek(fp, -4, SEEK_CUR);
1799       break;
1800     }
1801     op1 = op2;
1802   }
1803 }
1804 
1805 
1806 
ssgLoadMDL(const char * fname,const ssgLoaderOptions * options)1807 ssgEntity *ssgLoadMDL(const char *fname, const ssgLoaderOptions *options)
1808 {
1809   ssgSetCurrentOptions ( (ssgLoaderOptions*)options ) ;
1810   current_options = ssgGetCurrentOptions () ;
1811 
1812   ailerons_grp_ = NULL;
1813   elevator_grp_ = NULL;
1814   rudder_grp_ = NULL;
1815   gear_grp_ = NULL;
1816   spoilers_grp_ = NULL;
1817   flaps_grp_ = NULL;
1818   prop_grp_ = NULL;
1819   TheVertexList = NULL;
1820   TheTextureList = NULL;
1821 
1822   char filename [ 1024 ] ;
1823   current_options -> makeModelPath ( filename, fname ) ;
1824 
1825   FILE *fp = fopen(filename, "rb");
1826   if(!fp)
1827   {
1828     ulSetError( UL_WARNING, "ssgLoadMDL: Couldn't open MDL file '%s'!",
1829       filename );
1830     return NULL;
1831   }
1832 
1833   // Find beginning of BGL Code segment
1834   unsigned long l = ulEndianReadLittle32(fp);
1835   fseek(fp, 0, SEEK_SET);
1836   if (l == lRIFF) // This is somewhat of a kludge, since ther "RIFF" is mostly at the beginning of the file, but not always.
1837     FindBGLBeginRIFF(fp);
1838   else
1839   { FindBGLBeginOldVersion(fp);
1840     if(feof(fp))
1841     { // Ok - so it is not "RIFF" at the beginning of the file and it is not an old file format -
1842       // so search for RIFF anywhere
1843       // I know of at least one file (the Wellesly V1.4, where the V1.4 is important)
1844       // that had two "RIFF"s and ionyl the second one is the one we want.
1845       // So we have to search for a place with "RIFF"
1846       fseek(fp, 0, SEEK_SET);
1847       l = ulEndianReadLittle32(fp);
1848       while(!feof(fp))
1849       {
1850         unsigned char c = fgetc(fp);
1851         l = (l >> 8) | (c << 24);
1852         if ( l == lRIFF )
1853         { // check whether it is a red herring...
1854           ulEndianReadLittle32(fp); // ignore length
1855           unsigned long ll = ulEndianReadLittle32(fp);
1856           if (ll == lMDL8)
1857           { // found it !!
1858             fseek(fp, -12, SEEK_CUR);
1859             unsigned long addr = ftell(fp);
1860             if(addr&1L)
1861               printf("strange... found RIFF, but on an odd adress %lx\n", addr);
1862             else
1863               printf("found a good RIFF header at address %lx\n", addr);
1864             FindBGLBeginRIFF(fp);
1865             break; // breaks the while(!feof(fp))
1866           }
1867         }
1868       }
1869     }
1870   }
1871 
1872   if(feof(fp))
1873   {
1874     ulSetError( UL_WARNING, "ssgLoadMDL: No BGL Code found in file '%s'!",
1875       filename );
1876     fclose(fp);
1877     return NULL;
1878   }
1879   // end find begin
1880 
1881 
1882   // Initialize object graph
1883   model_ = new ssgBranch();
1884   char* model_name = new char[128];
1885   char *ptr = (char*)&fname[strlen(fname) - 1];
1886   while(ptr != &fname[0] && *ptr != '/') ptr--;
1887   if(*ptr == '/') ptr++;
1888   strcpy(model_name, ptr);
1889   ptr = &model_name[strlen(model_name)];
1890   while(*ptr != '.' && ptr != &model_name[0]) ptr--;
1891   *ptr = '\0';
1892   model_->setName(model_name);
1893 
1894   // Create group nodes for textured and non-textured objects
1895   curr_vtx_  = new ssgVertexArray();
1896   curr_norm_ = new ssgNormalArray();
1897 
1898   vertex_array_ = new ssgVertexArray();
1899   normal_array_ = new ssgNormalArray();
1900 
1901   tex_coords_ = new ssgTexCoordArray();
1902 
1903   start_idx_ = 0;
1904   last_idx_  = 0;
1905   curr_var_ = 0;
1906   stack_depth_ = 0;
1907   noLoDs = 1; // if there is no "branch" and no "++noLoDs" is called, we have 1 LoD.
1908               // If there is one branch, we have 2, etc.
1909   curr_lod = 0;
1910   sgMakeIdentMat4(curr_matrix_);
1911 
1912   // Parse opcodes
1913 #ifdef DEBUG
1914 #ifdef _MSC_VER
1915   wkfp=fopen("c:\\mdl_file_structure.txt", "wt");
1916 #else
1917   wkfp=fopen("mdl_file_structure.txt", "wt");
1918 #endif
1919 #endif
1920 
1921   ParseBGL(fp); // "traversing" through the file
1922 
1923 
1924   fclose(fp);
1925 #ifdef DEBUG
1926   fclose(wkfp);
1927 #endif
1928 // :-(((  delete curr_vtx_;
1929   delete curr_norm_;
1930 
1931   DEBUGPRINT("\n" << vertex_array_->getNum() << " vertices\n");
1932   printf("NoLoDs = %d\n", (int)noLoDs);
1933   printf("noGT=%d, noLT=%d, no0=%d\n", noGT, noLT, no0);
1934   g_noLoDs = noLoDs;
1935 
1936   return model_;
1937 }
1938 
1939 #else
1940 
ssgLoadMDLTexture(const char * fname,ssgTextureInfo * info)1941 bool ssgLoadMDLTexture ( const char *fname, ssgTextureInfo* info )
1942 {
1943   ulSetError ( UL_WARNING,
1944     "ssgLoadTexture: '%s' - MDL support not configured", fname ) ;
1945   return false ;
1946 }
1947 
1948 
1949 #endif
1950 
1951