2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Model/ModelRead.cpp $
15 * file which reads and deciphers POF information
18 * Revision 1.13 2006/04/26 19:44:19 taylor
19 * better error handing of a Volition bug
21 * Revision 1.12 2004/09/20 01:31:44 theoddone33
24 * Revision 1.11 2004/07/04 11:39:06 taylor
25 * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
27 * Revision 1.10 2004/06/11 21:39:44 taylor
28 * x86 compile fixes for OSX patch
30 * Revision 1.9 2004/06/10 23:55:39 tigital
31 * byte-swapping changes for bigendian systems
33 * Revision 1.8 2003/06/11 18:30:33 taylor
36 * Revision 1.7 2003/01/30 19:43:57 relnev
37 * added a missing "not" in the model warning
39 * Revision 1.6 2002/06/17 06:33:09 relnev
40 * ryan's struct patch for gcc 2.95
42 * Revision 1.5 2002/06/09 04:41:23 relnev
43 * added copyright header
45 * Revision 1.4 2002/06/09 03:16:04 relnev
48 * removed unneeded asm, old sdl 2d setup.
50 * fixed crash caused by opengl_get_region.
52 * Revision 1.3 2002/06/01 07:12:33 relnev
53 * a few NDEBUG updates.
55 * removed a few warnings.
57 * Revision 1.2 2002/05/07 03:16:47 theoddone33
58 * The Great Newline Fix
60 * Revision 1.1.1.1 2002/05/03 03:28:10 root
64 * 36 7/22/99 2:26p Mattk
65 * DA: don't show engine wash error if no subsystems. (ie, Pofview...)
67 * 35 7/19/99 12:02p Andsager
68 * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
69 * only blow up subsystem if its strength is > 0
71 * 34 7/07/99 5:00p Andsager
72 * Fixed bug in stepped rotation
74 * 33 7/06/99 4:42p Anoop
75 * Add MiniCap hack for engine wash
77 * 32 7/06/99 3:52p Andsager
78 * That wacky model_subsystem name vs. subobj_name thing bites us in the
81 * 31 7/06/99 3:15p Anoop
82 * Better debug info for engine wash error.
84 * 30 7/06/99 11:37a Andsager
85 * Fix warning message.
87 * 29 7/06/99 10:45a Andsager
88 * Modify engine wash to work on any ship that is not small. Add AWACS
91 * 28 7/01/99 4:23p Dave
92 * Full support for multiple linked ambient engine sounds. Added "big
95 * 27 6/08/99 2:33p Dave
96 * Fixed release build warning. Put in hud config stuff.
98 * 26 6/01/99 8:35p Dave
99 * Finished lockarm weapons. Added proper supercap weapons/damage. Added
100 * awacs-set-radius sexpression.
102 * 25 5/28/99 1:51p Andsager
103 * Allow models to rotate in both direction on an axis, specifying a
104 * negative rotation time in submodel properties.
106 * 24 5/27/99 3:48p Andsager
107 * Add assert to model_read code to not allow overwrite of memory
109 * 23 5/27/99 12:11p Andsager
110 * Allow more than 1 subsystem to have live debris on any ship
112 * 22 5/27/99 10:57a Mattk
113 * Put in a warning about too many firing points per turret.
115 * 21 5/24/99 5:45p Dave
116 * Added detail levels to the nebula, with a decent speedup. Split nebula
117 * lightning into its own section.
119 * 20 5/19/99 11:09a Andsager
120 * Turn on engine wash. Check every 1/4 sec.
122 * 19 5/11/99 10:16p Andsager
123 * First pass on engine wash effect. Rotation (control input), damage,
126 * 18 4/09/99 11:38a Andsager
127 * Change POF format for Freespace2
129 * 17 3/23/99 5:17p Dave
130 * Changed model file format somewhat to account for LOD's on insignias
132 * 16 3/20/99 3:46p Dave
133 * Added support for model-based background nebulae. Added 3 new
136 * 15 3/19/99 6:15p Dave
137 * Put in checks for insignia bitmaps for having too many faces or
140 * 14 2/21/99 6:01p Dave
141 * Fixed standalone WSS packets.
143 * 13 2/19/99 11:42a Dave
144 * Put in model rendering autocentering.
146 * 12 2/10/99 3:52p Enricco
149 * 11 1/14/99 6:06p Dave
150 * 100% full squad logo support for single player and multiplayer.
152 * 10 1/11/99 12:42p Andsager
153 * Add live debris - debris which is created from a destroyed subsystem,
154 * when the ship is still alive
156 * 9 1/08/99 2:08p Dave
157 * Fixed software rendering for pofview. Super early support for AWACS and
160 * 8 1/06/99 2:24p Dave
161 * Stubs and release build fixes.
163 * 7 12/23/98 2:53p Andsager
164 * Added stepped rotation support
166 * 6 12/04/98 3:34p Andsager
167 * Handle norotating submodels
169 * 5 12/03/98 3:14p Andsager
170 * Check in code that checks rotating submodel actually has ship subsystem
172 * 4 11/19/98 11:07p Andsager
173 * Check in of physics and collision detection of rotating submodels
175 * 3 10/23/98 3:03p Andsager
176 * Initial support for changing rotation rate.
178 * 2 10/07/98 10:53a Dave
181 * 1 10/07/98 10:50a Dave
183 * 177 8/28/98 3:29p Dave
184 * EMP effect done. AI effects may need some tweaking as required.
186 * 176 5/19/98 8:31p Andsager
187 * Added split planes (for big ship explosions)
189 * 175 5/07/98 5:39p Andsager
190 * Changes to model to hold cross section info
192 * 174 4/30/98 4:53p John
193 * Restructured and cleaned up cfile code. Added capability to read off
194 * of CD-ROM drive and out of multiple pack files.
196 * 173 4/22/98 9:43p John
197 * Added code to allow checking of invisible faces, flagged by any texture
198 * name with invisible in it.
200 * 172 4/14/98 11:11p John
201 * Made ships with < 50% hull left show electrical damage arcs.
203 * 171 4/01/98 5:34p John
204 * Made only the used POFs page in for a level. Reduced some interp
205 * arrays. Made custom detail level work differently.
207 * 170 4/01/98 9:14a Mike
210 * 169 3/31/98 5:18p John
211 * Removed demo/save/restore. Made NDEBUG defined compile. Removed a
212 * bunch of debug stuff out of player file. Made model code be able to
213 * unload models and malloc out only however many models are needed.
216 * 168 3/17/98 2:46p Allender
217 * Copy subsystem name when copying model subsystems
219 * 167 3/02/98 5:42p John
220 * Removed WinAVI stuff from Freespace. Made all HUD gauges wriggle from
221 * afterburner. Made gr_set_clip work good with negative x &y. Made
222 * model_caching be on by default. Made each cached model have it's own
223 * bitmap id. Made asteroids not rotate when model_caching is on.
225 * 166 2/24/98 5:04p Allender
226 * allow different ship classes to use the same model. Lot's of subsystem
229 * 165 2/16/98 3:46p John
230 * Made mass be proportional to area in newer pofs
232 * 164 2/13/98 5:19p John
233 * First rev of new model caching system. Needs to be ripped out and
234 * moved into ship stuff.
236 * 163 1/29/98 5:52p John
237 * new version of electrical arcing code
239 * 162 1/29/98 8:39a Andsager
240 * Changed mass and moment of intertia based area vs. volume
242 * 161 1/28/98 2:15p Mike
243 * Convert volume to surface area at runtime. Write note to John to
244 * cleanup if he modifies bspgen.
246 * 160 1/27/98 11:02a John
247 * Added first rev of sparks. Made all code that calls model_render call
248 * clear_instance first. Made debris pieces not render by default when
249 * clear_instance is called.
251 * 159 1/19/98 6:15p John
252 * Fixed all my Optimized Build compiler warnings
254 * 158 1/09/98 11:25a John
255 * Took the thruster animation stuff out of the model.
257 * 157 12/31/97 4:47p John
258 * Made all the eye points fit inside core radius
260 * 156 12/31/97 2:35p John
261 * Added code for core_radius
263 * 155 12/17/97 2:41p John
264 * made target bounding box be on hull only.
266 * 154 12/17/97 9:54a John
267 * added code for precalculated weapon flash lighting.
269 * 153 11/29/97 2:05p John
270 * made g3_draw_bitmap and g3_draw_rotated bitmap take w&h, not w/2 & h/2,
271 * like they used to incorrectly assume. Added code to model to read in
274 * 152 11/25/97 11:41a John
275 * added code to read in pof v20.05, which adds radius to thrusters.
277 * 151 10/28/97 12:34a Mike
278 * Set useful values of mass and inertia matrix if values not otherwise
279 * set. Generate Warnings for unexpected ones.
281 * 150 10/22/97 5:54p Lawrance
282 * get rid of leading '$' when storing parent_name for paths
284 * 149 10/20/97 5:57p Lawrance
285 * have index to polymodel->paths[] within the model_subsystem struct.
287 * 148 9/15/97 5:45p John
288 * took out chunk stuff.
289 * made pofview display thrusters as blue polies.
291 * 147 9/09/97 3:39p Sandeep
292 * warning level 4 bugs
294 * 146 9/04/97 5:09p Andsager
295 * implement physics using moment of inertia and mass (from BSPgen).
296 * Added to phys_info struct. Updated ship_info, polymodel structs.
297 * Updated weapon ($Mass and $Force) and ship ($Mass -> $Density) tables
299 * 145 9/03/97 5:57p Lawrance
302 * 144 9/03/97 4:32p John
303 * changed bmpman to only accept ani and pcx's. made passing .pcx or .ani
304 * to bm_load functions not needed. Made bmpman keep track of palettes
305 * for bitmaps not mapped into game palettes.
307 * 143 8/19/97 11:49p Lawrance
308 * using atan2_safe() in place of atan2()
310 * 142 8/19/97 8:44a John
311 * Initial hooks for thruster stuff.
313 * 141 8/15/97 4:10p John
314 * new code to use the new octrees style bsp trees
316 * 140 8/14/97 5:46p John
317 * added code to read in mass,center of mass, and moment of interia from
318 * the pof. Also added code to display green center of mass dot when Show
319 * debug points is on in pofview.
321 * 139 7/10/97 8:34a John
322 * Added code to read TGA files.
324 * 138 6/26/97 3:18p Mike
325 * Recognize "turret", etc. in a subsystem field anywhere in name.
326 * Generate warning for bogus subsystem names.
328 * 137 6/25/97 5:11p John
329 * added foundation for model octants.
331 * 136 6/25/97 10:28a John
332 * Made debris chunks radius be set correctly to the submodel size, not
333 * hardcoded to 10.0f.
335 * 135 6/06/97 11:55a Mike
336 * Fix firing direction for zero piece turrets.
338 * 134 5/31/97 2:36p Mike
339 * Comment out mprintfs pertaining to debris.
341 * 133 5/30/97 4:41p Hoffoss
342 * Made some additions required for Fred.
344 * 132 5/30/97 3:42p Mike
345 * Shield mesh and hit system.
347 * 131 5/30/97 10:40a Hoffoss
348 * Added a function for Fred to get docking point names.
350 * 130 5/29/97 4:34p Allender
351 * removed Int3 from model_find_dock_index.
353 * 129 4/30/97 10:41a Allender
354 * don't dump out token 'none' when dumping out subsystem information
356 * 128 4/17/97 6:06p John
357 * New code/data for v19 of BSPGEN with smoothing and zbuffer
360 * 127 4/09/97 3:31p Lawrance
361 * if any points of bounding box don't project, return project fail code
362 * in model_find_2d_bound_min()
364 * 126 4/07/97 10:37a John
365 * Took out limits on number of children a submodel can have. (So now
366 * capital ships can have tons of children on them).
368 * 125 4/03/97 3:18p Allender
370 * 124 4/03/97 1:29p Allender
371 * subsystem enhancement. Initial work on making subsystem status
374 * 123 3/28/97 2:49p John
375 * added code to fix targetting of debris chunks to draw bounding boxes
378 * 122 3/28/97 1:19p John
379 * Made TestCode not show damaged submodels.
381 * 121 3/28/97 11:17a John
382 * added code for linking lower detail submodels to the highest one; added
383 * code to pofview to view these.
385 * 120 3/17/97 11:24a John
387 * 119 3/15/97 5:06p John
388 * added bounding boxes for each subobject and code to display them.
390 * 118 3/13/97 10:32a John
391 * Added code for tiled 256x256 textures in certain models.
393 * 117 3/06/97 5:36p Mike
394 * Change vec_normalize_safe() back to vec_normalize().
395 * Spruce up docking a bit.
397 * 116 3/06/97 1:06p Allender
398 * more docking stuff. docking points now specified by strings in mission
399 * file. Changes to builtin missions and code to reflect this change.
401 * 115 3/06/97 10:56a Mike
402 * Write error checking version of vm_vec_normalize().
403 * Fix resultant problems.
405 * 114 3/06/97 10:05a Lawrance
406 * added missing cfclose's
408 * 113 3/05/97 5:27p John
409 * more work with turret stuff
411 * 112 3/05/97 12:49p John
412 * added Viewer_obj. Took out the interp_??? variables for turning
413 * outline,etc on and put them in flags you pass to model_render.
414 * Cleaned up model_interp code to fit new coding styles.
416 * 111 3/05/97 12:42p Adam
417 * john: fixed bug while model transversel code that assumed the root
420 * 110 3/05/97 11:09a Allender
421 * DOH! MAX_SLOTS just way too low. Upped to 25 and Asserts put in to
424 * 109 3/04/97 5:08p John
425 * Started adding debug code to make it so you can view from a turret's
428 * 108 3/04/97 3:36p John
429 * took out old debug "h' key. Made zbuffer flag bit bit field so you
430 * can turn on/off each value. Fixed a bug with turret rotation where
431 * different handedness turrets wouldn't work. Fixed a bug with two
432 * large ships within each other's radius not rendering correctly.
434 * 107 3/04/97 12:52p Allender
435 * docking styff. Added dock type to docking structure in model code.
436 * Renamed structure member in ai_goals. Temporary checkin. (i.e.
437 * rearming will use rearm dock points)
439 * 106 3/03/97 5:47p John
440 * fixed some rotating object discrepencies. Took out the gun firing
441 * point average beause it was inconsitent and wsn't as correct as using
444 * 105 3/03/97 4:20p Hoffoss
445 * Fixed bug in bug I just fixed. :)
447 * 104 3/03/97 4:00p Hoffoss
448 * fixed bug that caused docking bays without a name to be garbage when
451 * 103 3/03/97 8:58a Lawrance
452 * replaced a fprintf() with a cfputs()
454 * 102 3/01/97 2:12p Lawrance
455 * made to work with new cfile changes
457 * 101 2/28/97 10:57a John
458 * Made so you can blow off any subsystems, not just radars.
461 * 100 2/27/97 3:45p John
462 * Added a tree dialog to pofview that shows a model's tree.
464 * 99 2/27/97 12:07p John
465 * Added code to suppord debris chunks after a ship is exploded..
467 * 98 2/26/97 5:28p John
468 * Fixed a bunch of chunked model bugs. Basically they almost work better
469 * than non-chunked objects!
471 * 97 2/24/97 1:13p Allender
472 * support for multiple eye positions. Still some kinks to be worked out
475 * 96 2/20/97 4:06p Allender
476 * allow for multiple spline paths to lead to a docking bay
478 * 95 2/20/97 3:25p Allender
479 * change to docking bay structure -- added a name (for use in Fred
480 * mainly), and index into spline paths to reach this docking point
482 * 94 2/19/97 4:03p Allender
483 * dump a 0.0 instead of a 0 when dumping out missing subsystem
486 * 93 2/19/97 2:52p Allender
487 * added crewspot name to subsystems (used only with turrets). Will be
488 * used by fred for ai class stuff for turrets
490 * 92 2/17/97 4:30p John
491 * Added code to automatically rotate any subsystems with the $rotate
492 * value set (except for turrets)
494 * 91 2/17/97 12:03p John
495 * Added code to set the angles of a subobject.
497 * 90 2/14/97 4:02p John
498 * changed a vector being passed as value to reference.
500 * 89 2/14/97 3:16p Mike
501 * Start on AI path garbage collection
503 * 88 2/14/97 1:06p John
504 * upped bspgen version number.
506 * 87 2/13/97 10:17a John
507 * INCORPORATED THE FOLLOWING CHANGES:
508 * ??? 2/13/97 9:49a John
509 * added code for geometric centers of objects.
510 * ??? 2/12/97 6:30p John
511 * upped model version number.
514 * 86 2/13/97 9:49a John
515 * ROLLEDBACK TO ALLENDER'S 85
517 * 85 2/12/97 5:34p Allender
518 * added a ton of debug code to help recify the subsystems in ships.tbl
519 * with subsystems in the models. Fixed a bug with subsystems not being
520 * given the correct type.
522 * 84 2/11/97 4:10p Allender
523 * major change of subsystem stuff. obj_subsystem renamed to
524 * model_subsystem. Dyamically allocate space in ship_info for
527 * 83 2/10/97 4:20p Allender
528 * added distance to closest geometry to spline points
530 * 82 2/10/97 10:58a John
531 * Added back in sparks for where laser bolts hit.
532 * Added code to red damaged objects and replace then.
534 * 81 2/07/97 1:24p Allender
535 * added turret lists for spline points
537 * 80 2/06/97 2:56p John
538 * Added a "blown-off" field to the instance of a model. Also took out
539 * the model_set_angles functions and replaced with the more general
540 * model_set_instance functions.
542 * 79 2/04/97 7:37p John
543 * Finally!!! Turret stuff working!
545 * 78 1/31/97 12:51p John
546 * Added turret_fov and turret_fov_cos
548 * 77 1/31/97 12:43p John
549 * Turrets working nicely, finally!
551 * 76 1/31/97 11:36a John
553 * 75 1/29/97 4:46p John
554 * Code to read $crewpoint flag on turrets and store it in obj_subsystem
557 * 74 1/29/97 3:14p John
558 * Added field to ships.tbl to control how fast turret rotates.
560 * 73 1/28/97 11:23a John
561 * added functions to get/set model angles.
563 * 72 1/28/97 10:07a John
564 * More turret stuff, still not working, though.
566 * 71 1/27/97 2:46p John
569 * 70 1/27/97 11:35a Allender
570 * upped compatible object version
572 * 69 1/24/97 5:25p John
573 * More turret stuff. Not working yet, though.
575 * 68 1/24/97 4:55p John
576 * Started adding code for turning turrets.
577 * Had to change POF format to 18.0
579 * 67 1/20/97 2:16p Allender
580 * new shield structures being used
582 * 66 1/15/97 5:19p Allender
583 * new POF version. old version obsolete. Added shield and eye stuff.
584 * New format for gun/missile/docking points
586 * 65 1/10/97 5:15p Mike
587 * Moved ship-specific parameters from obj_subsystem to ship_subsys.
589 * Added turret code to AI system.
591 * 64 1/10/97 2:25p John
592 * Added code to support detail level distances.
594 * 63 1/02/97 11:45a Allender
595 * changed turret code to have > 1 firing point as well as fixing the
596 * problems of turrets being multiple subobjects. Turret norms should be
597 * getting stuffed correctly now.
599 * 62 12/31/96 4:14p Mike
601 * Correct bug in HUDtarget.cpp, showing wrong target.
603 * 61 12/27/96 4:09p Mike
604 * Integrate John's new model code.
606 * 60 12/26/96 1:42p John
607 * Changed function ordering to put model_init at top.
609 * 59 12/23/96 3:56p John
610 * Changed POF code to support pof 15.0.
612 * 58 12/23/96 11:00a John
613 * Restructured POF stuff to support LOD in one pof.
615 * 57 12/20/96 11:55a Allender
618 * 56 12/20/96 11:40a Allender
619 * modifed subsystem stuff to do correct hit percentages and other cool
622 * 55 12/18/96 4:04p Allender
623 * added "unknown" subsystem type for subsystems which aren't recognized.
625 * 54 12/17/96 11:37a Mike
628 * 53 12/17/96 8:59a Mike
629 * Path-extraction-from-mdoel test code.
631 * 52 12/13/96 5:27p John
632 * fixed my sloppy code that didn't free model data.
634 * 51 12/13/96 3:54p Mike
635 * Major improvement to model_draw_paths -- connect the dots! Uses
636 * sophisticated connect-to-next algorithm.
638 * 50 12/13/96 2:59p John
639 * Added some code to test spline paths.
641 * 49 12/13/96 2:41p John
642 * Added code to read in spline paths on models.
644 * 48 12/13/96 10:29a Adam
645 * from allender: fixed up linked list walking when checking for turrets
648 * 47 12/12/96 3:38p Allender
649 * fix up docking special object names. Added $thruster stuff to model
652 * 46 12/12/96 2:35p Mike
655 * 45 12/12/96 12:29p Lawrance
656 * added function to find the 2d bound for a subobject
658 * 44 12/11/96 5:35p John
659 * Added variables for viewer eye.
661 * 43 12/11/96 5:15p John
662 * Made the dist for detail level be calculated before the object is
665 * 42 12/11/96 4:50p John
666 * Added detail levels to the non-chunky ships.
668 * 41 12/11/96 4:33p Mike
669 * Temporary version so other people can build.
671 * 40 12/11/96 4:24p Mike
672 * Fix broken code...oops! clumsy in editor!
674 * 39 12/11/96 4:17p Adam
676 * 38 12/11/96 12:57p Mike
677 * Support for "Big Ship" AI behavior.
680 * 37 12/11/96 12:43p Allender
681 * $gun and $missile don't get appended to models gun/missile list if the
682 * gun/missile belongs to a turret. Turrets are special instances of
685 * 36 12/11/96 11:57a Allender
686 * added objnum reference to obj_subsystems so that $gun and $missile
687 * special polygons can refernce a particular subsystem if needed (i.e. to
688 * get the norms, etc).
690 * 35 12/11/96 10:33a Allender
691 * added stuff for FOV for turrets
693 * 34 12/10/96 3:27p Allender
695 * 33 12/09/96 5:05p Allender
696 * made subsystem lists and model_special lists as seperate entities.
698 * 32 12/09/96 1:16p Allender
699 * made seperate lists for guns/missiles/subsystems, etc
701 * 31 12/09/96 11:01a Allender
702 * added special polygon information to models. Linked list contains
703 * gun/missile/docking/subsystem/etc information.
716 #include "floating.h"
719 #include "modelsinc.h"
722 #include "3dinternal.h"
723 #include "linklist.h"
725 #include "freespace.h" // For flFrameTime
729 #define MAX_SUBMODEL_COLLISION_ROT_ANGLE (PI / 6.0f) // max 30 degrees per frame
731 // info for special polygon lists
733 polymodel *Polygon_models[MAX_POLYGON_MODELS];
735 static int model_initted = 0;
738 CFILE *ss_fp; // file pointer used to dump subsystem information
739 char model_filename[MAX_PATH_LEN]; // temp used to store filename
740 char debug_name[MAX_PATH_LEN];
741 int ss_warning_shown; // have we shown the warning dialog concerning the subsystems?
742 char Global_filename[MAX_PATH_LEN];
743 int Model_ram = 0; // How much RAM the models use total
748 // Anything less than this is considered incompatible.
749 #define PM_COMPATIBLE_VERSION 1900
751 // Anything greater than or equal to PM_COMPATIBLE_VERSION and
752 // whose major version is less than or equal to this is considered
754 #define PM_OBJFILE_MAJOR_VERSION 21
756 static int Model_signature = 0;
758 // Free up a model, getting rid of all its memory
759 // Can't be called from outside of model code because more
760 // than one person might be using this model so we can't free
762 static void model_unload(int modelnum)
766 if ( (modelnum < 0) || (modelnum>MAX_POLYGON_MODELS)) {
770 polymodel *pm = Polygon_models[modelnum];
777 Model_ram -= pm->ram_used;
781 for (i=0; i<pm->n_paths; i++ ) {
782 for (j=0; j<pm->paths[i].nverts; j++ ) {
783 if ( pm->paths[i].verts[j].turret_ids ) {
784 free(pm->paths[i].verts[j].turret_ids);
787 if (pm->paths[i].verts) {
788 free(pm->paths[i].verts);
794 if ( pm->shield.verts ) {
795 free( pm->shield.verts );
798 if ( pm->shield.tris ) {
799 free(pm->shield.tris);
802 if ( pm->missile_banks ) {
803 free(pm->missile_banks);
806 if ( pm->docking_bays ) {
807 for (i=0; i<pm->n_docks; i++ ) {
808 if ( pm->docking_bays[i].splines ) {
809 free( pm->docking_bays[i].splines );
812 free(pm->docking_bays);
815 // this is from ship.cpp but we don't get the polymodel there so free here
816 if ( pm->ship_bay != NULL ) {
821 if ( pm->thrusters ) {
826 if ( pm->debug_info ) {
827 free(pm->debug_info);
831 model_octant_free( pm );
834 for (i=0; i<pm->n_models; i++ ) {
835 if ( pm->submodel[i].bsp_data ) {
836 free(pm->submodel[i].bsp_data);
850 if ( pm->gun_banks ) {
855 memset( pm, 0, sizeof(polymodel));
858 Polygon_models[modelnum] = NULL;
861 void model_free_all()
865 if ( !model_initted) {
870 mprintf(( "Freeing all existing models...\n" ));
872 for (i=0;i<MAX_POLYGON_MODELS;i++) {
882 if ( model_initted ) {
883 Int3(); // Model_init shouldn't be called twice!
891 for (i=0;i<MAX_POLYGON_MODELS;i++) {
892 Polygon_models[i] = NULL;
895 atexit( model_free_all );
899 // routine to parse out values from a user property field of an object
900 void get_user_prop_value(char *buf, char *value, const int max_vlen)
905 while ( isspace(*p) || (*p == '=') ) // skip white space and equal sign
908 while ( !iscntrl(*p1) )
912 SDL_strlcpy(value, p, max_vlen);
916 // funciton to copy model data from one subsystem set to another subsystem set. This function
917 // is called when two ships use the same model data, but since the model only gets read in one time,
918 // the subsystem data is only present in one location. The ship code will call this routine to fix
919 // this situation by copying stuff from the source subsystem set to the dest subsystem set.
920 void model_copy_subsystems( int n_subsystems, model_subsystem *d_sp, model_subsystem *s_sp )
923 model_subsystem *source, *dest;
925 for (i = 0; i < n_subsystems; i++ ) {
927 for ( j = 0; j < n_subsystems; j++ ) {
929 if ( !SDL_strcasecmp( source->subobj_name, dest->subobj_name) ) {
930 dest->flags = source->flags;
931 dest->subobj_num = source->subobj_num;
932 dest->model_num = source->model_num;
933 dest->pnt = source->pnt;
934 dest->radius = source->radius;
935 dest->type = source->type;
936 dest->turn_rate = source->turn_rate;
937 dest->turret_gun_sobj = source->turret_gun_sobj;
939 SDL_strlcpy( dest->name, source->name, SDL_arraysize(dest->name) );
941 if ( dest->type == SUBSYSTEM_TURRET ) {
944 dest->turret_fov = source->turret_fov;
945 dest->turret_num_firing_points = source->turret_num_firing_points;
946 dest->turret_norm = source->turret_norm;
947 dest->turret_matrix = source->turret_matrix;
949 for (nfp = 0; nfp < dest->turret_num_firing_points; nfp++ )
950 dest->turret_firing_point[nfp] = source->turret_firing_point[nfp];
952 if ( dest->flags & MSS_FLAG_CREWPOINT )
953 SDL_strlcpy(dest->crewspot, source->crewspot, SDL_arraysize(dest->crewspot));
958 if ( j == n_subsystems )
959 Int3(); // get allender -- something is amiss with models
964 // routine to get/set subsystem information
965 static void set_subsystem_info( model_subsystem *subsystemp, char *props, char *dname )
971 if ( (p = strstr(props, "$name")) != NULL)
972 get_user_prop_value(p+5, subsystemp->name, SDL_arraysize(subsystemp->name));
974 SDL_strlcpy( subsystemp->name, dname, SDL_arraysize(subsystemp->name) );
976 SDL_strlcpy(lcdname, dname, SDL_arraysize(lcdname));
979 // check the name for it's specific type
980 if ( strstr(lcdname, "engine") ) {
981 subsystemp->type = SUBSYSTEM_ENGINE;
982 } else if ( strstr(lcdname, "radar") ) {
983 subsystemp->type = SUBSYSTEM_RADAR;
984 } else if ( strstr(lcdname, "turret") ) {
987 subsystemp->type = SUBSYSTEM_TURRET;
988 if ( (p = strstr(props, "$fov")) != NULL )
989 get_user_prop_value(p+4, buf, SDL_arraysize(buf)); // get the value of the fov
991 SDL_strlcpy(buf,"180", SDL_arraysize(buf));
992 angle = ANG_TO_RAD(atoi(buf))/2.0f;
993 subsystemp->turret_fov = (float)cos(angle);
994 subsystemp->turret_num_firing_points = 0;
996 if ( (p = strstr(props, "$crewspot")) != NULL) {
997 subsystemp->flags |= MSS_FLAG_CREWPOINT;
998 get_user_prop_value(p+9, subsystemp->crewspot, SDL_arraysize(subsystemp->crewspot));
1001 } else if ( strstr(lcdname, "navigation") ) {
1002 subsystemp->type = SUBSYSTEM_NAVIGATION;
1003 } else if ( strstr(lcdname, "communication") ) {
1004 subsystemp->type = SUBSYSTEM_COMMUNICATION;
1005 } else if ( strstr(lcdname, "weapons") ) {
1006 subsystemp->type = SUBSYSTEM_WEAPONS;
1007 } else if ( strstr(lcdname, "sensors") ) {
1008 subsystemp->type = SUBSYSTEM_SENSORS;
1009 } else if ( strstr(lcdname, "solar") ) {
1010 subsystemp->type = SUBSYSTEM_SOLAR;
1012 } else if ( strstr(lcdname, "gas") ) {
1013 subsystemp->type = SUBSYSTEM_GAS_COLLECT;
1014 } else if ( strstr(lcdname, "activator") ) {
1015 subsystemp->type = SUBSYSTEM_ACTIVATION;
1017 } else { // If unrecognized type, set to unknown so artist can continue working...
1018 subsystemp->type = SUBSYSTEM_UNKNOWN;
1019 mprintf(("Warning: Ignoring unrecognized subsystem %s, believed to be in ship %s\n", dname, Global_filename));
1022 // Rotating subsystem
1023 if ( (p = strstr(props, "$rotate")) != NULL) {
1024 subsystemp->flags |= MSS_FLAG_ROTATES;
1026 // get time for (a) complete rotation (b) step (c) activation
1028 get_user_prop_value(p+7, buf, SDL_arraysize(buf));
1029 turn_time = (float)atof(buf);
1031 // CASE OF STEPPED ROTATION
1032 if ( (p = strstr(props, "$stepped")) != NULL) {
1034 subsystemp->stepped_rotation = new(stepped_rotation);
1035 subsystemp->flags |= MSS_FLAG_STEPPED_ROTATE;
1037 // get number of steps
1038 if ( (p = strstr(props, "$steps")) != NULL) {
1039 get_user_prop_value(p+6, buf, SDL_arraysize(buf));
1040 subsystemp->stepped_rotation->num_steps = atoi(buf);
1042 subsystemp->stepped_rotation->num_steps = 8;
1046 if ( (p = strstr(props, "$t_paused")) != NULL) {
1047 get_user_prop_value(p+9, buf, SDL_arraysize(buf));
1048 subsystemp->stepped_rotation->t_pause = (float)atof(buf);
1050 subsystemp->stepped_rotation->t_pause = 2.0f;
1053 // get transition time - time to go between steps
1054 if ( (p = strstr(props, "$t_transit")) != NULL) {
1055 get_user_prop_value(p+10, buf, SDL_arraysize(buf));
1056 subsystemp->stepped_rotation->t_transit = (float)atof(buf);
1058 subsystemp->stepped_rotation->t_transit = 2.0f;
1061 // get fraction of time spent in accel
1062 if ( (p = strstr(props, "$fraction_accel")) != NULL) {
1063 get_user_prop_value(p+15, buf, SDL_arraysize(buf));
1064 subsystemp->stepped_rotation->fraction = (float)atof(buf);
1065 SDL_assert(subsystemp->stepped_rotation->fraction > 0 && subsystemp->stepped_rotation->fraction < 0.5);
1067 subsystemp->stepped_rotation->fraction = 0.3f;
1070 int num_steps = subsystemp->stepped_rotation->num_steps;
1071 float t_trans = subsystemp->stepped_rotation->t_transit;
1072 float fraction = subsystemp->stepped_rotation->fraction;
1074 subsystemp->stepped_rotation->max_turn_accel = PI2 / (fraction*(1.0f - fraction) * num_steps * t_trans*t_trans);
1075 subsystemp->stepped_rotation->max_turn_rate = PI2 / ((1.0f - fraction) * num_steps *t_trans);
1079 // CASE OF AI ROTATION
1080 else if ( (p = strstr(props, "$ai")) != NULL) {
1081 get_user_prop_value(p+8, buf, SDL_arraysize(buf));
1082 subsystemp->flags |= MSS_FLAG_AI_ROTATE;
1084 // get parameters - ie, speed / dist / other ??
1089 // CASE OF NORMAL CONTINUOUS ROTATION
1091 if ( fabs(turn_time) < 1 ) {
1092 // Warning(LOCATION, "%s has subsystem %s with rotation time less than 1 sec", dname, Global_filename );
1093 subsystemp->flags &= ~MSS_FLAG_ROTATES;
1094 subsystemp->turn_rate = 0.0f;
1096 subsystemp->turn_rate = PI2 / turn_time;
1103 // used in collision code to check if submode rotates too far
1104 float get_submodel_delta_angle(submodel_instance_info *sii)
1107 vm_vec_sub(&diff, (vector*)&sii->angs, (vector*)&sii->prev_angs);
1110 float delta_angle = vm_vec_mag(&diff);
1112 // make sure we get the short way around
1113 if (delta_angle > PI) {
1114 delta_angle = (PI2 - delta_angle);
1120 void do_new_subsystem( int n_subsystems, model_subsystem *slist, int subobj_num, float rad, vector *pnt, char *props, char *subobj_name, int model_num )
1123 model_subsystem *subsystemp;
1126 if ( slist==NULL ) return; // For TestCode, POFView, etc don't bother
1128 // try to find the name of the subsystem passed here on the list of subsystems currently on the
1129 // ship. Assign the values only when the right subsystem is found
1131 for (i = 0; i < n_subsystems; i++ ) {
1132 subsystemp = &slist[i];
1133 if ( !SDL_strcasecmp(subobj_name, subsystemp->subobj_name) ) {
1134 subsystemp->flags = 0;
1135 subsystemp->subobj_num = subobj_num;
1136 subsystemp->turret_gun_sobj = -1;
1137 subsystemp->model_num = model_num;
1138 subsystemp->pnt = *pnt; // use the offset to get the center point of the subsystem
1139 subsystemp->radius = rad;
1140 set_subsystem_info( subsystemp, props, subobj_name);
1141 SDL_strlcpy(subsystemp->subobj_name, subobj_name, SDL_arraysize(subsystemp->subobj_name)); // copy the object name
1146 if ( !ss_warning_shown) {
1147 char bname[MAX_FILENAME_LEN];
1149 base_filename(model_filename, bname, SDL_arraysize(bname));
1150 Warning(LOCATION, "A subsystem was found in model %s that does not have a record in ships.tbl.\nA list of subsystems for this ship will be dumped to:\n\ndata\\tables\\%s.subsystems for inclusion\n into ships.tbl.", model_filename, bname);
1152 ss_warning_shown = 1;
1155 nprintf(("warning", "Subsystem %s in ships.tbl not found in model!\n", subobj_name));
1158 char tmp_buffer[128];
1159 SDL_snprintf(tmp_buffer, SDL_arraysize(tmp_buffer), "$Subsystem:\t\t\t%s,1,0.0\n", subobj_name);
1160 cfputs(tmp_buffer, ss_fp);
1166 void print_family_tree( polymodel *obj, int modelnum, const char * ident, int islast )
1170 if ( modelnum < 0 ) return;
1171 if (obj==NULL) return;
1173 if (strlen(ident)==0 ) {
1174 mprintf(( " %s", obj->submodel[modelnum].name ));
1175 SDL_snprintf( temp, SDL_arraysize(temp), " " );
1176 } else if ( islast ) {
1177 mprintf(( "%s��%s", ident, obj->submodel[modelnum].name ));
1178 SDL_snprintf( temp, SDL_arraysize(temp), "%s ", ident );
1180 mprintf(( "%s��%s", ident, obj->submodel[modelnum].name ));
1181 SDL_snprintf( temp, SDL_arraysize(temp), "%s� ", ident );
1186 int child = obj->submodel[modelnum].first_child;
1187 while( child > -1 ) {
1188 if ( obj->submodel[child].next_sibling < 0 )
1189 print_family_tree( obj, child, temp,1 );
1191 print_family_tree( obj, child, temp,0 );
1192 child = obj->submodel[child].next_sibling;
1196 void dump_object_tree(polymodel *obj)
1198 print_family_tree( obj, 0, "", 0 );
1202 void create_family_tree(polymodel *obj)
1205 for (i=0; i<obj->n_models; i++ ) {
1206 obj->submodel[i].num_children = 0;
1207 obj->submodel[i].first_child = -1;
1208 obj->submodel[i].next_sibling = -1;
1211 for (i=0; i<obj->n_models; i++ ) {
1213 pn = obj->submodel[i].parent;
1215 obj->submodel[pn].num_children++;
1216 int tmp = obj->submodel[pn].first_child;
1217 obj->submodel[pn].first_child = i;
1218 obj->submodel[i].next_sibling = tmp;
1223 void model_calc_bound_box( vector *box, vector *big_mn, vector *big_mx)
1225 box[0].xyz.x = big_mn->xyz.x; box[0].xyz.y = big_mn->xyz.y; box[0].xyz.z = big_mn->xyz.z;
1226 box[1].xyz.x = big_mx->xyz.x; box[1].xyz.y = big_mn->xyz.y; box[1].xyz.z = big_mn->xyz.z;
1227 box[2].xyz.x = big_mx->xyz.x; box[2].xyz.y = big_mx->xyz.y; box[2].xyz.z = big_mn->xyz.z;
1228 box[3].xyz.x = big_mn->xyz.x; box[3].xyz.y = big_mx->xyz.y; box[3].xyz.z = big_mn->xyz.z;
1231 box[4].xyz.x = big_mn->xyz.x; box[4].xyz.y = big_mn->xyz.y; box[4].xyz.z = big_mx->xyz.z;
1232 box[5].xyz.x = big_mx->xyz.x; box[5].xyz.y = big_mn->xyz.y; box[5].xyz.z = big_mx->xyz.z;
1233 box[6].xyz.x = big_mx->xyz.x; box[6].xyz.y = big_mx->xyz.y; box[6].xyz.z = big_mx->xyz.z;
1234 box[7].xyz.x = big_mn->xyz.x; box[7].xyz.y = big_mx->xyz.y; box[7].xyz.z = big_mx->xyz.z;
1238 // Debug thing so we don't repeatedly show warning messages.
1240 int Bogus_warning_flag_1903 = 0;
1243 //reads a binary file containing a 3d model
1244 int read_model_file(polymodel * pm, const char *filename, int n_subsystems, model_subsystem *subsystems)
1248 int id, len, next_chunk;
1252 SDL_strlcpy(Global_filename, filename, SDL_arraysize(Global_filename));
1255 fp = cfopen(filename,"rb");
1257 Error( LOCATION, "Can't open file <%s>",filename);
1261 // code to get a filename to write out subsystem information for each model that
1262 // is read. This info is essentially debug stuff that is used to help get models
1263 // into the game quicker
1266 char bname[MAX_PATH_LEN];
1268 base_filename(filename, bname, SDL_arraysize(bname));
1269 SDL_snprintf(debug_name, SDL_arraysize(debug_name), "%s.subsystems", bname);
1270 ss_fp = cfopen(debug_name, "wb", CFILE_NORMAL, CF_TYPE_TABLES );
1272 mprintf(( "Can't open debug file for writing subsystems for %s\n", filename));
1274 SDL_strlcpy(model_filename, filename, SDL_arraysize(model_filename));
1275 ss_warning_shown = 0;
1280 id = cfread_int(fp);
1283 Error( LOCATION, "Bad ID in model file <%s>",filename);
1285 // Version is major*100+minor
1286 // So, major = version / 100;
1287 // minor = version % 100;
1288 version = cfread_int(fp);
1290 //Warning( LOCATION, "POF Version = %d", version );
1292 if (version < PM_COMPATIBLE_VERSION || (version/100) > PM_OBJFILE_MAJOR_VERSION) {
1293 Warning(LOCATION,"Bad version (%d) in model file <%s>",version,filename);
1297 pm->version = version;
1298 SDL_assert( strlen(filename) < FILENAME_LEN );
1299 SDL_strlcpy(pm->filename, filename, FILENAME_LEN);
1301 memset( &pm->view_positions, 0, sizeof(pm->view_positions) );
1303 // reset insignia counts
1306 id = cfread_int(fp);
1307 len = cfread_int(fp);
1308 next_chunk = cftell(fp) + len;
1310 while (!cfeof(fp)) {
1312 // mprintf(("Processing chunk <%c%c%c%c>, len = %d\n",id,id>>8,id>>16,id>>24,len));
1317 case ID_OHDR: { //Object header
1320 //mprintf(0,"Got chunk OHDR, len=%d\n",len);
1322 #if defined( FREESPACE1_FORMAT )
1323 pm->n_models = cfread_int(fp);
1324 // mprintf(( "Num models = %d\n", pm->n_models ));
1325 pm->rad = cfread_float(fp);
1326 pm->flags = cfread_int(fp); // 1=Allow tiling
1327 #elif defined( FREESPACE2_FORMAT )
1328 pm->rad = cfread_float(fp);
1329 pm->flags = cfread_int(fp); // 1=Allow tiling
1330 pm->n_models = cfread_int(fp);
1331 // mprintf(( "Num models = %d\n", pm->n_models ));
1334 pm->submodel = (bsp_info *)malloc( sizeof(bsp_info)*pm->n_models );
1335 SDL_assert(pm->submodel != NULL );
1336 memset( pm->submodel, 0, sizeof(bsp_info)*pm->n_models );
1338 //SDL_assert(pm->n_models <= MAX_SUBMODELS);
1340 cfread_vector(&pm->mins,fp);
1341 cfread_vector(&pm->maxs,fp);
1342 model_calc_bound_box(pm->bounding_box,&pm->mins,&pm->maxs);
1344 pm->n_detail_levels = cfread_int(fp);
1345 // mprintf(( "There are %d detail levels\n", pm->n_detail_levels ));
1346 for (i=0; i<pm->n_detail_levels;i++ ) {
1347 pm->detail[i] = cfread_int(fp);
1348 pm->detail_depth[i] = 0.0f;
1349 /// mprintf(( "Detail level %d is model %d.\n", i, pm->detail[i] ));
1352 pm->num_debris_objects = cfread_int(fp);
1353 SDL_assert( pm->num_debris_objects <= MAX_DEBRIS_OBJECTS );
1354 // mprintf(( "There are %d debris objects\n", pm->num_debris_objects ));
1355 for (i=0; i<pm->num_debris_objects;i++ ) {
1356 pm->debris_objects[i] = cfread_int(fp);
1357 // mprintf(( "Debris object %d is model %d.\n", i, pm->debris_objects[i] ));
1360 if ( pm->version >= 1903 ) {
1362 if ( pm->version >= 2009 ) {
1364 pm->mass = cfread_float(fp);
1365 cfread_vector( &pm->center_of_mass, fp );
1366 cfread_vector( &pm->moment_of_inertia.v.rvec, fp );
1367 cfread_vector( &pm->moment_of_inertia.v.uvec, fp );
1368 cfread_vector( &pm->moment_of_inertia.v.fvec, fp );
1370 // old code where mass wasn't based on area, so do the calculation manually
1372 float vol_mass = cfread_float(fp);
1373 // Attn: John Slagel: The following is better done in bspgen.
1374 // Convert volume (cubic) to surface area (quadratic) and scale so 100 -> 100
1375 float area_mass = (float) pow(vol_mass, 0.6667f) * 4.65f;
1377 pm->mass = area_mass;
1378 float mass_ratio = vol_mass / area_mass;
1380 cfread_vector( &pm->center_of_mass, fp );
1381 cfread_vector( &pm->moment_of_inertia.v.rvec, fp );
1382 cfread_vector( &pm->moment_of_inertia.v.uvec, fp );
1383 cfread_vector( &pm->moment_of_inertia.v.fvec, fp );
1385 // John remove this with change to bspgen
1386 vm_vec_scale( &pm->moment_of_inertia.v.rvec, mass_ratio );
1387 vm_vec_scale( &pm->moment_of_inertia.v.uvec, mass_ratio );
1388 vm_vec_scale( &pm->moment_of_inertia.v.fvec, mass_ratio );
1392 if (SDL_strcasecmp("fighter04.pof", filename)) {
1393 if (Bogus_warning_flag_1903 == 0) {
1394 Warning(LOCATION, "Ship %s is old. Cannot compute mass.\nSetting to 50.0f. Talk to John.", filename);
1395 Bogus_warning_flag_1903 = 1;
1400 vm_vec_zero( &pm->center_of_mass );
1401 vm_set_identity( &pm->moment_of_inertia );
1402 vm_vec_scale(&pm->moment_of_inertia.v.rvec, 0.001f);
1403 vm_vec_scale(&pm->moment_of_inertia.v.uvec, 0.001f);
1404 vm_vec_scale(&pm->moment_of_inertia.v.fvec, 0.001f);
1407 // read in cross section info
1409 if ( pm->version >= 2014 ) {
1410 pm->num_xc = cfread_int(fp);
1411 if (pm->num_xc > 0) {
1412 pm->xc = (cross_section*) malloc(pm->num_xc*sizeof(cross_section));
1413 for (int i=0; i<pm->num_xc; i++) {
1414 pm->xc[i].z = cfread_float(fp);
1415 pm->xc[i].radius = cfread_float(fp);
1422 if ( pm->version >= 2007 ) {
1423 pm->num_lights = cfread_int(fp);
1424 //mprintf(( "Found %d lights!\n", pm->num_lights ));
1426 pm->lights = (bsp_light *)malloc( sizeof(bsp_light)*pm->num_lights );
1427 for (i=0; i<pm->num_lights; i++ ) {
1428 cfread_vector(&pm->lights[i].pos,fp);
1429 pm->lights[i].type = cfread_int(fp);
1430 pm->lights[i].value = 0.0f;
1440 case ID_SOBJ: { //Subobject header
1442 char *p, props[MAX_PROP_LEN];
1445 //mprintf(0,"Got chunk SOBJ, len=%d\n",len);
1449 SDL_assert(n < pm->n_models );
1451 #if defined( FREESPACE2_FORMAT )
1452 pm->submodel[n].rad = cfread_float(fp); //radius
1455 pm->submodel[n].parent = cfread_int(fp);
1457 // cfread_vector(&pm->submodel[n].norm,fp);
1458 // d = cfread_float(fp);
1459 // cfread_vector(&pm->submodel[n].pnt,fp);
1460 cfread_vector(&pm->submodel[n].offset,fp);
1462 // mprintf(( "Subobj %d, offs = %.1f, %.1f, %.1f\n", n, pm->submodel[n].offset.xyz.x, pm->submodel[n].offset.xyz.y, pm->submodel[n].offset.xyz.z ));
1464 #if defined ( FREESPACE1_FORMAT )
1465 pm->submodel[n].rad = cfread_float(fp); //radius
1468 // pm->submodel[n].tree_offset = cfread_int(fp); //offset
1469 // pm->submodel[n].data_offset = cfread_int(fp); //offset
1471 cfread_vector(&pm->submodel[n].geometric_center,fp);
1473 cfread_vector(&pm->submodel[n].min,fp);
1474 cfread_vector(&pm->submodel[n].max,fp);
1476 model_calc_bound_box(pm->submodel[n].bounding_box,&pm->submodel[n].min,&pm->submodel[n].max);
1478 pm->submodel[n].name[0] = '\0';
1480 cfread_string_len( pm->submodel[n].name, MAX_NAME_LEN, fp); // get the name
1481 cfread_string_len(props, MAX_PROP_LEN, fp); // and the user properites
1483 pm->submodel[n].movement_type = cfread_int(fp);
1484 pm->submodel[n].movement_axis = cfread_int(fp);
1486 // change turret movement type to MOVEMENT_TYPE_ROT_SPECIAL
1487 if (pm->submodel[n].movement_type == MOVEMENT_TYPE_ROT) {
1488 if ( strstr(pm->submodel[n].name, "turret") || strstr(pm->submodel[n].name, "gun") || strstr(pm->submodel[n].name, "cannon")) {
1489 pm->submodel[n].movement_type = MOVEMENT_TYPE_ROT_SPECIAL;
1490 } else if (strstr(pm->submodel[n].name, "thruster")) {
1492 pm->submodel[n].movement_type = MOVEMENT_TYPE_NONE;
1493 pm->submodel[n].movement_axis = MOVEMENT_AXIS_NONE;
1497 if ( pm->submodel[n].name[0] == '\0' ) {
1498 SDL_strlcpy(pm->submodel[n].name, "unknown object name", MAX_NAME_LEN);
1501 bool rotating_submodel_has_subsystem = !(pm->submodel[n].movement_type == MOVEMENT_TYPE_ROT);
1502 if ( ( p = strstr(props, "$special"))!= NULL ) {
1505 get_user_prop_value(p+9, type, SDL_arraysize(type));
1506 if ( !SDL_strcasecmp(type, "subsystem") ) { // if we have a subsystem, put it into the list!
1507 do_new_subsystem( n_subsystems, subsystems, n, pm->submodel[n].rad, &pm->submodel[n].offset, props, pm->submodel[n].name, pm->id );
1508 rotating_submodel_has_subsystem = true;
1509 } else if ( !SDL_strcasecmp(type, "no_rotate") ) {
1510 // mark those submodels which should not rotate - ie, those with no subsystem
1511 pm->submodel[n].movement_type = MOVEMENT_TYPE_NONE;
1512 pm->submodel[n].movement_axis = MOVEMENT_AXIS_NONE;
1514 // if submodel rotates (via bspgen), then there is either a subsys or special=no_rotate
1515 SDL_assert( pm->submodel[n].movement_type != MOVEMENT_TYPE_ROT );
1519 if ( !rotating_submodel_has_subsystem ) {
1520 nprintf(("Model", "Model %s: Rotating Submodel without subsystem: %s\n", pm->filename, pm->submodel[n].name));
1522 // mark those submodels which should not rotate - ie, those with no subsystem
1523 pm->submodel[n].movement_type = MOVEMENT_TYPE_NONE;
1524 pm->submodel[n].movement_axis = MOVEMENT_AXIS_NONE;
1528 pm->submodel[n].angs.p = 0.0f;
1529 pm->submodel[n].angs.b = 0.0f;
1530 pm->submodel[n].angs.h = 0.0f;
1533 int nchunks = cfread_int( fp ); // Throw away nchunks
1534 if ( nchunks > 0 ) {
1535 Error( LOCATION, "Model '%s' is chunked. See John or Adam!\n", pm->filename );
1538 pm->submodel[n].bsp_data_size = cfread_int(fp);
1539 if ( pm->submodel[n].bsp_data_size > 0 ) {
1540 pm->submodel[n].bsp_data = (ubyte *)malloc(pm->submodel[n].bsp_data_size);
1541 cfread(pm->submodel[n].bsp_data,1,pm->submodel[n].bsp_data_size,fp);
1542 #if (SDL_BYTEORDER == SDL_BIG_ENDIAN) //tigital
1543 swap_bsp_data( pm, pm->submodel[n].bsp_data );
1546 pm->submodel[n].bsp_data = NULL;
1549 if ( strstr( pm->submodel[n].name, "thruster") )
1550 pm->submodel[n].is_thruster=1;
1552 pm->submodel[n].is_thruster=0;
1554 if ( strstr( pm->submodel[n].name, "-destroyed") )
1555 pm->submodel[n].is_damaged=1;
1557 pm->submodel[n].is_damaged=0;
1559 //mprintf(( "Submodel %d, name '%s', parent = %d\n", n, pm->submodel[n].name, pm->submodel[n].parent ));
1562 //mprintf(( "Submodel %d, tree offset %d\n", n, pm->submodel[n].tree_offset ));
1563 //mprintf(( "Submodel %d, data offset %d\n", n, pm->submodel[n].data_offset ));
1575 nverts = cfread_int( fp ); // get the number of vertices in the list
1576 pm->shield.nverts = nverts;
1577 pm->shield.verts = (shield_vertex *)malloc(nverts * sizeof(shield_vertex) );
1578 SDL_assert( pm->shield.verts );
1579 for ( i = 0; i < nverts; i++ ) // read in the vertex list
1580 cfread_vector( &(pm->shield.verts[i].pos), fp );
1582 ntris = cfread_int( fp ); // get the number of triangles that compose the shield
1583 pm->shield.ntris = ntris;
1584 pm->shield.tris = (shield_tri *)malloc(ntris * sizeof(shield_tri) );
1585 SDL_assert( pm->shield.tris );
1586 for ( i = 0; i < ntris; i++ ) {
1587 cfread_vector( &(pm->shield.tris[i].norm), fp );
1588 for ( j = 0; j < 3; j++ ) {
1589 pm->shield.tris[i].verts[j] = cfread_int( fp ); // read in the indices into the shield_vertex list
1592 if (pm->shield.tris[i].verts[j] >= nverts)
1593 if (!warning_displayed) {
1594 warning_displayed = 1;
1595 Warning(LOCATION, "Ship %s has a bogus shield mesh.\nOnly %i vertices, index %i found.\n", filename, nverts, pm->shield.tris[i].verts[j]);
1600 for ( j = 0; j < 3; j++ )
1601 pm->shield.tris[i].neighbors[j] = cfread_int( fp ); // read in the neighbor indices -- indexes into tri list
1611 pm->n_guns = cfread_int(fp);
1612 pm->gun_banks = (w_bank *)malloc(sizeof(w_bank) * pm->n_guns);
1613 SDL_assert( pm->gun_banks != NULL );
1615 for (i = 0; i < pm->n_guns; i++ ) {
1616 w_bank *bank = &pm->gun_banks[i];
1618 bank->num_slots = cfread_int(fp);
1619 SDL_assert ( bank->num_slots < MAX_SLOTS );
1620 for (j = 0; j < bank->num_slots; j++) {
1621 cfread_vector( &(bank->pnt[j]), fp );
1622 cfread_vector( &(bank->norm[j]), fp );
1628 pm->n_missiles = cfread_int(fp);
1629 pm->missile_banks = (w_bank *)malloc(sizeof(w_bank) * pm->n_missiles);
1630 SDL_assert( pm->missile_banks != NULL );
1632 for (i = 0; i < pm->n_missiles; i++ ) {
1633 w_bank *bank = &pm->missile_banks[i];
1635 bank->num_slots = cfread_int(fp);
1636 SDL_assert ( bank->num_slots < MAX_SLOTS );
1637 for (j = 0; j < bank->num_slots; j++) {
1638 cfread_vector( &(bank->pnt[j]), fp );
1639 cfread_vector( &(bank->norm[j]), fp );
1645 char props[MAX_PROP_LEN];
1647 pm->n_docks = cfread_int(fp);
1648 pm->docking_bays = (dock_bay *)malloc(sizeof(dock_bay) * pm->n_docks);
1649 SDL_assert( pm->docking_bays != NULL );
1651 for (i = 0; i < pm->n_docks; i++ ) {
1653 dock_bay *bay = &pm->docking_bays[i];
1655 cfread_string_len( props, MAX_PROP_LEN, fp );
1656 if ( (p = strstr(props, "$name"))!= NULL )
1657 get_user_prop_value(p+5, bay->name, SDL_arraysize(bay->name));
1659 SDL_snprintf(bay->name, SDL_arraysize(bay->name), "<unnamed bay %c>", 'A' + i);
1660 bay->num_spline_paths = cfread_int( fp );
1661 if ( bay->num_spline_paths > 0 ) {
1662 bay->splines = (int *)malloc(sizeof(int) * bay->num_spline_paths);
1663 for ( j = 0; j < bay->num_spline_paths; j++ )
1664 bay->splines[j] = cfread_int(fp);
1666 bay->splines = NULL;
1669 // determine what this docking bay can be used for
1670 if ( !SDL_strncasecmp(bay->name, "cargo", 5) )
1671 bay->type_flags = DOCK_TYPE_CARGO;
1673 bay->type_flags = (DOCK_TYPE_REARM | DOCK_TYPE_GENERIC);
1675 bay->num_slots = cfread_int(fp);
1676 SDL_assert( bay->num_slots == 2 ); // Get Allender if Asserted!
1677 for (j = 0; j < bay->num_slots; j++) {
1678 cfread_vector( &(bay->pnt[j]), fp );
1679 cfread_vector( &(bay->norm[j]), fp );
1686 char props[MAX_PROP_LEN];
1687 pm->n_thrusters = cfread_int(fp);
1688 pm->thrusters = (thruster_bank *)malloc(sizeof(thruster_bank) * pm->n_thrusters);
1689 SDL_assert( pm->thrusters != NULL );
1691 for (i = 0; i < pm->n_thrusters; i++ ) {
1692 thruster_bank *bank = &pm->thrusters[i];
1694 bank->num_slots = cfread_int(fp);
1696 if (pm->version < 2117) {
1697 bank->wash_info_index = -1;
1699 cfread_string_len( props, MAX_PROP_LEN, fp );
1700 // look for $engine_subsystem=xxx
1701 int length = strlen(props);
1703 int base_length = strlen("$engine_subsystem=");
1704 SDL_assert( strstr( (const char *)&props, "$engine_subsystem=") != NULL );
1705 SDL_assert( length > base_length );
1706 char *engine_subsys_name = props + base_length;
1707 if (engine_subsys_name[0] == '$') {
1708 engine_subsys_name++;
1711 nprintf(("wash", "Ship %s with engine wash associated with subsys %s\n", filename, engine_subsys_name));
1713 // set wash_info_index to invalid
1714 int table_error = 1;
1715 bank->wash_info_index = -1;
1716 for (int k=0; k<n_subsystems; k++) {
1717 if ( 0 == SDL_strcasecmp(subsystems[k].subobj_name, engine_subsys_name) ) {
1718 bank->wash_info_index = subsystems[k].engine_wash_index;
1719 if (bank->wash_info_index >= 0) {
1726 if ( (bank->wash_info_index == -1) && (n_subsystems > 0) ) {
1728 Warning(LOCATION, "No engine wash table entry in ships.tbl for ship model %s", filename);
1730 Warning(LOCATION, "Inconsistent model: Engine wash engine subsystem does not match any ship subsytem names for ship model %s", filename);
1734 bank->wash_info_index = -1;
1738 for (j = 0; j < bank->num_slots; j++) {
1740 cfread_vector( &(bank->pnt[j]), fp );
1741 cfread_vector( &(bank->norm[j]), fp );
1742 if ( pm->version > 2004 ) {
1743 bank->radius[j] = cfread_float( fp );
1744 //mprintf(( "Rad = %.2f\n", rad ));
1746 bank->radius[j] = 1.0f;
1749 //mprintf(( "Num slots = %d\n", bank->num_slots ));
1756 int n_banks, n_slots, parent;
1757 model_subsystem *subsystemp;
1761 n_banks = cfread_int(fp); // number of turret points
1762 for ( i = 0; i < n_banks; i++ ) {
1763 int physical_parent; // who are we attached to?
1764 parent = cfread_int( fp ); // get the turret parent of the object
1766 physical_parent = cfread_int(fp); // The parent subobj that this is physically attached to
1769 for ( snum = 0; snum < n_subsystems; snum++ ) {
1770 subsystemp = &subsystems[snum];
1772 if ( parent == subsystemp->subobj_num ) {
1773 cfread_vector( &subsystemp->turret_norm, fp );
1774 vm_vector_2_matrix(&subsystemp->turret_matrix,&subsystemp->turret_norm,NULL,NULL);
1776 n_slots = cfread_int( fp );
1777 subsystemp->turret_gun_sobj = physical_parent;
1778 SDL_assert(n_slots <= MAX_TFP); // only MAX_TFP firing points per model_subsystem
1779 for (j = 0; j < n_slots; j++ ) {
1780 if ( j < MAX_TFP ) {
1781 cfread_vector( &subsystemp->turret_firing_point[j], fp );
1783 cfread_vector( &bogus, fp );
1786 SDL_assert( n_slots > 0 );
1788 subsystemp->turret_num_firing_points = (n_slots > MAX_TFP) ? MAX_TFP : n_slots;
1797 if ( (n_subsystems == 0) || (snum == n_subsystems) ) {
1798 nprintf(("Warning", "Turret object not found for turret firing point in model %s\n", model_filename));
1799 cfread_vector( &bogus, fp );
1800 n_slots = cfread_int( fp );
1801 for (j = 0; j < n_slots; j++ )
1802 cfread_vector( &bogus, fp );
1809 char name[MAX_NAME_LEN], props[MAX_PROP_LEN], *p;
1814 n_specials = cfread_int(fp); // get the number of special subobjects we have
1815 for (i = 0; i < n_specials; i++) {
1817 // get the next free object of the subobject list. Flag error if no more room
1819 cfread_string_len(name, MAX_NAME_LEN, fp); // get the name of this special polygon
1821 cfread_string_len(props, MAX_PROP_LEN, fp); // will definately have properties as well!
1822 cfread_vector( &pnt, fp );
1823 radius = cfread_float( fp );
1826 p = strstr(name, "$split");
1828 pm->split_plane[pm->num_split_plane] = pnt.xyz.z;
1829 pm->num_split_plane++;
1830 SDL_assert(pm->num_split_plane <= MAX_SPLIT_PLANE);
1831 } else if ( ( p = strstr(props, "$special"))!= NULL ) {
1834 get_user_prop_value(p+9, type, SDL_arraysize(type));
1835 if ( !SDL_strcasecmp(type, "subsystem") ) // if we have a subsystem, put it into the list!
1836 do_new_subsystem( n_subsystems, subsystems, -1, radius, &pnt, props, &name[1], pm->id ); // skip the first '$' character of the name
1837 } else if ( strstr(name, "$enginelarge") || strstr(name, "$enginehuge") ){
1838 do_new_subsystem( n_subsystems, subsystems, -1, radius, &pnt, props, &name[1], pm->id ); // skip the first '$' character of the name
1840 nprintf(("Warning", "Unknown special object type %s while reading model %s\n", name, pm->filename));
1846 case ID_TXTR: { //Texture filename list
1848 // char name_buf[128];
1850 //mprintf(0,"Got chunk TXTR, len=%d\n",len);
1854 // Dont overwrite memory!!
1855 SDL_assert(n <= MAX_MODEL_TEXTURES);
1856 //mprintf(0," num textures = %d\n",n);
1857 for (i=0; i<n; i++ ) {
1859 cfread_string_len(tmp_name,127,fp);
1861 if ( strstr(tmp_name, "thruster") || strstr(tmp_name, "invisible") ) {
1862 // Don't load textures for thruster animations or invisible textures
1863 pm->textures[i] = -1;
1865 pm->textures[i] = bm_load( tmp_name );
1866 if (pm->textures[i]<0) {
1867 Error( LOCATION, "Couldn't open texture '%s'\nreferenced by model '%s'\n", tmp_name, pm->filename );
1870 pm->original_textures[i] = pm->textures[i];
1871 //mprintf(0,"<%s>\n",name_buf);
1877 /* case ID_IDTA: //Interpreter data
1878 //mprintf(0,"Got chunk IDTA, len=%d\n",len);
1880 pm->model_data = (ubyte *)malloc(len);
1881 pm->model_data_size = len;
1882 SDL_assert(pm->model_data != NULL );
1884 cfread(pm->model_data,1,len,fp);
1889 case ID_INFO: // don't need to do anything with info stuff
1892 pm->debug_info_size = len;
1893 pm->debug_info = (char *)malloc(pm->debug_info_size+1);
1894 SDL_assert(pm->debug_info!=NULL);
1895 memset(pm->debug_info,0,len+1);
1896 cfread( pm->debug_info, 1, len, fp );
1904 pm->n_paths = cfread_int( fp );
1905 pm->paths = (model_path *)malloc(sizeof(model_path)*pm->n_paths);
1906 SDL_assert( pm->paths != NULL );
1908 for (i=0; i<pm->n_paths; i++ ) {
1909 cfread_string_len(pm->paths[i].name , MAX_NAME_LEN-1, fp);
1910 if ( pm->version >= 2002 ) {
1911 // store the sub_model name number of the parent
1912 cfread_string_len(pm->paths[i].parent_name , MAX_NAME_LEN-1, fp);
1913 // get rid of leading '$' char in name
1914 if ( pm->paths[i].parent_name[0] == '$' ) {
1915 char tmpbuf[MAX_NAME_LEN];
1916 SDL_strlcpy(tmpbuf, pm->paths[i].parent_name+1, SDL_arraysize(tmpbuf));
1917 SDL_strlcpy(pm->paths[i].parent_name, tmpbuf, MAX_NAME_LEN);
1919 // store the sub_model index (ie index into pm->submodel) of the parent
1920 pm->paths[i].parent_submodel = -1;
1921 for ( j = 0; j < pm->n_models; j++ ) {
1922 if ( !SDL_strcasecmp( pm->submodel[j].name, pm->paths[i].parent_name) ) {
1923 pm->paths[i].parent_submodel = j;
1927 pm->paths[i].parent_name[0] = 0;
1928 pm->paths[i].parent_submodel = -1;
1930 pm->paths[i].nverts = cfread_int( fp );
1931 pm->paths[i].verts = (mp_vert *)malloc( sizeof(mp_vert) * pm->paths[i].nverts );
1932 pm->paths[i].goal = pm->paths[i].nverts - 1;
1933 pm->paths[i].type = MP_TYPE_UNUSED;
1934 pm->paths[i].value = 0;
1935 SDL_assert(pm->paths[i].verts!=NULL);
1936 for (j=0; j<pm->paths[i].nverts; j++ ) {
1937 cfread_vector(&pm->paths[i].verts[j].pos,fp );
1938 pm->paths[i].verts[j].radius = cfread_float( fp );
1940 { // version 1802 added turret stuff
1943 nturrets = cfread_int( fp );
1944 pm->paths[i].verts[j].nturrets = nturrets;
1945 pm->paths[i].verts[j].turret_ids = (int *)malloc( sizeof(int) * nturrets );
1946 for ( k = 0; k < nturrets; k++ )
1947 pm->paths[i].verts[j].turret_ids[k] = cfread_int( fp );
1954 case ID_EYE: // an eye position(s)
1958 // all eyes points are stored simply as vectors and their normals.
1959 // 0th element is used as usual player view position.
1961 num_eyes = cfread_int( fp );
1962 pm->n_view_positions = num_eyes;
1963 SDL_assert ( num_eyes < MAX_EYES );
1964 for (i = 0; i < num_eyes; i++ ) {
1965 pm->view_positions[i].parent = cfread_int( fp );
1966 cfread_vector( &pm->view_positions[i].pnt, fp );
1967 cfread_vector( &pm->view_positions[i].norm, fp );
1973 int num_ins, num_verts, num_faces, idx, idx2, idx3;
1975 // get the # of insignias
1976 num_ins = cfread_int(fp);
1977 pm->num_ins = num_ins;
1979 // read in the insignias
1980 for(idx=0; idx<num_ins; idx++){
1981 // get the detail level
1982 pm->ins[idx].detail_level = cfread_int(fp);
1985 num_faces = cfread_int(fp);
1986 pm->ins[idx].num_faces = num_faces;
1987 SDL_assert(num_faces <= MAX_INS_FACES);
1990 num_verts = cfread_int(fp);
1991 SDL_assert(num_verts <= MAX_INS_VECS);
1993 // read in all the vertices
1994 for(idx2=0; idx2<num_verts; idx2++){
1995 cfread_vector(&pm->ins[idx].vecs[idx2], fp);
1998 // read in world offset
1999 cfread_vector(&pm->ins[idx].offset, fp);
2001 // read in all the faces
2002 for(idx2=0; idx2<pm->ins[idx].num_faces; idx2++){
2003 // read in 3 vertices
2004 for(idx3=0; idx3<3; idx3++){
2005 pm->ins[idx].faces[idx2][idx3] = cfread_int(fp);
2006 pm->ins[idx].u[idx2][idx3] = cfread_float(fp);
2007 pm->ins[idx].v[idx2][idx3] = cfread_float(fp);
2013 // autocentering info
2015 cfread_vector(&pm->autocenter, fp);
2016 pm->flags |= PM_FLAG_AUTOCEN;
2020 mprintf(("Unknown chunk <%c%c%c%c>, len = %d\n",id,id>>8,id>>16,id>>24,len));
2021 cfseek(fp,len,SEEK_CUR);
2025 cfseek(fp,next_chunk,SEEK_SET);
2027 id = cfread_int(fp);
2028 len = cfread_int(fp);
2029 next_chunk = cftell(fp) + len;
2038 ss_fp = cfopen(debug_name, "rb");
2040 size = cfilelength(ss_fp);
2043 cf_delete(debug_name, CF_TYPE_TABLES);
2050 // mprintf(("Done processing chunks\n"));
2056 //returns the number of this model
2057 int model_load(const char *filename, int n_subsystems, model_subsystem *subsystems)
2059 int i, num, arc_idx;
2061 if ( !model_initted )
2064 // int Model_ram = 0;
2067 int ram_before = TotalRam;
2070 //SDL_assert(strlen(filename) <= 12);
2074 for (i=0; i< MAX_POLYGON_MODELS; i++) {
2075 if ( Polygon_models[i] ) {
2076 if (!SDL_strcasecmp(filename, Polygon_models[i]->filename)) {
2077 // Model already loaded; just return.
2078 return Polygon_models[i]->id;
2080 } else if ( num == -1 ) {
2081 // This is the first empty slot
2088 Error( LOCATION, "Too many models" );
2092 mprintf(( "Loading model '%s'\n", filename ));
2094 polymodel * pm = (polymodel *)malloc( sizeof(polymodel) );
2096 Polygon_models[num] = pm;
2098 memset(pm, 0, sizeof(polymodel));
2103 Model_signature+=MAX_POLYGON_MODELS;
2104 if ( Model_signature < MAX_POLYGON_MODELS ) {
2105 Model_signature = 0;
2107 SDL_assert( (Model_signature % MAX_POLYGON_MODELS) == 0 );
2108 pm->id = Model_signature + num;
2110 if (!read_model_file(pm, filename, n_subsystems, subsystems)) {
2114 //mprintf(( "Loading model '%s'\n", filename ));
2117 //=============================
2118 // Find the destroyed replacement models
2120 // Set up the default values
2121 for (i=0; i<pm->n_models; i++ ) {
2122 pm->submodel[i].my_replacement = -1; // assume nothing replaces this
2123 pm->submodel[i].i_replace = -1; // assume this doesn't replaces anything
2126 // Search for models that have destroyed versions
2127 for (i=0; i<pm->n_models; i++ ) {
2129 char destroyed_name[128];
2131 SDL_strlcpy( destroyed_name, pm->submodel[i].name, SDL_arraysize(destroyed_name) );
2132 SDL_strlcat( destroyed_name, "-destroyed", SDL_arraysize(destroyed_name) );
2133 for (j=0; j<pm->n_models; j++ ) {
2134 if ( !SDL_strcasecmp( pm->submodel[j].name, destroyed_name )) {
2135 // mprintf(( "Found destroyed model for '%s'\n", pm->submodel[i].name ));
2136 pm->submodel[i].my_replacement = j;
2137 pm->submodel[j].i_replace = i;
2141 // Search for models with live debris
2142 // This debris comes from a destroyed subsystem when ship is still alive
2143 char live_debris_name[128];
2145 SDL_strlcpy( live_debris_name, "debris-", SDL_arraysize(live_debris_name) );
2146 SDL_strlcat( live_debris_name, pm->submodel[i].name, SDL_arraysize(live_debris_name) );
2149 pm->submodel[i].num_live_debris = 0;
2150 for (j=0; j<pm->n_models; j++ ) {
2151 // check if current model name is substring of destroyed
2152 if ( strstr( pm->submodel[j].name, live_debris_name )) {
2153 mprintf(( "Found live debris model for '%s'\n", pm->submodel[i].name ));
2154 SDL_assert(pm->submodel[i].num_live_debris < MAX_LIVE_DEBRIS);
2155 pm->submodel[i].live_debris[pm->submodel[i].num_live_debris++] = j;
2156 pm->submodel[j].is_live_debris = 1;
2162 create_family_tree(pm);
2163 // dump_object_tree(pm);
2165 //==============================
2166 // Find all the lower detail versions of the hires model
2167 for (i=0; i<pm->n_models; i++ ) {
2169 bsp_info * sm1 = &pm->submodel[i];
2171 // set all arc types to be default
2172 for(arc_idx=0; arc_idx < MAX_ARC_EFFECTS; arc_idx++){
2173 sm1->arc_type[arc_idx] = MARC_TYPE_NORMAL;
2176 sm1->num_details = 0;
2177 l1 = strlen(sm1->name);
2179 for (j=0; j<pm->num_debris_objects;j++ ) {
2180 if ( i == pm->debris_objects[j] ) {
2181 sm1->is_damaged = 1;
2186 for (j=0; j<MAX_MODEL_DETAIL_LEVELS; j++ ) {
2187 sm1->details[j] = -1;
2190 for (j=0; j<pm->n_models; j++ ) {
2192 bsp_info * sm2 = &pm->submodel[j];
2194 if ( i==j ) continue;
2196 // set all arc types to be default
2197 for(arc_idx=0; arc_idx < MAX_ARC_EFFECTS; arc_idx++){
2198 sm2->arc_type[arc_idx] = MARC_TYPE_NORMAL;
2201 // if sm2 is a detail of sm1 and sm1 is a high detail, then add it to sm1's list
2202 if ((int)strlen(sm2->name)!=l1) continue;
2206 for ( k=0; k<l1; k++) {
2207 if (sm1->name[k] != sm2->name[k] ) {
2208 if (ndiff==0) first_diff = k;
2212 if (ndiff==1) { // They only differ by one character!
2214 dl1 = tolower(sm1->name[first_diff]) - 'a';
2215 dl2 = tolower(sm2->name[first_diff]) - 'a';
2217 if ( (dl1<0) || (dl2<0) || (dl1>=MAX_MODEL_DETAIL_LEVELS) || (dl2>=MAX_MODEL_DETAIL_LEVELS) ) continue; // invalid detail levels
2220 dl2--; // Start from 1 up...
2221 if (dl2 >= sm1->num_details ) sm1->num_details = dl2+1;
2222 sm1->details[dl2] = j;
2223 //mprintf(( "Submodel '%s' is detail level %d of '%s'\n", sm2->name, dl2, sm1->name ));
2228 for (j=0; j<sm1->num_details; j++ ) {
2229 if ( sm1->details[j] == -1 ) {
2230 Warning( LOCATION, "Model '%s' could not find all detail levels for submodel '%s'", pm->filename, sm1->name );
2231 sm1->num_details = 0;
2238 model_octant_create( pm );
2240 // Find the core_radius... the minimum of
2242 rx = fl_abs( pm->submodel[pm->detail[0]].max.xyz.x - pm->submodel[pm->detail[0]].min.xyz.x );
2243 ry = fl_abs( pm->submodel[pm->detail[0]].max.xyz.y - pm->submodel[pm->detail[0]].min.xyz.y );
2244 rz = fl_abs( pm->submodel[pm->detail[0]].max.xyz.z - pm->submodel[pm->detail[0]].min.xyz.z );
2246 pm->core_radius = min( rx, min(ry, rz) ) / 2.0f;
2248 for (i=0; i<pm->n_view_positions; i++ ) {
2249 if ( pm->view_positions[i].parent == pm->detail[0] ) {
2250 float d = vm_vec_mag( &pm->view_positions[i].pnt );
2252 d += 0.1f; // Make the eye 1/10th of a meter inside the sphere.
2254 if ( d > pm->core_radius ) {
2255 //mprintf(( "Model %s core radius increased from %.1f to %.1f to fit eye\n", pm->filename, pm->core_radius, d ));
2256 pm->core_radius = d;
2262 int ram_after = TotalRam;
2264 pm->ram_used = ram_after - ram_before;
2265 Model_ram += pm->ram_used;
2266 //mprintf(( "Model RAM = %d KB\n", Model_ram ));
2272 // Get "parent" submodel for live debris submodel
2273 int model_get_parent_submodel_for_live_debris( int model_num, int live_debris_model_num )
2275 polymodel *pm = model_get(model_num);
2277 SDL_assert(pm->submodel[live_debris_model_num].is_live_debris == 1);
2282 // Start with the high level of detail hull
2283 // Check all its children until we find the submodel to which the live debris belongs
2284 child = &pm->submodel[pm->detail[0]];
2285 mn = child->first_child;
2288 child = &pm->submodel[mn];
2290 if (child->num_live_debris > 0) {
2291 // check all live debris submodels for the current child
2292 for (int idx=0; idx<child->num_live_debris; idx++) {
2293 if (child->live_debris[idx] == live_debris_model_num) {
2297 // DKA 5/26/99: can multiple live debris subsystems with each ship
2298 // NO LONGER TRUE Can only be 1 submodel with live debris
2299 // Error( LOCATION, "Could not find parent submodel for live debris. Possible model error");
2303 mn = child->next_sibling;
2305 Error( LOCATION, "Could not find parent submodel for live debris");
2310 float model_get_radius( int modelnum )
2314 pm = model_get(modelnum);
2319 float model_get_core_radius( int modelnum )
2323 pm = model_get(modelnum);
2325 return pm->core_radius;
2328 float submodel_get_radius( int modelnum, int submodelnum )
2332 pm = model_get(modelnum);
2334 return pm->submodel[submodelnum].rad;
2339 polymodel * model_get(int model_num)
2341 SDL_assert( model_num > -1 );
2343 int num = model_num % MAX_POLYGON_MODELS;
2345 SDL_assert( num > -1 );
2346 SDL_assert( num < MAX_POLYGON_MODELS );
2347 SDL_assert( Polygon_models[num]->id == model_num );
2349 return Polygon_models[num];
2354 // Finds the 3d bounding box of a model. If submodel_num is -1,
2355 // then it starts from the root object. If inc_children is non-zero,
2356 // then this will recurse and find the bounding box for all children
2358 void model_find_bound_box_3d(int model_num,int submodel_num, int inc_children, matrix *orient, vector * pos, vector * box )
2361 vector to_root_xlat;
2362 matrix to_root_rotate;
2363 int n_steps, steps[16];
2366 if ( (model_num < 0) || (model_num >= N_polygon_models) ) return;
2368 pm = &Polygon_models[model_num];
2370 if ( submodel_num < 0 ) submodel_num = pm->detail[0];
2372 // traverse up the model tree to a root object.
2373 // Store this path in n_steps,
2375 tmp_sobj = submodel_num;
2376 while( tmp_sobj > -1 ) {
2377 steps[n_steps++] = tmp_sobj;
2378 tmp_sobj = pm->submodel[tmp_sobj].parent;
2383 // vm_copy_transpose_matrix(&to_world_rotate, orient );
2384 // to_world_xlat = *pos;
2391 // Returns zero is x1,y1,x2,y2 are valid
2392 // returns 1 for invalid model, 2 for point offscreen.
2393 // note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates!
2394 int model_find_2d_bound_min(int model_num,matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 )
2398 int i, x,y,min_x, min_y, max_x, max_y;
2401 po = model_get(model_num);
2403 g3_start_instance_matrix(pos,orient);
2407 int hull = po->detail[0];
2409 min_x = min_y = max_x = max_y = 0;
2411 for (i=0; i<8; i++ ) {
2415 flags = g3_rotate_vertex(&pt,&po->submodel[hull].bounding_box[i]);
2416 if ( !(flags&CC_BEHIND) ) {
2417 g3_project_vertex(&pt);
2419 if (!(pt.flags & PF_OVERFLOW)) {
2422 if ( n_valid_pts == 0 ) {
2428 if ( x < min_x ) min_x = x;
2429 if ( y < min_y ) min_y = y;
2431 if ( x > max_x ) max_x = x;
2432 if ( y > max_y ) max_y = y;
2439 if ( n_valid_pts < 8 ) {
2443 if (x1) *x1 = min_x;
2444 if (y1) *y1 = min_y;
2446 if (x2) *x2 = max_x;
2447 if (y2) *y2 = max_y;
2455 // Returns zero is x1,y1,x2,y2 are valid
2456 // returns 1 for invalid model, 2 for point offscreen.
2457 // note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates!
2458 int submodel_find_2d_bound_min(int model_num,int submodel, matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 )
2462 int i, x,y,min_x, min_y, max_x, max_y;
2465 po = model_get(model_num);
2466 if ( (submodel < 0) || (submodel >= po->n_models ) ) return 1;
2467 sm = &po->submodel[submodel];
2469 g3_start_instance_matrix(pos,orient);
2473 min_x = min_y = max_x = max_y = 0;
2475 for (i=0; i<8; i++ ) {
2479 flags = g3_rotate_vertex(&pt,&sm->bounding_box[i]);
2480 if ( !(flags&CC_BEHIND) ) {
2481 g3_project_vertex(&pt);
2483 if (!(pt.flags & PF_OVERFLOW)) {
2486 if ( n_valid_pts == 0 ) {
2492 if ( x < min_x ) min_x = x;
2493 if ( y < min_y ) min_y = y;
2495 if ( x > max_x ) max_x = x;
2496 if ( y > max_y ) max_y = y;
2503 if ( n_valid_pts == 0 ) {
2507 if (x1) *x1 = min_x;
2508 if (y1) *y1 = min_y;
2510 if (x2) *x2 = max_x;
2511 if (y2) *y2 = max_y;
2519 // Returns zero is x1,y1,x2,y2 are valid
2520 // returns 1 for invalid model, 2 for point offscreen.
2521 // note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates!
2522 int model_find_2d_bound(int model_num,matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 )
2528 po = model_get(model_num);
2529 float width = po->rad;
2530 float height = po->rad;
2532 g3_rotate_vertex(&pnt,pos);
2534 if ( pnt.flags & CC_BEHIND )
2537 if (!(pnt.flags&PF_PROJECTED))
2538 g3_project_vertex(&pnt);
2540 if (pnt.flags & PF_OVERFLOW)
2543 t = (width * Canv_w2)/pnt.z;
2544 w = t*Matrix_scale.xyz.x;
2546 t = (height*Canv_h2)/pnt.z;
2547 h = t*Matrix_scale.xyz.y;
2549 if (x1) *x1 = fl2i(pnt.sx - w);
2550 if (y1) *y1 = fl2i(pnt.sy - h);
2552 if (x2) *x2 = fl2i(pnt.sx + w);
2553 if (y2) *y2 = fl2i(pnt.sy + h);
2558 // Returns zero is x1,y1,x2,y2 are valid
2559 // returns 2 for point offscreen.
2560 // note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates!
2561 int subobj_find_2d_bound(float radius ,matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 )
2566 float width = radius;
2567 float height = radius;
2569 g3_rotate_vertex(&pnt,pos);
2571 if ( pnt.flags & CC_BEHIND )
2574 if (!(pnt.flags&PF_PROJECTED))
2575 g3_project_vertex(&pnt);
2577 if (pnt.flags & PF_OVERFLOW)
2580 t = (width * Canv_w2)/pnt.z;
2581 w = t*Matrix_scale.xyz.x;
2583 t = (height*Canv_h2)/pnt.z;
2584 h = t*Matrix_scale.xyz.y;
2586 if (x1) *x1 = fl2i(pnt.sx - w);
2587 if (y1) *y1 = fl2i(pnt.sy - h);
2589 if (x2) *x2 = fl2i(pnt.sx + w);
2590 if (y2) *y2 = fl2i(pnt.sy + h);
2596 // Given a vector that is in sub_model_num's frame of
2597 // reference, and given the object's orient and position,
2598 // return the vector in the model's frame of reference.
2599 void model_find_obj_dir(vector *w_vec, vector *m_vec, object *ship_obj, int sub_model_num)
2605 SDL_assert(ship_obj->type == OBJ_SHIP);
2607 polymodel *pm = model_get(Ships[ship_obj->instance].modelnum);
2611 // instance up the tree for this point
2612 while ((mn>-1) && (pm->submodel[mn].parent > -1)) {
2614 vm_angles_2_matrix(&m, &pm->submodel[mn].angs);
2615 vm_vec_unrotate(&tvec, &vec, &m);
2618 mn = pm->submodel[mn].parent;
2621 // now instance for the entire object
2622 vm_vec_unrotate(w_vec, &vec, &ship_obj->orient);
2626 // Given a point (pnt) that is in sub_model_num's frame of
2627 // reference, return the point in in the object's frame of reference
2628 void model_rot_sub_into_obj(vector * outpnt, vector *mpnt,polymodel *pm, int sub_model_num)
2638 //instance up the tree for this point
2639 while ((mn>-1) && (pm->submodel[mn].parent > -1) ) {
2641 vm_angles_2_matrix(&m,&pm->submodel[mn].angs );
2642 vm_transpose_matrix(&m);
2643 vm_vec_rotate(&tpnt,&pnt,&m);
2645 vm_vec_add(&pnt,&tpnt,&pm->submodel[mn].offset );
2647 mn = pm->submodel[mn].parent;
2650 //now instance for the entire object
2655 // Given a rotating submodel, find the ship and world axes or rotatation.
2656 void model_get_rotating_submodel_axis(vector *model_axis, vector *world_axis, int modelnum, int submodel_num, object *obj)
2658 polymodel *pm = model_get(modelnum);
2660 bsp_info *sm = &pm->submodel[submodel_num];
2661 SDL_assert(sm->movement_type == MOVEMENT_TYPE_ROT);
2663 if (sm->movement_axis == MOVEMENT_AXIS_X) {
2664 (void) vm_vec_make(model_axis, 1.0f, 0.0f, 0.0f);
2665 } else if (sm->movement_axis == MOVEMENT_AXIS_Y) {
2666 (void) vm_vec_make(model_axis, 0.0f, 1.0f, 0.0f);
2668 SDL_assert(sm->movement_axis == MOVEMENT_AXIS_Z);
2669 (void) vm_vec_make(model_axis, 0.0f, 0.0f, 1.0f);
2672 model_find_obj_dir(world_axis, model_axis, obj, submodel_num);
2676 // Does stepped rotation of a submodel
2677 void submodel_stepped_rotate(model_subsystem *psub, submodel_instance_info *sii)
2679 SDL_assert(psub->flags & MSS_FLAG_STEPPED_ROTATE);
2681 if ( psub->subobj_num < 0 ) return;
2683 polymodel *pm = model_get(psub->model_num);
2684 bsp_info *sm = &pm->submodel[psub->subobj_num];
2686 if ( sm->movement_type != MOVEMENT_TYPE_ROT ) return;
2688 // get active rotation time this frame
2689 int end_stamp = timestamp();
2690 float rotation_time = 0.001f * (end_stamp - sii->step_zero_timestamp);
2691 SDL_assert(rotation_time >= 0);
2694 sii->prev_angs = sii->angs;
2696 // float pointer into struct to get angle (either p,b,h)
2697 float *ang_next = NULL;
2698 switch( sm->movement_axis ) {
2699 case MOVEMENT_AXIS_X:
2700 ang_next = &sii->angs.p;
2703 case MOVEMENT_AXIS_Y:
2704 ang_next = &sii->angs.h;
2707 case MOVEMENT_AXIS_Z:
2708 ang_next = &sii->angs.b;
2712 if (ang_next == NULL) {
2717 // angular displacement of one step
2718 float step_size = (PI2 / psub->stepped_rotation->num_steps);
2720 // get time to complete one step, including pause
2721 float step_time = psub->stepped_rotation->t_transit + psub->stepped_rotation->t_pause;
2723 // cur_step is step number relative to zero (0 - num_steps)
2724 // step_offset_time is TIME into current step
2725 float step_offset_time = (float)fmod(rotation_time, step_time);
2726 // subtract off fractional step part, round up (ie, 1.999999 -> 2)
2727 int cur_step = int( ((rotation_time - step_offset_time) / step_time) + 0.5f);
2728 // mprintf(("cur step %d\n", cur_step));
2729 // SDL_assert(step_offset_time >= 0);
2731 if (cur_step >= psub->stepped_rotation->num_steps) {
2732 // I don;t know why, but removing this line makes it all good.
2733 // sii->step_zero_timestamp += int(1000.0f * (psub->stepped_rotation->num_steps * step_time) + 0.5f);
2735 // reset cur_step (use mod to handle physics/ai pause)
2736 cur_step = cur_step % psub->stepped_rotation->num_steps;
2740 *ang_next = cur_step * step_size;
2742 // determine which phase of rotation we're in
2743 float coast_start_time = psub->stepped_rotation->fraction * psub->stepped_rotation->t_transit;
2744 float decel_start_time = psub->stepped_rotation->t_transit * (1.0f - psub->stepped_rotation->fraction);
2745 float pause_start_time = psub->stepped_rotation->t_transit;
2747 float start_coast_angle = 0.5f * psub->stepped_rotation->max_turn_accel * coast_start_time * coast_start_time;
2749 if (step_offset_time < coast_start_time) {
2751 float accel_time = step_offset_time;
2752 *ang_next += 0.5f * psub->stepped_rotation->max_turn_accel * accel_time * accel_time;
2753 sii->cur_turn_rate = psub->stepped_rotation->max_turn_accel * accel_time;
2754 } else if (step_offset_time < decel_start_time) {
2756 float coast_time = step_offset_time - coast_start_time;
2757 *ang_next += start_coast_angle + psub->stepped_rotation->max_turn_rate * coast_time;
2758 sii->cur_turn_rate = psub->stepped_rotation->max_turn_rate;
2759 } else if (step_offset_time < pause_start_time) {
2761 float time_to_pause = psub->stepped_rotation->t_transit - step_offset_time;
2762 *ang_next += (step_size - 0.5f * psub->stepped_rotation->max_turn_accel * time_to_pause * time_to_pause);
2763 sii->cur_turn_rate = psub->stepped_rotation->max_turn_rate * time_to_pause;
2766 *ang_next += step_size;
2767 sii->cur_turn_rate = 0.0f;
2771 // Rotates the angle of a submodel. Use this so the right unlocked axis
2773 void submodel_rotate(model_subsystem *psub, submodel_instance_info *sii)
2777 if ( psub->subobj_num < 0 ) return;
2779 polymodel *pm = model_get(psub->model_num);
2780 sm = &pm->submodel[psub->subobj_num];
2782 if ( sm->movement_type != MOVEMENT_TYPE_ROT ) return;
2785 sii->prev_angs = sii->angs;
2787 // probably send in a calculated desired turn rate
2788 float diff = sii->desired_turn_rate - sii->cur_turn_rate;
2790 float final_turn_rate;
2792 final_turn_rate = sii->cur_turn_rate + sii->turn_accel * flFrametime;
2793 if (final_turn_rate > sii->desired_turn_rate) {
2794 final_turn_rate = sii->desired_turn_rate;
2796 } else if (diff < 0) {
2797 final_turn_rate = sii->cur_turn_rate - sii->turn_accel * flFrametime;
2798 if (final_turn_rate < sii->desired_turn_rate) {
2799 final_turn_rate = sii->desired_turn_rate;
2802 final_turn_rate = sii->desired_turn_rate;
2805 float delta = (sii->cur_turn_rate + final_turn_rate) * 0.5f * flFrametime;
2806 sii->cur_turn_rate = final_turn_rate;
2809 //float delta = psub->turn_rate * flFrametime;
2811 switch( sm->movement_axis ) {
2812 case MOVEMENT_AXIS_X:
2813 sii->angs.p += delta;
2814 if (sii->angs.p > PI2 )
2816 else if (sii->angs.p < 0.0f )
2819 case MOVEMENT_AXIS_Y:
2820 sii->angs.h += delta;
2821 if (sii->angs.h > PI2 )
2823 else if (sii->angs.h < 0.0f )
2826 case MOVEMENT_AXIS_Z:
2827 sii->angs.b += delta;
2828 if (sii->angs.b > PI2 )
2830 else if (sii->angs.b < 0.0f )
2837 //=========================================================================
2838 // Make a turret's correct orientation matrix. This should be done when
2839 // the model is read, but I wasn't sure at what point all the data that I
2840 // needed was read, so I just check a flag and call this routine when
2841 // I determine I need the correct matrix. In this code, you can't use
2842 // vm_vec_2_matrix or anything, since these turrets could be either
2843 // right handed or left handed.
2844 void model_make_turrent_matrix(int model_num, model_subsystem * turret )
2847 vector fvec, uvec, rvec;
2849 pm = model_get(model_num);
2850 bsp_info * sm = &pm->submodel[turret->turret_gun_sobj];
2851 bsp_info * sm_parent = &pm->submodel[turret->subobj_num];
2854 model_clear_instance(model_num);
2855 model_find_world_dir(&fvec, &turret->turret_norm, model_num, turret->turret_gun_sobj, &vmd_identity_matrix, NULL );
2857 sm_parent->angs.h = -PI/2.0f;
2858 sm->angs.p = -PI/2.0f;
2859 model_find_world_dir(&rvec, &turret->turret_norm, model_num, turret->turret_gun_sobj, &vmd_identity_matrix, NULL );
2861 sm_parent->angs.h = 0.0f;
2862 sm->angs.p = -PI/2.0f;
2863 model_find_world_dir(&uvec, &turret->turret_norm, model_num, turret->turret_gun_sobj, &vmd_identity_matrix, NULL );
2865 vm_vec_normalize(&fvec);
2866 vm_vec_normalize(&rvec);
2867 vm_vec_normalize(&uvec);
2869 turret->turret_matrix.v.fvec = fvec;
2870 turret->turret_matrix.v.rvec = rvec;
2871 turret->turret_matrix.v.uvec = uvec;
2873 // vm_vector_2_matrix(&turret->turret_matrix,&turret->turret_norm,NULL,NULL);
2875 // HACK!! WARNING!!!
2876 // I'm doing nothing to verify that this matrix is orthogonal!!
2877 // In other words, there's no guarantee that the vectors are 90 degrees
2879 // I'm not doing this because I don't know how to do it without ruining
2880 // the handedness of the matrix... however, I'm not too worried about
2881 // this because I am creating these 3 vectors by making them 90 degrees
2882 // apart, so this should be close enough. I think this will start
2883 // causing weird errors when we view from turrets. -John
2884 turret->flags |= MSS_FLAG_TURRET_MATRIX;
2887 // Tries to move joints so that the turrent points to the point dst.
2888 // turret1 is the angles of the turret, turret2 is the angles of the gun from turret
2889 // Returns 1 if rotated gun, 0 if no gun to rotate (rotation handled by AI)
2890 int model_rotate_gun(int model_num, model_subsystem *turret, matrix *orient, angles *turret1, angles *turret2, vector *pos, vector *dst)
2894 pm = model_get(model_num);
2895 bsp_info * sm = &pm->submodel[turret->turret_gun_sobj];
2896 bsp_info * sm_parent = &pm->submodel[turret->subobj_num];
2898 // Check for a valid turret
2899 SDL_assert( turret->turret_num_firing_points > 0 );
2902 if ( sm_parent == sm ) {
2906 // Build the correct turret matrix if there isn't already one
2907 if ( !(turret->flags & MSS_FLAG_TURRET_MATRIX) )
2908 model_make_turrent_matrix(model_num, turret );
2910 SDL_assert( turret->flags & MSS_FLAG_TURRET_MATRIX);
2911 // SDL_assert( sm->movement_axis == MOVEMENT_AXIS_X ); // Gun must be able to change pitch
2912 // SDL_assert( sm_parent->movement_axis == MOVEMENT_AXIS_Z ); // Parent must be able to change heading
2914 //======================================================
2915 // DEBUG code to draw the normal out of this gun and a circle
2916 // at the gun point.
2921 vertex dpnt1, dpnt2;
2923 model_clear_instance(model_num);
2924 sm->angs.p = turret2->p;
2925 sm_parent->angs.h = turret1->h;
2927 model_find_world_point(&tmp, &vmd_zero_vector, model_num, turret->turret_gun_sobj, orient, pos );
2928 gr_set_color(255,0,0);
2929 g3_rotate_vertex( &dpnt1, &tmp );
2931 gr_set_color(255,0,0);
2932 g3_draw_sphere(&dpnt1,1.0f);
2934 vm_vec_copy_scale( &tmp1, &turret->turret_matrix.v.fvec, 10.0f );
2935 model_find_world_point(&tmp, &tmp1, model_num, turret->turret_gun_sobj, orient, pos );
2936 g3_rotate_vertex( &dpnt2, &tmp );
2938 gr_set_color(0,255,0);
2939 g3_draw_line(&dpnt1,&dpnt2);
2940 gr_set_color(0,128,0);
2941 g3_draw_sphere(&dpnt2,0.2f);
2943 vm_vec_copy_scale( &tmp1, &turret->turret_matrix.v.rvec, 10.0f );
2944 model_find_world_point(&tmp, &tmp1, model_num, turret->turret_gun_sobj, orient, pos );
2945 g3_rotate_vertex( &dpnt2, &tmp );
2947 gr_set_color(0,0,255);
2948 g3_draw_line(&dpnt1,&dpnt2);
2950 vm_vec_copy_scale( &tmp1, &turret->turret_matrix.v.uvec, 10.0f );
2951 model_find_world_point(&tmp, &tmp1, model_num, turret->turret_gun_sobj, orient, pos );
2952 g3_rotate_vertex( &dpnt2, &tmp );
2954 gr_set_color(255,0,0);
2955 g3_draw_line(&dpnt1,&dpnt2);
2960 // rotate the dest point into the turret gun normal's frame of
2961 // reference, but not using the turret's angles.
2962 // Call this vector of_dst
2964 matrix world_to_turret_matrix; // converts world coordinates to turret's FOR
2965 vector world_to_turret_translate; // converts world coordinates to turret's FOR
2968 vm_vec_unrotate( &tempv, &sm_parent->offset, orient);
2969 vm_vec_add( &world_to_turret_translate, pos, &tempv );
2971 vm_matrix_x_matrix( &world_to_turret_matrix, orient, &turret->turret_matrix );
2973 vm_vec_sub( &tempv, dst, &world_to_turret_translate );
2974 vm_vec_rotate( &of_dst, &tempv, &world_to_turret_matrix );
2976 vm_vec_normalize(&of_dst);
2979 // Find the heading and pitch that the gun needs to turn to
2980 // by extracting them from the of_dst vector.
2981 // Call this the desired_angles
2982 angles desired_angles;
2984 desired_angles.p = (float)acos(of_dst.xyz.z);
2985 desired_angles.h = PI - atan2_safe(of_dst.xyz.x, of_dst.xyz.y);
2986 desired_angles.b = 0.0f;
2988 // mprintf(( "Z = %.1f, atan= %.1f\n", of_dst.xyz.z, desired_angles.p ));
2991 // Gradually turn the turret towards the desired angles
2992 float step_size = turret->turret_turning_rate * flFrametime;
2994 vm_interp_angle(&turret1->h,desired_angles.h,step_size);
2995 vm_interp_angle(&turret2->p,desired_angles.p,step_size);
2997 // turret1->h -= step_size*(key_down_timef(SDLK_1)-key_down_timef(SDLK_2) );
2998 // turret2->p += step_size*(key_down_timef(SDLK_3)-key_down_timef(SDLK_4) );
3005 // Given a point (pnt) that is in sub_model_num's frame of
3006 // reference, and given the object's orient and position,
3007 // return the point in 3-space in outpnt.
3008 void model_find_world_point(vector * outpnt, vector *mpnt,int model_num,int sub_model_num, matrix * objorient, vector * objpos )
3014 polymodel *pm = model_get(model_num);
3016 SDL_assert(mpnt != NULL);
3021 //instance up the tree for this point
3022 while ((mn>-1) && (pm->submodel[mn].parent > -1) ) {
3024 vm_angles_2_matrix(&m,&pm->submodel[mn].angs );
3025 vm_vec_unrotate(&tpnt,&pnt,&m);
3027 vm_vec_add(&pnt,&tpnt,&pm->submodel[mn].offset );
3029 mn = pm->submodel[mn].parent;
3032 //now instance for the entire object
3033 vm_vec_unrotate(outpnt,&pnt,objorient);
3034 vm_vec_add2(outpnt,objpos);
3037 // Given a point in the world RF, find the corresponding point in the model RF.
3038 // This is special purpose code, specific for model collision.
3039 // NOTE - this code ASSUMES submodel is 1 level down from hull (detail[0])
3041 // out - point in model RF
3042 // world_pt - point in world RF
3043 // pm - polygon model
3044 // submodel_num - submodel in whose RF we're trying to find the corresponding world point
3045 // orient - orient matrix of ship
3046 // pos - pos vector of ship
3047 void world_find_model_point(vector *out, vector *world_pt, polymodel *pm, int submodel_num, matrix *orient, vector *pos)
3049 SDL_assert( (pm->submodel[submodel_num].parent == pm->detail[0]) || (pm->submodel[submodel_num].parent == -1) );
3051 vector tempv1, tempv2;
3055 vm_vec_sub(&tempv1, world_pt, pos);
3056 vm_vec_rotate(&tempv2, &tempv1, orient);
3058 if (pm->submodel[submodel_num].parent == -1) {
3063 // put into submodel RF
3064 vm_vec_sub2(&tempv2, &pm->submodel[submodel_num].offset);
3065 vm_angles_2_matrix(&m, &pm->submodel[submodel_num].angs);
3066 vm_vec_rotate(out, &tempv2, &m);
3069 // Verify rotating submodel has corresponding ship subsystem -- info in which to store rotation angle
3070 int rotating_submodel_has_ship_subsys(int submodel, ship *shipp)
3072 model_subsystem *psub;
3077 // Go through all subsystems and look for submodel
3078 // the subsystems that need it.
3079 for ( pss = GET_FIRST(&shipp->subsys_list); pss != END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
3080 psub = pss->system_info;
3081 if (psub->subobj_num == submodel) {
3090 void model_get_rotating_submodel_list(int *submodel_list, int *num_rotating_submodels, object *objp)
3092 SDL_assert(objp->type == OBJ_SHIP);
3094 // Check if not currently rotating - then treat as part of superstructure.
3095 int modelnum = Ships[objp->instance].modelnum;
3096 polymodel *pm = model_get(modelnum);
3097 bsp_info *child_submodel;
3099 *num_rotating_submodels = 0;
3100 child_submodel = &pm->submodel[pm->detail[0]];
3102 int i = child_submodel->first_child;
3104 child_submodel = &pm->submodel[i];
3106 // Don't check it or its children if it is destroyed or it is a replacement (non-moving)
3107 if ( !child_submodel->blown_off && (child_submodel->i_replace == -1) ) {
3109 // Only look for submodels that rotate
3110 if (child_submodel->movement_type == MOVEMENT_TYPE_ROT) {
3112 // find ship subsys and check submodel rotation is less than max allowed.
3113 ship *pship = &Ships[objp->instance];
3114 ship_subsys *subsys;
3116 for ( subsys = GET_FIRST(&pship->subsys_list); subsys !=END_OF_LIST(&pship->subsys_list); subsys = GET_NEXT(subsys) ) {
3117 SDL_assert(subsys->system_info->model_num == modelnum);
3118 if (i == subsys->system_info->subobj_num) {
3119 // found the correct subsystem - now check delta rotation angle not too large
3120 float delta_angle = get_submodel_delta_angle(&subsys->submodel_info_1);
3121 if (delta_angle < MAX_SUBMODEL_COLLISION_ROT_ANGLE) {
3122 SDL_assert(*num_rotating_submodels < MAX_ROTATING_SUBMODELS-1);
3123 submodel_list[(*num_rotating_submodels)++] = i;
3130 i = child_submodel->next_sibling;
3134 //#define MODEL_CHECK
3136 ship *pship = &Ships[objp->instance];
3137 for (int idx=0; idx<*num_rotating_submodels; idx++) {
3138 int valid = rotating_submodel_has_ship_subsys(submodel_list[idx], pship);
3139 // SDL_assert( valid );
3142 Warning( LOCATION, "Ship %s has rotating submodel [%s] without ship subsystem\n", pship->ship_name, pm->submodel[submodel_list[idx]].name );
3143 pm->submodel[submodel_list[idx]].movement_type &= ~MOVEMENT_TYPE_ROT;
3144 *num_rotating_submodels = 0;
3152 // Given a direction (pnt) that is in sub_model_num's frame of
3153 // reference, and given the object's orient and position,
3154 // return the point in 3-space in outpnt.
3155 void model_find_world_dir(vector * out_dir, vector *in_dir,int model_num, int sub_model_num, matrix * objorient, vector * objpos )
3161 polymodel *pm = model_get(model_num);
3166 //instance up the tree for this point
3167 while ((mn>-1) && (pm->submodel[mn].parent > -1) ) {
3169 vm_angles_2_matrix(&m,&pm->submodel[mn].angs );
3170 vm_vec_unrotate(&tpnt,&pnt,&m);
3173 mn = pm->submodel[mn].parent;
3176 //now instance for the entire object
3177 vm_vec_unrotate(out_dir,&pnt,objorient);
3182 // Clears all the submodel instances stored in a model to their defaults.
3183 void model_clear_instance(int model_num)
3188 pm = model_get(model_num);
3190 // reset textures to original ones
3191 for (i=0; i<pm->n_textures; i++ ) {
3192 pm->textures[i] = pm->original_textures[i];
3195 for (i=0; i<pm->n_models; i++ ) {
3196 bsp_info *sm = &pm->submodel[i];
3198 if ( pm->submodel[i].is_damaged ) {
3207 // set pointer to other ship subsystem info [turn rate, accel, moment, axis, ...]
3210 sm->num_arcs = 0; // Turn off any electric arcing effects
3213 for (i=0; i<pm->num_lights; i++ ) {
3214 pm->lights[i].value = 0.0f;
3217 interp_clear_instance();
3219 // if ( key_pressed(SDLK_1) ) pm->lights[0].value = 1.0f/255.0f;
3220 // if ( key_pressed(SDLK_2) ) pm->lights[1].value = 1.0f/255.0f;
3221 // if ( key_pressed(SDLK_3) ) pm->lights[2].value = 1.0f/255.0f;
3222 // if ( key_pressed(SDLK_4) ) pm->lights[3].value = 1.0f/255.0f;
3223 // if ( key_pressed(SDLK_5) ) pm->lights[4].value = 1.0f/255.0f;
3224 // if ( key_pressed(SDLK_6) ) pm->lights[5].value = 1.0f/255.0f;
3229 // initialization during ship set
3230 void model_clear_instance_info( submodel_instance_info * sii )
3236 sii->prev_angs.p = 0.0f;
3237 sii->prev_angs.b = 0.0f;
3238 sii->prev_angs.h = 0.0f;
3240 sii->cur_turn_rate = 0.0f;
3241 sii->desired_turn_rate = 0.0f;
3242 sii->turn_accel = 0.0f;
3245 // initialization during ship set
3246 void model_set_instance_info(submodel_instance_info *sii, float turn_rate, float turn_accel)
3252 sii->prev_angs.p = 0.0f;
3253 sii->prev_angs.b = 0.0f;
3254 sii->prev_angs.h = 0.0f;
3256 sii->cur_turn_rate = turn_rate * 0.0f;
3257 sii->desired_turn_rate = turn_rate;
3258 sii->turn_accel = turn_accel;
3260 sii->step_zero_timestamp = timestamp();
3265 // Sets the submodel instance data in a submodel (for all detail levels)
3266 void model_set_instance(int model_num, int sub_model_num, submodel_instance_info * sii)
3271 pm = model_get(model_num);
3273 SDL_assert( sub_model_num >= 0 );
3274 SDL_assert( sub_model_num < pm->n_models );
3276 if ( sub_model_num < 0 ) return;
3277 if ( sub_model_num >= pm->n_models ) return;
3278 bsp_info *sm = &pm->submodel[sub_model_num];
3280 // Set the "blown out" flags
3281 sm->blown_off = sii->blown_off;
3283 if ( sm->blown_off ) {
3284 if ( sm->my_replacement > -1 ) {
3285 pm->submodel[sm->my_replacement].blown_off = 0;
3286 pm->submodel[sm->my_replacement].angs = sii->angs;
3287 pm->submodel[sm->my_replacement].sii = sii;
3290 if ( sm->my_replacement > -1 ) {
3291 pm->submodel[sm->my_replacement].blown_off = 1;
3296 sm->angs = sii->angs;
3299 // For all the detail levels of this submodel, set them also.
3300 for (i=0; i<sm->num_details; i++ ) {
3301 model_set_instance(model_num, sm->details[i], sii );
3306 // Finds a point on the rotation axis of a submodel, used in collision, generally find rotational velocity
3307 void model_init_submodel_axis_pt(submodel_instance_info *sii, int model_num, int submodel_num)
3310 vector *mpoint1, *mpoint2;
3311 vector p1, v1, p2, v2, int1;
3313 polymodel *pm = model_get(model_num);
3314 SDL_assert(pm->submodel[submodel_num].movement_type == MOVEMENT_TYPE_ROT);
3320 // find 2 fixed points in submodel RF
3321 // these will be rotated to about the axis an angle of 0 and PI and we'll find the intersection of the
3322 // two lines to find a point on the axis
3323 if (pm->submodel[submodel_num].movement_axis == MOVEMENT_AXIS_X) {
3324 axis = vmd_x_vector;
3325 mpoint1 = &vmd_y_vector;
3326 mpoint2 = &vmd_z_vector;
3327 } else if (pm->submodel[submodel_num].movement_axis == MOVEMENT_AXIS_Y) {
3328 mpoint1 = &vmd_x_vector;
3329 axis = vmd_z_vector; // rotation about y is a change in heading (p,b,h), so we need z
3330 mpoint2 = &vmd_z_vector;
3331 } else if (pm->submodel[submodel_num].movement_axis == MOVEMENT_AXIS_Z) {
3332 mpoint1 = &vmd_x_vector;
3333 mpoint2 = &vmd_y_vector;
3334 axis = vmd_y_vector; // rotation about z is a change in bank (p,b,h), so we need y
3336 // must be one of these axes or submodel_rot_hit is incorrectly set
3340 // copy submodel angs
3341 angles copy_angs = pm->submodel[submodel_num].angs;
3343 // find two points rotated into model RF when angs set to 0
3344 vm_vec_copy_scale((vector*)&pm->submodel[submodel_num].angs, &axis, 0.0f);
3345 model_find_world_point(&p1, mpoint1, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector);
3346 model_find_world_point(&p2, mpoint2, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector);
3348 // find two points rotated into model RF when angs set to PI
3349 vm_vec_copy_scale((vector*)&pm->submodel[submodel_num].angs, &axis, PI);
3350 model_find_world_point(&v1, mpoint1, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector);
3351 model_find_world_point(&v2, mpoint2, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector);
3353 // reset submodel angs
3354 pm->submodel[submodel_num].angs = copy_angs;
3356 // find direction vectors of the two lines
3357 vm_vec_sub2(&v1, &p1);
3358 vm_vec_sub2(&v2, &p2);
3360 // find the intersection of the two lines
3362 fvi_two_lines_in_3space(&p1, &v1, &p2, &v2, &s, &t);
3364 // find the actual intersection points
3365 vm_vec_scale_add(&int1, &p1, &v1, s);
3368 sii->pt_on_axis = int1;
3373 // Adds an electrical arcing effect to a submodel
3374 void model_add_arc(int model_num, int sub_model_num, vector *v1, vector *v2, int arc_type )
3378 pm = model_get(model_num);
3380 if ( sub_model_num == -1 ) {
3381 sub_model_num = pm->detail[0];
3384 SDL_assert( sub_model_num >= 0 );
3385 SDL_assert( sub_model_num < pm->n_models );
3387 if ( sub_model_num < 0 ) return;
3388 if ( sub_model_num >= pm->n_models ) return;
3389 bsp_info *sm = &pm->submodel[sub_model_num];
3391 if ( sm->num_arcs < MAX_ARC_EFFECTS ) {
3392 sm->arc_type[sm->num_arcs] = (ubyte)arc_type;
3393 sm->arc_pts[sm->num_arcs][0] = *v1;
3394 sm->arc_pts[sm->num_arcs][1] = *v2;
3399 // function to return an index into the docking_bays array which matches the criteria passed
3400 // to this function. dock_type is one of the DOCK_TYPE_XXX defines in model.h
3401 int model_find_dock_index(int modelnum, int dock_type)
3407 pm = model_get(modelnum);
3409 // no docking points -- return -1
3410 if ( pm->n_docks <= 0 )
3413 for (i = 0; i < pm->n_docks; i++ ) {
3414 if ( dock_type & pm->docking_bays[i].type_flags )
3421 int model_get_dock_index_type(int modelnum, int index)
3423 polymodel *pm = model_get(modelnum);
3425 return pm->docking_bays[index].type_flags;
3428 // get all the different docking point types on a model
3429 int model_get_dock_types(int modelnum)
3434 pm = model_get(modelnum);
3435 for (i=0; i<pm->n_docks; i++)
3436 type |= pm->docking_bays[i].type_flags;
3441 // function to return an index into the docking_bays array which matches the string passed
3442 // Fred uses strings to identify docking positions. This functin also accepts generic strings
3443 // so that a desginer doesn't have to know exact names if building a mission from hand.
3444 int model_find_dock_name_index( int modelnum, char *name )
3449 pm = model_get(modelnum);
3450 if ( pm->n_docks <= 0 )
3453 // check the generic names and call previous function to find first dock point of
3454 // the specified type
3455 if ( !SDL_strcasecmp(name, "cargo") )
3456 return model_find_dock_index( modelnum, DOCK_TYPE_CARGO );
3457 else if (!SDL_strcasecmp( name, "rearm") )
3458 return model_find_dock_index( modelnum, DOCK_TYPE_REARM );
3459 else if (!SDL_strcasecmp( name, "generic") )
3460 return model_find_dock_index( modelnum, DOCK_TYPE_GENERIC );
3462 for (i = 0; i < pm->n_docks; i++ ) {
3463 if ( !SDL_strcasecmp(pm->docking_bays[i].name, name) )
3467 // if we get here, name wasn't found -- return -1 and hope for the best
3471 // returns the actual name of a docking point on a model, needed by Fred.
3472 char *model_get_dock_name(int modelnum, int index)
3476 pm = model_get(modelnum);
3477 SDL_assert((index >= 0) && (index < pm->n_docks));
3478 return pm->docking_bays[index].name;
3481 int model_get_num_dock_points(int modelnum)
3485 pm = model_get(modelnum);
3489 #if SDL_BYTEORDER == SDL_BIG_ENDIAN // tigital
3490 void swap_bsp_defpoints(ubyte * p)
3493 int nverts = INTEL_INT( w(p+8) );
3495 int offset = INTEL_INT( w(p+16) );
3497 int n_norms = INTEL_INT( w(p+12) );
3500 ubyte * normcount = p+20;
3501 vector *src = vp(p+offset);
3503 SDL_assert( nverts < MAX_POLYGON_VECS );
3504 // SDL_assert( nnorms < MAX_POLYGON_NORMS );
3506 for (n=0; n<nverts; n++ ) {
3507 src->xyz.x = INTEL_FLOAT( src->xyz.x );
3508 src->xyz.y = INTEL_FLOAT( src->xyz.y );
3509 src->xyz.z = INTEL_FLOAT( src->xyz.z );
3511 Interp_verts[n] = src;
3513 for (i=0;i<normcount[n];i++){
3514 src->xyz.x = INTEL_FLOAT( src->xyz.x );
3515 src->xyz.y = INTEL_FLOAT( src->xyz.y );
3516 src->xyz.z = INTEL_FLOAT( src->xyz.z );
3522 void swap_bsp_tmappoly( polymodel * pm, ubyte * p )
3525 model_tmap_vert *verts;
3526 vector * normal = vp(p+8);
3527 vector * center = vp(p+20);
3528 float radius = INTEL_FLOAT( fl(p+32) );
3530 normal->xyz.x = INTEL_FLOAT( normal->xyz.x );
3531 normal->xyz.y = INTEL_FLOAT( normal->xyz.y );
3532 normal->xyz.z = INTEL_FLOAT( normal->xyz.z );
3533 center->xyz.x = INTEL_FLOAT( center->xyz.x );
3534 center->xyz.y = INTEL_FLOAT( center->xyz.y );
3535 center->xyz.z = INTEL_FLOAT( center->xyz.z );
3537 nv = INTEL_INT( w(p+36));
3539 int tmap_num = INTEL_INT( w(p+40) );
3542 if ( nv < 0 ) return;
3544 verts = (model_tmap_vert *)(p+44);
3546 verts[i].vertnum = INTEL_SHORT( verts[i].vertnum );
3547 verts[i].normnum = INTEL_SHORT( verts[i].normnum );
3548 verts[i].u = INTEL_FLOAT( verts[i].u );
3549 verts[i].v = INTEL_FLOAT( verts[i].v );
3552 if ( pm->version < 2003 ) {
3553 // Set the "normal_point" part of field to be the center of the polygon
3554 vector center_point;
3555 vm_vec_zero( ¢er_point );
3557 for (i=0;i<nv;i++) {
3558 vm_vec_add2( ¢er_point, Interp_verts[verts[i].vertnum] );
3561 center_point.xyz.x /= nv;
3562 center_point.xyz.y /= nv;
3563 center_point.xyz.z /= nv;
3565 *vp(p+20) = center_point;
3569 for (i=0;i<nv;i++) {
3570 float dist = vm_vec_dist( ¢er_point, Interp_verts[verts[i].vertnum] );
3578 void swap_bsp_flatpoly( polymodel * pm, ubyte * p )
3582 vector * normal = vp(p+8);
3583 vector * center = vp(p+20);
3584 float radius = INTEL_FLOAT( fl(p+32) );
3586 mprintf(("flatpoly radius = %f\n", radius ));
3587 normal->xyz.x = INTEL_FLOAT( normal->xyz.x );
3588 normal->xyz.y = INTEL_FLOAT( normal->xyz.y );
3589 normal->xyz.z = INTEL_FLOAT( normal->xyz.z );
3590 center->xyz.x = INTEL_FLOAT( center->xyz.x );
3591 center->xyz.y = INTEL_FLOAT( center->xyz.y );
3592 center->xyz.z = INTEL_FLOAT( center->xyz.z );
3594 nv = INTEL_INT( w(p+36)); //tigital
3597 if ( nv < 0 ) return;
3599 verts = (short *)(p+44);
3600 for (i=0; i<nv*2; i++){
3601 verts[i] = INTEL_SHORT( verts[i] );
3604 if ( pm->version < 2003 ) {
3605 // Set the "normal_point" part of field to be the center of the polygon
3606 vector center_point;
3607 vm_vec_zero( ¢er_point );
3609 for (i=0;i<nv;i++) {
3610 vm_vec_add2( ¢er_point, Interp_verts[verts[i*2]] );
3613 center_point.xyz.x /= nv;
3614 center_point.xyz.y /= nv;
3615 center_point.xyz.z /= nv;
3617 *vp(p+20) = center_point;
3621 for (i=0;i<nv;i++) {
3622 float dist = vm_vec_dist( ¢er_point, Interp_verts[verts[i*2]] );
3630 void swap_bsp_sortnorms( polymodel * pm, ubyte * p )
3632 int frontlist = INTEL_INT( w(p+36) );
3633 int backlist = INTEL_INT( w(p+40) );
3634 int prelist = INTEL_INT( w(p+44) );
3635 int postlist = INTEL_INT( w(p+48) );
3636 int onlist = INTEL_INT( w(p+52) );
3637 w(p+36) = frontlist;
3643 vector * normal = vp(p+8);
3644 vector * center = vp(p+20);
3645 int tmp = INTEL_INT( w(p+32) );
3647 normal->xyz.x = INTEL_FLOAT( normal->xyz.x );
3648 normal->xyz.y = INTEL_FLOAT( normal->xyz.y );
3649 normal->xyz.z = INTEL_FLOAT( normal->xyz.z );
3650 center->xyz.x = INTEL_FLOAT( center->xyz.x );
3651 center->xyz.y = INTEL_FLOAT( center->xyz.y );
3652 center->xyz.z = INTEL_FLOAT( center->xyz.z );
3654 vector * bmin = vp(p+56);
3655 vector * bmax = vp(p+68);
3656 bmin->xyz.x = INTEL_FLOAT( bmin->xyz.x );
3657 bmin->xyz.y = INTEL_FLOAT( bmin->xyz.y );
3658 bmin->xyz.z = INTEL_FLOAT( bmin->xyz.z );
3659 bmax->xyz.x = INTEL_FLOAT( bmax->xyz.x );
3660 bmax->xyz.y = INTEL_FLOAT( bmax->xyz.y );
3661 bmax->xyz.z = INTEL_FLOAT( bmax->xyz.z );
3663 if (prelist) swap_bsp_data(pm,p+prelist);
3664 if (backlist) swap_bsp_data(pm,p+backlist);
3665 if (onlist) swap_bsp_data(pm,p+onlist);
3666 if (frontlist) swap_bsp_data(pm,p+frontlist);
3667 if (postlist) swap_bsp_data(pm,p+postlist);
3669 void swap_bsp_data( polymodel * pm, void *model_ptr )
3671 ubyte *p = (ubyte *)model_ptr;
3672 int chunk_type, chunk_size;
3676 chunk_type = INTEL_INT( w(p) );
3677 chunk_size = INTEL_INT( w(p+4) );
3679 w(p+4) = chunk_size;
3681 while (chunk_type != OP_EOF) {
3683 switch (chunk_type) {
3687 swap_bsp_defpoints(p);
3690 swap_bsp_flatpoly(pm, p ); break;
3692 swap_bsp_tmappoly(pm, p ); break;
3694 swap_bsp_sortnorms(pm,p ); break;
3698 min->xyz.x = INTEL_FLOAT( min->xyz.x );
3699 min->xyz.y = INTEL_FLOAT( min->xyz.y );
3700 min->xyz.z = INTEL_FLOAT( min->xyz.z );
3701 max->xyz.x = INTEL_FLOAT( max->xyz.x );
3702 max->xyz.y = INTEL_FLOAT( max->xyz.y );
3703 max->xyz.z = INTEL_FLOAT( max->xyz.z );
3706 mprintf(( "Bad chunk type %d, len=%d in modelread:swap_bsp_data\n", chunk_type, chunk_size ));
3707 Int3(); // Bad chunk type!
3711 chunk_type = INTEL_INT( w(p));
3712 chunk_size = INTEL_INT( w(p+4) );
3714 w(p+4) = chunk_size;
3718 #endif // SDL_BYTEORDER == SDL_BIG_ENDIAN