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