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