]> icculus.org git repositories - taylor/freespace2.git/blob - src/model/modelread.cpp
switch to SDL_min/SDL_max (fixes compiler errors)
[taylor/freespace2.git] / src / model / modelread.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Model/ModelRead.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * file which reads and deciphers POF information
16  *
17  * $Log$
18  * Revision 1.13  2006/04/26 19:44:19  taylor
19  * better error handing of a Volition bug
20  *
21  * Revision 1.12  2004/09/20 01:31:44  theoddone33
22  * GCC 3.4 fixes.
23  *
24  * Revision 1.11  2004/07/04 11:39:06  taylor
25  * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
26  *
27  * Revision 1.10  2004/06/11 21:39:44  taylor
28  * x86 compile fixes for OSX patch
29  *
30  * Revision 1.9  2004/06/10 23:55:39  tigital
31  * byte-swapping changes for bigendian systems
32  *
33  * Revision 1.8  2003/06/11 18:30:33  taylor
34  * plug memory leaks
35  *
36  * Revision 1.7  2003/01/30 19:43:57  relnev
37  * added a missing "not" in the model warning
38  *
39  * Revision 1.6  2002/06/17 06:33:09  relnev
40  * ryan's struct patch for gcc 2.95
41  *
42  * Revision 1.5  2002/06/09 04:41:23  relnev
43  * added copyright header
44  *
45  * Revision 1.4  2002/06/09 03:16:04  relnev
46  * added _splitpath.
47  *
48  * removed unneeded asm, old sdl 2d setup.
49  *
50  * fixed crash caused by opengl_get_region.
51  *
52  * Revision 1.3  2002/06/01 07:12:33  relnev
53  * a few NDEBUG updates.
54  *
55  * removed a few warnings.
56  *
57  * Revision 1.2  2002/05/07 03:16:47  theoddone33
58  * The Great Newline Fix
59  *
60  * Revision 1.1.1.1  2002/05/03 03:28:10  root
61  * Initial import.
62  *
63  * 
64  * 36    7/22/99 2:26p Mattk
65  * DA: don't show engine wash error if no subsystems.  (ie, Pofview...)
66  * 
67  * 35    7/19/99 12:02p Andsager
68  * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
69  * only blow up subsystem if its strength is > 0
70  * 
71  * 34    7/07/99 5:00p Andsager
72  * Fixed bug in stepped rotation
73  * 
74  * 33    7/06/99 4:42p Anoop
75  * Add MiniCap hack for engine wash
76  * 
77  * 32    7/06/99 3:52p Andsager
78  * That wacky model_subsystem name vs. subobj_name thing bites us in the
79  * ass again.
80  * 
81  * 31    7/06/99 3:15p Anoop
82  * Better debug info for engine wash error.
83  * 
84  * 30    7/06/99 11:37a Andsager
85  * Fix warning message.
86  * 
87  * 29    7/06/99 10:45a Andsager
88  * Modify engine wash to work on any ship that is not small.  Add AWACS
89  * ask for help.
90  * 
91  * 28    7/01/99 4:23p Dave
92  * Full support for multiple linked ambient engine sounds. Added "big
93  * damage" flag.
94  * 
95  * 27    6/08/99 2:33p Dave
96  * Fixed release build warning. Put in hud config stuff.
97  * 
98  * 26    6/01/99 8:35p Dave
99  * Finished lockarm weapons. Added proper supercap weapons/damage. Added
100  * awacs-set-radius sexpression.
101  * 
102  * 25    5/28/99 1:51p Andsager
103  * Allow models to rotate in both direction on an axis, specifying a
104  * negative rotation time in submodel properties.
105  * 
106  * 24    5/27/99 3:48p Andsager
107  * Add assert to model_read code to not allow overwrite of memory
108  * 
109  * 23    5/27/99 12:11p Andsager
110  * Allow more than 1 subsystem to have live debris on any ship
111  * 
112  * 22    5/27/99 10:57a Mattk
113  * Put in a warning about too many firing points per turret.
114  * 
115  * 21    5/24/99 5:45p Dave
116  * Added detail levels to the nebula, with a decent speedup. Split nebula
117  * lightning into its own section.
118  * 
119  * 20    5/19/99 11:09a Andsager
120  * Turn on engine wash.  Check every 1/4 sec.
121  * 
122  * 19    5/11/99 10:16p Andsager
123  * First pass on engine wash effect.  Rotation (control input), damage,
124  * shake.  
125  * 
126  * 18    4/09/99 11:38a Andsager
127  * Change POF format for Freespace2
128  * 
129  * 17    3/23/99 5:17p Dave
130  * Changed model file format somewhat to account for LOD's on insignias
131  * 
132  * 16    3/20/99 3:46p Dave
133  * Added support for model-based background nebulae. Added 3 new
134  * sexpressions.
135  * 
136  * 15    3/19/99 6:15p Dave
137  * Put in checks for insignia bitmaps for having too many faces or
138  * vertices
139  * 
140  * 14    2/21/99 6:01p Dave
141  * Fixed standalone WSS packets. 
142  * 
143  * 13    2/19/99 11:42a Dave
144  * Put in model rendering autocentering.
145  * 
146  * 12    2/10/99 3:52p Enricco
147  * Fix stupid assert
148  * 
149  * 11    1/14/99 6:06p Dave
150  * 100% full squad logo support for single player and multiplayer.
151  * 
152  * 10    1/11/99 12:42p Andsager
153  * Add live debris - debris which is created from a destroyed subsystem,
154  * when the ship is still alive
155  * 
156  * 9     1/08/99 2:08p Dave
157  * Fixed software rendering for pofview. Super early support for AWACS and
158  * beam weapons.
159  * 
160  * 8     1/06/99 2:24p Dave
161  * Stubs and release build fixes.
162  * 
163  * 7     12/23/98 2:53p Andsager
164  * Added stepped rotation support
165  * 
166  * 6     12/04/98 3:34p Andsager
167  * Handle norotating submodels
168  * 
169  * 5     12/03/98 3:14p Andsager
170  * Check in code that checks rotating submodel actually has ship subsystem
171  * 
172  * 4     11/19/98 11:07p Andsager
173  * Check in of physics and collision detection of rotating submodels
174  * 
175  * 3     10/23/98 3:03p Andsager
176  * Initial support for changing rotation rate.
177  * 
178  * 2     10/07/98 10:53a Dave
179  * Initial checkin.
180  * 
181  * 1     10/07/98 10:50a Dave
182  * 
183  * 177   8/28/98 3:29p Dave
184  * EMP effect done. AI effects may need some tweaking as required.
185  * 
186  * 176   5/19/98 8:31p Andsager
187  * Added split planes (for big ship explosions)
188  * 
189  * 175   5/07/98 5:39p Andsager
190  * Changes to model to hold cross section info
191  * 
192  * 174   4/30/98 4:53p John
193  * Restructured and cleaned up cfile code.  Added capability to read off
194  * of CD-ROM drive and out of multiple pack files.
195  * 
196  * 173   4/22/98 9:43p John
197  * Added code to allow checking of invisible faces, flagged by any texture
198  * name with invisible in it.
199  * 
200  * 172   4/14/98 11:11p John
201  * Made ships with < 50% hull left show electrical damage arcs.
202  * 
203  * 171   4/01/98 5:34p John
204  * Made only the used POFs page in for a level.   Reduced some interp
205  * arrays.    Made custom detail level work differently.
206  * 
207  * 170   4/01/98 9:14a Mike
208  * Fixed free bug
209  * 
210  * 169   3/31/98 5:18p John
211  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
212  * bunch of debug stuff out of player file.  Made model code be able to
213  * unload models and malloc out only however many models are needed.
214  *  
215  * 
216  * 168   3/17/98 2:46p Allender
217  * Copy subsystem name when copying model subsystems
218  * 
219  * 167   3/02/98 5:42p John
220  * Removed WinAVI stuff from Freespace.  Made all HUD gauges wriggle from
221  * afterburner.  Made gr_set_clip work good with negative x &y.  Made
222  * model_caching be on by default.  Made each cached model have it's own
223  * bitmap id.  Made asteroids not rotate when model_caching is on.  
224  * 
225  * 166   2/24/98 5:04p Allender
226  * allow different ship classes to use the same model.  Lot's of subsystem
227  * stuff to deal with
228  * 
229  * 165   2/16/98 3:46p John
230  * Made mass be proportional to area in newer pofs
231  * 
232  * 164   2/13/98 5:19p John
233  * First rev of new model caching system.  Needs to be ripped out and
234  * moved into ship stuff.
235  * 
236  * 163   1/29/98 5:52p John
237  * new version of electrical arcing code
238  * 
239  * 162   1/29/98 8:39a Andsager
240  * Changed mass and moment of intertia based area vs. volume
241  * 
242  * 161   1/28/98 2:15p Mike
243  * Convert volume to surface area at runtime.  Write note to John to
244  * cleanup if he modifies bspgen.
245  * 
246  * 160   1/27/98 11:02a John
247  * Added first rev of sparks.   Made all code that calls model_render call
248  * clear_instance first.   Made debris pieces not render by default when
249  * clear_instance is called.
250  * 
251  * 159   1/19/98 6:15p John
252  * Fixed all my Optimized Build compiler warnings
253  * 
254  * 158   1/09/98 11:25a John
255  * Took the thruster animation stuff out of the model.
256  * 
257  * 157   12/31/97 4:47p John
258  * Made all the eye points fit inside core radius
259  * 
260  * 156   12/31/97 2:35p John
261  * Added code for core_radius
262  * 
263  * 155   12/17/97 2:41p John
264  * made target bounding box be on hull only.
265  * 
266  * 154   12/17/97 9:54a John
267  * added code for precalculated weapon flash lighting.
268  * 
269  * 153   11/29/97 2:05p John
270  * made g3_draw_bitmap and g3_draw_rotated bitmap take w&h, not w/2 & h/2,
271  * like they used to incorrectly assume.   Added code to model to read in
272  * thruster radius's.
273  * 
274  * 152   11/25/97 11:41a John
275  * added code to read in pof v20.05, which adds radius to thrusters.
276  * 
277  * 151   10/28/97 12:34a Mike
278  * Set useful values of mass and inertia matrix if values not otherwise
279  * set.  Generate Warnings for unexpected ones.
280  * 
281  * 150   10/22/97 5:54p Lawrance
282  * get rid of leading '$' when storing parent_name for paths
283  * 
284  * 149   10/20/97 5:57p Lawrance
285  * have index to polymodel->paths[] within the model_subsystem struct.
286  * 
287  * 148   9/15/97 5:45p John
288  * took out chunk stuff.
289  * made pofview display thrusters as blue polies.
290  * 
291  * 147   9/09/97 3:39p Sandeep
292  * warning level 4 bugs
293  * 
294  * 146   9/04/97 5:09p Andsager
295  * implement physics using moment of inertia and mass (from BSPgen).
296  * Added to phys_info struct.  Updated ship_info, polymodel structs.
297  * Updated weapon ($Mass and $Force) and ship ($Mass -> $Density) tables
298  * 
299  * 145   9/03/97 5:57p Lawrance
300  * take out warning
301  * 
302  * 144   9/03/97 4:32p John
303  * changed bmpman to only accept ani and pcx's.  made passing .pcx or .ani
304  * to bm_load functions not needed.   Made bmpman keep track of palettes
305  * for bitmaps not mapped into game palettes.
306  * 
307  * 143   8/19/97 11:49p Lawrance
308  * using atan2_safe() in place of atan2()
309  * 
310  * 142   8/19/97 8:44a John
311  * Initial hooks for thruster stuff.
312  * 
313  * 141   8/15/97 4:10p John
314  * new code to use the new octrees style bsp trees
315  * 
316  * 140   8/14/97 5:46p John
317  * added code to read in mass,center of mass, and moment of interia from
318  * the pof.  Also added code to display green center of mass dot when Show
319  * debug points is on in pofview.
320  * 
321  * 139   7/10/97 8:34a John
322  * Added code to read TGA files.
323  * 
324  * 138   6/26/97 3:18p Mike
325  * Recognize "turret", etc. in a subsystem field anywhere in name.
326  * Generate warning for bogus subsystem names.
327  * 
328  * 137   6/25/97 5:11p John
329  * added foundation for model octants.
330  * 
331  * 136   6/25/97 10:28a John
332  * Made debris chunks radius be set correctly to the submodel size, not
333  * hardcoded to 10.0f.
334  * 
335  * 135   6/06/97 11:55a Mike
336  * Fix firing direction for zero piece turrets.
337  * 
338  * 134   5/31/97 2:36p Mike
339  * Comment out mprintfs pertaining to debris.
340  * 
341  * 133   5/30/97 4:41p Hoffoss
342  * Made some additions required for Fred.
343  * 
344  * 132   5/30/97 3:42p Mike
345  * Shield mesh and hit system.
346  * 
347  * 131   5/30/97 10:40a Hoffoss
348  * Added a function for Fred to get docking point names.
349  * 
350  * 130   5/29/97 4:34p Allender
351  * removed Int3 from model_find_dock_index.
352  * 
353  * 129   4/30/97 10:41a Allender
354  * don't dump out token 'none' when dumping out subsystem information
355  * 
356  * 128   4/17/97 6:06p John
357  * New code/data for v19 of BSPGEN with smoothing and zbuffer
358  * optimizations.
359  * 
360  * 127   4/09/97 3:31p Lawrance
361  * if any points of bounding box don't project, return project fail code
362  * in model_find_2d_bound_min()
363  * 
364  * 126   4/07/97 10:37a John
365  * Took out limits on number of children a submodel can have.    (So now
366  * capital ships can have tons of children on them).
367  * 
368  * 125   4/03/97 3:18p Allender
369  * 
370  * 124   4/03/97 1:29p Allender
371  * subsystem enhancement.  Initial work on making subsystem status
372  * meaningful
373  * 
374  * 123   3/28/97 2:49p John
375  * added code to fix targetting of debris chunks to draw bounding boxes
376  * right.
377  * 
378  * 122   3/28/97 1:19p John
379  * Made TestCode not show damaged submodels.
380  * 
381  * 121   3/28/97 11:17a John
382  * added code for linking lower detail submodels to the highest one; added
383  * code to pofview to view these.
384  * 
385  * 120   3/17/97 11:24a John
386  * 
387  * 119   3/15/97 5:06p John
388  * added bounding boxes for each subobject and code to display them.
389  * 
390  * 118   3/13/97 10:32a John
391  * Added code for tiled 256x256 textures in certain models.
392  * 
393  * 117   3/06/97 5:36p Mike
394  * Change vec_normalize_safe() back to vec_normalize().
395  * Spruce up docking a bit.
396  * 
397  * 116   3/06/97 1:06p Allender
398  * more docking stuff.  docking points now specified by strings in mission
399  * file.  Changes to builtin missions and code to reflect this change.
400  * 
401  * 115   3/06/97 10:56a Mike
402  * Write error checking version of vm_vec_normalize().
403  * Fix resultant problems.
404  * 
405  * 114   3/06/97 10:05a Lawrance
406  * added missing cfclose's
407  * 
408  * 113   3/05/97 5:27p John
409  * more work with turret stuff
410  * 
411  * 112   3/05/97 12:49p John
412  * added Viewer_obj.  Took out the interp_??? variables for turning
413  * outline,etc on and put them in flags you pass to model_render.
414  * Cleaned up model_interp code to fit new coding styles.
415  * 
416  * 111   3/05/97 12:42p Adam
417  * john: fixed bug while model transversel code that assumed the root
418  * subobject was 0.   
419  * 
420  * 110   3/05/97 11:09a Allender
421  * DOH!  MAX_SLOTS just way too low.  Upped to 25 and Asserts put in to
422  * check bounds.
423  * 
424  * 109   3/04/97 5:08p John
425  * Started adding debug code to make it so you can view from a turret's
426  * view.
427  * 
428  * 108   3/04/97 3:36p John
429  * took out old debug "h' key.   Made zbuffer flag bit bit field so you
430  * can turn on/off each value.   Fixed a bug with turret rotation where
431  * different handedness turrets wouldn't work.   Fixed a bug with two
432  * large ships within each other's radius not rendering correctly.
433  * 
434  * 107   3/04/97 12:52p Allender
435  * docking styff.  Added dock type to docking structure in model code.
436  * Renamed structure member in ai_goals.  Temporary checkin.  (i.e.
437  * rearming will use rearm dock points)
438  * 
439  * 106   3/03/97 5:47p John
440  * fixed some rotating object discrepencies.   Took out the gun firing
441  * point average beause it was inconsitent and wsn't as correct as using
442  * the pivot point.
443  * 
444  * 105   3/03/97 4:20p Hoffoss
445  * Fixed bug in bug I just fixed. :)
446  * 
447  * 104   3/03/97 4:00p Hoffoss
448  * fixed bug that caused docking bays without a name to be garbage when
449  * loaded.
450  * 
451  * 103   3/03/97 8:58a Lawrance
452  * replaced a fprintf() with a cfputs()
453  * 
454  * 102   3/01/97 2:12p Lawrance
455  * made to work with new cfile changes
456  * 
457  * 101   2/28/97 10:57a John
458  * Made so you can blow off any subsystems, not just radars.
459  * 
460  * 
461  * 100   2/27/97 3:45p John
462  * Added a tree dialog to pofview that shows a model's tree.
463  * 
464  * 99    2/27/97 12:07p John
465  * Added code to suppord debris chunks after a ship is exploded..
466  * 
467  * 98    2/26/97 5:28p John
468  * Fixed a bunch of chunked model bugs.  Basically they almost work better
469  * than non-chunked objects!
470  * 
471  * 97    2/24/97 1:13p Allender
472  * support for multiple eye positions.  Still some kinks to be worked out
473  * though....
474  * 
475  * 96    2/20/97 4:06p Allender
476  * allow for multiple spline paths to lead to a docking bay
477  * 
478  * 95    2/20/97 3:25p Allender
479  * change to docking bay structure -- added a name (for use in Fred
480  * mainly), and index into spline paths to reach this docking point
481  * 
482  * 94    2/19/97 4:03p Allender
483  * dump a 0.0 instead of a 0 when dumping out missing subsystem
484  * information
485  * 
486  * 93    2/19/97 2:52p Allender
487  * added crewspot name to subsystems (used only with turrets).  Will be
488  * used by fred for ai class stuff for turrets
489  * 
490  * 92    2/17/97 4:30p John
491  * Added code to automatically rotate any subsystems with the $rotate
492  * value set (except for turrets)
493  * 
494  * 91    2/17/97 12:03p John
495  * Added code to set the angles of a subobject.
496  * 
497  * 90    2/14/97 4:02p John
498  * changed a vector being passed as value to reference.
499  * 
500  * 89    2/14/97 3:16p Mike
501  * Start on AI path garbage collection
502  * 
503  * 88    2/14/97 1:06p John
504  * upped bspgen version number.
505  * 
506  * 87    2/13/97 10:17a John
507  * INCORPORATED THE FOLLOWING CHANGES:
508  * ???    2/13/97 9:49a John
509  * added code for geometric centers of objects.
510  *  ???   2/12/97 6:30p John
511  *  upped model version number.
512  *  
513  * 
514  * 86    2/13/97 9:49a John
515  * ROLLEDBACK TO ALLENDER'S 85 
516  * 
517  * 85    2/12/97 5:34p Allender
518  * added a ton of debug code to help recify the subsystems in ships.tbl
519  * with subsystems in the models.   Fixed a bug with subsystems not being
520  * given the correct type.
521  * 
522  * 84    2/11/97 4:10p Allender
523  * major change of subsystem stuff.  obj_subsystem renamed to
524  * model_subsystem.  Dyamically allocate space in ship_info for
525  * subsystems.
526  * 
527  * 83    2/10/97 4:20p Allender
528  * added distance to closest geometry to spline points
529  * 
530  * 82    2/10/97 10:58a John
531  * Added back in sparks for where laser bolts hit.
532  * Added code to red damaged objects and replace then.
533  * 
534  * 81    2/07/97 1:24p Allender
535  * added turret lists for spline points
536  * 
537  * 80    2/06/97 2:56p John
538  * Added a "blown-off" field to the instance of a model.  Also took out
539  * the model_set_angles functions and replaced with the more general
540  * model_set_instance functions.
541  * 
542  * 79    2/04/97 7:37p John
543  * Finally!!! Turret stuff working!
544  * 
545  * 78    1/31/97 12:51p John
546  * Added turret_fov and turret_fov_cos
547  * 
548  * 77    1/31/97 12:43p John
549  * Turrets working nicely, finally!
550  * 
551  * 76    1/31/97 11:36a John
552  * 
553  * 75    1/29/97 4:46p John
554  * Code to read $crewpoint flag on turrets and store it in obj_subsystem
555  * info.
556  * 
557  * 74    1/29/97 3:14p John
558  * Added field to ships.tbl to control how fast turret rotates.
559  * 
560  * 73    1/28/97 11:23a John
561  * added functions to get/set model angles.
562  * 
563  * 72    1/28/97 10:07a John
564  * More turret stuff, still not working, though.
565  * 
566  * 71    1/27/97 2:46p John
567  * more turret stuff
568  * 
569  * 70    1/27/97 11:35a Allender
570  * upped compatible object version
571  * 
572  * 69    1/24/97 5:25p John
573  * More turret stuff.   Not working yet, though.
574  * 
575  * 68    1/24/97 4:55p John
576  * Started adding code for turning turrets.
577  * Had to change POF format to 18.0
578  * 
579  * 67    1/20/97 2:16p Allender
580  * new shield structures being used
581  * 
582  * 66    1/15/97 5:19p Allender
583  * new POF version.  old version obsolete.  Added shield and eye stuff.
584  * New format for gun/missile/docking points
585  * 
586  * 65    1/10/97 5:15p Mike
587  * Moved ship-specific parameters from obj_subsystem to ship_subsys.
588  * 
589  * Added turret code to AI system.
590  * 
591  * 64    1/10/97 2:25p John
592  * Added code to support detail level distances.
593  * 
594  * 63    1/02/97 11:45a Allender
595  * changed turret code to have > 1 firing point as well as fixing the
596  * problems of turrets being multiple subobjects.  Turret norms should be
597  * getting stuffed correctly now.
598  * 
599  * 62    12/31/96 4:14p Mike
600  * Turret firing.
601  * Correct bug in HUDtarget.cpp, showing wrong target.
602  * 
603  * 61    12/27/96 4:09p Mike
604  * Integrate John's new model code.
605  * 
606  * 60    12/26/96 1:42p John
607  * Changed function ordering to put model_init at top.
608  * 
609  * 59    12/23/96 3:56p John
610  * Changed POF code to support pof 15.0.
611  * 
612  * 58    12/23/96 11:00a John
613  * Restructured POF stuff to support LOD in one pof.
614  * 
615  * 57    12/20/96 11:55a Allender
616  * removed debug code
617  * 
618  * 56    12/20/96 11:40a Allender
619  * modifed subsystem stuff to do correct hit percentages and other cool
620  * stuff
621  * 
622  * 55    12/18/96 4:04p Allender
623  * added "unknown" subsystem type for subsystems which aren't recognized.
624  * 
625  * 54    12/17/96 11:37a Mike
626  * Big ship AI.
627  * 
628  * 53    12/17/96 8:59a Mike
629  * Path-extraction-from-mdoel test code.
630  * 
631  * 52    12/13/96 5:27p John
632  * fixed my sloppy code that didn't free model data.
633  * 
634  * 51    12/13/96 3:54p Mike
635  * Major improvement to model_draw_paths -- connect the dots!  Uses
636  * sophisticated connect-to-next algorithm.
637  * 
638  * 50    12/13/96 2:59p John
639  * Added some code to test spline paths.
640  * 
641  * 49    12/13/96 2:41p John
642  * Added code to read in spline paths on models.
643  * 
644  * 48    12/13/96 10:29a Adam
645  * from allender:  fixed up linked list walking when checking for turrets
646  * in subsystems
647  * 
648  * 47    12/12/96 3:38p Allender
649  * fix up docking special object names.  Added $thruster stuff to model
650  * and code
651  * 
652  * 46    12/12/96 2:35p Mike
653  * Subsystem support
654  * 
655  * 45    12/12/96 12:29p Lawrance
656  * added function to find the 2d bound for a subobject
657  * 
658  * 44    12/11/96 5:35p John
659  * Added variables for viewer eye.
660  * 
661  * 43    12/11/96 5:15p John
662  * Made the dist for detail level be calculated before the object is
663  * instanced.
664  * 
665  * 42    12/11/96 4:50p John
666  * Added detail levels to the non-chunky ships.
667  * 
668  * 41    12/11/96 4:33p Mike
669  * Temporary version so other people can build.
670  * 
671  * 40    12/11/96 4:24p Mike
672  * Fix broken code...oops! clumsy in editor!
673  * 
674  * 39    12/11/96 4:17p Adam
675  * 
676  * 38    12/11/96 12:57p Mike
677  * Support for "Big Ship" AI behavior.
678  * More turret stuff.
679  * 
680  * 37    12/11/96 12:43p Allender
681  * $gun and $missile don't get appended to models gun/missile list if the
682  * gun/missile belongs to a turret.  Turrets are special instances of
683  * these
684  * 
685  * 36    12/11/96 11:57a Allender
686  * added objnum reference to obj_subsystems so that $gun and $missile
687  * special polygons can refernce a particular subsystem if needed (i.e. to
688  * get the norms, etc).
689  * 
690  * 35    12/11/96 10:33a Allender
691  * added stuff for FOV for turrets
692  * 
693  * 34    12/10/96 3:27p Allender
694  * 
695  * 33    12/09/96 5:05p Allender
696  * made subsystem lists and model_special lists as seperate entities.
697  * 
698  * 32    12/09/96 1:16p Allender
699  * made seperate lists for guns/missiles/subsystems, etc
700  * 
701  * 31    12/09/96 11:01a Allender
702  * added special polygon information to models.  Linked list contains
703  * gun/missile/docking/subsystem/etc information.
704  *
705  * $NoKeywords: $
706  */
707
708 #include <string.h>
709 #include <ctype.h>
710
711 #define MODEL_LIB
712
713 #include "cfile.h"
714 #include "model.h"
715 #include "bmpman.h"
716 #include "floating.h"
717 #include "3d.h"
718 #include "ship.h"
719 #include "modelsinc.h"
720 #include "key.h"
721 #include "2d.h"
722 #include "3dinternal.h"
723 #include "linklist.h"
724 #include "timer.h"
725 #include "freespace.h"          // For flFrameTime
726 #include "fvi.h"
727
728
729 #define MAX_SUBMODEL_COLLISION_ROT_ANGLE (PI / 6.0f)    // max 30 degrees per frame
730
731 // info for special polygon lists
732
733 polymodel *Polygon_models[MAX_POLYGON_MODELS];
734
735 static int model_initted = 0;
736
737 #ifndef NDEBUG
738 CFILE *ss_fp;                   // file pointer used to dump subsystem information
739 char  model_filename[MAX_PATH_LEN];             // temp used to store filename
740 char    debug_name[MAX_PATH_LEN];
741 int ss_warning_shown;           // have we shown the warning dialog concerning the subsystems?
742 char    Global_filename[MAX_PATH_LEN];
743 int Model_ram = 0;                      // How much RAM the models use total
744 #endif
745
746
747
748 // Anything less than this is considered incompatible.
749 #define PM_COMPATIBLE_VERSION 1900
750
751 // Anything greater than or equal to PM_COMPATIBLE_VERSION and 
752 // whose major version is less than or equal to this is considered
753 // compatible.  
754 #define PM_OBJFILE_MAJOR_VERSION 21
755
756 static int Model_signature = 0;
757
758 // Free up a model, getting rid of all its memory
759 // Can't be called from outside of model code because more
760 // than one person might be using this model so we can't free 
761 // it.
762 static void model_unload(int modelnum)
763 {
764         int i,j;
765
766         if ( (modelnum < 0) || (modelnum>MAX_POLYGON_MODELS))   {
767                 return;
768         }
769
770         polymodel *pm = Polygon_models[modelnum];
771
772         if ( !pm )      {
773                 return;
774         }
775
776 #ifndef NDEBUG
777         Model_ram -= pm->ram_used;
778 #endif
779         
780         if (pm->paths)  {
781                 for (i=0; i<pm->n_paths; i++ )  {
782                         for (j=0; j<pm->paths[i].nverts; j++ )  {
783                                 if ( pm->paths[i].verts[j].turret_ids ) {
784                                         free(pm->paths[i].verts[j].turret_ids);
785                                 }
786                         }
787                         if (pm->paths[i].verts) {
788                                 free(pm->paths[i].verts);
789                         }
790                 }
791                 free(pm->paths);
792         }
793
794         if ( pm->shield.verts ) {
795                 free( pm->shield.verts );
796         }
797
798         if ( pm->shield.tris )  {
799                 free(pm->shield.tris);
800         }
801
802         if ( pm->missile_banks )        {
803                 free(pm->missile_banks);
804         }
805
806         if ( pm->docking_bays ) {
807                 for (i=0; i<pm->n_docks; i++ )  {
808                         if ( pm->docking_bays[i].splines )      {
809                                 free( pm->docking_bays[i].splines );
810                         }
811                 }
812                 free(pm->docking_bays);
813         }
814
815         // this is from ship.cpp but we don't get the polymodel there so free here
816         if ( pm->ship_bay != NULL ) {
817                 free(pm->ship_bay);
818                 pm->ship_bay = NULL;
819         }
820
821         if ( pm->thrusters )    {
822                 free(pm->thrusters);
823         }
824
825 #ifndef NDEBUG
826         if ( pm->debug_info )   {
827                 free(pm->debug_info);
828         }
829 #endif
830
831         model_octant_free( pm );
832
833         if (pm->submodel)       {
834                 for (i=0; i<pm->n_models; i++ ) {
835                         if ( pm->submodel[i].bsp_data ) {
836                                 free(pm->submodel[i].bsp_data);
837                         }
838                 }
839                 free(pm->submodel);
840         }
841
842         if ( pm->xc ) {
843                 free(pm->xc);
844         }
845
846         if ( pm->lights )       {
847                 free(pm->lights);
848         }
849
850         if ( pm->gun_banks )    {
851                 free(pm->gun_banks);
852         }
853
854         pm->id = 0;
855         memset( pm, 0, sizeof(polymodel));
856         free( pm );
857
858         Polygon_models[modelnum] = NULL;        
859 }
860
861 void model_free_all()
862 {
863         int i;
864
865         if ( !model_initted)    {
866                 model_init();
867                 return;
868         }
869
870         mprintf(( "Freeing all existing models...\n" ));
871
872         for (i=0;i<MAX_POLYGON_MODELS;i++) {
873                 model_unload(i);                
874         }
875
876 }
877
878 void model_init()
879 {
880         int i;
881
882         if ( model_initted )            {
883                 Int3();         // Model_init shouldn't be called twice!
884                 return;
885         }
886
887 #ifndef NDEBUG
888         Model_ram = 0;
889 #endif
890
891         for (i=0;i<MAX_POLYGON_MODELS;i++) {
892                 Polygon_models[i] = NULL;
893         }
894
895         atexit( model_free_all );
896         model_initted = 1;
897 }
898
899 // routine to parse out values from a user property field of an object
900 void get_user_prop_value(char *buf, char *value, const int max_vlen)
901 {
902         char *p, *p1, c;
903
904         p = buf;
905         while ( isspace(*p) || (*p == '=') )            // skip white space and equal sign
906                 p++;
907         p1 = p;
908         while ( !iscntrl(*p1) )
909                 p1++;
910         c = *p1;
911         *p1 = '\0';
912         SDL_strlcpy(value, p, max_vlen);
913         *p1 = c;
914 }
915
916 // funciton to copy model data from one subsystem set to another subsystem set.  This function
917 // is called when two ships use the same model data, but since the model only gets read in one time,
918 // the subsystem data is only present in one location.  The ship code will call this routine to fix
919 // this situation by copying stuff from the source subsystem set to the dest subsystem set.
920 void model_copy_subsystems( int n_subsystems, model_subsystem *d_sp, model_subsystem *s_sp )
921 {
922         int i, j;
923         model_subsystem *source, *dest;
924
925         for (i = 0; i < n_subsystems; i++ ) {
926                 source = &s_sp[i];
927                 for ( j = 0; j < n_subsystems; j++ ) {
928                         dest = &d_sp[j];
929                         if ( !SDL_strcasecmp( source->subobj_name, dest->subobj_name) ) {
930                                 dest->flags = source->flags;
931                                 dest->subobj_num = source->subobj_num;
932                                 dest->model_num = source->model_num;
933                                 dest->pnt = source->pnt;
934                                 dest->radius = source->radius;
935                                 dest->type = source->type;
936                                 dest->turn_rate = source->turn_rate;
937                                 dest->turret_gun_sobj = source->turret_gun_sobj;
938
939                                 SDL_strlcpy( dest->name, source->name, SDL_arraysize(dest->name) );
940
941                                 if ( dest->type == SUBSYSTEM_TURRET ) {
942                                         int nfp;
943
944                                         dest->turret_fov = source->turret_fov;
945                                         dest->turret_num_firing_points = source->turret_num_firing_points;
946                                         dest->turret_norm = source->turret_norm;
947                                         dest->turret_matrix = source->turret_matrix;
948
949                                         for (nfp = 0; nfp < dest->turret_num_firing_points; nfp++ )
950                                                 dest->turret_firing_point[nfp] = source->turret_firing_point[nfp];
951
952                                         if ( dest->flags & MSS_FLAG_CREWPOINT )
953                                                 SDL_strlcpy(dest->crewspot, source->crewspot, SDL_arraysize(dest->crewspot));
954                                 }
955                                 break;
956                         }
957                 }
958                 if ( j == n_subsystems )
959                         Int3();                                                 // get allender -- something is amiss with models
960
961         }
962 }
963
964 // routine to get/set subsystem information
965 static void set_subsystem_info( model_subsystem *subsystemp, char *props, char *dname )
966 {
967         char *p;
968         char buf[32];
969         char    lcdname[256];
970
971         if ( (p = strstr(props, "$name")) != NULL)
972                 get_user_prop_value(p+5, subsystemp->name, SDL_arraysize(subsystemp->name));
973         else
974                 SDL_strlcpy( subsystemp->name, dname, SDL_arraysize(subsystemp->name) );
975
976         SDL_strlcpy(lcdname, dname, SDL_arraysize(lcdname));
977         SDL_strlwr(lcdname);
978
979         // check the name for it's specific type
980         if ( strstr(lcdname, "engine") ) {
981                 subsystemp->type = SUBSYSTEM_ENGINE;
982         } else if ( strstr(lcdname, "radar") ) {
983                 subsystemp->type = SUBSYSTEM_RADAR;
984         } else if ( strstr(lcdname, "turret") ) {
985                 float angle;
986
987                 subsystemp->type = SUBSYSTEM_TURRET;
988                 if ( (p = strstr(props, "$fov")) != NULL )
989                         get_user_prop_value(p+4, buf, SDL_arraysize(buf));                      // get the value of the fov
990                 else
991                         SDL_strlcpy(buf,"180", SDL_arraysize(buf));
992                 angle = ANG_TO_RAD(atoi(buf))/2.0f;
993                 subsystemp->turret_fov = (float)cos(angle);
994                 subsystemp->turret_num_firing_points = 0;
995
996                 if ( (p = strstr(props, "$crewspot")) != NULL) {
997                         subsystemp->flags |= MSS_FLAG_CREWPOINT;
998                         get_user_prop_value(p+9, subsystemp->crewspot, SDL_arraysize(subsystemp->crewspot));
999                 }
1000
1001         } else if ( strstr(lcdname, "navigation") ) {
1002                 subsystemp->type = SUBSYSTEM_NAVIGATION;
1003         } else if ( strstr(lcdname, "communication") )  {
1004                 subsystemp->type = SUBSYSTEM_COMMUNICATION;
1005         } else if ( strstr(lcdname, "weapons") ) {
1006                 subsystemp->type = SUBSYSTEM_WEAPONS;
1007         } else if ( strstr(lcdname, "sensors") ) {
1008                 subsystemp->type = SUBSYSTEM_SENSORS;
1009         } else if ( strstr(lcdname, "solar") ) {
1010                 subsystemp->type = SUBSYSTEM_SOLAR;
1011 #ifndef MAKE_FS1
1012         } else if ( strstr(lcdname, "gas") ) {
1013                 subsystemp->type = SUBSYSTEM_GAS_COLLECT;
1014         } else if ( strstr(lcdname, "activator") ) {
1015                 subsystemp->type = SUBSYSTEM_ACTIVATION;
1016 #endif
1017         }  else { // If unrecognized type, set to unknown so artist can continue working...
1018                 subsystemp->type = SUBSYSTEM_UNKNOWN;
1019                 mprintf(("Warning: Ignoring unrecognized subsystem %s, believed to be in ship %s\n", dname, Global_filename));
1020         }
1021
1022         // Rotating subsystem
1023         if ( (p = strstr(props, "$rotate")) != NULL)    {
1024                 subsystemp->flags |= MSS_FLAG_ROTATES;
1025
1026                 // get time for (a) complete rotation (b) step (c) activation
1027                 float turn_time;
1028                 get_user_prop_value(p+7, buf, SDL_arraysize(buf));
1029                 turn_time = (float)atof(buf);
1030
1031                 // CASE OF STEPPED ROTATION
1032                 if ( (p = strstr(props, "$stepped")) != NULL) {
1033
1034                         subsystemp->stepped_rotation = new(stepped_rotation);
1035                         subsystemp->flags |= MSS_FLAG_STEPPED_ROTATE;
1036
1037                         // get number of steps
1038                         if ( (p = strstr(props, "$steps")) != NULL) {
1039                                 get_user_prop_value(p+6, buf, SDL_arraysize(buf));
1040                            subsystemp->stepped_rotation->num_steps = atoi(buf);
1041                          } else {
1042                             subsystemp->stepped_rotation->num_steps = 8;
1043                          }
1044
1045                         // get pause time
1046                         if ( (p = strstr(props, "$t_paused")) != NULL) {
1047                                 get_user_prop_value(p+9, buf, SDL_arraysize(buf));
1048                            subsystemp->stepped_rotation->t_pause = (float)atof(buf);
1049                          } else {
1050                             subsystemp->stepped_rotation->t_pause = 2.0f;
1051                          }
1052
1053                         // get transition time - time to go between steps
1054                         if ( (p = strstr(props, "$t_transit")) != NULL) {
1055                                 get_user_prop_value(p+10, buf, SDL_arraysize(buf));
1056                             subsystemp->stepped_rotation->t_transit = (float)atof(buf);
1057                         } else {
1058                             subsystemp->stepped_rotation->t_transit = 2.0f;
1059                         }
1060
1061                         // get fraction of time spent in accel
1062                         if ( (p = strstr(props, "$fraction_accel")) != NULL) {
1063                                 get_user_prop_value(p+15, buf, SDL_arraysize(buf));
1064                             subsystemp->stepped_rotation->fraction = (float)atof(buf);
1065                            SDL_assert(subsystemp->stepped_rotation->fraction > 0 && subsystemp->stepped_rotation->fraction < 0.5);
1066                         } else {
1067                             subsystemp->stepped_rotation->fraction = 0.3f;
1068                         }
1069
1070                         int num_steps = subsystemp->stepped_rotation->num_steps;
1071                         float t_trans = subsystemp->stepped_rotation->t_transit;
1072                         float fraction = subsystemp->stepped_rotation->fraction;
1073
1074                         subsystemp->stepped_rotation->max_turn_accel = PI2 / (fraction*(1.0f - fraction) * num_steps * t_trans*t_trans);
1075                         subsystemp->stepped_rotation->max_turn_rate =  PI2 / ((1.0f - fraction) * num_steps *t_trans);
1076
1077                 }
1078
1079                 // CASE OF AI ROTATION
1080                 else if ( (p = strstr(props, "$ai")) != NULL) {
1081                         get_user_prop_value(p+8, buf, SDL_arraysize(buf));
1082                         subsystemp->flags |= MSS_FLAG_AI_ROTATE;
1083
1084                         // get parameters - ie, speed / dist / other ??
1085                         // time to activate
1086                         // condition
1087                 }
1088
1089                 // CASE OF NORMAL CONTINUOUS ROTATION
1090                 else {
1091                         if ( fabs(turn_time) < 1 ) {                            
1092                                 // Warning(LOCATION, "%s has subsystem %s with rotation time less than 1 sec", dname, Global_filename );
1093                                 subsystemp->flags &= ~MSS_FLAG_ROTATES;
1094                                 subsystemp->turn_rate = 0.0f;
1095                         } else {
1096                                 subsystemp->turn_rate = PI2 / turn_time;
1097                         }
1098                 }
1099         }
1100
1101 }
1102
1103 // used in collision code to check if submode rotates too far
1104 float get_submodel_delta_angle(submodel_instance_info *sii)
1105 {
1106         vector diff;
1107         vm_vec_sub(&diff, (vector*)&sii->angs, (vector*)&sii->prev_angs);
1108
1109         // find the angle
1110         float delta_angle = vm_vec_mag(&diff);
1111
1112         // make sure we get the short way around
1113         if (delta_angle > PI) {
1114                 delta_angle = (PI2 - delta_angle);
1115         }
1116
1117         return delta_angle;
1118 }
1119
1120 void do_new_subsystem( int n_subsystems, model_subsystem *slist, int subobj_num, float rad, vector *pnt, char *props, char *subobj_name, int model_num )
1121 {
1122         int i;
1123         model_subsystem *subsystemp;
1124
1125
1126         if ( slist==NULL ) return;                      // For TestCode, POFView, etc don't bother
1127         
1128         // try to find the name of the subsystem passed here on the list of subsystems currently on the
1129         // ship.  Assign the values only when the right subsystem is found
1130
1131         for (i = 0; i < n_subsystems; i++ ) {
1132                 subsystemp = &slist[i];
1133                 if ( !SDL_strcasecmp(subobj_name, subsystemp->subobj_name) ) {
1134                         subsystemp->flags = 0;
1135                         subsystemp->subobj_num = subobj_num;
1136                         subsystemp->turret_gun_sobj = -1;
1137                         subsystemp->model_num = model_num;
1138                         subsystemp->pnt = *pnt;                         // use the offset to get the center point of the subsystem
1139                         subsystemp->radius = rad;
1140                         set_subsystem_info( subsystemp, props, subobj_name);
1141                         SDL_strlcpy(subsystemp->subobj_name, subobj_name, SDL_arraysize(subsystemp->subobj_name));                                              // copy the object name
1142                         return;
1143                 }
1144         }
1145 #ifndef NDEBUG
1146         if ( !ss_warning_shown) {
1147                 char bname[MAX_FILENAME_LEN];
1148
1149                 base_filename(model_filename, bname, SDL_arraysize(bname));
1150                 Warning(LOCATION, "A subsystem was found in model %s that does not have a record in ships.tbl.\nA list of subsystems for this ship will be dumped to:\n\ndata\\tables\\%s.subsystems for inclusion\n into ships.tbl.", model_filename, bname);
1151
1152                 ss_warning_shown = 1;
1153         } else
1154 #endif
1155         nprintf(("warning", "Subsystem %s in ships.tbl not found in model!\n", subobj_name));
1156 #ifndef NDEBUG
1157         if ( ss_fp )    {
1158                 char tmp_buffer[128];
1159                 SDL_snprintf(tmp_buffer, SDL_arraysize(tmp_buffer), "$Subsystem:\t\t\t%s,1,0.0\n", subobj_name);
1160                 cfputs(tmp_buffer, ss_fp);
1161         }
1162 #endif
1163
1164 }
1165
1166 void print_family_tree( polymodel *obj, int modelnum, const char * ident, int islast )  
1167 {
1168         char temp[50];
1169
1170         if ( modelnum < 0 ) return;
1171         if (obj==NULL) return;
1172
1173         if (strlen(ident)==0 )  {
1174                 mprintf(( " %s", obj->submodel[modelnum].name ));
1175                 SDL_snprintf( temp, SDL_arraysize(temp), " " );
1176         } else if ( islast )    {
1177                 mprintf(( "%s��%s", ident, obj->submodel[modelnum].name ));
1178                 SDL_snprintf( temp, SDL_arraysize(temp), "%s  ", ident );
1179         } else {
1180                 mprintf(( "%s��%s", ident, obj->submodel[modelnum].name ));
1181                 SDL_snprintf( temp, SDL_arraysize(temp), "%s� ", ident );
1182         }
1183
1184         mprintf(( "\n" ));
1185
1186         int child = obj->submodel[modelnum].first_child;
1187         while( child > -1 )     {
1188                 if ( obj->submodel[child].next_sibling < 0 )
1189                         print_family_tree( obj, child, temp,1 );
1190                 else
1191                         print_family_tree( obj, child, temp,0 );
1192                 child = obj->submodel[child].next_sibling;
1193         }
1194 }
1195
1196 void dump_object_tree(polymodel *obj)
1197 {
1198         print_family_tree( obj, 0, "", 0 );
1199         key_getch();
1200 }
1201
1202 void create_family_tree(polymodel *obj)
1203 {
1204         int i;
1205         for (i=0; i<obj->n_models; i++ )        {
1206                 obj->submodel[i].num_children = 0;
1207                 obj->submodel[i].first_child = -1;
1208                 obj->submodel[i].next_sibling = -1;
1209         }
1210
1211         for (i=0; i<obj->n_models; i++ )        {
1212                 int pn;
1213                 pn = obj->submodel[i].parent;
1214                 if ( pn > -1 )  {
1215                         obj->submodel[pn].num_children++;
1216                         int tmp = obj->submodel[pn].first_child;
1217                         obj->submodel[pn].first_child = i;
1218                         obj->submodel[i].next_sibling = tmp;
1219                 }
1220         }
1221 }
1222
1223 void model_calc_bound_box( vector *box, vector *big_mn, vector *big_mx)
1224 {
1225         box[0].xyz.x = big_mn->xyz.x; box[0].xyz.y = big_mn->xyz.y; box[0].xyz.z = big_mn->xyz.z;
1226         box[1].xyz.x = big_mx->xyz.x; box[1].xyz.y = big_mn->xyz.y; box[1].xyz.z = big_mn->xyz.z;
1227         box[2].xyz.x = big_mx->xyz.x; box[2].xyz.y = big_mx->xyz.y; box[2].xyz.z = big_mn->xyz.z;
1228         box[3].xyz.x = big_mn->xyz.x; box[3].xyz.y = big_mx->xyz.y; box[3].xyz.z = big_mn->xyz.z;
1229
1230
1231         box[4].xyz.x = big_mn->xyz.x; box[4].xyz.y = big_mn->xyz.y; box[4].xyz.z = big_mx->xyz.z;
1232         box[5].xyz.x = big_mx->xyz.x; box[5].xyz.y = big_mn->xyz.y; box[5].xyz.z = big_mx->xyz.z;
1233         box[6].xyz.x = big_mx->xyz.x; box[6].xyz.y = big_mx->xyz.y; box[6].xyz.z = big_mx->xyz.z;
1234         box[7].xyz.x = big_mn->xyz.x; box[7].xyz.y = big_mx->xyz.y; box[7].xyz.z = big_mx->xyz.z;
1235 }
1236
1237
1238 //      Debug thing so we don't repeatedly show warning messages.
1239 #ifndef NDEBUG
1240 int Bogus_warning_flag_1903 = 0;
1241 #endif
1242
1243 //reads a binary file containing a 3d model
1244 int read_model_file(polymodel * pm, const char *filename, int n_subsystems, model_subsystem *subsystems)
1245 {
1246         CFILE *fp;
1247         int version;
1248         int id, len, next_chunk;
1249         int i,j;
1250
1251 #ifndef NDEBUG
1252         SDL_strlcpy(Global_filename, filename, SDL_arraysize(Global_filename));
1253 #endif
1254
1255         fp = cfopen(filename,"rb");
1256         if (!fp){
1257                 Error( LOCATION, "Can't open file <%s>",filename);
1258                 return 0;
1259         }               
1260
1261         // code to get a filename to write out subsystem information for each model that
1262         // is read.  This info is essentially debug stuff that is used to help get models
1263         // into the game quicker
1264 #if 0
1265         {
1266                 char bname[MAX_PATH_LEN];
1267
1268                 base_filename(filename, bname, SDL_arraysize(bname));
1269                 SDL_snprintf(debug_name, SDL_arraysize(debug_name), "%s.subsystems", bname);
1270                 ss_fp = cfopen(debug_name, "wb", CFILE_NORMAL, CF_TYPE_TABLES );
1271                 if ( !ss_fp )   {
1272                         mprintf(( "Can't open debug file for writing subsystems for %s\n", filename));
1273                 } else {
1274                         SDL_strlcpy(model_filename, filename, SDL_arraysize(model_filename));
1275                         ss_warning_shown = 0;
1276                 }
1277         }
1278 #endif
1279
1280         id = cfread_int(fp);
1281
1282         if (id != ID_PSPO)
1283                 Error( LOCATION, "Bad ID in model file <%s>",filename);
1284
1285         // Version is major*100+minor
1286         // So, major = version / 100;
1287         //     minor = version % 100;
1288         version = cfread_int(fp);
1289
1290         //Warning( LOCATION, "POF Version = %d", version );
1291         
1292         if (version < PM_COMPATIBLE_VERSION || (version/100) > PM_OBJFILE_MAJOR_VERSION)        {
1293                 Warning(LOCATION,"Bad version (%d) in model file <%s>",version,filename);
1294                 return 0;
1295         }
1296
1297         pm->version = version;
1298         SDL_assert( strlen(filename) < FILENAME_LEN );
1299         SDL_strlcpy(pm->filename, filename, FILENAME_LEN);
1300
1301         memset( &pm->view_positions, 0, sizeof(pm->view_positions) );
1302
1303         // reset insignia counts
1304         pm->num_ins = 0;
1305
1306         id = cfread_int(fp);
1307         len = cfread_int(fp);
1308         next_chunk = cftell(fp) + len;
1309
1310         while (!cfeof(fp)) {
1311
1312 //              mprintf(("Processing chunk <%c%c%c%c>, len = %d\n",id,id>>8,id>>16,id>>24,len));
1313 //              key_getch();
1314
1315                 switch (id) {
1316
1317                         case ID_OHDR: {         //Object header
1318                                 //vector v;
1319
1320                                 //mprintf(0,"Got chunk OHDR, len=%d\n",len);
1321
1322 #if defined( FREESPACE1_FORMAT )
1323                                 pm->n_models = cfread_int(fp);
1324 //                              mprintf(( "Num models = %d\n", pm->n_models ));
1325                                 pm->rad = cfread_float(fp);
1326                                 pm->flags = cfread_int(fp);     // 1=Allow tiling
1327 #elif defined( FREESPACE2_FORMAT )
1328                                 pm->rad = cfread_float(fp);
1329                                 pm->flags = cfread_int(fp);     // 1=Allow tiling
1330                                 pm->n_models = cfread_int(fp);
1331 //                              mprintf(( "Num models = %d\n", pm->n_models ));
1332 #endif
1333                                 
1334                                 pm->submodel = (bsp_info *)malloc( sizeof(bsp_info)*pm->n_models );
1335                                 SDL_assert(pm->submodel != NULL );
1336                                 memset( pm->submodel, 0, sizeof(bsp_info)*pm->n_models );
1337
1338                                 //SDL_assert(pm->n_models <= MAX_SUBMODELS);
1339
1340                                 cfread_vector(&pm->mins,fp);
1341                                 cfread_vector(&pm->maxs,fp);
1342                                 model_calc_bound_box(pm->bounding_box,&pm->mins,&pm->maxs);
1343                                 
1344                                 pm->n_detail_levels = cfread_int(fp);
1345                         //      mprintf(( "There are %d detail levels\n", pm->n_detail_levels ));
1346                                 for (i=0; i<pm->n_detail_levels;i++ )   {
1347                                         pm->detail[i] = cfread_int(fp);
1348                                         pm->detail_depth[i] = 0.0f;
1349                         ///             mprintf(( "Detail level %d is model %d.\n", i, pm->detail[i] ));
1350                                 }
1351
1352                                 pm->num_debris_objects = cfread_int(fp);
1353                                 SDL_assert( pm->num_debris_objects <= MAX_DEBRIS_OBJECTS );
1354                                 // mprintf(( "There are %d debris objects\n", pm->num_debris_objects ));
1355                                 for (i=0; i<pm->num_debris_objects;i++ )        {
1356                                         pm->debris_objects[i] = cfread_int(fp);
1357                                         // mprintf(( "Debris object %d is model %d.\n", i, pm->debris_objects[i] ));
1358                                 }
1359
1360                                 if ( pm->version >= 1903 )      {
1361         
1362                                         if ( pm->version >= 2009 )      {
1363                                                                                                                                         
1364                                                 pm->mass = cfread_float(fp);
1365                                                 cfread_vector( &pm->center_of_mass, fp );
1366                                                 cfread_vector( &pm->moment_of_inertia.v.rvec, fp );
1367                                                 cfread_vector( &pm->moment_of_inertia.v.uvec, fp );
1368                                                 cfread_vector( &pm->moment_of_inertia.v.fvec, fp );
1369                                         } else {
1370                                                 // old code where mass wasn't based on area, so do the calculation manually
1371
1372                                                 float vol_mass = cfread_float(fp);
1373                                                 //      Attn: John Slagel:  The following is better done in bspgen.
1374                                                 // Convert volume (cubic) to surface area (quadratic) and scale so 100 -> 100
1375                                                 float area_mass = (float) pow(vol_mass, 0.6667f) * 4.65f;
1376
1377                                                 pm->mass = area_mass;
1378                                                 float mass_ratio = vol_mass / area_mass; 
1379                                                         
1380                                                 cfread_vector( &pm->center_of_mass, fp );
1381                                                 cfread_vector( &pm->moment_of_inertia.v.rvec, fp );
1382                                                 cfread_vector( &pm->moment_of_inertia.v.uvec, fp );
1383                                                 cfread_vector( &pm->moment_of_inertia.v.fvec, fp );
1384
1385                                                 // John remove this with change to bspgen
1386                                                 vm_vec_scale( &pm->moment_of_inertia.v.rvec, mass_ratio );
1387                                                 vm_vec_scale( &pm->moment_of_inertia.v.uvec, mass_ratio );
1388                                                 vm_vec_scale( &pm->moment_of_inertia.v.fvec, mass_ratio );
1389                                         }       
1390                                 } else {
1391 #ifndef NDEBUG
1392                                         if (SDL_strcasecmp("fighter04.pof", filename)) {
1393                                                 if (Bogus_warning_flag_1903 == 0) {
1394                                                         Warning(LOCATION, "Ship %s is old.  Cannot compute mass.\nSetting to 50.0f.  Talk to John.", filename);
1395                                                         Bogus_warning_flag_1903 = 1;
1396                                                 }
1397                                         }
1398 #endif
1399                                         pm->mass = 50.0f;
1400                                         vm_vec_zero( &pm->center_of_mass );
1401                                         vm_set_identity( &pm->moment_of_inertia );
1402                                         vm_vec_scale(&pm->moment_of_inertia.v.rvec, 0.001f);
1403                                         vm_vec_scale(&pm->moment_of_inertia.v.uvec, 0.001f);
1404                                         vm_vec_scale(&pm->moment_of_inertia.v.fvec, 0.001f);
1405                                 }
1406
1407                                 // read in cross section info
1408                                 pm->xc = NULL;
1409                                 if ( pm->version >= 2014 ) {
1410                                         pm->num_xc = cfread_int(fp);
1411                                         if (pm->num_xc > 0) {
1412                                                 pm->xc = (cross_section*) malloc(pm->num_xc*sizeof(cross_section));
1413                                                 for (i=0; i<pm->num_xc; i++) {
1414                                                         pm->xc[i].z = cfread_float(fp);
1415                                                         pm->xc[i].radius = cfread_float(fp);
1416                                                 }
1417                                         }
1418                                 } else {
1419                                         pm->num_xc = 0;
1420                                 }
1421
1422                                 if ( pm->version >= 2007 )      {
1423                                         pm->num_lights = cfread_int(fp);
1424                                         //mprintf(( "Found %d lights!\n", pm->num_lights ));
1425
1426                                         pm->lights = (bsp_light *)malloc( sizeof(bsp_light)*pm->num_lights );
1427                                         for (i=0; i<pm->num_lights; i++ )       {                       
1428                                                 cfread_vector(&pm->lights[i].pos,fp);
1429                                                 pm->lights[i].type = cfread_int(fp);
1430                                                 pm->lights[i].value = 0.0f;
1431                                         }                                                               
1432                                 } else {
1433                                         pm->num_lights = 0;
1434                                         pm->lights = NULL;
1435                                 }
1436
1437                                 break;
1438                         }
1439                         
1440                         case ID_SOBJ: {         //Subobject header
1441                                 int n;
1442                                 char *p, props[MAX_PROP_LEN];
1443 //                              float d;
1444
1445                                 //mprintf(0,"Got chunk SOBJ, len=%d\n",len);
1446
1447                                 n = cfread_int(fp);
1448
1449                                 SDL_assert(n < pm->n_models );
1450
1451 #if defined( FREESPACE2_FORMAT )        
1452                                 pm->submodel[n].rad = cfread_float(fp);         //radius
1453 #endif
1454
1455                                 pm->submodel[n].parent = cfread_int(fp);
1456
1457 //                              cfread_vector(&pm->submodel[n].norm,fp);
1458 //                              d = cfread_float(fp);                           
1459 //                              cfread_vector(&pm->submodel[n].pnt,fp);
1460                                 cfread_vector(&pm->submodel[n].offset,fp);
1461
1462 //                      mprintf(( "Subobj %d, offs = %.1f, %.1f, %.1f\n", n, pm->submodel[n].offset.xyz.x, pm->submodel[n].offset.xyz.y, pm->submodel[n].offset.xyz.z ));
1463         
1464 #if defined ( FREESPACE1_FORMAT )
1465                                 pm->submodel[n].rad = cfread_float(fp);         //radius
1466 #endif
1467
1468 //                              pm->submodel[n].tree_offset = cfread_int(fp);   //offset
1469 //                              pm->submodel[n].data_offset = cfread_int(fp);   //offset
1470
1471                                 cfread_vector(&pm->submodel[n].geometric_center,fp);
1472
1473                                 cfread_vector(&pm->submodel[n].min,fp);
1474                                 cfread_vector(&pm->submodel[n].max,fp);
1475
1476                                 model_calc_bound_box(pm->submodel[n].bounding_box,&pm->submodel[n].min,&pm->submodel[n].max);
1477
1478                                 pm->submodel[n].name[0] = '\0';
1479
1480                                 cfread_string_len( pm->submodel[n].name, MAX_NAME_LEN, fp);             // get the name
1481                                 cfread_string_len(props, MAX_PROP_LEN, fp);                     // and the user properites
1482
1483                                 pm->submodel[n].movement_type = cfread_int(fp);
1484                                 pm->submodel[n].movement_axis = cfread_int(fp);
1485
1486                                 // change turret movement type to MOVEMENT_TYPE_ROT_SPECIAL
1487                                 if (pm->submodel[n].movement_type == MOVEMENT_TYPE_ROT) {
1488                                         if ( strstr(pm->submodel[n].name, "turret") || strstr(pm->submodel[n].name, "gun") || strstr(pm->submodel[n].name, "cannon")) {
1489                                                 pm->submodel[n].movement_type = MOVEMENT_TYPE_ROT_SPECIAL;
1490                                         } else if (strstr(pm->submodel[n].name, "thruster")) {
1491                                                 // Int3();
1492                                                 pm->submodel[n].movement_type = MOVEMENT_TYPE_NONE;
1493                                                 pm->submodel[n].movement_axis = MOVEMENT_AXIS_NONE;
1494                                         }
1495                                 }
1496
1497                                 if ( pm->submodel[n].name[0] == '\0' ) {
1498                                         SDL_strlcpy(pm->submodel[n].name, "unknown object name", MAX_NAME_LEN);
1499                                 }
1500
1501                                 bool rotating_submodel_has_subsystem = !(pm->submodel[n].movement_type == MOVEMENT_TYPE_ROT);
1502                                 if ( ( p = strstr(props, "$special"))!= NULL ) {
1503                                         char type[32];
1504
1505                                         get_user_prop_value(p+9, type, SDL_arraysize(type));
1506                                         if ( !SDL_strcasecmp(type, "subsystem") ) {     // if we have a subsystem, put it into the list!
1507                                                 do_new_subsystem( n_subsystems, subsystems, n, pm->submodel[n].rad, &pm->submodel[n].offset, props, pm->submodel[n].name, pm->id );
1508                                                 rotating_submodel_has_subsystem = true;
1509                                         } else if ( !SDL_strcasecmp(type, "no_rotate") ) {
1510                                                 // mark those submodels which should not rotate - ie, those with no subsystem
1511                                                 pm->submodel[n].movement_type = MOVEMENT_TYPE_NONE;
1512                                                 pm->submodel[n].movement_axis = MOVEMENT_AXIS_NONE;
1513                                         } else {
1514                                                 // if submodel rotates (via bspgen), then there is either a subsys or special=no_rotate
1515                                                 SDL_assert( pm->submodel[n].movement_type != MOVEMENT_TYPE_ROT );
1516                                         }
1517                                 }
1518
1519                                 if ( !rotating_submodel_has_subsystem ) {
1520                                         nprintf(("Model", "Model %s: Rotating Submodel without subsystem: %s\n", pm->filename, pm->submodel[n].name));
1521
1522                                         // mark those submodels which should not rotate - ie, those with no subsystem
1523                                         pm->submodel[n].movement_type = MOVEMENT_TYPE_NONE;
1524                                         pm->submodel[n].movement_axis = MOVEMENT_AXIS_NONE;
1525                                 }
1526
1527
1528                                 pm->submodel[n].angs.p = 0.0f;
1529                                 pm->submodel[n].angs.b = 0.0f;
1530                                 pm->submodel[n].angs.h = 0.0f;
1531
1532                                 {
1533                                         int nchunks = cfread_int( fp );         // Throw away nchunks
1534                                         if ( nchunks > 0 )      {
1535                                                 Error( LOCATION, "Model '%s' is chunked.  See John or Adam!\n", pm->filename );
1536                                         }
1537                                 }
1538                                 pm->submodel[n].bsp_data_size = cfread_int(fp);
1539                                 if ( pm->submodel[n].bsp_data_size > 0 )        {
1540                                         pm->submodel[n].bsp_data = (ubyte *)malloc(pm->submodel[n].bsp_data_size);
1541                                         cfread(pm->submodel[n].bsp_data,1,pm->submodel[n].bsp_data_size,fp);
1542 #if (SDL_BYTEORDER == SDL_BIG_ENDIAN)           //tigital
1543                                         swap_bsp_data( pm, pm->submodel[n].bsp_data );
1544 #endif
1545                                 } else {
1546                                         pm->submodel[n].bsp_data = NULL;
1547                                 }
1548
1549                                 if ( strstr( pm->submodel[n].name, "thruster") )        
1550                                         pm->submodel[n].is_thruster=1;
1551                                 else
1552                                         pm->submodel[n].is_thruster=0;
1553
1554                                 if ( strstr( pm->submodel[n].name, "-destroyed") )      
1555                                         pm->submodel[n].is_damaged=1;
1556                                 else
1557                                         pm->submodel[n].is_damaged=0;
1558                                         
1559                                 //mprintf(( "Submodel %d, name '%s', parent = %d\n", n, pm->submodel[n].name, pm->submodel[n].parent ));
1560                                 //key_getch();
1561
1562                 //mprintf(( "Submodel %d, tree offset %d\n", n, pm->submodel[n].tree_offset ));
1563                 //mprintf(( "Submodel %d, data offset %d\n", n, pm->submodel[n].data_offset ));
1564                 //key_getch();
1565
1566
1567                                 break;
1568
1569                         }
1570
1571                         case ID_SHLD:
1572                                 {
1573                                         int nverts, ntris;                                      
1574
1575                                         nverts = cfread_int( fp );              // get the number of vertices in the list
1576                                         pm->shield.nverts = nverts;
1577                                         pm->shield.verts = (shield_vertex *)malloc(nverts * sizeof(shield_vertex) );
1578                                         SDL_assert( pm->shield.verts );
1579                                         for ( i = 0; i < nverts; i++ )                                                  // read in the vertex list
1580                                                 cfread_vector( &(pm->shield.verts[i].pos), fp );
1581
1582                                         ntris = cfread_int( fp );               // get the number of triangles that compose the shield
1583                                         pm->shield.ntris = ntris;
1584                                         pm->shield.tris = (shield_tri *)malloc(ntris * sizeof(shield_tri) );
1585                                         SDL_assert( pm->shield.tris );
1586                                         for ( i = 0; i < ntris; i++ ) {
1587                                                 cfread_vector( &(pm->shield.tris[i].norm), fp );
1588                                                 for ( j = 0; j < 3; j++ ) {
1589                                                         pm->shield.tris[i].verts[j] = cfread_int( fp );         // read in the indices into the shield_vertex list
1590                                                         /*
1591 #ifndef NDEBUG
1592                                                         if (pm->shield.tris[i].verts[j] >= nverts)
1593                                                                 if (!warning_displayed) {
1594                                                                         warning_displayed = 1;
1595                                                                         Warning(LOCATION, "Ship %s has a bogus shield mesh.\nOnly %i vertices, index %i found.\n", filename, nverts, pm->shield.tris[i].verts[j]);
1596                                                                 }
1597 #endif
1598                                                                 */
1599                                                 }
1600                                                 for ( j = 0; j < 3; j++ )
1601                                                         pm->shield.tris[i].neighbors[j] = cfread_int( fp );     // read in the neighbor indices -- indexes into tri list
1602                                         }
1603                                         break;
1604
1605
1606                                         
1607                                 }
1608                                 break;
1609
1610                         case ID_GPNT:
1611                                 pm->n_guns = cfread_int(fp);
1612                                 pm->gun_banks = (w_bank *)malloc(sizeof(w_bank) * pm->n_guns);
1613                                 SDL_assert( pm->gun_banks != NULL );
1614
1615                                 for (i = 0; i < pm->n_guns; i++ ) {
1616                                         w_bank *bank = &pm->gun_banks[i];
1617
1618                                         bank->num_slots = cfread_int(fp);
1619                                         SDL_assert ( bank->num_slots < MAX_SLOTS );
1620                                         for (j = 0; j < bank->num_slots; j++) {
1621                                                 cfread_vector( &(bank->pnt[j]), fp );
1622                                                 cfread_vector( &(bank->norm[j]), fp );
1623                                         }
1624                                 }
1625                                 break;
1626                         
1627                         case ID_MPNT:
1628                                 pm->n_missiles = cfread_int(fp);
1629                                 pm->missile_banks = (w_bank *)malloc(sizeof(w_bank) * pm->n_missiles);
1630                                 SDL_assert( pm->missile_banks != NULL );
1631
1632                                 for (i = 0; i < pm->n_missiles; i++ ) {
1633                                         w_bank *bank = &pm->missile_banks[i];
1634
1635                                         bank->num_slots = cfread_int(fp);
1636                                         SDL_assert ( bank->num_slots < MAX_SLOTS );
1637                                         for (j = 0; j < bank->num_slots; j++) {
1638                                                 cfread_vector( &(bank->pnt[j]), fp );
1639                                                 cfread_vector( &(bank->norm[j]), fp );
1640                                         }
1641                                 }
1642                                 break;
1643
1644                         case ID_DOCK: {
1645                                 char props[MAX_PROP_LEN];
1646
1647                                 pm->n_docks = cfread_int(fp);
1648                                 pm->docking_bays = (dock_bay *)malloc(sizeof(dock_bay) * pm->n_docks);
1649                                 SDL_assert( pm->docking_bays != NULL );
1650
1651                                 for (i = 0; i < pm->n_docks; i++ ) {
1652                                         char *p;
1653                                         dock_bay *bay = &pm->docking_bays[i];
1654
1655                                         cfread_string_len( props, MAX_PROP_LEN, fp );
1656                                         if ( (p = strstr(props, "$name"))!= NULL )
1657                                                 get_user_prop_value(p+5, bay->name, SDL_arraysize(bay->name));
1658                                         else
1659                                                 SDL_snprintf(bay->name, SDL_arraysize(bay->name), "<unnamed bay %c>", 'A' + i);
1660                                         bay->num_spline_paths = cfread_int( fp );
1661                                         if ( bay->num_spline_paths > 0 ) {
1662                                                 bay->splines = (int *)malloc(sizeof(int) * bay->num_spline_paths);
1663                                                 for ( j = 0; j < bay->num_spline_paths; j++ )
1664                                                         bay->splines[j] = cfread_int(fp);
1665                                         } else {
1666                                                 bay->splines = NULL;
1667                                         }
1668
1669                                         // determine what this docking bay can be used for
1670                                         if ( !SDL_strncasecmp(bay->name, "cargo", 5) )
1671                                                 bay->type_flags = DOCK_TYPE_CARGO;
1672                                         else
1673                                                 bay->type_flags = (DOCK_TYPE_REARM | DOCK_TYPE_GENERIC);
1674
1675                                         bay->num_slots = cfread_int(fp);
1676                                         SDL_assert( bay->num_slots == 2 );                                      // Get Allender if Asserted!
1677                                         for (j = 0; j < bay->num_slots; j++) {
1678                                                 cfread_vector( &(bay->pnt[j]), fp );
1679                                                 cfread_vector( &(bay->norm[j]), fp );
1680                                         }
1681                                 }
1682                                 break;
1683                         }
1684
1685                         case ID_FUEL: {
1686                                 char props[MAX_PROP_LEN];
1687                                 pm->n_thrusters = cfread_int(fp);
1688                                 pm->thrusters = (thruster_bank *)malloc(sizeof(thruster_bank) * pm->n_thrusters);
1689                                 SDL_assert( pm->thrusters != NULL );
1690
1691                                 for (i = 0; i < pm->n_thrusters; i++ ) {
1692                                         thruster_bank *bank = &pm->thrusters[i];
1693
1694                                         bank->num_slots = cfread_int(fp);
1695
1696                                         if (pm->version < 2117) {
1697                                                 bank->wash_info_index = -1;
1698                                         } else {
1699                                                 cfread_string_len( props, MAX_PROP_LEN, fp );
1700                                                 // look for $engine_subsystem=xxx
1701                                                 int length = strlen(props);
1702                                                 if (length > 0) {
1703                                                         int base_length = strlen("$engine_subsystem=");
1704                                                         SDL_assert( strstr( (const char *)&props, "$engine_subsystem=") != NULL );
1705                                                         SDL_assert( length > base_length );
1706                                                         char *engine_subsys_name = props + base_length;
1707                                                         if (engine_subsys_name[0] == '$') {
1708                                                                 engine_subsys_name++;
1709                                                         }
1710
1711                                                         nprintf(("wash", "Ship %s with engine wash associated with subsys %s\n", filename, engine_subsys_name));
1712
1713                                                         // set wash_info_index to invalid
1714                                                         int table_error = 1;
1715                                                         bank->wash_info_index = -1;
1716                                                         for (int k=0; k<n_subsystems; k++) {
1717                                                                 if ( 0 == SDL_strcasecmp(subsystems[k].subobj_name, engine_subsys_name) ) {
1718                                                                         bank->wash_info_index = subsystems[k].engine_wash_index;
1719                                                                         if (bank->wash_info_index >= 0) {
1720                                                                                 table_error = 0;
1721                                                                         }
1722                                                                         break;
1723                                                                 }
1724                                                         }
1725
1726                                                         if ( (bank->wash_info_index == -1) && (n_subsystems > 0) ) {
1727                                                                 if (table_error) {
1728                                                                         Warning(LOCATION, "No engine wash table entry in ships.tbl for ship model %s", filename);
1729                                                                 } else {
1730                                                                         Warning(LOCATION, "Inconsistent model: Engine wash engine subsystem does not match any ship subsytem names for ship model %s", filename);
1731                                                                 }
1732                                                         }
1733                                                 } else {
1734                                                         bank->wash_info_index = -1;
1735                                                 }
1736                                         }
1737
1738                                         for (j = 0; j < bank->num_slots; j++) {
1739
1740                                                 cfread_vector( &(bank->pnt[j]), fp );
1741                                                 cfread_vector( &(bank->norm[j]), fp );
1742                                                 if ( pm->version > 2004 )       {
1743                                                         bank->radius[j] = cfread_float( fp );
1744                                                         //mprintf(( "Rad = %.2f\n", rad ));
1745                                                 } else {
1746                                                         bank->radius[j] = 1.0f;
1747                                                 }
1748                                         }
1749                                         //mprintf(( "Num slots = %d\n", bank->num_slots ));
1750
1751                                 }
1752                                 break;
1753                         }
1754
1755                         case ID_TGUN:
1756                         case ID_TMIS: {
1757                                 int n_banks, n_slots, parent;
1758                                 model_subsystem *subsystemp;
1759                                 int snum=-1;
1760                                 vector bogus;
1761         
1762                                 n_banks = cfread_int(fp);                               // number of turret points
1763                                 for ( i = 0; i < n_banks; i++ ) {
1764                                         int physical_parent;                    // who are we attached to?
1765                                         parent = cfread_int( fp );                      // get the turret parent of the object
1766
1767                                         physical_parent = cfread_int(fp);       // The parent subobj that this is physically attached to
1768
1769                                         if ( subsystems ) {
1770                                                 for ( snum = 0; snum < n_subsystems; snum++ ) {
1771                                                         subsystemp = &subsystems[snum];
1772
1773                                                         if ( parent == subsystemp->subobj_num ) {
1774                                                                 cfread_vector( &subsystemp->turret_norm, fp );
1775                                                                 vm_vector_2_matrix(&subsystemp->turret_matrix,&subsystemp->turret_norm,NULL,NULL);
1776
1777                                                                 n_slots = cfread_int( fp );
1778                                                                 subsystemp->turret_gun_sobj = physical_parent;
1779                                                                 SDL_assert(n_slots <= MAX_TFP);         // only MAX_TFP firing points per model_subsystem
1780                                                                 for (j = 0; j < n_slots; j++ )  {
1781                                                                         if ( j < MAX_TFP ) {
1782                                                                                 cfread_vector( &subsystemp->turret_firing_point[j], fp );
1783                                                                         } else {
1784                                                                                 cfread_vector( &bogus, fp );
1785                                                                         }
1786                                                                 }
1787                                                                 SDL_assert( n_slots > 0 );
1788
1789                                                                 subsystemp->turret_num_firing_points = (n_slots > MAX_TFP) ? MAX_TFP : n_slots;
1790
1791                                                                 break;
1792                                                         }
1793                                                 }
1794                                         }
1795
1796 //turret_gun_sobj
1797
1798                                         if ( (n_subsystems == 0) || (snum == n_subsystems) ) {
1799                                                 nprintf(("Warning", "Turret object not found for turret firing point in model %s\n", model_filename));
1800                                                 cfread_vector( &bogus, fp );
1801                                                 n_slots = cfread_int( fp );
1802                                                 for (j = 0; j < n_slots; j++ )
1803                                                         cfread_vector( &bogus, fp );
1804                                         }
1805                                 }
1806                                 break;
1807                         }
1808
1809                         case ID_SPCL: {
1810                                 char name[MAX_NAME_LEN], props[MAX_PROP_LEN], *p;
1811                                 int n_specials;
1812                                 float radius;
1813                                 vector pnt;
1814
1815                                 n_specials = cfread_int(fp);            // get the number of special subobjects we have
1816                                 for (i = 0; i < n_specials; i++) {
1817
1818                                         // get the next free object of the subobject list.  Flag error if no more room
1819
1820                                         cfread_string_len(name, MAX_NAME_LEN, fp);                      // get the name of this special polygon
1821
1822                                         cfread_string_len(props, MAX_PROP_LEN, fp);             // will definately have properties as well!
1823                                         cfread_vector( &pnt, fp );
1824                                         radius = cfread_float( fp );
1825
1826                                         // check if $Split
1827                                         p = strstr(name, "$split");
1828                                         if (p != NULL) {
1829                                                 pm->split_plane[pm->num_split_plane] = pnt.xyz.z;
1830                                                 pm->num_split_plane++;
1831                                                 SDL_assert(pm->num_split_plane <= MAX_SPLIT_PLANE);
1832                                         } else if ( ( p = strstr(props, "$special"))!= NULL ) {
1833                                                 char type[32];
1834
1835                                                 get_user_prop_value(p+9, type, SDL_arraysize(type));
1836                                                 if ( !SDL_strcasecmp(type, "subsystem") )                                               // if we have a subsystem, put it into the list!
1837                                                         do_new_subsystem( n_subsystems, subsystems, -1, radius, &pnt, props, &name[1], pm->id );                // skip the first '$' character of the name
1838                                         } else if ( strstr(name, "$enginelarge") || strstr(name, "$enginehuge") ){
1839                                                 do_new_subsystem( n_subsystems, subsystems, -1, radius, &pnt, props, &name[1], pm->id );                // skip the first '$' character of the name
1840                                         } else {
1841                                                 nprintf(("Warning", "Unknown special object type %s while reading model %s\n", name, pm->filename));
1842                                         }                                       
1843                                 }
1844                                 break;
1845                         }
1846                         
1847                         case ID_TXTR: {         //Texture filename list
1848                                 int n;
1849 //                              char name_buf[128];
1850
1851                                 //mprintf(0,"Got chunk TXTR, len=%d\n",len);
1852
1853                                 n = cfread_int(fp);
1854                                 pm->n_textures = n;
1855                                 // Dont overwrite memory!!
1856                                 SDL_assert(n <= MAX_MODEL_TEXTURES);
1857                                 //mprintf(0,"  num textures = %d\n",n);
1858                                 for (i=0; i<n; i++ )    {
1859                                         char tmp_name[256];
1860                                         cfread_string_len(tmp_name,127,fp);
1861
1862                                         if ( strstr(tmp_name, "thruster") || strstr(tmp_name, "invisible") )    {
1863                                                 // Don't load textures for thruster animations or invisible textures
1864                                                 pm->textures[i] = -1;
1865                                         } else {
1866                                                 pm->textures[i] = bm_load( tmp_name );
1867                                                 if (pm->textures[i]<0)  {
1868                                                         Error( LOCATION, "Couldn't open texture '%s'\nreferenced by model '%s'\n", tmp_name, pm->filename );
1869                                                 }
1870                                         }
1871                                         pm->original_textures[i] = pm->textures[i];
1872                                         //mprintf(0,"<%s>\n",name_buf);
1873                                 }
1874
1875                                 break;
1876                         }
1877                         
1878 /*                      case ID_IDTA:           //Interpreter data
1879                                 //mprintf(0,"Got chunk IDTA, len=%d\n",len);
1880
1881                                 pm->model_data = (ubyte *)malloc(len);
1882                                 pm->model_data_size = len;
1883                                 SDL_assert(pm->model_data != NULL );
1884                         
1885                                 cfread(pm->model_data,1,len,fp);
1886                         
1887                                 break;
1888 */
1889
1890                         case ID_INFO:           // don't need to do anything with info stuff
1891
1892                                 #ifndef NDEBUG
1893                                         pm->debug_info_size = len;
1894                                         pm->debug_info = (char *)malloc(pm->debug_info_size+1);
1895                                         SDL_assert(pm->debug_info!=NULL);
1896                                         memset(pm->debug_info,0,len+1);
1897                                         cfread( pm->debug_info, 1, len, fp );
1898                                 #endif
1899                                 break;
1900
1901                         case ID_GRID:
1902                                 break;
1903
1904                         case ID_PATH:
1905                                 pm->n_paths = cfread_int( fp );
1906                                 pm->paths = (model_path *)malloc(sizeof(model_path)*pm->n_paths);
1907                                 SDL_assert( pm->paths != NULL );
1908                                         
1909                                 for (i=0; i<pm->n_paths; i++ )  {
1910                                         cfread_string_len(pm->paths[i].name , MAX_NAME_LEN-1, fp);
1911                                         if ( pm->version >= 2002 ) {
1912                                                 // store the sub_model name number of the parent
1913                                                 cfread_string_len(pm->paths[i].parent_name , MAX_NAME_LEN-1, fp);
1914                                                 // get rid of leading '$' char in name
1915                                                 if ( pm->paths[i].parent_name[0] == '$' ) {
1916                                                         char tmpbuf[MAX_NAME_LEN];
1917                                                         SDL_strlcpy(tmpbuf, pm->paths[i].parent_name+1, SDL_arraysize(tmpbuf));
1918                                                         SDL_strlcpy(pm->paths[i].parent_name, tmpbuf, MAX_NAME_LEN);
1919                                                 }
1920                                                 // store the sub_model index (ie index into pm->submodel) of the parent
1921                                                 pm->paths[i].parent_submodel = -1;
1922                                                 for ( j = 0; j < pm->n_models; j++ ) {
1923                                                         if ( !SDL_strcasecmp( pm->submodel[j].name, pm->paths[i].parent_name) ) {
1924                                                                 pm->paths[i].parent_submodel = j;
1925                                                         }
1926                                                 }
1927                                         } else {
1928                                                 pm->paths[i].parent_name[0] = 0;
1929                                                 pm->paths[i].parent_submodel = -1;
1930                                         }
1931                                         pm->paths[i].nverts = cfread_int( fp );
1932                                         pm->paths[i].verts = (mp_vert *)malloc( sizeof(mp_vert) * pm->paths[i].nverts );
1933                                         pm->paths[i].goal = pm->paths[i].nverts - 1;
1934                                         pm->paths[i].type = MP_TYPE_UNUSED;
1935                                         pm->paths[i].value = 0;
1936                                         SDL_assert(pm->paths[i].verts!=NULL);
1937                                         for (j=0; j<pm->paths[i].nverts; j++ )  {
1938                                                 cfread_vector(&pm->paths[i].verts[j].pos,fp );
1939                                                 pm->paths[i].verts[j].radius = cfread_float( fp );
1940                                                 
1941                                                 {                                       // version 1802 added turret stuff
1942                                                         int nturrets, k;
1943
1944                                                         nturrets = cfread_int( fp );
1945                                                         pm->paths[i].verts[j].nturrets = nturrets;
1946                                                         pm->paths[i].verts[j].turret_ids = (int *)malloc( sizeof(int) * nturrets );
1947                                                         for ( k = 0; k < nturrets; k++ )
1948                                                                 pm->paths[i].verts[j].turret_ids[k] = cfread_int( fp );
1949                                                 } 
1950                                                 
1951                                         }
1952                                 }
1953                                 break;
1954
1955                         case ID_EYE:                                    // an eye position(s)
1956                                 {
1957                                         int num_eyes;
1958
1959                                         // all eyes points are stored simply as vectors and their normals.
1960                                         // 0th element is used as usual player view position.
1961
1962                                         num_eyes = cfread_int( fp );
1963                                         pm->n_view_positions = num_eyes;
1964                                         SDL_assert ( num_eyes < MAX_EYES );
1965                                         for (i = 0; i < num_eyes; i++ ) {
1966                                                 pm->view_positions[i].parent = cfread_int( fp );
1967                                                 cfread_vector( &pm->view_positions[i].pnt, fp );
1968                                                 cfread_vector( &pm->view_positions[i].norm, fp );
1969                                         }
1970                                 }
1971                                 break;                  
1972
1973                         case ID_INSG:                           
1974                                 int num_ins, num_verts, num_faces, idx, idx2, idx3;                     
1975                                 
1976                                 // get the # of insignias
1977                                 num_ins = cfread_int(fp);
1978                                 pm->num_ins = num_ins;
1979                                 
1980                                 // read in the insignias
1981                                 for(idx=0; idx<num_ins; idx++){
1982                                         // get the detail level
1983                                         pm->ins[idx].detail_level = cfread_int(fp);
1984
1985                                         // # of faces
1986                                         num_faces = cfread_int(fp);
1987                                         pm->ins[idx].num_faces = num_faces;
1988                                         SDL_assert(num_faces <= MAX_INS_FACES);
1989
1990                                         // # of vertices
1991                                         num_verts = cfread_int(fp);
1992                                         SDL_assert(num_verts <= MAX_INS_VECS);
1993
1994                                         // read in all the vertices
1995                                         for(idx2=0; idx2<num_verts; idx2++){
1996                                                 cfread_vector(&pm->ins[idx].vecs[idx2], fp);
1997                                         }
1998
1999                                         // read in world offset
2000                                         cfread_vector(&pm->ins[idx].offset, fp);
2001
2002                                         // read in all the faces
2003                                         for(idx2=0; idx2<pm->ins[idx].num_faces; idx2++){                                               
2004                                                 // read in 3 vertices
2005                                                 for(idx3=0; idx3<3; idx3++){
2006                                                         pm->ins[idx].faces[idx2][idx3] = cfread_int(fp);
2007                                                         pm->ins[idx].u[idx2][idx3] = cfread_float(fp);
2008                                                         pm->ins[idx].v[idx2][idx3] = cfread_float(fp);
2009                                                 }
2010                                         }
2011                                 }                                       
2012                                 break;
2013
2014                         // autocentering info
2015                         case ID_ACEN:
2016                                 cfread_vector(&pm->autocenter, fp);
2017                                 pm->flags |= PM_FLAG_AUTOCEN;
2018                                 break;
2019
2020                         default:
2021                                 mprintf(("Unknown chunk <%c%c%c%c>, len = %d\n",id,id>>8,id>>16,id>>24,len));
2022                                 cfseek(fp,len,SEEK_CUR);
2023                                 break;
2024
2025                 }
2026                 cfseek(fp,next_chunk,SEEK_SET);
2027
2028                 id = cfread_int(fp);
2029                 len = cfread_int(fp);
2030                 next_chunk = cftell(fp) + len;
2031
2032         }
2033
2034 #ifndef NDEBUG
2035         if ( ss_fp) {
2036                 int size;
2037                 
2038                 cfclose(ss_fp);
2039                 ss_fp = cfopen(debug_name, "rb");
2040                 if ( ss_fp )    {
2041                         size = cfilelength(ss_fp);
2042                         cfclose(ss_fp);
2043                         if ( size <= 0 )        {
2044                                 cf_delete(debug_name, CF_TYPE_TABLES);
2045                         }
2046                 }
2047         }
2048 #endif
2049
2050         cfclose(fp);
2051         // mprintf(("Done processing chunks\n"));
2052         return 1;
2053 }
2054
2055
2056
2057 //returns the number of this model
2058 int model_load(const char *filename, int n_subsystems, model_subsystem *subsystems)
2059 {
2060         int i, num, arc_idx;
2061
2062         if ( !model_initted )
2063                 model_init();
2064
2065 //      int Model_ram = 0;
2066
2067 #ifndef NDEBUG
2068         int ram_before = TotalRam;
2069 #endif
2070
2071         //SDL_assert(strlen(filename) <= 12);
2072
2073         num = -1;
2074
2075         for (i=0; i< MAX_POLYGON_MODELS; i++)   {
2076                 if ( Polygon_models[i] )        {
2077                         if (!SDL_strcasecmp(filename, Polygon_models[i]->filename))             {
2078                                 // Model already loaded; just return.
2079                                 return Polygon_models[i]->id;
2080                         }
2081                 } else if ( num == -1 ) {
2082                         // This is the first empty slot
2083                         num = i;
2084                 }
2085         }
2086
2087         // No empty slot
2088         if ( num == -1 )        {
2089                 Error( LOCATION, "Too many models" );
2090                 return -1;
2091         }       
2092
2093         mprintf(( "Loading model '%s'\n", filename ));
2094
2095         polymodel * pm = (polymodel *)malloc( sizeof(polymodel) );
2096         
2097         Polygon_models[num] = pm;
2098         
2099         memset(pm, 0, sizeof(polymodel));
2100
2101         pm->n_paths = 0;
2102         pm->paths = NULL;
2103
2104         Model_signature+=MAX_POLYGON_MODELS;
2105         if ( Model_signature < MAX_POLYGON_MODELS )     {
2106                 Model_signature = 0;
2107         }
2108         SDL_assert( (Model_signature % MAX_POLYGON_MODELS) == 0 );
2109         pm->id = Model_signature + num;
2110
2111         if (!read_model_file(pm, filename, n_subsystems, subsystems))   {
2112                 return -1;
2113         }
2114
2115 //mprintf(( "Loading model '%s'\n", filename ));
2116 //key_getch();
2117
2118 //=============================
2119 // Find the destroyed replacement models
2120
2121         // Set up the default values
2122         for (i=0; i<pm->n_models; i++ ) {
2123                 pm->submodel[i].my_replacement = -1;    // assume nothing replaces this
2124                 pm->submodel[i].i_replace = -1;         // assume this doesn't replaces anything
2125         }
2126
2127         // Search for models that have destroyed versions
2128         for (i=0; i<pm->n_models; i++ ) {
2129                 int j;
2130                 char destroyed_name[128];
2131
2132                 SDL_strlcpy( destroyed_name, pm->submodel[i].name, SDL_arraysize(destroyed_name) );
2133                 SDL_strlcat( destroyed_name, "-destroyed", SDL_arraysize(destroyed_name) );
2134                 for (j=0; j<pm->n_models; j++ ) {
2135                         if ( !SDL_strcasecmp( pm->submodel[j].name, destroyed_name ))   {
2136                                 // mprintf(( "Found destroyed model for '%s'\n", pm->submodel[i].name ));
2137                                 pm->submodel[i].my_replacement = j;
2138                                 pm->submodel[j].i_replace = i;
2139                         }
2140                 }
2141
2142                 // Search for models with live debris
2143                 // This debris comes from a destroyed subsystem when ship is still alive
2144                 char live_debris_name[128];
2145
2146                 SDL_strlcpy( live_debris_name, "debris-", SDL_arraysize(live_debris_name) );
2147                 SDL_strlcat( live_debris_name, pm->submodel[i].name, SDL_arraysize(live_debris_name) );
2148
2149
2150                 pm->submodel[i].num_live_debris = 0;
2151                 for (j=0; j<pm->n_models; j++ ) {
2152                         // check if current model name is substring of destroyed
2153                         if ( strstr( pm->submodel[j].name, live_debris_name ))  {
2154                                 mprintf(( "Found live debris model for '%s'\n", pm->submodel[i].name ));
2155                                 SDL_assert(pm->submodel[i].num_live_debris < MAX_LIVE_DEBRIS);
2156                                 pm->submodel[i].live_debris[pm->submodel[i].num_live_debris++] = j;
2157                                 pm->submodel[j].is_live_debris = 1;
2158                         }
2159                 }
2160
2161         }
2162
2163         create_family_tree(pm);
2164 //      dump_object_tree(pm);
2165
2166 //==============================
2167 // Find all the lower detail versions of the hires model
2168         for (i=0; i<pm->n_models; i++ ) {
2169                 int j, l1;
2170                 bsp_info * sm1 = &pm->submodel[i];
2171
2172                 // set all arc types to be default              
2173                 for(arc_idx=0; arc_idx < MAX_ARC_EFFECTS; arc_idx++){
2174                         sm1->arc_type[arc_idx] = MARC_TYPE_NORMAL;
2175                 }
2176
2177                 sm1->num_details = 0;
2178                 l1 = strlen(sm1->name);
2179
2180                 for (j=0; j<pm->num_debris_objects;j++ )        {
2181                         if ( i == pm->debris_objects[j] )       {
2182                                 sm1->is_damaged = 1;
2183                         } 
2184                 }
2185
2186
2187                 for (j=0; j<MAX_MODEL_DETAIL_LEVELS; j++ )      {
2188                         sm1->details[j] = -1;
2189                 }
2190
2191                 for (j=0; j<pm->n_models; j++ ) {
2192                         int k;
2193                         bsp_info * sm2 = &pm->submodel[j];
2194
2195                         if ( i==j ) continue;
2196                         
2197                         // set all arc types to be default              
2198                         for(arc_idx=0; arc_idx < MAX_ARC_EFFECTS; arc_idx++){
2199                                 sm2->arc_type[arc_idx] = MARC_TYPE_NORMAL;
2200                         }
2201
2202                         // if sm2 is a detail of sm1 and sm1 is a high detail, then add it to sm1's list
2203                         if ((int)strlen(sm2->name)!=l1) continue;
2204         
2205                         int ndiff = 0;
2206                         int first_diff = 0;
2207                         for ( k=0; k<l1; k++)   {
2208                                 if (sm1->name[k] != sm2->name[k] )      {
2209                                         if (ndiff==0) first_diff = k;
2210                                         ndiff++;
2211                                 }
2212                         }
2213                         if (ndiff==1)   {               // They only differ by one character!
2214                                 int dl1, dl2;
2215                                 dl1 = tolower(sm1->name[first_diff]) - 'a';
2216                                 dl2 = tolower(sm2->name[first_diff]) - 'a';
2217
2218                                 if ( (dl1<0) || (dl2<0) || (dl1>=MAX_MODEL_DETAIL_LEVELS) || (dl2>=MAX_MODEL_DETAIL_LEVELS) ) continue; // invalid detail levels
2219
2220                                 if ( dl1 == 0 ) {
2221                                         dl2--;  // Start from 1 up...
2222                                         if (dl2 >= sm1->num_details ) sm1->num_details = dl2+1;
2223                                         sm1->details[dl2] = j;
2224                                         //mprintf(( "Submodel '%s' is detail level %d of '%s'\n", sm2->name, dl2, sm1->name ));
2225                                 }
2226                         }
2227                 }
2228
2229                 for (j=0; j<sm1->num_details; j++ )     {
2230                         if ( sm1->details[j] == -1 )    {
2231                                 Warning( LOCATION, "Model '%s' could not find all detail levels for submodel '%s'", pm->filename, sm1->name );
2232                                 sm1->num_details = 0;
2233                         }
2234                 }
2235
2236         }
2237
2238
2239         model_octant_create( pm );
2240
2241         // Find the core_radius... the minimum of 
2242         float rx, ry, rz;
2243         rx = fl_abs( pm->submodel[pm->detail[0]].max.xyz.x - pm->submodel[pm->detail[0]].min.xyz.x );
2244         ry = fl_abs( pm->submodel[pm->detail[0]].max.xyz.y - pm->submodel[pm->detail[0]].min.xyz.y );
2245         rz = fl_abs( pm->submodel[pm->detail[0]].max.xyz.z - pm->submodel[pm->detail[0]].min.xyz.z );
2246
2247         pm->core_radius = SDL_min( rx, SDL_min(ry, rz) ) / 2.0f;
2248
2249         for (i=0; i<pm->n_view_positions; i++ ) {
2250                 if ( pm->view_positions[i].parent == pm->detail[0] )    {
2251                         float d = vm_vec_mag( &pm->view_positions[i].pnt );
2252
2253                         d += 0.1f;              // Make the eye 1/10th of a meter inside the sphere.
2254
2255                         if ( d > pm->core_radius )      {
2256                                 //mprintf(( "Model %s core radius increased from %.1f to %.1f to fit eye\n", pm->filename, pm->core_radius, d ));
2257                                 pm->core_radius = d;
2258                         }               
2259                 }
2260         }
2261
2262 #ifndef NDEBUG
2263         int ram_after = TotalRam;
2264
2265         pm->ram_used = ram_after - ram_before;
2266         Model_ram += pm->ram_used;
2267         //mprintf(( "Model RAM = %d KB\n", Model_ram ));
2268 #endif
2269
2270         return pm->id;
2271 }
2272
2273 // Get "parent" submodel for live debris submodel
2274 int model_get_parent_submodel_for_live_debris( int model_num, int live_debris_model_num )
2275 {
2276         polymodel *pm = model_get(model_num);
2277
2278         SDL_assert(pm->submodel[live_debris_model_num].is_live_debris == 1);
2279
2280         int mn;
2281         bsp_info *child;
2282
2283         // Start with the high level of detail hull 
2284         // Check all its children until we find the submodel to which the live debris belongs
2285         child = &pm->submodel[pm->detail[0]];
2286         mn = child->first_child;
2287
2288         while (mn > 0) {
2289                 child = &pm->submodel[mn];
2290
2291                 if (child->num_live_debris > 0) {
2292                         // check all live debris submodels for the current child
2293                         for (int idx=0; idx<child->num_live_debris; idx++) {
2294                                 if (child->live_debris[idx] == live_debris_model_num) {
2295                                         return mn;
2296                                 }
2297                         }
2298                         // DKA 5/26/99: can multiple live debris subsystems with each ship
2299                         // NO LONGER TRUE Can only be 1 submodel with live debris
2300                         // Error( LOCATION, "Could not find parent submodel for live debris.  Possible model error");
2301                 }
2302
2303                 // get next child
2304                 mn = child->next_sibling;
2305         }
2306         Error( LOCATION, "Could not find parent submodel for live debris");
2307         return -1;
2308 }
2309
2310
2311 float model_get_radius( int modelnum )
2312 {
2313         polymodel *pm;
2314
2315         pm = model_get(modelnum);
2316
2317         return pm->rad;
2318 }
2319
2320 float model_get_core_radius( int modelnum )
2321 {
2322         polymodel *pm;
2323
2324         pm = model_get(modelnum);
2325
2326         return pm->core_radius;
2327 }
2328
2329 float submodel_get_radius( int modelnum, int submodelnum )
2330 {
2331         polymodel *pm;
2332
2333         pm = model_get(modelnum);
2334
2335         return pm->submodel[submodelnum].rad;
2336 }
2337
2338
2339
2340 polymodel * model_get(int model_num)
2341 {
2342         SDL_assert( model_num > -1 );
2343
2344         int num = model_num % MAX_POLYGON_MODELS;
2345         
2346         SDL_assert( num > -1 );
2347         SDL_assert( num < MAX_POLYGON_MODELS );
2348         SDL_assert( Polygon_models[num]->id == model_num );
2349
2350         return Polygon_models[num];
2351 }
2352
2353
2354 /*
2355 // Finds the 3d bounding box of a model.  If submodel_num is -1,
2356 // then it starts from the root object.   If inc_children is non-zero, 
2357 // then this will recurse and find the bounding box for all children
2358 // also.
2359 void model_find_bound_box_3d(int model_num,int submodel_num, int inc_children, matrix *orient, vector * pos, vector * box )
2360 {
2361         polymodel * pm;
2362         vector to_root_xlat;
2363         matrix to_root_rotate;
2364         int n_steps, steps[16];
2365         int tmp_sobj;
2366         
2367         if ( (model_num < 0) || (model_num >= N_polygon_models) ) return;
2368
2369         pm = &Polygon_models[model_num];
2370
2371         if ( submodel_num < 0 ) submodel_num = pm->detail[0];
2372
2373         // traverse up the model tree to a root object.
2374         // Store this path in n_steps,
2375         n_steps = 0;
2376         tmp_sobj = submodel_num;
2377         while( tmp_sobj > -1 )  {
2378                 steps[n_steps++] = tmp_sobj;
2379                 tmp_sobj = pm->submodel[tmp_sobj].parent;
2380         }
2381         
2382         
2383
2384 //      vm_copy_transpose_matrix(&to_world_rotate, orient );
2385 //      to_world_xlat = *pos;
2386
2387 }
2388 */
2389
2390
2391
2392 // Returns zero is x1,y1,x2,y2 are valid
2393 // returns 1 for invalid model, 2 for point offscreen.
2394 // note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates!
2395 int model_find_2d_bound_min(int model_num,matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 )
2396 {
2397         polymodel * po;
2398         int n_valid_pts;
2399         int i, x,y,min_x, min_y, max_x, max_y;
2400         int rval = 0;
2401
2402         po = model_get(model_num);
2403
2404         g3_start_instance_matrix(pos,orient);
2405         
2406         n_valid_pts = 0;
2407
2408         int hull = po->detail[0];
2409
2410         min_x = min_y = max_x = max_y = 0;
2411
2412         for (i=0; i<8; i++ )    {
2413                 vertex pt;
2414                 ubyte flags;
2415
2416                 flags = g3_rotate_vertex(&pt,&po->submodel[hull].bounding_box[i]);
2417                 if ( !(flags&CC_BEHIND) ) {
2418                         g3_project_vertex(&pt);
2419
2420                         if (!(pt.flags & PF_OVERFLOW)) {
2421                                 x = fl2i(pt.sx);
2422                                 y = fl2i(pt.sy);
2423                                 if ( n_valid_pts == 0 ) {
2424                                         min_x = x;
2425                                         min_y = y;
2426                                         max_x = x;
2427                                         max_y = y;
2428                                 } else {
2429                                         if ( x < min_x ) min_x = x;
2430                                         if ( y < min_y ) min_y = y;
2431
2432                                         if ( x > max_x ) max_x = x;
2433                                         if ( y > max_y ) max_y = y;
2434                                 }
2435                                 n_valid_pts++;
2436                         }
2437                 }
2438         }
2439
2440         if ( n_valid_pts < 8 )  {
2441                 rval = 2;
2442         }
2443
2444         if (x1) *x1 = min_x;
2445         if (y1) *y1 = min_y;
2446
2447         if (x2) *x2 = max_x;
2448         if (y2) *y2 = max_y;
2449
2450         g3_done_instance();
2451
2452         return rval;
2453 }
2454
2455
2456 // Returns zero is x1,y1,x2,y2 are valid
2457 // returns 1 for invalid model, 2 for point offscreen.
2458 // note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates!
2459 int submodel_find_2d_bound_min(int model_num,int submodel, matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 )
2460 {
2461         polymodel * po;
2462         int n_valid_pts;
2463         int i, x,y,min_x, min_y, max_x, max_y;
2464         bsp_info * sm;
2465
2466         po = model_get(model_num);
2467         if ( (submodel < 0) || (submodel >= po->n_models ) ) return 1;
2468         sm = &po->submodel[submodel];
2469         
2470         g3_start_instance_matrix(pos,orient);
2471         
2472         n_valid_pts = 0;
2473
2474         min_x = min_y = max_x = max_y = 0;
2475
2476         for (i=0; i<8; i++ )    {
2477                 vertex pt;
2478                 ubyte flags;
2479
2480                 flags = g3_rotate_vertex(&pt,&sm->bounding_box[i]);
2481                 if ( !(flags&CC_BEHIND) ) {
2482                         g3_project_vertex(&pt);
2483
2484                         if (!(pt.flags & PF_OVERFLOW)) {
2485                                 x = fl2i(pt.sx);
2486                                 y = fl2i(pt.sy);
2487                                 if ( n_valid_pts == 0 ) {
2488                                         min_x = x;
2489                                         min_y = y;
2490                                         max_x = x;
2491                                         max_y = y;
2492                                 } else {
2493                                         if ( x < min_x ) min_x = x;
2494                                         if ( y < min_y ) min_y = y;
2495
2496                                         if ( x > max_x ) max_x = x;
2497                                         if ( y > max_y ) max_y = y;
2498                                 }
2499                                 n_valid_pts++;
2500                         }
2501                 }
2502         }
2503
2504         if ( n_valid_pts == 0 ) {
2505                 return 2;
2506         }
2507
2508         if (x1) *x1 = min_x;
2509         if (y1) *y1 = min_y;
2510
2511         if (x2) *x2 = max_x;
2512         if (y2) *y2 = max_y;
2513
2514         g3_done_instance();
2515
2516         return 0;
2517 }
2518
2519
2520 // Returns zero is x1,y1,x2,y2 are valid
2521 // returns 1 for invalid model, 2 for point offscreen.
2522 // note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates!
2523 int model_find_2d_bound(int model_num,matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 )
2524 {
2525         float t,w,h;
2526         vertex pnt;
2527         polymodel * po;
2528
2529         po = model_get(model_num);
2530         float width = po->rad;
2531         float height = po->rad;
2532
2533         g3_rotate_vertex(&pnt,pos);
2534
2535         if ( pnt.flags & CC_BEHIND ) 
2536                 return 2;
2537
2538         if (!(pnt.flags&PF_PROJECTED))
2539                 g3_project_vertex(&pnt);
2540
2541         if (pnt.flags & PF_OVERFLOW)
2542                 return 2;
2543
2544         t = (width * Canv_w2)/pnt.z;
2545         w = t*Matrix_scale.xyz.x;
2546
2547         t = (height*Canv_h2)/pnt.z;
2548         h = t*Matrix_scale.xyz.y;
2549
2550         if (x1) *x1 = fl2i(pnt.sx - w);
2551         if (y1) *y1 = fl2i(pnt.sy - h);
2552
2553         if (x2) *x2 = fl2i(pnt.sx + w);
2554         if (y2) *y2 = fl2i(pnt.sy + h);
2555
2556         return 0;
2557 }
2558
2559 // Returns zero is x1,y1,x2,y2 are valid
2560 // returns 2 for point offscreen.
2561 // note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates!
2562 int subobj_find_2d_bound(float radius ,matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 )
2563 {
2564         float t,w,h;
2565         vertex pnt;
2566
2567         float width = radius;
2568         float height = radius;
2569
2570         g3_rotate_vertex(&pnt,pos);
2571
2572         if ( pnt.flags & CC_BEHIND ) 
2573                 return 2;
2574
2575         if (!(pnt.flags&PF_PROJECTED))
2576                 g3_project_vertex(&pnt);
2577
2578         if (pnt.flags & PF_OVERFLOW)
2579                 return 2;
2580
2581         t = (width * Canv_w2)/pnt.z;
2582         w = t*Matrix_scale.xyz.x;
2583
2584         t = (height*Canv_h2)/pnt.z;
2585         h = t*Matrix_scale.xyz.y;
2586
2587         if (x1) *x1 = fl2i(pnt.sx - w);
2588         if (y1) *y1 = fl2i(pnt.sy - h);
2589
2590         if (x2) *x2 = fl2i(pnt.sx + w);
2591         if (y2) *y2 = fl2i(pnt.sy + h);
2592
2593         return 0;
2594 }
2595
2596
2597 // Given a vector that is in sub_model_num's frame of
2598 // reference, and given the object's orient and position,
2599 // return the vector in the model's frame of reference.
2600 void model_find_obj_dir(vector *w_vec, vector *m_vec, object *ship_obj, int sub_model_num)
2601 {
2602         vector tvec, vec;
2603         matrix m;
2604         int mn;
2605
2606         SDL_assert(ship_obj->type == OBJ_SHIP);
2607
2608         polymodel *pm = model_get(Ships[ship_obj->instance].modelnum);
2609         vec = *m_vec;
2610         mn = sub_model_num;
2611
2612         // instance up the tree for this point
2613         while ((mn>-1) && (pm->submodel[mn].parent > -1)) {
2614                 
2615                 vm_angles_2_matrix(&m, &pm->submodel[mn].angs);
2616                 vm_vec_unrotate(&tvec, &vec, &m);
2617                 vec = tvec;
2618
2619                 mn = pm->submodel[mn].parent;
2620         }
2621
2622         // now instance for the entire object
2623         vm_vec_unrotate(w_vec, &vec, &ship_obj->orient);
2624 }
2625
2626
2627 // Given a point (pnt) that is in sub_model_num's frame of
2628 // reference, return the point in in the object's frame of reference
2629 void model_rot_sub_into_obj(vector * outpnt, vector *mpnt,polymodel *pm, int sub_model_num)
2630 {
2631         vector pnt;
2632         vector tpnt;
2633         matrix m;
2634         int mn;
2635
2636         pnt = *mpnt;
2637         mn = sub_model_num;
2638
2639         //instance up the tree for this point
2640         while ((mn>-1) && (pm->submodel[mn].parent > -1) ) {
2641                 
2642                 vm_angles_2_matrix(&m,&pm->submodel[mn].angs );
2643                 vm_transpose_matrix(&m);
2644                 vm_vec_rotate(&tpnt,&pnt,&m);
2645
2646                 vm_vec_add(&pnt,&tpnt,&pm->submodel[mn].offset );
2647
2648                 mn = pm->submodel[mn].parent;
2649         }
2650
2651         //now instance for the entire object
2652         *outpnt = pnt;
2653 }
2654
2655
2656 // Given a rotating submodel, find the ship and world axes or rotatation.
2657 void model_get_rotating_submodel_axis(vector *model_axis, vector *world_axis, int modelnum, int submodel_num, object *obj)
2658 {
2659         polymodel *pm = model_get(modelnum);
2660
2661         bsp_info *sm = &pm->submodel[submodel_num];
2662         SDL_assert(sm->movement_type == MOVEMENT_TYPE_ROT);
2663
2664         if (sm->movement_axis == MOVEMENT_AXIS_X) {
2665                 (void) vm_vec_make(model_axis, 1.0f, 0.0f, 0.0f);
2666         } else if (sm->movement_axis == MOVEMENT_AXIS_Y) {
2667                 (void) vm_vec_make(model_axis, 0.0f, 1.0f, 0.0f);
2668         } else {
2669                 SDL_assert(sm->movement_axis == MOVEMENT_AXIS_Z);
2670                 (void) vm_vec_make(model_axis, 0.0f, 0.0f, 1.0f);
2671         }
2672
2673         model_find_obj_dir(world_axis, model_axis, obj, submodel_num);
2674 }
2675
2676
2677 // Does stepped rotation of a submodel
2678 void submodel_stepped_rotate(model_subsystem *psub, submodel_instance_info *sii)
2679 {
2680         SDL_assert(psub->flags & MSS_FLAG_STEPPED_ROTATE);
2681
2682         if ( psub->subobj_num < 0 ) return;
2683
2684         polymodel *pm = model_get(psub->model_num);
2685         bsp_info *sm = &pm->submodel[psub->subobj_num];
2686
2687         if ( sm->movement_type != MOVEMENT_TYPE_ROT ) return;
2688
2689         // get active rotation time this frame
2690         int end_stamp = timestamp();
2691         float rotation_time = 0.001f * (end_stamp - sii->step_zero_timestamp);
2692         SDL_assert(rotation_time >= 0);
2693
2694         // save last angles
2695         sii->prev_angs = sii->angs;
2696
2697         // float pointer into struct to get angle (either p,b,h)
2698         float *ang_next = NULL;
2699         switch( sm->movement_axis ) {
2700         case MOVEMENT_AXIS_X:
2701                 ang_next = &sii->angs.p;
2702                 break;
2703
2704         case MOVEMENT_AXIS_Y:   
2705                 ang_next = &sii->angs.h;
2706                 break;
2707
2708         case MOVEMENT_AXIS_Z:   
2709                 ang_next = &sii->angs.b;
2710                 break;
2711         }
2712
2713         if (ang_next == NULL) {
2714                 Int3();
2715                 return;
2716         }
2717
2718         // angular displacement of one step
2719         float step_size = (PI2 / psub->stepped_rotation->num_steps);
2720
2721         // get time to complete one step, including pause
2722         float step_time = psub->stepped_rotation->t_transit + psub->stepped_rotation->t_pause;
2723
2724         // cur_step is step number relative to zero (0 - num_steps)
2725         // step_offset_time is TIME into current step
2726         float step_offset_time = (float)fmod(rotation_time, step_time);
2727         // subtract off fractional step part, round up  (ie, 1.999999 -> 2)
2728         int cur_step = int( ((rotation_time - step_offset_time) / step_time) + 0.5f);
2729         // mprintf(("cur step %d\n", cur_step));
2730         // SDL_assert(step_offset_time >= 0);
2731
2732         if (cur_step >= psub->stepped_rotation->num_steps) {
2733                 // I don;t know why, but removing this line makes it all good.
2734                 // sii->step_zero_timestamp += int(1000.0f * (psub->stepped_rotation->num_steps * step_time) + 0.5f);
2735
2736                 // reset cur_step (use mod to handle physics/ai pause)
2737                 cur_step = cur_step % psub->stepped_rotation->num_steps;
2738         }
2739
2740         // get base angle
2741         *ang_next = cur_step * step_size;
2742
2743         // determine which phase of rotation we're in
2744         float coast_start_time = psub->stepped_rotation->fraction * psub->stepped_rotation->t_transit;
2745         float decel_start_time = psub->stepped_rotation->t_transit * (1.0f - psub->stepped_rotation->fraction);
2746         float pause_start_time = psub->stepped_rotation->t_transit;
2747
2748         float start_coast_angle = 0.5f * psub->stepped_rotation->max_turn_accel * coast_start_time * coast_start_time;
2749
2750         if (step_offset_time < coast_start_time) {
2751                 // do accel
2752                 float accel_time = step_offset_time;
2753                 *ang_next += 0.5f * psub->stepped_rotation->max_turn_accel * accel_time * accel_time;
2754                 sii->cur_turn_rate = psub->stepped_rotation->max_turn_accel * accel_time;
2755         } else if (step_offset_time < decel_start_time) {
2756                 // do coast
2757                 float coast_time = step_offset_time - coast_start_time;
2758                 *ang_next += start_coast_angle + psub->stepped_rotation->max_turn_rate * coast_time;
2759                 sii->cur_turn_rate = psub->stepped_rotation->max_turn_rate;
2760         } else if (step_offset_time < pause_start_time) {
2761                 // do decel
2762                 float time_to_pause = psub->stepped_rotation->t_transit - step_offset_time;
2763                 *ang_next += (step_size - 0.5f * psub->stepped_rotation->max_turn_accel * time_to_pause * time_to_pause);
2764                 sii->cur_turn_rate = psub->stepped_rotation->max_turn_rate * time_to_pause;
2765         } else {
2766                 // do pause
2767                 *ang_next += step_size;
2768                 sii->cur_turn_rate = 0.0f;
2769         }
2770 }
2771
2772 // Rotates the angle of a submodel.  Use this so the right unlocked axis
2773 // gets stuffed.
2774 void submodel_rotate(model_subsystem *psub, submodel_instance_info *sii)
2775 {
2776         bsp_info * sm;
2777
2778         if ( psub->subobj_num < 0 ) return;
2779
2780         polymodel *pm = model_get(psub->model_num);
2781         sm = &pm->submodel[psub->subobj_num];
2782
2783         if ( sm->movement_type != MOVEMENT_TYPE_ROT ) return;
2784
2785         // save last angles
2786         sii->prev_angs = sii->angs;
2787
2788         // probably send in a calculated desired turn rate
2789         float diff = sii->desired_turn_rate - sii->cur_turn_rate;
2790
2791         float final_turn_rate;
2792         if (diff > 0) {
2793                 final_turn_rate = sii->cur_turn_rate + sii->turn_accel * flFrametime;
2794                 if (final_turn_rate > sii->desired_turn_rate) {
2795                         final_turn_rate = sii->desired_turn_rate;
2796                 }
2797         } else if (diff < 0) {
2798                 final_turn_rate = sii->cur_turn_rate - sii->turn_accel * flFrametime;
2799                 if (final_turn_rate < sii->desired_turn_rate) {
2800                         final_turn_rate = sii->desired_turn_rate;
2801                 }
2802         } else {
2803                 final_turn_rate = sii->desired_turn_rate;
2804         }
2805
2806         float delta = (sii->cur_turn_rate + final_turn_rate) * 0.5f * flFrametime;
2807         sii->cur_turn_rate = final_turn_rate;
2808
2809
2810         //float delta = psub->turn_rate * flFrametime;
2811
2812         switch( sm->movement_axis )     {
2813         case MOVEMENT_AXIS_X:   
2814                 sii->angs.p += delta;
2815                 if (sii->angs.p > PI2 )
2816                         sii->angs.p -= PI2;
2817                 else if (sii->angs.p < 0.0f )
2818                         sii->angs.p += PI2;
2819                 break;
2820         case MOVEMENT_AXIS_Y:   
2821                 sii->angs.h += delta;
2822                 if (sii->angs.h > PI2 )
2823                         sii->angs.h -= PI2;
2824                 else if (sii->angs.h < 0.0f )
2825                         sii->angs.h += PI2;
2826                 break;
2827         case MOVEMENT_AXIS_Z:   
2828                 sii->angs.b += delta;
2829                 if (sii->angs.b > PI2 )
2830                         sii->angs.b -= PI2;
2831                 else if (sii->angs.b < 0.0f )
2832                         sii->angs.b += PI2;
2833                 break;
2834         }
2835 }
2836
2837
2838 //=========================================================================
2839 // Make a turret's correct orientation matrix.   This should be done when 
2840 // the model is read, but I wasn't sure at what point all the data that I
2841 // needed was read, so I just check a flag and call this routine when
2842 // I determine I need the correct matrix.   In this code, you can't use
2843 // vm_vec_2_matrix or anything, since these turrets could be either 
2844 // right handed or left handed.
2845 void model_make_turrent_matrix(int model_num, model_subsystem * turret )
2846 {
2847         polymodel * pm;
2848         vector fvec, uvec, rvec;
2849
2850         pm = model_get(model_num);
2851         bsp_info * sm = &pm->submodel[turret->turret_gun_sobj];
2852         bsp_info * sm_parent = &pm->submodel[turret->subobj_num];
2853
2854
2855         model_clear_instance(model_num);
2856         model_find_world_dir(&fvec, &turret->turret_norm, model_num, turret->turret_gun_sobj, &vmd_identity_matrix, NULL );
2857
2858         sm_parent->angs.h = -PI/2.0f;
2859         sm->angs.p = -PI/2.0f;
2860         model_find_world_dir(&rvec, &turret->turret_norm, model_num, turret->turret_gun_sobj, &vmd_identity_matrix, NULL );
2861
2862         sm_parent->angs.h = 0.0f;
2863         sm->angs.p = -PI/2.0f;
2864         model_find_world_dir(&uvec, &turret->turret_norm, model_num, turret->turret_gun_sobj, &vmd_identity_matrix, NULL );
2865                                                                         
2866         vm_vec_normalize(&fvec);
2867         vm_vec_normalize(&rvec);
2868         vm_vec_normalize(&uvec);
2869
2870         turret->turret_matrix.v.fvec = fvec;
2871         turret->turret_matrix.v.rvec = rvec;
2872         turret->turret_matrix.v.uvec = uvec;
2873
2874 //      vm_vector_2_matrix(&turret->turret_matrix,&turret->turret_norm,NULL,NULL);
2875
2876         // HACK!! WARNING!!!
2877         // I'm doing nothing to verify that this matrix is orthogonal!!
2878         // In other words, there's no guarantee that the vectors are 90 degrees
2879         // from each other.
2880         // I'm not doing this because I don't know how to do it without ruining
2881         // the handedness of the matrix... however, I'm not too worried about   
2882         // this because I am creating these 3 vectors by making them 90 degrees
2883         // apart, so this should be close enough.  I think this will start 
2884         // causing weird errors when we view from turrets. -John
2885         turret->flags |= MSS_FLAG_TURRET_MATRIX;
2886 }
2887
2888 // Tries to move joints so that the turrent points to the point dst.
2889 // turret1 is the angles of the turret, turret2 is the angles of the gun from turret
2890 //      Returns 1 if rotated gun, 0 if no gun to rotate (rotation handled by AI)
2891 int model_rotate_gun(int model_num, model_subsystem *turret, matrix *orient, angles *turret1, angles *turret2, vector *pos, vector *dst)
2892 {
2893         polymodel * pm;
2894
2895         pm = model_get(model_num);
2896         bsp_info * sm = &pm->submodel[turret->turret_gun_sobj];
2897         bsp_info * sm_parent = &pm->submodel[turret->subobj_num];
2898
2899         // Check for a valid turret
2900         SDL_assert( turret->turret_num_firing_points > 0 );
2901
2902
2903         if ( sm_parent == sm ) {
2904                 return 0;
2905         }
2906
2907         // Build the correct turret matrix if there isn't already one
2908         if ( !(turret->flags & MSS_FLAG_TURRET_MATRIX) )
2909                 model_make_turrent_matrix(model_num, turret );
2910
2911         SDL_assert( turret->flags & MSS_FLAG_TURRET_MATRIX);
2912 //      SDL_assert( sm->movement_axis == MOVEMENT_AXIS_X );                             // Gun must be able to change pitch
2913 //      SDL_assert( sm_parent->movement_axis == MOVEMENT_AXIS_Z );      // Parent must be able to change heading
2914
2915 //======================================================
2916 // DEBUG code to draw the normal out of this gun and a circle
2917 // at the gun point.
2918 #if 0
2919         {
2920                 vector tmp;
2921                 vector tmp1;
2922                 vertex dpnt1, dpnt2;
2923
2924                 model_clear_instance(model_num);
2925                 sm->angs.p = turret2->p;
2926                 sm_parent->angs.h = turret1->h;
2927
2928                 model_find_world_point(&tmp, &vmd_zero_vector, model_num, turret->turret_gun_sobj, orient, pos );
2929                 gr_set_color(255,0,0);
2930                 g3_rotate_vertex( &dpnt1, &tmp );
2931
2932                 gr_set_color(255,0,0);
2933                 g3_draw_sphere(&dpnt1,1.0f);
2934
2935                 vm_vec_copy_scale( &tmp1, &turret->turret_matrix.v.fvec, 10.0f );
2936                 model_find_world_point(&tmp, &tmp1, model_num, turret->turret_gun_sobj, orient, pos );
2937                 g3_rotate_vertex( &dpnt2, &tmp );
2938
2939                 gr_set_color(0,255,0);
2940                 g3_draw_line(&dpnt1,&dpnt2);
2941                 gr_set_color(0,128,0);
2942                 g3_draw_sphere(&dpnt2,0.2f);
2943
2944                 vm_vec_copy_scale( &tmp1, &turret->turret_matrix.v.rvec, 10.0f );
2945                 model_find_world_point(&tmp, &tmp1, model_num, turret->turret_gun_sobj, orient, pos );
2946                 g3_rotate_vertex( &dpnt2, &tmp );
2947
2948                 gr_set_color(0,0,255);
2949                 g3_draw_line(&dpnt1,&dpnt2);
2950
2951                 vm_vec_copy_scale( &tmp1, &turret->turret_matrix.v.uvec, 10.0f );
2952                 model_find_world_point(&tmp, &tmp1, model_num, turret->turret_gun_sobj, orient, pos );
2953                 g3_rotate_vertex( &dpnt2, &tmp );
2954
2955                 gr_set_color(255,0,0);
2956                 g3_draw_line(&dpnt1,&dpnt2);
2957         }
2958 #endif
2959
2960         //------------  
2961         // rotate the dest point into the turret gun normal's frame of
2962         // reference, but not using the turret's angles.
2963         // Call this vector of_dst
2964         vector of_dst;                                                  
2965         matrix world_to_turret_matrix;          // converts world coordinates to turret's FOR
2966         vector world_to_turret_translate;       // converts world coordinates to turret's FOR
2967         vector tempv;
2968
2969         vm_vec_unrotate( &tempv, &sm_parent->offset, orient);
2970         vm_vec_add( &world_to_turret_translate, pos, &tempv );
2971
2972         vm_matrix_x_matrix( &world_to_turret_matrix, orient, &turret->turret_matrix );
2973
2974         vm_vec_sub( &tempv, dst, &world_to_turret_translate );
2975         vm_vec_rotate( &of_dst, &tempv, &world_to_turret_matrix );
2976
2977         vm_vec_normalize(&of_dst);      
2978
2979         //------------  
2980         // Find the heading and pitch that the gun needs to turn to
2981         // by extracting them from the of_dst vector.
2982         // Call this the desired_angles
2983         angles desired_angles;
2984
2985         desired_angles.p = (float)acos(of_dst.xyz.z);
2986         desired_angles.h = PI - atan2_safe(of_dst.xyz.x, of_dst.xyz.y);
2987         desired_angles.b = 0.0f;
2988
2989         //      mprintf(( "Z = %.1f, atan= %.1f\n", of_dst.xyz.z, desired_angles.p ));
2990
2991         //------------  
2992         // Gradually turn the turret towards the desired angles
2993         float step_size = turret->turret_turning_rate * flFrametime;
2994
2995         vm_interp_angle(&turret1->h,desired_angles.h,step_size);
2996         vm_interp_angle(&turret2->p,desired_angles.p,step_size);
2997
2998 //      turret1->h -= step_size*(key_down_timef(SDLK_1)-key_down_timef(SDLK_2) );
2999 //      turret2->p += step_size*(key_down_timef(SDLK_3)-key_down_timef(SDLK_4) );
3000
3001         return 1;
3002
3003 }
3004
3005
3006 // Given a point (pnt) that is in sub_model_num's frame of
3007 // reference, and given the object's orient and position, 
3008 // return the point in 3-space in outpnt.
3009 void model_find_world_point(vector * outpnt, vector *mpnt,int model_num,int sub_model_num, matrix * objorient, vector * objpos )
3010 {
3011         vector pnt;
3012         vector tpnt;
3013         matrix m;
3014         int mn;
3015         polymodel *pm = model_get(model_num);
3016
3017         SDL_assert(mpnt != NULL);
3018
3019         pnt = *mpnt;
3020         mn = sub_model_num;
3021
3022         //instance up the tree for this point
3023         while ((mn>-1) && (pm->submodel[mn].parent > -1) ) {
3024                 
3025                 vm_angles_2_matrix(&m,&pm->submodel[mn].angs );
3026                 vm_vec_unrotate(&tpnt,&pnt,&m);
3027
3028                 vm_vec_add(&pnt,&tpnt,&pm->submodel[mn].offset );
3029
3030                 mn = pm->submodel[mn].parent;
3031         }
3032
3033         //now instance for the entire object
3034         vm_vec_unrotate(outpnt,&pnt,objorient);
3035         vm_vec_add2(outpnt,objpos);
3036 }
3037
3038 // Given a point in the world RF, find the corresponding point in the model RF.
3039 // This is special purpose code, specific for model collision.
3040 // NOTE - this code ASSUMES submodel is 1 level down from hull (detail[0])
3041 //
3042 // out - point in model RF
3043 // world_pt - point in world RF
3044 // pm - polygon model
3045 // submodel_num - submodel in whose RF we're trying to find the corresponding world point
3046 // orient - orient matrix of ship
3047 // pos - pos vector of ship
3048 void world_find_model_point(vector *out, vector *world_pt, polymodel *pm, int submodel_num, matrix *orient, vector *pos)
3049 {
3050         SDL_assert( (pm->submodel[submodel_num].parent == pm->detail[0]) || (pm->submodel[submodel_num].parent == -1) );
3051
3052         vector tempv1, tempv2;
3053         matrix m;
3054
3055         // get into ship RF
3056         vm_vec_sub(&tempv1, world_pt, pos);
3057         vm_vec_rotate(&tempv2, &tempv1, orient);
3058
3059         if (pm->submodel[submodel_num].parent == -1) {
3060                 *out  = tempv2;
3061                 return;
3062         }
3063
3064         // put into submodel RF
3065         vm_vec_sub2(&tempv2, &pm->submodel[submodel_num].offset);
3066         vm_angles_2_matrix(&m, &pm->submodel[submodel_num].angs);
3067         vm_vec_rotate(out, &tempv2, &m);
3068 }
3069
3070 // Verify rotating submodel has corresponding ship subsystem -- info in which to store rotation angle
3071 int rotating_submodel_has_ship_subsys(int submodel, ship *shipp)
3072 {
3073         model_subsystem *psub;
3074         ship_subsys                     *pss;
3075
3076         int found = 0;
3077
3078         // Go through all subsystems and look for submodel
3079         // the subsystems that need it.
3080         for ( pss = GET_FIRST(&shipp->subsys_list); pss != END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
3081                 psub = pss->system_info;
3082                 if (psub->subobj_num == submodel) {
3083                         found = 1;
3084                         break;
3085                 }
3086         }
3087         
3088         return found;
3089 }
3090
3091 void model_get_rotating_submodel_list(int *submodel_list, int *num_rotating_submodels, object *objp)
3092 {
3093         SDL_assert(objp->type == OBJ_SHIP);
3094
3095         // Check if not currently rotating - then treat as part of superstructure.
3096         int modelnum = Ships[objp->instance].modelnum;
3097         polymodel *pm = model_get(modelnum);
3098         bsp_info *child_submodel;
3099
3100         *num_rotating_submodels = 0;
3101         child_submodel = &pm->submodel[pm->detail[0]];
3102
3103         int i = child_submodel->first_child;
3104         while ( i>-1 )  {
3105                 child_submodel = &pm->submodel[i];
3106
3107                 // Don't check it or its children if it is destroyed or it is a replacement (non-moving)
3108                 if ( !child_submodel->blown_off && (child_submodel->i_replace == -1) )  {
3109
3110                         // Only look for submodels that rotate
3111                         if (child_submodel->movement_type == MOVEMENT_TYPE_ROT) {
3112
3113                                 // find ship subsys and check submodel rotation is less than max allowed.
3114                                 ship *pship = &Ships[objp->instance];
3115                                 ship_subsys *subsys;
3116
3117                                 for ( subsys = GET_FIRST(&pship->subsys_list); subsys !=END_OF_LIST(&pship->subsys_list); subsys = GET_NEXT(subsys) ) {
3118                                         SDL_assert(subsys->system_info->model_num == modelnum);
3119                                         if (i == subsys->system_info->subobj_num) {
3120                                                 // found the correct subsystem - now check delta rotation angle not too large
3121                                                 float delta_angle = get_submodel_delta_angle(&subsys->submodel_info_1);
3122                                                 if (delta_angle < MAX_SUBMODEL_COLLISION_ROT_ANGLE) {
3123                                                         SDL_assert(*num_rotating_submodels < MAX_ROTATING_SUBMODELS-1);
3124                                                         submodel_list[(*num_rotating_submodels)++] = i;
3125                                                 }
3126                                                 break;
3127                                         }
3128                                 }
3129                         }
3130                 }
3131                 i = child_submodel->next_sibling;
3132         }
3133
3134         // error checking
3135 //#define MODEL_CHECK
3136 #ifdef MODEL_CHECK
3137         ship *pship = &Ships[objp->instance];
3138         for (int idx=0; idx<*num_rotating_submodels; idx++) {
3139                 int valid = rotating_submodel_has_ship_subsys(submodel_list[idx], pship);
3140 //              SDL_assert( valid );
3141                 if ( !valid ) {
3142
3143                         Warning( LOCATION, "Ship %s has rotating submodel [%s] without ship subsystem\n", pship->ship_name, pm->submodel[submodel_list[idx]].name );
3144                         pm->submodel[submodel_list[idx]].movement_type &= ~MOVEMENT_TYPE_ROT;
3145                         *num_rotating_submodels = 0;
3146                 }
3147         }
3148 #endif
3149
3150 }
3151
3152
3153 // Given a direction (pnt) that is in sub_model_num's frame of
3154 // reference, and given the object's orient and position, 
3155 // return the point in 3-space in outpnt.
3156 void model_find_world_dir(vector * out_dir, vector *in_dir,int model_num, int sub_model_num, matrix * objorient, vector * objpos )
3157 {
3158         vector pnt;
3159         vector tpnt;
3160         matrix m;
3161         int mn;
3162         polymodel *pm = model_get(model_num);
3163
3164         pnt = *in_dir;
3165         mn = sub_model_num;
3166
3167         //instance up the tree for this point
3168         while ((mn>-1) && (pm->submodel[mn].parent > -1) ) {
3169                 
3170                 vm_angles_2_matrix(&m,&pm->submodel[mn].angs );
3171                 vm_vec_unrotate(&tpnt,&pnt,&m);
3172                 pnt = tpnt;
3173
3174                 mn = pm->submodel[mn].parent;
3175         }
3176
3177         //now instance for the entire object
3178         vm_vec_unrotate(out_dir,&pnt,objorient);
3179 }
3180
3181
3182
3183 // Clears all the submodel instances stored in a model to their defaults.
3184 void model_clear_instance(int model_num)
3185 {
3186         polymodel * pm;
3187         int i;
3188
3189         pm = model_get(model_num);
3190
3191         // reset textures to original ones
3192         for (i=0; i<pm->n_textures; i++ )       {
3193                 pm->textures[i] = pm->original_textures[i];
3194         }
3195         
3196         for (i=0; i<pm->n_models; i++ ) {
3197                 bsp_info *sm = &pm->submodel[i];
3198                 
3199                 if ( pm->submodel[i].is_damaged )       {
3200                         sm->blown_off = 1;
3201                 } else {
3202                         sm->blown_off = 0;
3203                 }
3204                 sm->angs.p = 0.0f;
3205                 sm->angs.b = 0.0f;
3206                 sm->angs.h = 0.0f;
3207
3208                 // set pointer to other ship subsystem info [turn rate, accel, moment, axis, ...]
3209                 sm->sii = NULL;
3210
3211                 sm->num_arcs = 0;               // Turn off any electric arcing effects
3212         }
3213
3214         for (i=0; i<pm->num_lights; i++ )       {
3215                 pm->lights[i].value = 0.0f;
3216         }
3217
3218         interp_clear_instance();
3219
3220 //      if ( key_pressed(SDLK_1) ) pm->lights[0].value = 1.0f/255.0f;
3221 //      if ( key_pressed(SDLK_2) ) pm->lights[1].value = 1.0f/255.0f;
3222 //      if ( key_pressed(SDLK_3) ) pm->lights[2].value = 1.0f/255.0f;
3223 //      if ( key_pressed(SDLK_4) ) pm->lights[3].value = 1.0f/255.0f;
3224 //      if ( key_pressed(SDLK_5) ) pm->lights[4].value = 1.0f/255.0f;
3225 //      if ( key_pressed(SDLK_6) ) pm->lights[5].value = 1.0f/255.0f;
3226
3227
3228 }
3229
3230 // initialization during ship set
3231 void model_clear_instance_info( submodel_instance_info * sii )
3232 {
3233         sii->blown_off = 0;
3234         sii->angs.p = 0.0f;
3235         sii->angs.b = 0.0f;
3236         sii->angs.h = 0.0f;
3237         sii->prev_angs.p = 0.0f;
3238         sii->prev_angs.b = 0.0f;
3239         sii->prev_angs.h = 0.0f;
3240
3241         sii->cur_turn_rate = 0.0f;
3242         sii->desired_turn_rate = 0.0f;
3243         sii->turn_accel = 0.0f;
3244 }
3245
3246 // initialization during ship set
3247 void model_set_instance_info(submodel_instance_info *sii, float turn_rate, float turn_accel)
3248 {
3249         sii->blown_off = 0;
3250         sii->angs.p = 0.0f;
3251         sii->angs.b = 0.0f;
3252         sii->angs.h = 0.0f;
3253         sii->prev_angs.p = 0.0f;
3254         sii->prev_angs.b = 0.0f;
3255         sii->prev_angs.h = 0.0f;
3256
3257         sii->cur_turn_rate = turn_rate * 0.0f;
3258         sii->desired_turn_rate = turn_rate;
3259         sii->turn_accel = turn_accel;
3260         sii->axis_set = 0;
3261         sii->step_zero_timestamp = timestamp();
3262 }
3263
3264
3265
3266 // Sets the submodel instance data in a submodel (for all detail levels)
3267 void model_set_instance(int model_num, int sub_model_num, submodel_instance_info * sii)
3268 {
3269         int i;
3270         polymodel * pm;
3271
3272         pm = model_get(model_num);
3273
3274         SDL_assert( sub_model_num >= 0 );
3275         SDL_assert( sub_model_num < pm->n_models );
3276
3277         if ( sub_model_num < 0 ) return;
3278         if ( sub_model_num >= pm->n_models ) return;
3279         bsp_info *sm = &pm->submodel[sub_model_num];
3280
3281         // Set the "blown out" flags    
3282         sm->blown_off = sii->blown_off;
3283
3284         if ( sm->blown_off )    {
3285                 if ( sm->my_replacement > -1 )  {
3286                         pm->submodel[sm->my_replacement].blown_off = 0;
3287                         pm->submodel[sm->my_replacement].angs = sii->angs;
3288                         pm->submodel[sm->my_replacement].sii = sii;
3289                 }
3290         } else {
3291                 if ( sm->my_replacement > -1 )  {
3292                         pm->submodel[sm->my_replacement].blown_off = 1;
3293                 }
3294         }
3295
3296         // Set the angles
3297         sm->angs = sii->angs;
3298         sm->sii = sii;
3299
3300         // For all the detail levels of this submodel, set them also.
3301         for (i=0; i<sm->num_details; i++ )      {
3302                 model_set_instance(model_num, sm->details[i], sii );
3303         }
3304 }
3305
3306
3307 // Finds a point on the rotation axis of a submodel, used in collision, generally find rotational velocity
3308 void model_init_submodel_axis_pt(submodel_instance_info *sii, int model_num, int submodel_num)
3309 {
3310         vector axis;
3311         vector *mpoint1, *mpoint2;
3312         vector p1, v1, p2, v2, int1;
3313
3314         polymodel *pm = model_get(model_num);
3315         SDL_assert(pm->submodel[submodel_num].movement_type == MOVEMENT_TYPE_ROT);
3316         SDL_assert(sii);
3317
3318         mpoint1 = NULL;
3319         mpoint2 = NULL;
3320
3321         // find 2 fixed points in submodel RF
3322         // these will be rotated to about the axis an angle of 0 and PI and we'll find the intersection of the
3323         // two lines to find a point on the axis
3324         if (pm->submodel[submodel_num].movement_axis == MOVEMENT_AXIS_X) {
3325                 axis = vmd_x_vector;
3326                 mpoint1 = &vmd_y_vector;
3327                 mpoint2 = &vmd_z_vector;
3328         } else if (pm->submodel[submodel_num].movement_axis == MOVEMENT_AXIS_Y) {
3329                 mpoint1 = &vmd_x_vector;
3330                 axis = vmd_z_vector;            // rotation about y is a change in heading (p,b,h), so we need z
3331                 mpoint2 = &vmd_z_vector;
3332         } else if (pm->submodel[submodel_num].movement_axis == MOVEMENT_AXIS_Z) {
3333                 mpoint1 = &vmd_x_vector;
3334                 mpoint2 = &vmd_y_vector;
3335                 axis = vmd_y_vector;            // rotation about z is a change in bank (p,b,h), so we need y
3336         } else {
3337                 // must be one of these axes or submodel_rot_hit is incorrectly set
3338                 Int3();
3339         }
3340
3341         // copy submodel angs
3342         angles copy_angs = pm->submodel[submodel_num].angs;
3343
3344         // find two points rotated into model RF when angs set to 0
3345         vm_vec_copy_scale((vector*)&pm->submodel[submodel_num].angs, &axis, 0.0f);
3346         model_find_world_point(&p1, mpoint1, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector);
3347         model_find_world_point(&p2, mpoint2, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector);
3348
3349         // find two points rotated into model RF when angs set to PI
3350         vm_vec_copy_scale((vector*)&pm->submodel[submodel_num].angs, &axis, PI);
3351         model_find_world_point(&v1, mpoint1, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector);
3352         model_find_world_point(&v2, mpoint2, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector);
3353
3354         // reset submodel angs
3355         pm->submodel[submodel_num].angs = copy_angs;
3356
3357         // find direction vectors of the two lines
3358         vm_vec_sub2(&v1, &p1);
3359         vm_vec_sub2(&v2, &p2);
3360
3361         // find the intersection of the two lines
3362         float s, t;
3363         fvi_two_lines_in_3space(&p1, &v1, &p2, &v2, &s, &t);
3364
3365         // find the actual intersection points
3366         vm_vec_scale_add(&int1, &p1, &v1, s);
3367
3368         // set flag to init
3369         sii->pt_on_axis = int1;
3370         sii->axis_set = 1;
3371 }
3372
3373
3374 // Adds an electrical arcing effect to a submodel
3375 void model_add_arc(int model_num, int sub_model_num, vector *v1, vector *v2, int arc_type )
3376 {
3377         polymodel * pm;
3378
3379         pm = model_get(model_num);
3380
3381         if ( sub_model_num == -1 )      {
3382                 sub_model_num = pm->detail[0];
3383         }
3384
3385         SDL_assert( sub_model_num >= 0 );
3386         SDL_assert( sub_model_num < pm->n_models );
3387
3388         if ( sub_model_num < 0 ) return;
3389         if ( sub_model_num >= pm->n_models ) return;
3390         bsp_info *sm = &pm->submodel[sub_model_num];
3391
3392         if ( sm->num_arcs < MAX_ARC_EFFECTS )   {
3393                 sm->arc_type[sm->num_arcs] = (ubyte)arc_type;
3394                 sm->arc_pts[sm->num_arcs][0] = *v1;
3395                 sm->arc_pts[sm->num_arcs][1] = *v2;
3396                 sm->num_arcs++;
3397         }
3398 }
3399
3400 // function to return an index into the docking_bays array which matches the criteria passed
3401 // to this function.  dock_type is one of the DOCK_TYPE_XXX defines in model.h
3402 int model_find_dock_index(int modelnum, int dock_type)
3403 {
3404         int i;
3405
3406         polymodel *pm;
3407
3408         pm = model_get(modelnum);
3409
3410         // no docking points -- return -1
3411         if ( pm->n_docks <= 0 )
3412                 return -1;
3413
3414         for (i = 0; i < pm->n_docks; i++ ) {
3415                 if ( dock_type & pm->docking_bays[i].type_flags )
3416                         return i;
3417         }
3418
3419         return -1;
3420 }
3421
3422 int model_get_dock_index_type(int modelnum, int index)
3423 {
3424         polymodel *pm = model_get(modelnum);                            
3425
3426         return pm->docking_bays[index].type_flags;
3427 }
3428
3429 // get all the different docking point types on a model
3430 int model_get_dock_types(int modelnum)
3431 {
3432         int i, type = 0;
3433         polymodel *pm;
3434
3435         pm = model_get(modelnum);
3436         for (i=0; i<pm->n_docks; i++)
3437                 type |= pm->docking_bays[i].type_flags;
3438
3439         return type;
3440 }
3441
3442 // function to return an index into the docking_bays array which matches the string passed
3443 // Fred uses strings to identify docking positions.  This functin also accepts generic strings
3444 // so that a desginer doesn't have to know exact names if building a mission from hand.
3445 int model_find_dock_name_index( int modelnum, char *name )
3446 {
3447         int i;
3448         polymodel *pm;
3449
3450         pm = model_get(modelnum);
3451         if ( pm->n_docks <= 0 )
3452                 return -1;
3453
3454         // check the generic names and call previous function to find first dock point of
3455         // the specified type
3456         if ( !SDL_strcasecmp(name, "cargo") )
3457                 return model_find_dock_index( modelnum, DOCK_TYPE_CARGO );
3458         else if (!SDL_strcasecmp( name, "rearm") )
3459                 return model_find_dock_index( modelnum, DOCK_TYPE_REARM );
3460         else if (!SDL_strcasecmp( name, "generic") )
3461                 return model_find_dock_index( modelnum, DOCK_TYPE_GENERIC );
3462
3463         for (i = 0; i < pm->n_docks; i++ ) {
3464                 if ( !SDL_strcasecmp(pm->docking_bays[i].name, name) )
3465                         return i;
3466         }
3467
3468         // if we get here, name wasn't found -- return -1 and hope for the best
3469         return -1;
3470 }
3471
3472 // returns the actual name of a docking point on a model, needed by Fred.
3473 char *model_get_dock_name(int modelnum, int index)
3474 {
3475         polymodel *pm;
3476
3477         pm = model_get(modelnum);
3478         SDL_assert((index >= 0) && (index < pm->n_docks));
3479         return pm->docking_bays[index].name;
3480 }
3481
3482 int model_get_num_dock_points(int modelnum)
3483 {
3484         polymodel *pm;
3485
3486         pm = model_get(modelnum);
3487         return pm->n_docks;
3488 }
3489
3490 #if SDL_BYTEORDER == SDL_BIG_ENDIAN                     // tigital
3491 void swap_bsp_defpoints(ubyte * p)
3492 {
3493         int n, i;
3494         int nverts = INTEL_INT( w(p+8) );
3495         w(p+8) = nverts;        
3496         int offset = INTEL_INT( w(p+16) );
3497         w(p+16) = offset;
3498         int n_norms = INTEL_INT( w(p+12) );
3499         w(p+12) = n_norms;
3500
3501         ubyte * normcount = p+20;
3502         vector *src = vp(p+offset);
3503
3504         SDL_assert( nverts < MAX_POLYGON_VECS );
3505         // SDL_assert( nnorms < MAX_POLYGON_NORMS );
3506
3507         for (n=0; n<nverts; n++ )       {
3508             src->xyz.x = INTEL_FLOAT( src->xyz.x );
3509             src->xyz.y = INTEL_FLOAT( src->xyz.y );
3510             src->xyz.z = INTEL_FLOAT( src->xyz.z );
3511
3512             Interp_verts[n] = src;
3513             src++;
3514             for (i=0;i<normcount[n];i++){
3515                 src->xyz.x = INTEL_FLOAT( src->xyz.x );
3516                 src->xyz.y = INTEL_FLOAT( src->xyz.y );
3517                 src->xyz.z = INTEL_FLOAT( src->xyz.z );
3518                 src++;
3519             }
3520             
3521         }
3522 }
3523 void swap_bsp_tmappoly( polymodel * pm, ubyte * p )
3524 {
3525         int i, nv;
3526         model_tmap_vert *verts;
3527         vector * normal = vp(p+8);
3528         vector * center = vp(p+20);
3529         float radius = INTEL_FLOAT( fl(p+32) );
3530         fl(p+32) = radius;
3531         normal->xyz.x = INTEL_FLOAT( normal->xyz.x );
3532         normal->xyz.y = INTEL_FLOAT( normal->xyz.y );
3533         normal->xyz.z = INTEL_FLOAT( normal->xyz.z );
3534         center->xyz.x = INTEL_FLOAT( center->xyz.x );
3535         center->xyz.y = INTEL_FLOAT( center->xyz.y );
3536         center->xyz.z = INTEL_FLOAT( center->xyz.z );
3537
3538         nv = INTEL_INT( w(p+36));
3539         w(p+36) = nv;
3540         int tmap_num = INTEL_INT( w(p+40) );
3541         w(p+40) = tmap_num;
3542         
3543         if ( nv < 0 ) return;
3544
3545         verts = (model_tmap_vert *)(p+44);
3546         for (i=0;i<nv;i++){
3547             verts[i].vertnum = INTEL_SHORT( verts[i].vertnum );
3548             verts[i].normnum = INTEL_SHORT( verts[i].normnum );
3549             verts[i].u = INTEL_FLOAT( verts[i].u );
3550             verts[i].v = INTEL_FLOAT( verts[i].v );
3551         }
3552
3553         if ( pm->version < 2003 )       {
3554                 // Set the "normal_point" part of field to be the center of the polygon
3555                 vector center_point;
3556                 vm_vec_zero( &center_point );
3557
3558                 for (i=0;i<nv;i++)      {
3559                         vm_vec_add2( &center_point, Interp_verts[verts[i].vertnum] );
3560                 }
3561
3562                 center_point.xyz.x /= nv;
3563                 center_point.xyz.y /= nv;
3564                 center_point.xyz.z /= nv;
3565
3566                 *vp(p+20) = center_point;
3567
3568                 float rad = 0.0f;
3569
3570                 for (i=0;i<nv;i++)      {
3571                         float dist = vm_vec_dist( &center_point, Interp_verts[verts[i].vertnum] );
3572                         if ( dist > rad )       {
3573                                 rad = dist;
3574                         }
3575                 }
3576                 fl(p+32) = rad;
3577         }
3578 }
3579 void swap_bsp_flatpoly( polymodel * pm, ubyte * p )
3580 {
3581         int i, nv;
3582         short *verts;
3583         vector * normal = vp(p+8);
3584         vector * center = vp(p+20);
3585         float radius = INTEL_FLOAT( fl(p+32) );
3586         fl(p+32) = radius; 
3587         mprintf(("flatpoly radius = %f\n", radius ));
3588         normal->xyz.x = INTEL_FLOAT( normal->xyz.x );
3589         normal->xyz.y = INTEL_FLOAT( normal->xyz.y );
3590         normal->xyz.z = INTEL_FLOAT( normal->xyz.z );
3591         center->xyz.x = INTEL_FLOAT( center->xyz.x );
3592         center->xyz.y = INTEL_FLOAT( center->xyz.y );
3593         center->xyz.z = INTEL_FLOAT( center->xyz.z );
3594
3595         nv = INTEL_INT( w(p+36));               //tigital
3596         w(p+36) = nv;
3597         
3598         if ( nv < 0 ) return;
3599
3600         verts = (short *)(p+44);
3601         for (i=0; i<nv*2; i++){
3602             verts[i] = INTEL_SHORT( verts[i] );
3603         }
3604
3605         if ( pm->version < 2003 )       {
3606                 // Set the "normal_point" part of field to be the center of the polygon
3607                 vector center_point;
3608                 vm_vec_zero( &center_point );
3609
3610                 for (i=0;i<nv;i++)      {
3611                         vm_vec_add2( &center_point, Interp_verts[verts[i*2]] );
3612                 }
3613
3614                 center_point.xyz.x /= nv;
3615                 center_point.xyz.y /= nv;
3616                 center_point.xyz.z /= nv;
3617
3618                 *vp(p+20) = center_point;
3619
3620                 float rad = 0.0f;
3621
3622                 for (i=0;i<nv;i++)      {
3623                         float dist = vm_vec_dist( &center_point, Interp_verts[verts[i*2]] );
3624                         if ( dist > rad )       {
3625                                 rad = dist;
3626                         }
3627                 }
3628                 fl(p+32) = rad;
3629         }
3630 }
3631 void swap_bsp_sortnorms( polymodel * pm, ubyte * p )
3632 {
3633     int frontlist = INTEL_INT( w(p+36) );
3634     int backlist = INTEL_INT( w(p+40) );
3635     int prelist = INTEL_INT( w(p+44) );
3636     int postlist = INTEL_INT( w(p+48) );
3637     int onlist = INTEL_INT( w(p+52) );
3638     w(p+36) = frontlist;
3639     w(p+40) = backlist;
3640     w(p+44) = prelist;
3641     w(p+48) = postlist;
3642     w(p+52) = onlist;
3643             
3644     vector * normal = vp(p+8);
3645     vector * center = vp(p+20);
3646     int  tmp = INTEL_INT( w(p+32) );
3647     w(p+32) = tmp;
3648     normal->xyz.x = INTEL_FLOAT( normal->xyz.x );
3649     normal->xyz.y = INTEL_FLOAT( normal->xyz.y );
3650     normal->xyz.z = INTEL_FLOAT( normal->xyz.z );
3651     center->xyz.x = INTEL_FLOAT( center->xyz.x );
3652     center->xyz.y = INTEL_FLOAT( center->xyz.y );
3653     center->xyz.z = INTEL_FLOAT( center->xyz.z );
3654     
3655     vector * bmin = vp(p+56);
3656     vector * bmax = vp(p+68);
3657     bmin->xyz.x = INTEL_FLOAT( bmin->xyz.x );
3658     bmin->xyz.y = INTEL_FLOAT( bmin->xyz.y );
3659     bmin->xyz.z = INTEL_FLOAT( bmin->xyz.z );
3660     bmax->xyz.x = INTEL_FLOAT( bmax->xyz.x );
3661     bmax->xyz.y = INTEL_FLOAT( bmax->xyz.y );
3662     bmax->xyz.z = INTEL_FLOAT( bmax->xyz.z );
3663
3664     if (prelist) swap_bsp_data(pm,p+prelist);
3665     if (backlist) swap_bsp_data(pm,p+backlist);
3666     if (onlist) swap_bsp_data(pm,p+onlist);
3667     if (frontlist) swap_bsp_data(pm,p+frontlist);
3668     if (postlist) swap_bsp_data(pm,p+postlist);
3669 }
3670 void swap_bsp_data( polymodel * pm, void *model_ptr )
3671 {
3672     ubyte *p = (ubyte *)model_ptr;
3673     int chunk_type, chunk_size;
3674     vector * min;
3675     vector * max;
3676     
3677     chunk_type = INTEL_INT( w(p) );
3678     chunk_size = INTEL_INT( w(p+4) );
3679     w(p) = chunk_type;
3680     w(p+4) = chunk_size;
3681         
3682     while (chunk_type != OP_EOF)        {
3683
3684         switch (chunk_type) {
3685             case OP_EOF:
3686                     return;
3687             case OP_DEFPOINTS:
3688                     swap_bsp_defpoints(p); 
3689                     break;
3690             case OP_FLATPOLY:
3691                     swap_bsp_flatpoly(pm, p ); break;
3692             case OP_TMAPPOLY:
3693                     swap_bsp_tmappoly(pm, p ); break;
3694             case OP_SORTNORM:
3695                     swap_bsp_sortnorms(pm,p ); break;
3696             case OP_BOUNDBOX:
3697                     min = vp(p+8);
3698                     max = vp(p+20);
3699                     min->xyz.x = INTEL_FLOAT( min->xyz.x );
3700                     min->xyz.y = INTEL_FLOAT( min->xyz.y );
3701                     min->xyz.z = INTEL_FLOAT( min->xyz.z );
3702                     max->xyz.x = INTEL_FLOAT( max->xyz.x );
3703                     max->xyz.y = INTEL_FLOAT( max->xyz.y );
3704                     max->xyz.z = INTEL_FLOAT( max->xyz.z );
3705                     break;
3706         default:
3707             mprintf(( "Bad chunk type %d, len=%d in modelread:swap_bsp_data\n", chunk_type, chunk_size ));
3708             Int3();             // Bad chunk type!
3709             return;
3710         }
3711         p += chunk_size;
3712         chunk_type = INTEL_INT( w(p));
3713         chunk_size = INTEL_INT( w(p+4) );
3714         w(p) = chunk_type;
3715         w(p+4) = chunk_size;
3716     }
3717     return;
3718 }
3719 #endif // SDL_BYTEORDER == SDL_BIG_ENDIAN