mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 04:49:39 -10:00
5456 lines
162 KiB
C++
5456 lines
162 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* $Logfile: /Freespace2/code/Model/ModelRead.cpp $
|
|
* $Revision: 2.105.2.21 $
|
|
* $Date: 2007-12-03 04:47:02 $
|
|
* $Author: Goober5000 $
|
|
*
|
|
* file which reads and deciphers POF information
|
|
*
|
|
* $Log: not supported by cvs2svn $
|
|
* Revision 2.105.2.20 2007/12/02 08:21:46 Goober5000
|
|
* watch out for bad MOI values
|
|
*
|
|
* Revision 2.105.2.19 2007/10/21 22:11:17 Goober5000
|
|
* fix a minor bug in the dockpoint system (affects sm2-07a in the Port)
|
|
*
|
|
* Revision 2.105.2.18 2007/02/21 01:43:32 Goober5000
|
|
* remove duplicate model texture replacement
|
|
*
|
|
* Revision 2.105.2.17 2007/02/20 04:19:22 Goober5000
|
|
* the great big duplicate model removal commit
|
|
*
|
|
* Revision 2.105.2.16 2007/02/12 00:19:48 taylor
|
|
* IBX version 2 support (includes Bobboau's earlier D3D fixes for it)
|
|
*
|
|
* Revision 2.105.2.15 2006/12/28 22:47:15 Goober5000
|
|
* fix spelling... *twitch*
|
|
*
|
|
* Revision 2.105.2.14 2006/11/04 17:22:54 Goober5000
|
|
* improve error message
|
|
*
|
|
* Revision 2.105.2.13 2006/11/01 18:35:57 taylor
|
|
* make glow_point array for thrusters and glow_point_banks dynamic (a proper fix for old Mantis bug #43)
|
|
*
|
|
* Revision 2.105.2.12 2006/10/28 04:01:33 taylor
|
|
* forgot this part of the render box fix (obviously no one ever really tried to use this feature, since it has never worked)
|
|
*
|
|
* Revision 2.105.2.11 2006/10/24 13:24:11 taylor
|
|
* various bits of cleanup (slight reformatting to help readability, remove old/dead code bits, etc.)
|
|
* deal with a index_buffer memory leak that Valgrind has always complained about
|
|
* make HTL model buffers dynamic (get rid of MAX_BUFFERS_PER_SUBMODEL)
|
|
* get rid of MAX_BUFFERS
|
|
* make D3D vertex buffers dynamic, like OGL has already done
|
|
*
|
|
* Revision 2.105.2.10 2006/08/19 04:38:47 taylor
|
|
* maybe optimize the (PI/2), (PI*2) and (RAND_MAX/2) stuff a little bit
|
|
*
|
|
* Revision 2.105.2.9 2006/07/21 16:08:32 taylor
|
|
* minor changes to game_busy() debug text
|
|
* - don't alloc it for bmpman, and make sure to only call on textures that we are loading
|
|
* - add text for model loading, gets rid of the long-wait issue when it appears to not be doing anything early on
|
|
*
|
|
* Revision 2.105.2.8 2006/07/17 01:09:45 taylor
|
|
* make glow point banks dynamic
|
|
*
|
|
* Revision 2.105.2.7 2006/07/13 22:11:37 taylor
|
|
* fix for animated texture map issues (*part one*), this should be faster than before too, and fix inf-loop/div-by-0 issues
|
|
*
|
|
* Revision 2.105.2.6 2006/07/06 21:53:58 taylor
|
|
* rest of the map/glow changes
|
|
* - put glowmap activity back on a per-ship basis (via a SF2_* flag) rather than per-model
|
|
* - same for glowpoints, back on a per-ship basis
|
|
* - put specmaps and bumpmap back on a LOD0 and LOD1 affect (got changed to LOD0 only recently)
|
|
* - fix glowmaps for shockwaves again
|
|
* - add support for animated specmaps (mainly for TBP and Starfox mods)
|
|
* some minor code cleanup and compiler warning fixes
|
|
*
|
|
* Revision 2.105.2.5 2006/07/06 04:20:34 Goober5000
|
|
* GAH - repair a borked commit (plus a couple of typos)
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.105.2.4 2006/07/06 04:06:01 Goober5000
|
|
* 1) complete (almost) changeover to reorganized texture mapping system
|
|
* 2) finally fix texture animation; textures now animate at the correct speed
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.105.2.3 2006/07/05 23:36:56 Goober5000
|
|
* cvs comment tweaks
|
|
*
|
|
* Revision 2.105.2.2 2006/07/04 07:42:10 Goober5000
|
|
* --in preparation for fixing an annoying animated texture bug, reorganize the various texture structs and glow point structs and clarify several parts of the texture code :P
|
|
* --this breaks animated glow maps, and animated regular maps still aren't fixed, but these will be remedied shortly
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.105.2.1 2006/06/04 01:03:13 Goober5000
|
|
* add fighterbay restriction code
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.105 2006/05/31 03:05:42 Goober5000
|
|
* some cosmetic changes in preparation for bay arrival/departure code
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.104 2006/05/27 16:57:13 taylor
|
|
* comment out the model cache stuff, it's old and not actually used anyway
|
|
* minor cleanup of some modelinterp.cpp code, to make it more readable
|
|
* fix tertiary thruster batcher which had the radius value swapped
|
|
* comment out a couple places where we were doing a D3D or OGL check (it's always true so why bother)
|
|
* add more support for NO_DIRECT3D so that it can be used on Windows to build without D3D
|
|
*
|
|
* Revision 2.103 2006/05/16 15:54:39 Goober5000
|
|
* fix for Mantis #875
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.102 2006/04/20 06:32:15 Goober5000
|
|
* proper capitalization according to Volition
|
|
*
|
|
* Revision 2.101 2006/04/18 00:56:28 bobboau
|
|
* bugfix for the animation system
|
|
*
|
|
* Revision 2.100 2006/04/14 18:37:06 taylor
|
|
* more checking to make sure that the malloc calls are actually needed
|
|
*
|
|
* Revision 2.99 2006/04/13 12:19:33 taylor
|
|
* make sure we don't allocate any memory for turrets unless there are some, was causing a few various memory issues, mainly for debug builds
|
|
* (there is more of this same checking on the way for the other malloc() calls during model loading, and vm_malloc() will be set to
|
|
* Assert() on a 0 or less size as well)
|
|
*
|
|
* Revision 2.98 2006/03/25 22:44:52 taylor
|
|
* fix model_allocate_interp_data() extern for OSX
|
|
*
|
|
* Revision 2.97 2006/03/18 10:25:05 taylor
|
|
* never try and load glow or shine maps if we don't have a base map for it
|
|
* but WMC's bumpmap block in a BUMPMAPPING define since it useless most of the time
|
|
*
|
|
* Revision 2.96 2006/03/08 05:15:20 taylor
|
|
* add model_allocate_interp_data() extern for PPC machines
|
|
*
|
|
* Revision 2.95 2006/02/27 00:40:34 wmcoolmon
|
|
* Consistency in Lua 2D funcs; fix FRED2 loading screen images dialog boxes.
|
|
*
|
|
* Revision 2.94 2006/02/26 11:09:31 taylor
|
|
* one friggin line, should have been able to notice that sooner
|
|
*
|
|
* Revision 2.93 2006/02/25 21:47:07 Goober5000
|
|
* spelling
|
|
*
|
|
* Revision 2.92 2006/02/24 07:32:11 taylor
|
|
* vm_extract_angles_vector() doesn't do the same thing that this code used to so don't use it
|
|
*
|
|
* Revision 2.91 2006/02/16 05:31:00 taylor
|
|
* NULL vec warning fix (basically just setup as if it were NULL and skip the extra work if needed)
|
|
* more bmpman related fixes, mainly addresses the condition where it would try to render textures that have been released
|
|
* have model_unload() run through Ship_info[] and clear out modelnum's when they are free'd (makes more sense here than doing it on level load to keep counts right)
|
|
* fix missing subsystem warning messages
|
|
*
|
|
* Revision 2.90 2006/01/26 03:23:30 Goober5000
|
|
* pare down the pragmas some more
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.89 2006/01/18 16:14:04 taylor
|
|
* allow gr_render_buffer() to take TMAP flags
|
|
* let gr_render_buffer() render untextured polys (OGL only until some D3D people fix it on their side)
|
|
* add MR_SHOW_OUTLINE_HTL flag so we easily render using HTL mode for wireframe views
|
|
* make Interp_verts/Interp_norms/etc. dynamic and get rid of the extra htl_* versions
|
|
*
|
|
* Revision 2.88 2006/01/16 07:46:03 phreak
|
|
* Only display the capped normal message in debug builds
|
|
*
|
|
* Revision 2.87 2006/01/14 19:54:55 wmcoolmon
|
|
* Special shockwave and moving capship bugfix, (even more) scripting stuff, slight rearrangement of level management functions to facilitate scripting access.
|
|
*
|
|
* Revision 2.86 2006/01/11 21:23:43 wmcoolmon
|
|
* Made turret handling code more descriptive
|
|
*
|
|
* Revision 2.85 2005/12/29 08:08:37 wmcoolmon
|
|
* Codebase commit, most notably including objecttypes.tbl
|
|
*
|
|
* Revision 2.84 2005/12/15 04:31:05 phreak
|
|
* [V] can't spell 'turret'
|
|
*
|
|
* Revision 2.83 2005/11/13 06:47:26 taylor
|
|
* make sure that we don't get "-transport" in with the transparent filename check (TBP bug)
|
|
*
|
|
* Revision 2.82 2005/11/08 01:04:00 wmcoolmon
|
|
* More warnings instead of Int3s/Asserts, better Lua scripting, weapons_expl.tbl is no longer needed nor read, added "$Disarmed ImpactSnd:", fire-beam fix
|
|
*
|
|
* Revision 2.81 2005/10/29 09:00:41 wmcoolmon
|
|
* Minor change to an assert
|
|
*
|
|
* Revision 2.80 2005/10/22 22:22:41 Goober5000
|
|
* rolled back UnknownPlayer's commit
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.78 2005/10/20 15:33:44 taylor
|
|
* minor readability fix
|
|
* plug a memmory leak and possible Polygon_models[] madness if a POF can't be found or is otherwise invalid
|
|
*
|
|
* Revision 2.77 2005/10/17 14:23:21 taylor
|
|
* lowercase filenames in model_load_textures()
|
|
* turn texture loading debug messages back on but under "Maps" so they are easy to turn off if not wanted
|
|
*
|
|
* Revision 2.76 2005/10/17 01:51:01 wmcoolmon
|
|
* Weapon models now shown in lab
|
|
*
|
|
* Revision 2.75 2005/10/02 23:00:39 Goober5000
|
|
* juxtaposition for clarity
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.74 2005/09/15 23:54:58 Kazan
|
|
* comment out superfluous debug message
|
|
*
|
|
* Revision 2.73 2005/07/07 16:32:33 taylor
|
|
* compiler warning fixes
|
|
* add -noibx troubleshooting cmdline option to disable use of IBX files
|
|
* don't try to set thuster object number is only one thruster bank is specified (default method should go into affect)
|
|
*
|
|
* Revision 2.72 2005/06/22 15:25:41 taylor
|
|
* compiler warning fix (dizzy) and make sure that it's safe to continue after that switch statement
|
|
*
|
|
* Revision 2.71 2005/06/19 02:42:21 taylor
|
|
* really needed to mention those two things as well given the types of changes those are
|
|
*
|
|
* Revision 2.70 2005/06/19 02:28:56 taylor
|
|
* add a _fast version of bm_unload() to be used in modelinterp and future graphics API code
|
|
* clean up some modelinterp code to not use memcpy() everywhere so it's more platform compatible and matches old code (Jens Granseuer)
|
|
* NaN check to catch shards-of-death and prevent hitting an Assert() (Jens Granseuer)
|
|
* fix jumpnode code to catch model errors and close a memory leak
|
|
* make the call to bm_unload_all() after model_free_all() since we will get bmpman screwups otherwise
|
|
* don't show hardware sound RAM when using OpenAL build, it will always be 0
|
|
* print top-right memory figures in debug builds slighly further over when 1024+ res
|
|
* IBX checks for size and make sure we don't try to use a zero byte file
|
|
* don't replicate base textures to specular textures since it screws up bmpman accounting
|
|
*
|
|
* Revision 2.69 2005/06/03 18:18:02 taylor
|
|
* some IBX debugging info to help determine if an IBX is corrupt post-mortem
|
|
*
|
|
* Revision 2.68 2005/05/28 19:41:56 taylor
|
|
* add a NULL check to model_get() for better error detection over an outright CTD
|
|
*
|
|
* Revision 2.67 2005/05/12 17:49:14 taylor
|
|
* use vm_malloc(), vm_free(), vm_realloc(), vm_strdup() rather than system named macros
|
|
* fixes various problems and is past time to make the switch
|
|
*
|
|
* Revision 2.66 2005/05/11 00:25:42 phreak
|
|
* reverted a change that WMC made. It made Goober mad and delayed the release of
|
|
* 3.6.6 by two months.
|
|
*
|
|
* Revision 2.65 2005/04/25 00:26:53 wmcoolmon
|
|
* Dynamically allocated engine washes; subsystem sounds
|
|
*
|
|
* Revision 2.64 2005/04/21 15:49:21 taylor
|
|
* update of bmpman and model bitmap management, well tested but things may get a bit bumpy
|
|
* - use VM_* macros for bmpman since it didn't seem to register the memory correctly (temporary)
|
|
* - a little "stupid" fix for dds bitmap reading
|
|
* - fix it so that memory is released properly on bitmap read errors
|
|
* - some cleanup to model texture loading
|
|
* - allow model textures to get released rather than just unloaded, saves bitmap slots
|
|
* - bump MAX_BITMAPS to 4750, should be able to decrease after public testing of new code
|
|
*
|
|
* Revision 2.63 2005/04/05 05:53:20 taylor
|
|
* s/vector/vec3d/g, better support for different compilers (Jens Granseuer)
|
|
*
|
|
* Revision 2.62 2005/03/24 23:36:14 taylor
|
|
* fix compiler warnings with mismatched types and unused variables
|
|
* cleanup some debug messages so they can be turned off if needed
|
|
* get rid of extra strstr() check for thrusters since it should never get that far anyway
|
|
* page_in/page_out of model glows should be better now
|
|
* removed a bunch of unneeded casts and get type specific math functions right
|
|
*
|
|
* Revision 2.61 2005/03/10 08:00:10 taylor
|
|
* change min/max to MIN/MAX to fix GCC problems
|
|
* add lab stuff to Makefile
|
|
* build unbreakage for everything that's not MSVC++ 6
|
|
* lots of warning fixes
|
|
* fix OpenGL rendering problem with ship insignias
|
|
* no Warnings() in non-debug mode for Linux (like Windows)
|
|
* some campaign savefile fixage to stop reverting everyones data
|
|
*
|
|
* Revision 2.60 2005/03/06 17:16:08 taylor
|
|
* can't believe I missed something that obvious. *hides in shame*
|
|
*
|
|
* Revision 2.59 2005/03/03 06:05:30 wmcoolmon
|
|
* Merge of WMC's codebase. "Features and bugs, making Goober say "Grr!", as release would be stalled now for two months for sure"
|
|
*
|
|
* Revision 2.58 2005/03/01 23:08:24 taylor
|
|
* make sure starfield bitmaps render when not in HTL mode
|
|
* slight header fix for osapi.h
|
|
* add some string overflow protection to modelread and bmpman
|
|
* s/NO_NETWORKING/NO_NETWORK/g (Inferno builds)
|
|
*
|
|
* Revision 2.57 2005/03/01 06:55:41 bobboau
|
|
* oh, hey look I've commited something :D
|
|
* animation system, weapon models detail box alt-tab bug, probly other stuff
|
|
*
|
|
* Revision 2.56 2005/02/15 00:06:27 taylor
|
|
* clean up some model related globals
|
|
* code to disable individual thruster glows
|
|
* fix issue where 1 extra OGL light pass didn't render
|
|
*
|
|
* Revision 2.55 2005/02/08 23:49:59 taylor
|
|
* update/add .cvsignore files for project file changes
|
|
* silence warning about deprecated strings.h stuff for MSVC 2005
|
|
* final model_unload() stuff for WMCoolmon, put in missionweaponchoice.cpp
|
|
* remove really old project files
|
|
*
|
|
* Revision 2.54 2005/02/05 04:15:35 taylor
|
|
* more post merge happiness
|
|
*
|
|
* Revision 2.53 2005/02/04 23:29:32 taylor
|
|
* merge with Linux/OSX tree - p0204-3
|
|
*
|
|
* Revision 2.52 2005/01/30 09:27:40 Goober5000
|
|
* nitpicked some boolean tests, and fixed two small bugs
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.51 2005/01/28 11:06:23 Goober5000
|
|
* changed a bunch of transpose-rotate sequences to use unrotate instead
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.50 2005/01/27 11:26:23 Goober5000
|
|
* dock points on rotating submodels is *almost* working
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.49 2005/01/21 08:58:32 taylor
|
|
* cleanup debug messages for texture loading making the output more readable and useful
|
|
*
|
|
* Revision 2.48 2005/01/11 21:38:50 Goober5000
|
|
* multiple ship docking :)
|
|
* don't tell anyone yet... check the SCP internal
|
|
* --Goober500
|
|
*
|
|
* Revision 2.47 2004/12/05 22:01:12 bobboau
|
|
* sevral feature additions that WCS wanted,
|
|
* and the foundations of a submodel animation system,
|
|
* the calls to the animation triggering code (exept the procesing code,
|
|
* wich shouldn't do anything without the triggering code)
|
|
* have been commented out.
|
|
*
|
|
* Revision 2.46 2004/10/09 17:45:45 taylor
|
|
* da simple IBX code
|
|
*
|
|
* Revision 2.45 2004/07/26 20:47:41 Kazan
|
|
* remove MCD complete
|
|
*
|
|
* Revision 2.44 2004/07/12 16:32:56 Kazan
|
|
* MCD - define _MCD_CHECK to use memory tracking
|
|
*
|
|
* Revision 2.43 2004/06/28 02:13:08 bobboau
|
|
* high level index buffer suport and d3d implementation,
|
|
* OGL people need to get this working on your end as it's broke now
|
|
*
|
|
* Revision 2.42 2004/06/06 20:04:28 randomtiger
|
|
* Commented out mprintf, it was swamping debug console.
|
|
* Uncommented some code which I hope has plugged the memory leak.
|
|
*
|
|
* Revision 2.41 2004/05/09 15:02:00 taylor
|
|
* prefer ani over pcx for ship textures
|
|
*
|
|
* Revision 2.40 2004/04/26 12:37:10 taylor
|
|
* don't try to load glow maps if they are disabled
|
|
*
|
|
* Revision 2.39 2004/04/03 02:55:49 bobboau
|
|
* commiting recent minor bug fixes
|
|
*
|
|
* Revision 2.38 2004/04/01 15:31:22 taylor
|
|
* don't use interface anis as ship textures
|
|
*
|
|
* Revision 2.37 2004/03/31 05:42:28 Goober5000
|
|
* got rid of all those nasty warnings from xlocale and so forth; also added comments
|
|
* to indicate which warnings were being disabled
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.36 2004/03/21 10:34:05 bobboau
|
|
* fixed a texture loading bug
|
|
*
|
|
* Revision 2.35 2004/03/20 21:17:13 bobboau
|
|
* fixed -spec comand line option,
|
|
* probly some other stuf
|
|
*
|
|
* Revision 2.34 2004/03/05 09:02:07 Goober5000
|
|
* Uber pass at reducing #includes
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.33 2004/02/27 04:09:56 bobboau
|
|
* fixed a Z buffer error in HTL submodel rendering,
|
|
* and glow points,
|
|
* and other stuff
|
|
*
|
|
* Revision 2.32 2004/02/20 04:29:55 bobboau
|
|
* pluged memory leaks,
|
|
* 3D HTL lasers (they work perfictly)
|
|
* and posably fixed Turnsky's shinemap bug
|
|
*
|
|
* Revision 2.31 2004/02/15 06:02:32 bobboau
|
|
* fixed sevral asorted matrix errors,
|
|
* OGL people make sure I didn't break anything,
|
|
* most of what I did was replaceing falses with (if graphicts_mode == D3D)
|
|
*
|
|
* Revision 2.30 2004/02/13 04:17:14 randomtiger
|
|
* Turned off fog in OGL for Fred.
|
|
* Simulated speech doesnt say tags marked by $ now.
|
|
* The following are fixes to issues that came up testing TBP in fs2_open and fred2_open:
|
|
* Changed vm_vec_mag and parse_tmap to fail gracefully on bad data.
|
|
* Error now given on missing briefing icon and bad ship normal data.
|
|
* Solved more species divide by zero error.
|
|
* Fixed neb cube crash.
|
|
*
|
|
* Revision 2.29 2004/02/07 01:25:15 Goober5000
|
|
* hehe, fixed the subsystem compare so it's not destructive :-p
|
|
* and also fixed the model checking routine
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.28 2004/02/07 00:48:52 Goober5000
|
|
* made FS2 able to account for subsystem mismatches between ships.tbl and the
|
|
* model file - e.g. communication vs. communications
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.27 2003/11/11 18:06:49 phreak
|
|
* made some functions that are there for 2d drawing not modify T&L matrices
|
|
*
|
|
* Revision 2.26 2003/11/11 02:15:45 Goober5000
|
|
* ubercommit - basically spelling and language fixes with some additional
|
|
* warnings disabled
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.25 2003/10/14 17:39:15 randomtiger
|
|
* Implemented hardware fog for the HT&L code path.
|
|
* It doesnt use the backgrounds anymore but its still an improvement.
|
|
* Currently it fogs to a brighter colour than it should because of Bob specular code.
|
|
* I will fix this after discussing it with Bob.
|
|
*
|
|
* Also tided up some D3D stuff, a cmdline variable name and changed a small bit of
|
|
* the htl code to use the existing D3D engine instead of work around it.
|
|
* And added extra information in version number on bottom left of frontend screen.
|
|
*
|
|
* Revision 2.24 2003/10/12 03:41:37 Kazan
|
|
* #Kazan# FS2NetD client code gone multithreaded, some Fred2 Open -mod stuff [obvious code.lib] including a change in cmdline.cpp, changed Stick's "-nohtl" to "-htl" - HTL is _OFF_ by default here (Bobboau and I decided this was a better idea for now)
|
|
*
|
|
* Revision 2.23 2003/10/10 03:59:41 matt
|
|
* Added -nohtl command line param to disable HT&L, nothing is IFDEFd
|
|
* out now. -Sticks
|
|
*
|
|
* Revision 2.22 2003/09/26 14:37:15 bobboau
|
|
* commiting Hardware T&L code, everything is ifdefed out with the compile flag HTL
|
|
* still needs a lot of work, ubt the frame rates were getting with it are incredable
|
|
* the biggest problem it still has is a bad lightmanegment system, and the zbuffer
|
|
* doesn't work well with things still getting rendered useing the sofware pipeline, like thrusters,
|
|
* and weapons, I think these should be modifyed to be sent through hardware,
|
|
* it would be slightly faster and it would likely fix the problem
|
|
*
|
|
* also the thruster glow/particle stuff I did is now in.
|
|
*
|
|
* Revision 2.21 2003/08/28 20:42:18 Goober5000
|
|
* implemented rotating barrels for firing primary weapons
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.20 2003/08/22 07:35:09 bobboau
|
|
* specular code should be bugless now,
|
|
* cell shadeing has been added activated via the comand line '-cell',
|
|
* 3D shockwave models, and a transparency method I'm calling edge and center alpha that could be usefull for other things, ask for details
|
|
*
|
|
* Revision 2.19 2003/08/12 03:18:34 bobboau
|
|
* Specular 'shine' mapping;
|
|
* useing a phong lighting model I have made specular highlights
|
|
* that are mapped to the model,
|
|
* rendering them is still slow, but they look real purdy
|
|
*
|
|
* also 4 new (untested) comand lines, the XX is a floating point value
|
|
* -spec_exp XX
|
|
* the n value, you can set this from 0 to 200 (actualy more than that, but this is the recomended range), it will make the highlights bigger or smaller, defalt is 16.0 so start playing around there
|
|
* -spec_point XX
|
|
* -spec_static XX
|
|
* -spec_tube XX
|
|
* these are factors for the three diferent types of lights that FS uses, defalt is 1.0,
|
|
* static is the local stars,
|
|
* point is weapons/explosions/warp in/outs,
|
|
* tube is beam weapons,
|
|
* for thouse of you who think any of these lights are too bright you can configure them you're self for personal satisfaction
|
|
*
|
|
* Revision 2.18 2003/03/02 05:56:56 penguin
|
|
* Added #ifdef WIN32 around non-standard header file io.h
|
|
* - penguin
|
|
*
|
|
* Revision 2.17 2003/02/25 16:46:01 bobboau
|
|
* fixing my bug fix for the warp model, and not being able to find it
|
|
*
|
|
* Revision 2.16 2003/02/25 06:22:49 bobboau
|
|
* fixed a bunch of fighter beam bugs,
|
|
* most notabley the sound now works corectly,
|
|
* and they have limeted range with atenuated damage (table option)
|
|
* added bank specific compatabilities
|
|
*
|
|
* Revision 2.15 2003/01/19 01:07:41 bobboau
|
|
* redid the way glow maps are handled; you now must set a global variable before you render a poly that uses a glow map, then set it to -1 when you're done with it
|
|
* fixed a few other misc bugs too
|
|
*
|
|
* Revision 2.14 2003/01/17 01:48:49 Goober5000
|
|
* added capability to the $Texture replace code to substitute the textures
|
|
* without needing and extra model, however, this way you can't substitute
|
|
* transparent or animated textures
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.13 2003/01/16 06:49:11 Goober5000
|
|
* yay! got texture replacement to work!!!
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.12 2003/01/15 23:23:30 Goober5000
|
|
* NOW the model duplicates work! :p
|
|
* still gotta do the textures, but it shouldn't be hard now
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.11 2003/01/15 08:57:23 Goober5000
|
|
* assigning duplicate models to ships now works; committing so I have a base
|
|
* to fall back to as I work on texture replacement
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.10 2003/01/15 05:18:13 Goober5000
|
|
* moved texture loading around a bit; added Texture_replace in preparation
|
|
* for some cool stuff
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.9 2003/01/09 21:22:36 phreak
|
|
* fixed a small error in bob's glowmap code
|
|
*
|
|
* Revision 2.8 2003/01/09 05:52:25 Goober5000
|
|
* snagged a potential bug
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.7 2003/01/05 23:41:51 bobboau
|
|
* disabled decals (for now), removed the warp ray thingys,
|
|
* made some better error mesages while parseing weapons and ships tbls,
|
|
* and... oh ya, added glow mapping
|
|
*
|
|
* Revision 2.6 2002/12/07 01:37:42 bobboau
|
|
* initial decals code, if you are worried a bug is being caused by the decals code it's only references are in,
|
|
* collideshipweapon.cpp line 262, beam.cpp line 2771, and modelinterp.cpp line 2949.
|
|
* it needs a better renderer, but is in prety good shape for now,
|
|
* I also (think) I squashed a bug in the warpmodel code
|
|
*
|
|
* Revision 2.5 2002/11/14 04:18:16 bobboau
|
|
* added warp model and type 1 glow points
|
|
* and well as made the new glow file type,
|
|
* some general improvement to fighter beams,
|
|
*
|
|
* Revision 2.4 2002/10/19 19:29:27 bobboau
|
|
* initial commit, trying to get most of my stuff into FSO, there should be most of my fighter beam, beam rendering, beam shield hit, ABtrails, and ssm stuff. one thing you should be happy to know is the beam texture tileing is now set in the beam section section of the weapon table entry
|
|
*
|
|
* Revision 2.3 2002/08/01 01:41:07 penguin
|
|
* The big include file move
|
|
*
|
|
* Revision 2.2 2002/07/10 18:42:14 wmcoolmon
|
|
* Added Bobboau's glow code; all comments include "-Bobboau"
|
|
*
|
|
* Revision 2.1 2002/07/07 19:55:59 penguin
|
|
* Back-port to MSVC
|
|
*
|
|
* Revision 2.0 2002/06/03 04:02:25 penguin
|
|
* Warpcore CVS sync
|
|
*
|
|
* Revision 1.4 2002/05/16 00:42:54 mharris
|
|
* Use hex values instead of multi-chars for section IDs -- fewer
|
|
* compiler complaints and more portable to big-endian architectures.
|
|
*
|
|
* Revision 1.3 2002/05/15 15:00:23 mharris
|
|
* Don't use _splitpath() in non-Win32 -- it's only used for
|
|
* debugging anyway
|
|
*
|
|
* Revision 1.2 2002/05/04 04:52:22 mharris
|
|
* 1st draft at porting
|
|
*
|
|
* Revision 1.1 2002/05/02 18:03:10 mharris
|
|
* Initial checkin - converted filenames and includes to lower case
|
|
*
|
|
*
|
|
* 36 7/22/99 2:26p Mattk
|
|
* DA: don't show engine wash error if no subsystems. (ie, Pofview...)
|
|
*
|
|
* 35 7/19/99 12:02p Andsager
|
|
* Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
|
|
* only blow up subsystem if its strength is > 0
|
|
*
|
|
* 34 7/07/99 5:00p Andsager
|
|
* Fixed bug in stepped rotation
|
|
*
|
|
* 33 7/06/99 4:42p Anoop
|
|
* Add MiniCap hack for engine wash
|
|
*
|
|
* 32 7/06/99 3:52p Andsager
|
|
* That wacky model_subsystem name vs. subobj_name thing bites us in the
|
|
* ass again.
|
|
*
|
|
* 31 7/06/99 3:15p Anoop
|
|
* Better debug info for engine wash error.
|
|
*
|
|
* 30 7/06/99 11:37a Andsager
|
|
* Fix warning message.
|
|
*
|
|
* 29 7/06/99 10:45a Andsager
|
|
* Modify engine wash to work on any ship that is not small. Add AWACS
|
|
* ask for help.
|
|
*
|
|
* 28 7/01/99 4:23p Dave
|
|
* Full support for multiple linked ambient engine sounds. Added "big
|
|
* damage" flag.
|
|
*
|
|
* 27 6/08/99 2:33p Dave
|
|
* Fixed release build warning. Put in hud config stuff.
|
|
*
|
|
* 26 6/01/99 8:35p Dave
|
|
* Finished lockarm weapons. Added proper supercap weapons/damage. Added
|
|
* awacs-set-radius sexpression.
|
|
*
|
|
* 25 5/28/99 1:51p Andsager
|
|
* Allow models to rotate in both direction on an axis, specifying a
|
|
* negative rotation time in submodel properties.
|
|
*
|
|
* 24 5/27/99 3:48p Andsager
|
|
* Add assert to model_read code to not allow overwrite of memory
|
|
*
|
|
* 23 5/27/99 12:11p Andsager
|
|
* Allow more than 1 subsystem to have live debris on any ship
|
|
*
|
|
* 22 5/27/99 10:57a Mattk
|
|
* Put in a warning about too many firing points per turret.
|
|
*
|
|
* 21 5/24/99 5:45p Dave
|
|
* Added detail levels to the nebula, with a decent speedup. Split nebula
|
|
* lightning into its own section.
|
|
*
|
|
* 20 5/19/99 11:09a Andsager
|
|
* Turn on engine wash. Check every 1/4 sec.
|
|
*
|
|
* 19 5/11/99 10:16p Andsager
|
|
* First pass on engine wash effect. Rotation (control input), damage,
|
|
* shake.
|
|
*
|
|
* 18 4/09/99 11:38a Andsager
|
|
* Change POF format for FreeSpace2
|
|
*
|
|
* 17 3/23/99 5:17p Dave
|
|
* Changed model file format somewhat to account for LOD's on insignias
|
|
*
|
|
* 16 3/20/99 3:46p Dave
|
|
* Added support for model-based background nebulae. Added 3 new
|
|
* sexpressions.
|
|
*
|
|
* 15 3/19/99 6:15p Dave
|
|
* Put in checks for insignia bitmaps for having too many faces or
|
|
* vertices
|
|
*
|
|
* 14 2/21/99 6:01p Dave
|
|
* Fixed standalone WSS packets.
|
|
*
|
|
* 13 2/19/99 11:42a Dave
|
|
* Put in model rendering autocentering.
|
|
*
|
|
* 12 2/10/99 3:52p Enricco
|
|
* Fix stupid assert
|
|
*
|
|
* 11 1/14/99 6:06p Dave
|
|
* 100% full squad logo support for single player and multiplayer.
|
|
*
|
|
* 10 1/11/99 12:42p Andsager
|
|
* Add live debris - debris which is created from a destroyed subsystem,
|
|
* when the ship is still alive
|
|
*
|
|
* 9 1/08/99 2:08p Dave
|
|
* Fixed software rendering for pofview. Super early support for AWACS and
|
|
* beam weapons.
|
|
*
|
|
* 8 1/06/99 2:24p Dave
|
|
* Stubs and release build fixes.
|
|
*
|
|
* 7 12/23/98 2:53p Andsager
|
|
* Added stepped rotation support
|
|
*
|
|
* 6 12/04/98 3:34p Andsager
|
|
* Handle norotating submodels
|
|
*
|
|
* 5 12/03/98 3:14p Andsager
|
|
* Check in code that checks rotating submodel actually has ship subsystem
|
|
*
|
|
* 4 11/19/98 11:07p Andsager
|
|
* Check in of physics and collision detection of rotating submodels
|
|
*
|
|
* 3 10/23/98 3:03p Andsager
|
|
* Initial support for changing rotation rate.
|
|
*
|
|
* 2 10/07/98 10:53a Dave
|
|
* Initial checkin.
|
|
*
|
|
* 1 10/07/98 10:50a Dave
|
|
*
|
|
* 177 8/28/98 3:29p Dave
|
|
* EMP effect done. AI effects may need some tweaking as required.
|
|
*
|
|
* 176 5/19/98 8:31p Andsager
|
|
* Added split planes (for big ship explosions)
|
|
*
|
|
* 175 5/07/98 5:39p Andsager
|
|
* Changes to model to hold cross section info
|
|
*
|
|
* 174 4/30/98 4:53p John
|
|
* Restructured and cleaned up cfile code. Added capability to read off
|
|
* of CD-ROM drive and out of multiple pack files.
|
|
*
|
|
* 173 4/22/98 9:43p John
|
|
* Added code to allow checking of invisible faces, flagged by any texture
|
|
* name with invisible in it.
|
|
*
|
|
* 172 4/14/98 11:11p John
|
|
* Made ships with < 50% hull left show electrical damage arcs.
|
|
*
|
|
* 171 4/01/98 5:34p John
|
|
* Made only the used POFs page in for a level. Reduced some interp
|
|
* arrays. Made custom detail level work differently.
|
|
*
|
|
* 170 4/01/98 9:14a Mike
|
|
* Fixed free bug
|
|
*
|
|
* 169 3/31/98 5:18p John
|
|
* Removed demo/save/restore. Made NDEBUG defined compile. Removed a
|
|
* bunch of debug stuff out of player file. Made model code be able to
|
|
* unload models and malloc out only however many models are needed.
|
|
*
|
|
*
|
|
* 168 3/17/98 2:46p Allender
|
|
* Copy subsystem name when copying model subsystems
|
|
*
|
|
* 167 3/02/98 5:42p John
|
|
* Removed WinAVI stuff from FreeSpace. Made all HUD gauges wriggle from
|
|
* afterburner. Made gr_set_clip work good with negative x &y. Made
|
|
* model_caching be on by default. Made each cached model have it's own
|
|
* bitmap id. Made asteroids not rotate when model_caching is on.
|
|
*
|
|
* 166 2/24/98 5:04p Allender
|
|
* allow different ship classes to use the same model. Lot's of subsystem
|
|
* stuff to deal with
|
|
*
|
|
* 165 2/16/98 3:46p John
|
|
* Made mass be proportional to area in newer pofs
|
|
*
|
|
* 164 2/13/98 5:19p John
|
|
* First rev of new model caching system. Needs to be ripped out and
|
|
* moved into ship stuff.
|
|
*
|
|
* 163 1/29/98 5:52p John
|
|
* new version of electrical arcing code
|
|
*
|
|
* 162 1/29/98 8:39a Andsager
|
|
* Changed mass and moment of intertia based area vs. volume
|
|
*
|
|
* 161 1/28/98 2:15p Mike
|
|
* Convert volume to surface area at runtime. Write note to John to
|
|
* cleanup if he modifies bspgen.
|
|
*
|
|
* 160 1/27/98 11:02a John
|
|
* Added first rev of sparks. Made all code that calls model_render call
|
|
* clear_instance first. Made debris pieces not render by default when
|
|
* clear_instance is called.
|
|
*
|
|
* 159 1/19/98 6:15p John
|
|
* Fixed all my Optimized Build compiler warnings
|
|
*
|
|
* 158 1/09/98 11:25a John
|
|
* Took the thruster animation stuff out of the model.
|
|
*
|
|
* 157 12/31/97 4:47p John
|
|
* Made all the eye points fit inside core radius
|
|
*
|
|
* 156 12/31/97 2:35p John
|
|
* Added code for core_radius
|
|
*
|
|
* 155 12/17/97 2:41p John
|
|
* made target bounding box be on hull only.
|
|
*
|
|
* 154 12/17/97 9:54a John
|
|
* added code for precalculated weapon flash lighting.
|
|
*
|
|
* 153 11/29/97 2:05p John
|
|
* made g3_draw_bitmap and g3_draw_rotated bitmap take w&h, not w/2 & h/2,
|
|
* like they used to incorrectly assume. Added code to model to read in
|
|
* thruster radius's.
|
|
*
|
|
* 152 11/25/97 11:41a John
|
|
* added code to read in pof v20.05, which adds radius to thrusters.
|
|
*
|
|
* 151 10/28/97 12:34a Mike
|
|
* Set useful values of mass and inertia matrix if values not otherwise
|
|
* set. Generate Warnings for unexpected ones.
|
|
*
|
|
* 150 10/22/97 5:54p Lawrance
|
|
* get rid of leading '$' when storing parent_name for paths
|
|
*
|
|
* 149 10/20/97 5:57p Lawrance
|
|
* have index to polymodel->paths[] within the model_subsystem struct.
|
|
*
|
|
* 148 9/15/97 5:45p John
|
|
* took out chunk stuff.
|
|
* made pofview display thrusters as blue polies.
|
|
*
|
|
* 147 9/09/97 3:39p Sandeep
|
|
* warning level 4 bugs
|
|
*
|
|
* 146 9/04/97 5:09p Andsager
|
|
* implement physics using moment of inertia and mass (from BSPgen).
|
|
* Added to phys_info struct. Updated ship_info, polymodel structs.
|
|
* Updated weapon ($Mass and $Force) and ship ($Mass -> $Density) tables
|
|
*
|
|
* 145 9/03/97 5:57p Lawrance
|
|
* take out warning
|
|
*
|
|
* 144 9/03/97 4:32p John
|
|
* changed bmpman to only accept ani and pcx's. made passing .pcx or .ani
|
|
* to bm_load functions not needed. Made bmpman keep track of palettes
|
|
* for bitmaps not mapped into game palettes.
|
|
*
|
|
* 143 8/19/97 11:49p Lawrance
|
|
* using atan2_safe() in place of atan2()
|
|
*
|
|
* 142 8/19/97 8:44a John
|
|
* Initial hooks for thruster stuff.
|
|
*
|
|
* 141 8/15/97 4:10p John
|
|
* new code to use the new octrees style bsp trees
|
|
*
|
|
* 140 8/14/97 5:46p John
|
|
* added code to read in mass,center of mass, and moment of interia from
|
|
* the pof. Also added code to display green center of mass dot when Show
|
|
* debug points is on in pofview.
|
|
*
|
|
* 139 7/10/97 8:34a John
|
|
* Added code to read TGA files.
|
|
*
|
|
* 138 6/26/97 3:18p Mike
|
|
* Recognize "turret", etc. in a subsystem field anywhere in name.
|
|
* Generate warning for bogus subsystem names.
|
|
*
|
|
* 137 6/25/97 5:11p John
|
|
* added foundation for model octants.
|
|
*
|
|
* 136 6/25/97 10:28a John
|
|
* Made debris chunks radius be set correctly to the submodel size, not
|
|
* hardcoded to 10.0f.
|
|
*
|
|
* 135 6/06/97 11:55a Mike
|
|
* Fix firing direction for zero piece turrets.
|
|
*
|
|
* 134 5/31/97 2:36p Mike
|
|
* Comment out mprintfs pertaining to debris.
|
|
*
|
|
* 133 5/30/97 4:41p Hoffoss
|
|
* Made some additions required for Fred.
|
|
*
|
|
* 132 5/30/97 3:42p Mike
|
|
* Shield mesh and hit system.
|
|
*
|
|
* 131 5/30/97 10:40a Hoffoss
|
|
* Added a function for Fred to get docking point names.
|
|
*
|
|
* 130 5/29/97 4:34p Allender
|
|
* removed Int3 from model_find_dock_index.
|
|
*
|
|
* 129 4/30/97 10:41a Allender
|
|
* don't dump out token 'none' when dumping out subsystem information
|
|
*
|
|
* 128 4/17/97 6:06p John
|
|
* New code/data for v19 of BSPGEN with smoothing and zbuffer
|
|
* optimizations.
|
|
*
|
|
* 127 4/09/97 3:31p Lawrance
|
|
* if any points of bounding box don't project, return project fail code
|
|
* in model_find_2d_bound_min()
|
|
*
|
|
* 126 4/07/97 10:37a John
|
|
* Took out limits on number of children a submodel can have. (So now
|
|
* capital ships can have tons of children on them).
|
|
*
|
|
* 125 4/03/97 3:18p Allender
|
|
*
|
|
* 124 4/03/97 1:29p Allender
|
|
* subsystem enhancement. Initial work on making subsystem status
|
|
* meaningful
|
|
*
|
|
* 123 3/28/97 2:49p John
|
|
* added code to fix targetting of debris chunks to draw bounding boxes
|
|
* right.
|
|
*
|
|
* 122 3/28/97 1:19p John
|
|
* Made TestCode not show damaged submodels.
|
|
*
|
|
* 121 3/28/97 11:17a John
|
|
* added code for linking lower detail submodels to the highest one; added
|
|
* code to pofview to view these.
|
|
*
|
|
* 120 3/17/97 11:24a John
|
|
*
|
|
* 119 3/15/97 5:06p John
|
|
* added bounding boxes for each subobject and code to display them.
|
|
*
|
|
* 118 3/13/97 10:32a John
|
|
* Added code for tiled 256x256 textures in certain models.
|
|
*
|
|
* 117 3/06/97 5:36p Mike
|
|
* Change vec_normalize_safe() back to vec_normalize().
|
|
* Spruce up docking a bit.
|
|
*
|
|
* 116 3/06/97 1:06p Allender
|
|
* more docking stuff. docking points now specified by strings in mission
|
|
* file. Changes to builtin missions and code to reflect this change.
|
|
*
|
|
* 115 3/06/97 10:56a Mike
|
|
* Write error checking version of vm_vec_normalize().
|
|
* Fix resultant problems.
|
|
*
|
|
* 114 3/06/97 10:05a Lawrance
|
|
* added missing cfclose's
|
|
*
|
|
* 113 3/05/97 5:27p John
|
|
* more work with turret stuff
|
|
*
|
|
* 112 3/05/97 12:49p John
|
|
* added Viewer_obj. Took out the interp_??? variables for turning
|
|
* outline,etc on and put them in flags you pass to model_render.
|
|
* Cleaned up model_interp code to fit new coding styles.
|
|
*
|
|
* 111 3/05/97 12:42p Adam
|
|
* john: fixed bug while model transversel code that assumed the root
|
|
* subobject was 0.
|
|
*
|
|
* 110 3/05/97 11:09a Allender
|
|
* DOH! MAX_SLOTS just way too low. Upped to 25 and Asserts put in to
|
|
* check bounds.
|
|
*
|
|
* 109 3/04/97 5:08p John
|
|
* Started adding debug code to make it so you can view from a turret's
|
|
* view.
|
|
*
|
|
* 108 3/04/97 3:36p John
|
|
* took out old debug "h' key. Made zbuffer flag bit bit field so you
|
|
* can turn on/off each value. Fixed a bug with turret rotation where
|
|
* different handedness turrets wouldn't work. Fixed a bug with two
|
|
* large ships within each other's radius not rendering correctly.
|
|
*
|
|
* 107 3/04/97 12:52p Allender
|
|
* docking styff. Added dock type to docking structure in model code.
|
|
* Renamed structure member in ai_goals. Temporary checkin. (i.e.
|
|
* rearming will use rearm dock points)
|
|
*
|
|
* 106 3/03/97 5:47p John
|
|
* fixed some rotating object discrepencies. Took out the gun firing
|
|
* point average beause it was inconsitent and wsn't as correct as using
|
|
* the pivot point.
|
|
*
|
|
* 105 3/03/97 4:20p Hoffoss
|
|
* Fixed bug in bug I just fixed. :)
|
|
*
|
|
* 104 3/03/97 4:00p Hoffoss
|
|
* fixed bug that caused docking bays without a name to be garbage when
|
|
* loaded.
|
|
*
|
|
* 103 3/03/97 8:58a Lawrance
|
|
* replaced a fprintf() with a cfputs()
|
|
*
|
|
* 102 3/01/97 2:12p Lawrance
|
|
* made to work with new cfile changes
|
|
*
|
|
* 101 2/28/97 10:57a John
|
|
* Made so you can blow off any subsystems, not just radars.
|
|
*
|
|
*
|
|
* 100 2/27/97 3:45p John
|
|
* Added a tree dialog to pofview that shows a model's tree.
|
|
*
|
|
* 99 2/27/97 12:07p John
|
|
* Added code to suppord debris chunks after a ship is exploded..
|
|
*
|
|
* 98 2/26/97 5:28p John
|
|
* Fixed a bunch of chunked model bugs. Basically they almost work better
|
|
* than non-chunked objects!
|
|
*
|
|
* 97 2/24/97 1:13p Allender
|
|
* support for multiple eye positions. Still some kinks to be worked out
|
|
* though....
|
|
*
|
|
* 96 2/20/97 4:06p Allender
|
|
* allow for multiple spline paths to lead to a docking bay
|
|
*
|
|
* 95 2/20/97 3:25p Allender
|
|
* change to docking bay structure -- added a name (for use in Fred
|
|
* mainly), and index into spline paths to reach this docking point
|
|
*
|
|
* 94 2/19/97 4:03p Allender
|
|
* dump a 0.0 instead of a 0 when dumping out missing subsystem
|
|
* information
|
|
*
|
|
* 93 2/19/97 2:52p Allender
|
|
* added crewspot name to subsystems (used only with turrets). Will be
|
|
* used by fred for ai class stuff for turrets
|
|
*
|
|
* 92 2/17/97 4:30p John
|
|
* Added code to automatically rotate any subsystems with the $rotate
|
|
* value set (except for turrets)
|
|
*
|
|
* 91 2/17/97 12:03p John
|
|
* Added code to set the angles of a subobject.
|
|
*
|
|
* 90 2/14/97 4:02p John
|
|
* changed a vector being passed as value to reference.
|
|
*
|
|
* 89 2/14/97 3:16p Mike
|
|
* Start on AI path garbage collection
|
|
*
|
|
* 88 2/14/97 1:06p John
|
|
* upped bspgen version number.
|
|
*
|
|
* 87 2/13/97 10:17a John
|
|
* INCORPORATED THE FOLLOWING CHANGES:
|
|
* ??? 2/13/97 9:49a John
|
|
* added code for geometric centers of objects.
|
|
* ??? 2/12/97 6:30p John
|
|
* upped model version number.
|
|
*
|
|
*
|
|
* 86 2/13/97 9:49a John
|
|
* ROLLEDBACK TO ALLENDER'S 85
|
|
*
|
|
* 85 2/12/97 5:34p Allender
|
|
* added a ton of debug code to help recify the subsystems in ships.tbl
|
|
* with subsystems in the models. Fixed a bug with subsystems not being
|
|
* given the correct type.
|
|
*
|
|
* 84 2/11/97 4:10p Allender
|
|
* major change of subsystem stuff. obj_subsystem renamed to
|
|
* model_subsystem. Dyamically allocate space in ship_info for
|
|
* subsystems.
|
|
*
|
|
* 83 2/10/97 4:20p Allender
|
|
* added distance to closest geometry to spline points
|
|
*
|
|
* 82 2/10/97 10:58a John
|
|
* Added back in sparks for where laser bolts hit.
|
|
* Added code to red damaged objects and replace then.
|
|
*
|
|
* 81 2/07/97 1:24p Allender
|
|
* added turret lists for spline points
|
|
*
|
|
* 80 2/06/97 2:56p John
|
|
* Added a "blown-off" field to the instance of a model. Also took out
|
|
* the model_set_angles functions and replaced with the more general
|
|
* model_set_instance functions.
|
|
*
|
|
* 79 2/04/97 7:37p John
|
|
* Finally!!! Turret stuff working!
|
|
*
|
|
* 78 1/31/97 12:51p John
|
|
* Added turret_fov and turret_fov_cos
|
|
*
|
|
* 77 1/31/97 12:43p John
|
|
* Turrets working nicely, finally!
|
|
*
|
|
* 76 1/31/97 11:36a John
|
|
*
|
|
* 75 1/29/97 4:46p John
|
|
* Code to read $crewpoint flag on turrets and store it in obj_subsystem
|
|
* info.
|
|
*
|
|
* 74 1/29/97 3:14p John
|
|
* Added field to ships.tbl to control how fast turret rotates.
|
|
*
|
|
* 73 1/28/97 11:23a John
|
|
* added functions to get/set model angles.
|
|
*
|
|
* 72 1/28/97 10:07a John
|
|
* More turret stuff, still not working, though.
|
|
*
|
|
* 71 1/27/97 2:46p John
|
|
* more turret stuff
|
|
*
|
|
* 70 1/27/97 11:35a Allender
|
|
* upped compatible object version
|
|
*
|
|
* 69 1/24/97 5:25p John
|
|
* More turret stuff. Not working yet, though.
|
|
*
|
|
* 68 1/24/97 4:55p John
|
|
* Started adding code for turning turrets.
|
|
* Had to change POF format to 18.0
|
|
*
|
|
* 67 1/20/97 2:16p Allender
|
|
* new shield structures being used
|
|
*
|
|
* 66 1/15/97 5:19p Allender
|
|
* new POF version. old version obsolete. Added shield and eye stuff.
|
|
* New format for gun/missile/docking points
|
|
*
|
|
* 65 1/10/97 5:15p Mike
|
|
* Moved ship-specific parameters from obj_subsystem to ship_subsys.
|
|
*
|
|
* Added turret code to AI system.
|
|
*
|
|
* 64 1/10/97 2:25p John
|
|
* Added code to support detail level distances.
|
|
*
|
|
* 63 1/02/97 11:45a Allender
|
|
* changed turret code to have > 1 firing point as well as fixing the
|
|
* problems of turrets being multiple subobjects. Turret norms should be
|
|
* getting stuffed correctly now.
|
|
*
|
|
* 62 12/31/96 4:14p Mike
|
|
* Turret firing.
|
|
* Correct bug in HUDtarget.cpp, showing wrong target.
|
|
*
|
|
* 61 12/27/96 4:09p Mike
|
|
* Integrate John's new model code.
|
|
*
|
|
* 60 12/26/96 1:42p John
|
|
* Changed function ordering to put model_init at top.
|
|
*
|
|
* 59 12/23/96 3:56p John
|
|
* Changed POF code to support pof 15.0.
|
|
*
|
|
* 58 12/23/96 11:00a John
|
|
* Restructured POF stuff to support LOD in one pof.
|
|
*
|
|
* 57 12/20/96 11:55a Allender
|
|
* removed debug code
|
|
*
|
|
* 56 12/20/96 11:40a Allender
|
|
* modifed subsystem stuff to do correct hit percentages and other cool
|
|
* stuff
|
|
*
|
|
* 55 12/18/96 4:04p Allender
|
|
* added "unknown" subsystem type for subsystems which aren't recognized.
|
|
*
|
|
* 54 12/17/96 11:37a Mike
|
|
* Big ship AI.
|
|
*
|
|
* 53 12/17/96 8:59a Mike
|
|
* Path-extraction-from-mdoel test code.
|
|
*
|
|
* 52 12/13/96 5:27p John
|
|
* fixed my sloppy code that didn't free model data.
|
|
*
|
|
* 51 12/13/96 3:54p Mike
|
|
* Major improvement to model_draw_paths -- connect the dots! Uses
|
|
* sophisticated connect-to-next algorithm.
|
|
*
|
|
* 50 12/13/96 2:59p John
|
|
* Added some code to test spline paths.
|
|
*
|
|
* 49 12/13/96 2:41p John
|
|
* Added code to read in spline paths on models.
|
|
*
|
|
* 48 12/13/96 10:29a Adam
|
|
* from allender: fixed up linked list walking when checking for turrets
|
|
* in subsystems
|
|
*
|
|
* 47 12/12/96 3:38p Allender
|
|
* fix up docking special object names. Added $thruster stuff to model
|
|
* and code
|
|
*
|
|
* 46 12/12/96 2:35p Mike
|
|
* Subsystem support
|
|
*
|
|
* 45 12/12/96 12:29p Lawrance
|
|
* added function to find the 2d bound for a subobject
|
|
*
|
|
* 44 12/11/96 5:35p John
|
|
* Added variables for viewer eye.
|
|
*
|
|
* 43 12/11/96 5:15p John
|
|
* Made the dist for detail level be calculated before the object is
|
|
* instanced.
|
|
*
|
|
* 42 12/11/96 4:50p John
|
|
* Added detail levels to the non-chunky ships.
|
|
*
|
|
* 41 12/11/96 4:33p Mike
|
|
* Temporary version so other people can build.
|
|
*
|
|
* 40 12/11/96 4:24p Mike
|
|
* Fix broken code...oops! clumsy in editor!
|
|
*
|
|
* 39 12/11/96 4:17p Adam
|
|
*
|
|
* 38 12/11/96 12:57p Mike
|
|
* Support for "Big Ship" AI behavior.
|
|
* More turret stuff.
|
|
*
|
|
* 37 12/11/96 12:43p Allender
|
|
* $gun and $missile don't get appended to models gun/missile list if the
|
|
* gun/missile belongs to a turret. Turrets are special instances of
|
|
* these
|
|
*
|
|
* 36 12/11/96 11:57a Allender
|
|
* added objnum reference to obj_subsystems so that $gun and $missile
|
|
* special polygons can refernce a particular subsystem if needed (i.e. to
|
|
* get the norms, etc).
|
|
*
|
|
* 35 12/11/96 10:33a Allender
|
|
* added stuff for FOV for turrets
|
|
*
|
|
* 34 12/10/96 3:27p Allender
|
|
*
|
|
* 33 12/09/96 5:05p Allender
|
|
* made subsystem lists and model_special lists as seperate entities.
|
|
*
|
|
* 32 12/09/96 1:16p Allender
|
|
* made seperate lists for guns/missiles/subsystems, etc
|
|
*
|
|
* 31 12/09/96 11:01a Allender
|
|
* added special polygon information to models. Linked list contains
|
|
* gun/missile/docking/subsystem/etc information.
|
|
*
|
|
* $NoKeywords: $
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#ifdef _WIN32
|
|
#include <io.h>
|
|
#include <direct.h>
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#define MODEL_LIB
|
|
|
|
#include "model/model.h"
|
|
#include "model/modelsinc.h"
|
|
#include "math/vecmat.h"
|
|
#include "object/object.h"
|
|
#include "bmpman/bmpman.h"
|
|
#include "io/key.h"
|
|
#include "render/3dinternal.h"
|
|
#include "globalincs/linklist.h"
|
|
#include "io/timer.h"
|
|
#include "freespace2/freespace.h" // For flFrameTime
|
|
#include "math/fvi.h"
|
|
#include "ship/ship.h"
|
|
#include "cfile/cfile.h"
|
|
#include "parse/parselo.h"
|
|
#include "cmdline/cmdline.h"
|
|
|
|
|
|
#include "gamesnd/gamesnd.h"
|
|
|
|
|
|
#define MAX_SUBMODEL_COLLISION_ROT_ANGLE (PI / 6.0f) // max 30 degrees per frame
|
|
|
|
// info for special polygon lists
|
|
|
|
polymodel *Polygon_models[MAX_POLYGON_MODELS];
|
|
|
|
static int model_initted = 0;
|
|
extern int Cmdline_nohtl;
|
|
|
|
#ifndef NDEBUG
|
|
CFILE *ss_fp = NULL; // file pointer used to dump subsystem information
|
|
char model_filename[_MAX_PATH]; // temp used to store filename
|
|
char debug_name[_MAX_PATH];
|
|
int ss_warning_shown = 0; // have we shown the warning dialog concerning the subsystems?
|
|
char Global_filename[256];
|
|
int Model_ram = 0; // How much RAM the models use total
|
|
#endif
|
|
|
|
// Anything less than this is considered incompatible.
|
|
#define PM_COMPATIBLE_VERSION 1900
|
|
|
|
// Anything greater than or equal to PM_COMPATIBLE_VERSION and
|
|
// whose major version is less than or equal to this is considered
|
|
// compatible.
|
|
#define PM_OBJFILE_MAJOR_VERSION 30
|
|
|
|
static int Model_signature = 0;
|
|
|
|
void generate_vertex_buffers(bsp_info*, polymodel*);
|
|
void model_set_subsys_path_nums(polymodel *pm, int n_subsystems, model_subsystem *subsystems);
|
|
void model_set_bay_path_nums(polymodel *pm);
|
|
|
|
|
|
//WMC - For general compatibility stuff.
|
|
//Note that the order of the items in this list
|
|
//determine the order that they are tried in ai_goal_fixup_dockpoints
|
|
flag_def_list Dock_type_names[] =
|
|
{
|
|
{ "cargo", DOCK_TYPE_CARGO, 0 },
|
|
{ "rearm", DOCK_TYPE_REARM, 0 },
|
|
{ "generic", DOCK_TYPE_GENERIC, 0 }
|
|
};
|
|
|
|
int Num_dock_type_names = sizeof(Dock_type_names) / sizeof(flag_def_list);
|
|
|
|
// Free up a model, getting rid of all its memory
|
|
// With the basic page in system this can be called from outside of modelread.cpp
|
|
void model_unload(int modelnum, int force)
|
|
{
|
|
int i, j, num;
|
|
|
|
if ( modelnum >= MAX_POLYGON_MODELS ) {
|
|
num = modelnum % MAX_POLYGON_MODELS;
|
|
} else {
|
|
num = modelnum;
|
|
}
|
|
|
|
if ( (num < 0) || (num >= MAX_POLYGON_MODELS)) {
|
|
return;
|
|
}
|
|
|
|
polymodel *pm = Polygon_models[num];
|
|
|
|
if ( !pm ) {
|
|
return;
|
|
}
|
|
|
|
Assert( pm->used_this_mission >= 0 );
|
|
|
|
if (!force && (--pm->used_this_mission > 0))
|
|
return;
|
|
|
|
|
|
// so that the textures can be released
|
|
pm->used_this_mission = 0;
|
|
|
|
// we want to call bm_release() from here rather than just bm_unload() in order
|
|
// to get the slots back so we set "release" to true.
|
|
model_page_out_textures(pm->id, true);
|
|
|
|
#ifndef NDEBUG
|
|
Model_ram -= pm->ram_used;
|
|
#endif
|
|
|
|
safe_kill(pm->ship_bay);
|
|
|
|
if (pm->paths) {
|
|
for (i=0; i<pm->n_paths; i++ ) {
|
|
for (j=0; j<pm->paths[i].nverts; j++ ) {
|
|
if ( pm->paths[i].verts[j].turret_ids ) {
|
|
vm_free(pm->paths[i].verts[j].turret_ids);
|
|
}
|
|
}
|
|
if (pm->paths[i].verts) {
|
|
vm_free(pm->paths[i].verts);
|
|
}
|
|
}
|
|
vm_free(pm->paths);
|
|
}
|
|
|
|
if ( pm->shield.verts ) {
|
|
vm_free( pm->shield.verts );
|
|
}
|
|
|
|
if ( pm->shield.tris ) {
|
|
vm_free(pm->shield.tris);
|
|
}
|
|
|
|
if ( pm->missile_banks ) {
|
|
vm_free(pm->missile_banks);
|
|
}
|
|
|
|
if ( pm->docking_bays ) {
|
|
for (i=0; i<pm->n_docks; i++ ) {
|
|
if ( pm->docking_bays[i].splines ) {
|
|
vm_free( pm->docking_bays[i].splines );
|
|
}
|
|
}
|
|
vm_free(pm->docking_bays);
|
|
}
|
|
|
|
|
|
if ( pm->thrusters ) {
|
|
for (i = 0; i < pm->n_thrusters; i++) {
|
|
if (pm->thrusters[i].points)
|
|
vm_free(pm->thrusters[i].points);
|
|
}
|
|
|
|
vm_free(pm->thrusters);
|
|
}
|
|
|
|
if ( pm->glow_point_banks ) { // free the glows!!! -Bobboau
|
|
for (i = 0; i < pm->n_glow_point_banks; i++) {
|
|
if (pm->glow_point_banks[i].points)
|
|
vm_free(pm->glow_point_banks[i].points);
|
|
}
|
|
|
|
vm_free(pm->glow_point_banks);
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
if ( pm->debug_info ) {
|
|
vm_free(pm->debug_info);
|
|
}
|
|
#endif
|
|
|
|
model_octant_free( pm );
|
|
|
|
if (pm->submodel) {
|
|
for (i = 0; i < pm->n_models; i++) {
|
|
if ( !Cmdline_nohtl ) {
|
|
for (j = 0; j < (int)pm->submodel[i].buffer.size(); j++) {
|
|
pm->submodel[i].buffer[j].index_buffer.release();
|
|
}
|
|
|
|
pm->submodel[i].buffer.clear();
|
|
gr_destroy_buffer(pm->submodel[i].indexed_vertex_buffer);
|
|
}
|
|
|
|
if ( pm->submodel[i].bsp_data ) {
|
|
vm_free(pm->submodel[i].bsp_data);
|
|
}
|
|
}
|
|
|
|
vm_free(pm->submodel);
|
|
}
|
|
|
|
if ( pm->xc ) {
|
|
vm_free(pm->xc);
|
|
}
|
|
|
|
if ( pm->lights ) {
|
|
vm_free(pm->lights);
|
|
}
|
|
|
|
if ( pm->gun_banks ) {
|
|
vm_free(pm->gun_banks);
|
|
}
|
|
|
|
if ( pm->shield_collision_tree ) {
|
|
vm_free(pm->shield_collision_tree);
|
|
}
|
|
|
|
// run through Ship_info[] and if the model has been loaded we'll need to reset the modelnum to -1.
|
|
for (i = 0; i < Num_ship_classes; i++) {
|
|
if ( pm->id == Ship_info[i].model_num ) {
|
|
Ship_info[i].model_num = -1;
|
|
}
|
|
|
|
if ( pm->id == Ship_info[i].model_num_hud ) {
|
|
Ship_info[i].model_num_hud = -1;
|
|
}
|
|
}
|
|
|
|
pm->id = 0;
|
|
memset( pm, 0, sizeof(polymodel));
|
|
vm_free( pm );
|
|
|
|
Polygon_models[num] = NULL;
|
|
}
|
|
|
|
void model_free_all()
|
|
{
|
|
int i;
|
|
|
|
if ( !model_initted) {
|
|
model_init();
|
|
return;
|
|
}
|
|
|
|
mprintf(( "Freeing all existing models...\n" ));
|
|
|
|
for (i=0;i<MAX_POLYGON_MODELS;i++) {
|
|
// forcefully unload all loaded models (be careful with this)
|
|
model_unload(i, 1);
|
|
}
|
|
|
|
}
|
|
|
|
void model_page_in_start()
|
|
{
|
|
int i;
|
|
|
|
if ( !model_initted ) {
|
|
model_init();
|
|
return;
|
|
}
|
|
|
|
mprintf(( "Starting model page in...\n" ));
|
|
|
|
for (i=0; i<MAX_POLYGON_MODELS; i++) {
|
|
if (Polygon_models[i] != NULL)
|
|
Polygon_models[i]->used_this_mission = 0;
|
|
}
|
|
}
|
|
|
|
void model_page_in_stop()
|
|
{
|
|
int i;
|
|
|
|
Assert( model_initted );
|
|
|
|
mprintf(( "Stopping model page in...\n" ));
|
|
|
|
for (i=0; i<MAX_POLYGON_MODELS; i++) {
|
|
if (Polygon_models[i] == NULL)
|
|
continue;
|
|
|
|
if (Polygon_models[i]->used_this_mission)
|
|
continue;
|
|
|
|
model_unload(i);
|
|
}
|
|
}
|
|
|
|
void model_init()
|
|
{
|
|
int i;
|
|
|
|
if ( model_initted ) {
|
|
Int3(); // Model_init shouldn't be called twice!
|
|
return;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
Model_ram = 0;
|
|
#endif
|
|
|
|
for (i=0;i<MAX_POLYGON_MODELS;i++) {
|
|
Polygon_models[i] = NULL;
|
|
}
|
|
|
|
// Init the model caching system
|
|
// model_cache_init();
|
|
|
|
atexit( model_free_all );
|
|
model_initted = 1;
|
|
}
|
|
|
|
// routine to parse out values from a user property field of an object
|
|
void get_user_prop_value(char *buf, char *value)
|
|
{
|
|
char *p, *p1, c;
|
|
|
|
p = buf;
|
|
while ( isspace(*p) || (*p == '=') ) // skip white space and equal sign
|
|
p++;
|
|
p1 = p;
|
|
while ( !iscntrl(*p1) )
|
|
p1++;
|
|
c = *p1;
|
|
*p1 = '\0';
|
|
strcpy(value, p);
|
|
*p1 = c;
|
|
}
|
|
|
|
// funciton to copy model data from one subsystem set to another subsystem set. This function
|
|
// is called when two ships use the same model data, but since the model only gets read in one time,
|
|
// the subsystem data is only present in one location. The ship code will call this routine to fix
|
|
// this situation by copying stuff from the source subsystem set to the dest subsystem set.
|
|
void model_copy_subsystems( int n_subsystems, model_subsystem *d_sp, model_subsystem *s_sp )
|
|
{
|
|
int i, j;
|
|
model_subsystem *source, *dest;
|
|
|
|
for (i = 0; i < n_subsystems; i++ ) {
|
|
source = &s_sp[i];
|
|
for ( j = 0; j < n_subsystems; j++ ) {
|
|
dest = &d_sp[j];
|
|
if ( !subsystem_stricmp( source->subobj_name, dest->subobj_name) ) {
|
|
dest->flags = source->flags;
|
|
dest->subobj_num = source->subobj_num;
|
|
dest->model_num = source->model_num;
|
|
dest->pnt = source->pnt;
|
|
dest->radius = source->radius;
|
|
dest->type = source->type;
|
|
dest->turn_rate = source->turn_rate;
|
|
dest->turret_gun_sobj = source->turret_gun_sobj;
|
|
|
|
strcpy( dest->name, source->name );
|
|
|
|
if ( dest->type == SUBSYSTEM_TURRET ) {
|
|
int nfp;
|
|
|
|
dest->turret_fov = source->turret_fov;
|
|
dest->turret_num_firing_points = source->turret_num_firing_points;
|
|
dest->turret_norm = source->turret_norm;
|
|
dest->turret_matrix = source->turret_matrix;
|
|
|
|
for (nfp = 0; nfp < dest->turret_num_firing_points; nfp++ )
|
|
dest->turret_firing_point[nfp] = source->turret_firing_point[nfp];
|
|
|
|
if ( dest->flags & MSS_FLAG_CREWPOINT )
|
|
strcpy(dest->crewspot, source->crewspot);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if ( j == n_subsystems )
|
|
Int3(); // get allender -- something is amiss with models
|
|
|
|
}
|
|
}
|
|
|
|
// routine to get/set subsystem information
|
|
static void set_subsystem_info( model_subsystem *subsystemp, char *props, char *dname )
|
|
{
|
|
char *p;
|
|
char buf[64];
|
|
char lcdname[256];
|
|
|
|
if ( (p = strstr(props, "$name")) != NULL)
|
|
get_user_prop_value(p+5, subsystemp->name);
|
|
else
|
|
strcpy( subsystemp->name, dname );
|
|
|
|
strcpy(lcdname, dname);
|
|
strlwr(lcdname);
|
|
|
|
// check the name for its specific type
|
|
if ( strstr(lcdname, "engine") ) {
|
|
subsystemp->type = SUBSYSTEM_ENGINE;
|
|
} else if ( strstr(lcdname, "radar") ) {
|
|
subsystemp->type = SUBSYSTEM_RADAR;
|
|
} else if ( strstr(lcdname, "turret") ) {
|
|
float angle;
|
|
|
|
subsystemp->type = SUBSYSTEM_TURRET;
|
|
if ( (p = strstr(props, "$fov")) != NULL )
|
|
get_user_prop_value(p+4, buf); // get the value of the fov
|
|
else
|
|
strcpy(buf,"180");
|
|
angle = ANG_TO_RAD(atoi(buf))/2.0f;
|
|
subsystemp->turret_fov = (float)cos(angle);
|
|
subsystemp->turret_num_firing_points = 0;
|
|
|
|
if ( (p = strstr(props, "$crewspot")) != NULL) {
|
|
subsystemp->flags |= MSS_FLAG_CREWPOINT;
|
|
get_user_prop_value(p+9, subsystemp->crewspot);
|
|
}
|
|
|
|
} else if ( strstr(lcdname, "navigation") ) {
|
|
subsystemp->type = SUBSYSTEM_NAVIGATION;
|
|
} else if ( strstr(lcdname, "communication") ) {
|
|
subsystemp->type = SUBSYSTEM_COMMUNICATION;
|
|
} else if ( strstr(lcdname, "weapons") ) {
|
|
subsystemp->type = SUBSYSTEM_WEAPONS;
|
|
} else if ( strstr(lcdname, "sensors") ) {
|
|
subsystemp->type = SUBSYSTEM_SENSORS;
|
|
} else if ( strstr(lcdname, "solar") ) {
|
|
subsystemp->type = SUBSYSTEM_SOLAR;
|
|
} else if ( strstr(lcdname, "gas") ) {
|
|
subsystemp->type = SUBSYSTEM_GAS_COLLECT;
|
|
} else if ( strstr(lcdname, "activator") ) {
|
|
subsystemp->type = SUBSYSTEM_ACTIVATION;
|
|
} else { // If unrecognized type, set to unknown so artist can continue working...
|
|
subsystemp->type = SUBSYSTEM_UNKNOWN;
|
|
mprintf(("Warning: Ignoring unrecognized subsystem %s, believed to be in ship %s\n", dname, Global_filename));
|
|
}
|
|
|
|
if ( (p = strstr(props, "$triggered:")) != NULL ) {
|
|
subsystemp->flags |= MSS_FLAG_ROTATES;
|
|
subsystemp->flags |= MSS_FLAG_TRIGGERED;
|
|
}
|
|
|
|
// Rotating subsystem
|
|
if ( (p = strstr(props, "$rotate")) != NULL) {
|
|
subsystemp->flags |= MSS_FLAG_ROTATES;
|
|
|
|
// get time for (a) complete rotation (b) step (c) activation
|
|
float turn_time;
|
|
get_user_prop_value(p+7, buf);
|
|
turn_time = (float)atof(buf);
|
|
|
|
// CASE OF WEAPON ROTATION (primary only)
|
|
if ( (p = strstr(props, "$pbank")) != NULL) {
|
|
subsystemp->flags |= MSS_FLAG_ARTILLERY;
|
|
|
|
// get which pbank should trigger rotation
|
|
get_user_prop_value(p+6, buf);
|
|
subsystemp->weapon_rotation_pbank = (int)atoi(buf);
|
|
} // end of weapon rotation stuff
|
|
|
|
|
|
// *** determine how the subsys rotates ***
|
|
|
|
// CASE OF STEPPED ROTATION
|
|
if ( (p = strstr(props, "$stepped")) != NULL) {
|
|
|
|
subsystemp->stepped_rotation = new(stepped_rotation);
|
|
subsystemp->flags |= MSS_FLAG_STEPPED_ROTATE;
|
|
|
|
// get number of steps
|
|
if ( (p = strstr(props, "$steps")) != NULL) {
|
|
get_user_prop_value(p+6, buf);
|
|
subsystemp->stepped_rotation->num_steps = atoi(buf);
|
|
} else {
|
|
subsystemp->stepped_rotation->num_steps = 8;
|
|
}
|
|
|
|
// get pause time
|
|
if ( (p = strstr(props, "$t_paused")) != NULL) {
|
|
get_user_prop_value(p+9, buf);
|
|
subsystemp->stepped_rotation->t_pause = (float)atof(buf);
|
|
} else {
|
|
subsystemp->stepped_rotation->t_pause = 2.0f;
|
|
}
|
|
|
|
// get transition time - time to go between steps
|
|
if ( (p = strstr(props, "$t_transit")) != NULL) {
|
|
get_user_prop_value(p+10, buf);
|
|
subsystemp->stepped_rotation->t_transit = (float)atof(buf);
|
|
} else {
|
|
subsystemp->stepped_rotation->t_transit = 2.0f;
|
|
}
|
|
|
|
// get fraction of time spent in accel
|
|
if ( (p = strstr(props, "$fraction_accel")) != NULL) {
|
|
get_user_prop_value(p+15, buf);
|
|
subsystemp->stepped_rotation->fraction = (float)atof(buf);
|
|
Assert(subsystemp->stepped_rotation->fraction > 0 && subsystemp->stepped_rotation->fraction < 0.5);
|
|
} else {
|
|
subsystemp->stepped_rotation->fraction = 0.3f;
|
|
}
|
|
|
|
int num_steps = subsystemp->stepped_rotation->num_steps;
|
|
float t_trans = subsystemp->stepped_rotation->t_transit;
|
|
float fraction = subsystemp->stepped_rotation->fraction;
|
|
|
|
subsystemp->stepped_rotation->max_turn_accel = PI2 / (fraction*(1.0f - fraction) * num_steps * t_trans*t_trans);
|
|
subsystemp->stepped_rotation->max_turn_rate = PI2 / ((1.0f - fraction) * num_steps *t_trans);
|
|
}
|
|
|
|
// CASE OF NORMAL CONTINUOUS ROTATION
|
|
else {
|
|
// commented by Goober5000 to allow faster than 1sec rotation
|
|
/* if ( fabs(turn_time) < 1 )
|
|
{
|
|
// Warning(LOCATION, "%s has subsystem %s with rotation time less than 1 sec", dname, Global_filename );
|
|
subsystemp->flags &= ~MSS_FLAG_ROTATES;
|
|
subsystemp->turn_rate = 0.0f;
|
|
}
|
|
else */
|
|
{
|
|
subsystemp->turn_rate = PI2 / turn_time;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// used in collision code to check if submode rotates too far
|
|
float get_submodel_delta_angle(submodel_instance_info *sii)
|
|
{
|
|
vec3d diff;
|
|
vm_vec_sub(&diff, (vec3d*)&sii->angs, (vec3d*)&sii->prev_angs);
|
|
|
|
// find the angle
|
|
float delta_angle = vm_vec_mag(&diff);
|
|
|
|
// make sure we get the short way around
|
|
if (delta_angle > PI) {
|
|
delta_angle = (PI2 - delta_angle);
|
|
}
|
|
|
|
return delta_angle;
|
|
}
|
|
|
|
void do_new_subsystem( int n_subsystems, model_subsystem *slist, int subobj_num, float rad, vec3d *pnt, char *props, char *subobj_name, int model_num )
|
|
{
|
|
int i;
|
|
model_subsystem *subsystemp;
|
|
|
|
if ( slist==NULL ) return; // For TestCode, POFView, etc don't bother
|
|
|
|
// try to find the name of the subsystem passed here on the list of subsystems currently on the
|
|
// ship. Assign the values only when the right subsystem is found
|
|
|
|
for (i = 0; i < n_subsystems; i++ ) {
|
|
subsystemp = &slist[i];
|
|
|
|
#ifndef NDEBUG
|
|
// Goober5000 - notify if there's a mismatch
|
|
if ( stricmp(subobj_name, subsystemp->subobj_name) && !subsystem_stricmp(subobj_name, subsystemp->subobj_name) )
|
|
{
|
|
Warning(LOCATION, "Subsystem \"%s\" in model \"%s\" is represented as \"%s\" in ships.tbl. "
|
|
"Although FS2_OPEN 3.6 and later will catch and correct this error, earlier "
|
|
"versions (as well as retail FS2) will not. You are advised to fix this if "
|
|
"you plan to support earlier versions of FreeSpace.\n", subobj_name, model_get(model_num)->filename, subsystemp->subobj_name);
|
|
|
|
}
|
|
#endif
|
|
|
|
if (!subsystem_stricmp(subobj_name, subsystemp->subobj_name))
|
|
{
|
|
//commented by Goober5000 because this is also set when the table is parsed
|
|
//subsystemp->flags = 0;
|
|
|
|
subsystemp->subobj_num = subobj_num;
|
|
subsystemp->turret_gun_sobj = -1;
|
|
subsystemp->model_num = model_num;
|
|
subsystemp->pnt = *pnt; // use the offset to get the center point of the subsystem
|
|
subsystemp->radius = rad;
|
|
set_subsystem_info( subsystemp, props, subobj_name);
|
|
strcpy(subsystemp->subobj_name, subobj_name); // copy the object name
|
|
return;
|
|
}
|
|
}
|
|
#ifndef NDEBUG
|
|
char bname[_MAX_FNAME];
|
|
|
|
if ( !ss_warning_shown) {
|
|
_splitpath(model_filename, NULL, NULL, bname, NULL);
|
|
// 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%stables%s%s.subsystems for inclusion\ninto ships.tbl.", model_filename, DIR_SEPARATOR_STR, DIR_SEPARATOR_STR, bname);
|
|
ss_warning_shown = 1;
|
|
} else
|
|
#endif
|
|
mprintf(("Subsystem %s in ships.tbl not found in model!\n", subobj_name));
|
|
|
|
#ifndef NDEBUG
|
|
if ( ss_fp ) {
|
|
_splitpath(model_filename, NULL, NULL, bname, NULL);
|
|
mprintf(("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%stables%s%s.subsystems for inclusion\ninto ships.tbl.\n", model_filename, DIR_SEPARATOR_STR, DIR_SEPARATOR_STR, bname));
|
|
char tmp_buffer[128];
|
|
sprintf(tmp_buffer, "$Subsystem:\t\t\t%s,1,0.0\n", subobj_name);
|
|
cfputs(tmp_buffer, ss_fp);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
void print_family_tree( polymodel *obj, int modelnum, char * ident, int islast )
|
|
{
|
|
char temp[50];
|
|
|
|
if ( modelnum < 0 ) return;
|
|
if (obj==NULL) return;
|
|
|
|
if (strlen(ident)==0 ) {
|
|
mprintf(( " %s", obj->submodel[modelnum].name ));
|
|
sprintf( temp, " " );
|
|
} else if ( islast ) {
|
|
mprintf(( "%s��%s", ident, obj->submodel[modelnum].name ));
|
|
sprintf( temp, "%s ", ident );
|
|
} else {
|
|
mprintf(( "%s��%s", ident, obj->submodel[modelnum].name ));
|
|
sprintf( temp, "%s� ", ident );
|
|
}
|
|
|
|
mprintf(( "\n" ));
|
|
|
|
int child = obj->submodel[modelnum].first_child;
|
|
while( child > -1 ) {
|
|
if ( obj->submodel[child].next_sibling < 0 )
|
|
print_family_tree( obj, child, temp,1 );
|
|
else
|
|
print_family_tree( obj, child, temp,0 );
|
|
child = obj->submodel[child].next_sibling;
|
|
}
|
|
}
|
|
|
|
void dump_object_tree(polymodel *obj)
|
|
{
|
|
print_family_tree( obj, 0, "", 0 );
|
|
key_getch();
|
|
}
|
|
|
|
void create_family_tree(polymodel *obj)
|
|
{
|
|
int i;
|
|
for (i=0; i<obj->n_models; i++ ) {
|
|
obj->submodel[i].num_children = 0;
|
|
obj->submodel[i].first_child = -1;
|
|
obj->submodel[i].next_sibling = -1;
|
|
}
|
|
|
|
for (i=0; i<obj->n_models; i++ ) {
|
|
int pn;
|
|
pn = obj->submodel[i].parent;
|
|
if ( pn > -1 ) {
|
|
obj->submodel[pn].num_children++;
|
|
int tmp = obj->submodel[pn].first_child;
|
|
obj->submodel[pn].first_child = i;
|
|
obj->submodel[i].next_sibling = tmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Goober5000
|
|
bool maybe_swap_mins_maxs(vec3d *mins, vec3d *maxs)
|
|
{
|
|
float temp;
|
|
bool swap_was_necessary = false;
|
|
|
|
if (mins->xyz.x > maxs->xyz.x)
|
|
{
|
|
temp = mins->xyz.x;
|
|
mins->xyz.x = maxs->xyz.x;
|
|
maxs->xyz.x = temp;
|
|
swap_was_necessary = true;
|
|
}
|
|
if (mins->xyz.y > maxs->xyz.y)
|
|
{
|
|
temp = mins->xyz.y;
|
|
mins->xyz.y = maxs->xyz.y;
|
|
maxs->xyz.y = temp;
|
|
swap_was_necessary = true;
|
|
}
|
|
if (mins->xyz.z > maxs->xyz.z)
|
|
{
|
|
temp = mins->xyz.z;
|
|
mins->xyz.z = maxs->xyz.z;
|
|
maxs->xyz.z = temp;
|
|
swap_was_necessary = true;
|
|
}
|
|
|
|
// This is a mini utility that prints out the proper hex string for the
|
|
// mins and maxs so that the POF file can be modified in a hex editor.
|
|
// Currently none of the major POF editors allow editing of bounding boxes.
|
|
#if 0
|
|
if (swap_was_necessary)
|
|
{
|
|
// use C hackery to convert float values to raw bytes
|
|
const int NUM_BYTES = 24;
|
|
typedef struct converter
|
|
{
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
float min_x, min_y, min_z, max_x, max_y, max_z;
|
|
} _float;
|
|
ubyte _byte[NUM_BYTES];
|
|
};
|
|
} converter;
|
|
|
|
// fill in the values
|
|
converter z;
|
|
z._float.min_x = mins->xyz.x;
|
|
z._float.min_y = mins->xyz.y;
|
|
z._float.min_z = mins->xyz.z;
|
|
z._float.max_x = maxs->xyz.x;
|
|
z._float.max_y = maxs->xyz.y;
|
|
z._float.max_z = maxs->xyz.z;
|
|
|
|
// prep string
|
|
char hex_str[5];
|
|
char text[100 + (5 * NUM_BYTES)];
|
|
strcpy(text, "The following is the correct hex string for the minima and maxima:\n");
|
|
|
|
// append hex values to the string
|
|
for (int i = 0; i < NUM_BYTES; i++)
|
|
{
|
|
sprintf(hex_str, "%02X ", z._byte[i]);
|
|
strcat(text, hex_str);
|
|
}
|
|
|
|
// notify the user
|
|
Warning(LOCATION, text);
|
|
}
|
|
#endif
|
|
|
|
return swap_was_necessary;
|
|
}
|
|
|
|
void model_calc_bound_box( vec3d *box, vec3d *big_mn, vec3d *big_mx)
|
|
{
|
|
box[0].xyz.x = big_mn->xyz.x; box[0].xyz.y = big_mn->xyz.y; box[0].xyz.z = big_mn->xyz.z;
|
|
box[1].xyz.x = big_mx->xyz.x; box[1].xyz.y = big_mn->xyz.y; box[1].xyz.z = big_mn->xyz.z;
|
|
box[2].xyz.x = big_mx->xyz.x; box[2].xyz.y = big_mx->xyz.y; box[2].xyz.z = big_mn->xyz.z;
|
|
box[3].xyz.x = big_mn->xyz.x; box[3].xyz.y = big_mx->xyz.y; box[3].xyz.z = big_mn->xyz.z;
|
|
|
|
|
|
box[4].xyz.x = big_mn->xyz.x; box[4].xyz.y = big_mn->xyz.y; box[4].xyz.z = big_mx->xyz.z;
|
|
box[5].xyz.x = big_mx->xyz.x; box[5].xyz.y = big_mn->xyz.y; box[5].xyz.z = big_mx->xyz.z;
|
|
box[6].xyz.x = big_mx->xyz.x; box[6].xyz.y = big_mx->xyz.y; box[6].xyz.z = big_mx->xyz.z;
|
|
box[7].xyz.x = big_mn->xyz.x; box[7].xyz.y = big_mx->xyz.y; box[7].xyz.z = big_mx->xyz.z;
|
|
}
|
|
|
|
|
|
// Debug thing so we don't repeatedly show warning messages.
|
|
#ifndef NDEBUG
|
|
int Bogus_warning_flag_1903 = 0;
|
|
#endif
|
|
void parse_triggers(int &n_trig, queued_animation **triggers, char *props);
|
|
|
|
|
|
IBX ibuffer_info;
|
|
|
|
//reads a binary file containing a 3d model
|
|
int read_model_file(polymodel * pm, char *filename, int n_subsystems, model_subsystem *subsystems, int ferror)
|
|
{
|
|
CFILE *fp;
|
|
int version;
|
|
int id, len, next_chunk;
|
|
int i,j;
|
|
|
|
#ifndef NDEBUG
|
|
strcpy(Global_filename, filename);
|
|
#endif
|
|
|
|
// little test code i used in fred2
|
|
//char pwd[128];
|
|
//getcwd(pwd, 128);
|
|
|
|
fp = cfopen(filename,"rb");
|
|
|
|
if (!fp) {
|
|
if (ferror == 1) {
|
|
Error( LOCATION, "Can't open model file <%s>", filename );
|
|
} else {
|
|
Warning( LOCATION, "Can't open model file <%s>", filename );
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// Begin IBX code - taylor
|
|
if ( !Cmdline_nohtl && !Cmdline_noibx ) {
|
|
// generate checksum for the POF
|
|
uint pof_checksum = 0;
|
|
cfseek(fp, 0, SEEK_SET);
|
|
cf_chksum_long(fp, &pof_checksum);
|
|
cfseek(fp, 0, SEEK_SET);
|
|
|
|
// clear struct and prepare for IBX usage
|
|
memset( &ibuffer_info, 0, sizeof(IBX) );
|
|
|
|
// use the same filename as the POF but with an .ibx extension
|
|
strcpy( ibuffer_info.name, filename );
|
|
char *pb = strchr( ibuffer_info.name, '.' );
|
|
if ( pb ) *pb = 0;
|
|
strcat( ibuffer_info.name, NOX(".ibx") );
|
|
|
|
ibuffer_info.read = cfopen( ibuffer_info.name, "rb", CFILE_NORMAL, CF_TYPE_CACHE );
|
|
|
|
// check if it's a zero size file and if so bail out to create a new one
|
|
if ( (ibuffer_info.read != NULL) && !cfilelength(ibuffer_info.read) ) {
|
|
cfclose( ibuffer_info.read );
|
|
ibuffer_info.read = NULL;
|
|
}
|
|
|
|
if ( ibuffer_info.read != NULL ) {
|
|
bool ibx_valid = false;
|
|
|
|
// grab a checksum of the IBX, for debugging purposes
|
|
uint ibx_checksum = 0;
|
|
cfseek(ibuffer_info.read, 0, SEEK_SET);
|
|
cf_chksum_long(ibuffer_info.read, &ibx_checksum);
|
|
cfseek(ibuffer_info.read, 0, SEEK_SET);
|
|
|
|
// get the file size that we use to safety check with.
|
|
// be sure to subtract from this when we read something out
|
|
ibuffer_info.size = cfilelength( ibuffer_info.read );
|
|
|
|
// file id
|
|
int ibx = cfread_int( ibuffer_info.read );
|
|
ibuffer_info.size -= sizeof(int); // subtract
|
|
|
|
// make sure the file is valid
|
|
switch (ibx)
|
|
{
|
|
// "XBI " - (" IBX" in file)
|
|
case 0x58424920:
|
|
// "XBI2" - ("2IBX" in file)
|
|
case 0x58424932:
|
|
ibx_valid = true;
|
|
break;
|
|
}
|
|
|
|
if ( !ibx_valid ) {
|
|
cfclose( ibuffer_info.read );
|
|
ibuffer_info.read = NULL;
|
|
ibuffer_info.size = 0;
|
|
} else {
|
|
// file is valid so grab the checksum out of the .ibx and verify it matches the POF
|
|
uint ibx_sum = cfread_uint( ibuffer_info.read );
|
|
ibuffer_info.size -= sizeof(uint); // subtract
|
|
|
|
if ( ibx_sum != pof_checksum ) {
|
|
// bah, it's invalid for this POF
|
|
// reset and a write file will be used instead
|
|
cfclose( ibuffer_info.read );
|
|
ibuffer_info.read = NULL;
|
|
ibuffer_info.size = 0;
|
|
} else {
|
|
mprintf(("IBX: Found a good IBX to read for '%s'.\n", filename));
|
|
mprintf(("IBX-DEBUG => POF checksum: %10u, IBX checksum: %10u -- \"%s\"\n", pof_checksum, ibx_checksum, filename));
|
|
}
|
|
}
|
|
}
|
|
|
|
// if the read file is absent or invalid then write out the new info
|
|
if ( ibuffer_info.read == NULL ) {
|
|
ibuffer_info.write = cfopen( ibuffer_info.name, "wb", CFILE_NORMAL, CF_TYPE_CACHE );
|
|
|
|
if (ibuffer_info.write != NULL) {
|
|
mprintf(("IBX: Starting a new IBX for '%s'.\n", filename));
|
|
|
|
// file id, default to version 1
|
|
cfwrite_int( 0x58424920, ibuffer_info.write ); // "XBI " - (" IBX" in file)
|
|
|
|
// POF checksum
|
|
cfwrite_uint( pof_checksum, ibuffer_info.write );
|
|
}
|
|
}
|
|
} // End IBX code
|
|
|
|
// code to get a filename to write out subsystem information for each model that
|
|
// is read. This info is essentially debug stuff that is used to help get models
|
|
// into the game quicker
|
|
#if 0
|
|
{
|
|
char bname[_MAX_FNAME];
|
|
|
|
_splitpath(filename, NULL, NULL, bname, NULL);
|
|
sprintf(debug_name, "%s.subsystems", bname);
|
|
ss_fp = cfopen(debug_name, "wb", CFILE_NORMAL, CF_TYPE_TABLES );
|
|
if ( !ss_fp ) {
|
|
mprintf(( "Can't open debug file for writing subsystems for %s\n", filename));
|
|
} else {
|
|
strcpy(model_filename, filename);
|
|
ss_warning_shown = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
id = cfread_int(fp);
|
|
|
|
if (id != POF_HEADER_ID)
|
|
Error( LOCATION, "Bad ID in model file <%s>",filename);
|
|
|
|
// Version is major*100+minor
|
|
// So, major = version / 100;
|
|
// minor = version % 100;
|
|
version = cfread_int(fp);
|
|
|
|
//Warning( LOCATION, "POF Version = %d", version );
|
|
|
|
if (version < PM_COMPATIBLE_VERSION || (version/100) > PM_OBJFILE_MAJOR_VERSION) {
|
|
Warning(LOCATION,"Bad version (%d) in model file <%s>",version,filename);
|
|
return 0;
|
|
}
|
|
|
|
pm->version = version;
|
|
Assert( strlen(filename) < FILESPEC_LENGTH );
|
|
strncpy(pm->filename, filename, FILESPEC_LENGTH);
|
|
|
|
memset( &pm->view_positions, 0, sizeof(pm->view_positions) );
|
|
|
|
// reset insignia counts
|
|
pm->num_ins = 0;
|
|
|
|
// reset glow points!! - Goober5000
|
|
pm->n_glow_point_banks = 0;
|
|
|
|
// reset SLDC
|
|
pm->shield_collision_tree = NULL;
|
|
pm->sldc_size = 0;
|
|
|
|
id = cfread_int(fp);
|
|
len = cfread_int(fp);
|
|
next_chunk = cftell(fp) + len;
|
|
|
|
while (!cfeof(fp)) {
|
|
|
|
// mprintf(("Processing chunk <%c%c%c%c>, len = %d\n",id,id>>8,id>>16,id>>24,len));
|
|
// key_getch();
|
|
|
|
switch (id) {
|
|
|
|
case ID_OHDR: { //Object header
|
|
//vector v;
|
|
|
|
//mprintf(0,"Got chunk OHDR, len=%d\n",len);
|
|
|
|
#if defined( FREESPACE1_FORMAT )
|
|
pm->n_models = cfread_int(fp);
|
|
// mprintf(( "Num models = %d\n", pm->n_models ));
|
|
pm->rad = cfread_float(fp);
|
|
pm->flags = cfread_int(fp); // 1=Allow tiling
|
|
#elif defined( FREESPACE2_FORMAT )
|
|
pm->rad = cfread_float(fp);
|
|
pm->flags = cfread_int(fp); // 1=Allow tiling
|
|
pm->n_models = cfread_int(fp);
|
|
// mprintf(( "Num models = %d\n", pm->n_models ));
|
|
#endif
|
|
|
|
pm->submodel = (bsp_info *)vm_malloc( sizeof(bsp_info)*pm->n_models );
|
|
Assert(pm->submodel != NULL );
|
|
memset( pm->submodel, 0, sizeof(bsp_info)*pm->n_models );
|
|
|
|
//Assert(pm->n_models <= MAX_SUBMODELS);
|
|
|
|
cfread_vector(&pm->mins,fp);
|
|
cfread_vector(&pm->maxs,fp);
|
|
|
|
// sanity first!
|
|
if (maybe_swap_mins_maxs(&pm->mins, &pm->maxs)) {
|
|
Warning(LOCATION, "Inverted bounding box on model '%s'! Swapping values to compensate.", pm->filename);
|
|
}
|
|
model_calc_bound_box(pm->bounding_box, &pm->mins, &pm->maxs);
|
|
|
|
pm->n_detail_levels = cfread_int(fp);
|
|
// mprintf(( "There are %d detail levels\n", pm->n_detail_levels ));
|
|
for (i=0; i<pm->n_detail_levels;i++ ) {
|
|
pm->detail[i] = cfread_int(fp);
|
|
pm->detail_depth[i] = 0.0f;
|
|
/// mprintf(( "Detail level %d is model %d.\n", i, pm->detail[i] ));
|
|
}
|
|
|
|
pm->num_debris_objects = cfread_int(fp);
|
|
Assert( pm->num_debris_objects <= MAX_DEBRIS_OBJECTS );
|
|
// mprintf(( "There are %d debris objects\n", pm->num_debris_objects ));
|
|
for (i=0; i<pm->num_debris_objects;i++ ) {
|
|
pm->debris_objects[i] = cfread_int(fp);
|
|
// mprintf(( "Debris object %d is model %d.\n", i, pm->debris_objects[i] ));
|
|
}
|
|
|
|
if ( pm->version >= 1903 ) {
|
|
|
|
if ( pm->version >= 2009 ) {
|
|
|
|
pm->mass = cfread_float(fp);
|
|
cfread_vector( &pm->center_of_mass, fp );
|
|
cfread_vector( &pm->moment_of_inertia.vec.rvec, fp );
|
|
cfread_vector( &pm->moment_of_inertia.vec.uvec, fp );
|
|
cfread_vector( &pm->moment_of_inertia.vec.fvec, fp );
|
|
} else {
|
|
// old code where mass wasn't based on area, so do the calculation manually
|
|
|
|
float vol_mass = cfread_float(fp);
|
|
// Attn: John Slagel: The following is better done in bspgen.
|
|
// Convert volume (cubic) to surface area (quadratic) and scale so 100 -> 100
|
|
float area_mass = (float) pow(vol_mass, 0.6667f) * 4.65f;
|
|
|
|
pm->mass = area_mass;
|
|
float mass_ratio = vol_mass / area_mass;
|
|
|
|
cfread_vector( &pm->center_of_mass, fp );
|
|
cfread_vector( &pm->moment_of_inertia.vec.rvec, fp );
|
|
cfread_vector( &pm->moment_of_inertia.vec.uvec, fp );
|
|
cfread_vector( &pm->moment_of_inertia.vec.fvec, fp );
|
|
|
|
// John remove this with change to bspgen
|
|
vm_vec_scale( &pm->moment_of_inertia.vec.rvec, mass_ratio );
|
|
vm_vec_scale( &pm->moment_of_inertia.vec.uvec, mass_ratio );
|
|
vm_vec_scale( &pm->moment_of_inertia.vec.fvec, mass_ratio );
|
|
}
|
|
|
|
// a custom MOI is only used for ships, but we should probably log it anyway
|
|
if ( IS_VEC_NULL(&pm->moment_of_inertia.vec.rvec)
|
|
&& IS_VEC_NULL(&pm->moment_of_inertia.vec.uvec)
|
|
&& IS_VEC_NULL(&pm->moment_of_inertia.vec.fvec) )
|
|
{
|
|
mprintf(("Model %s has a null moment of inertia! (This is only a problem if the model is a ship.)\n", filename));
|
|
}
|
|
|
|
} else {
|
|
#ifndef NDEBUG
|
|
if (stricmp("fighter04.pof", filename)) {
|
|
if (Bogus_warning_flag_1903 == 0) {
|
|
Warning(LOCATION, "Ship %s is old. Cannot compute mass.\nSetting to 50.0f. Talk to John.", filename);
|
|
Bogus_warning_flag_1903 = 1;
|
|
}
|
|
}
|
|
#endif
|
|
pm->mass = 50.0f;
|
|
vm_vec_zero( &pm->center_of_mass );
|
|
vm_set_identity( &pm->moment_of_inertia );
|
|
vm_vec_scale(&pm->moment_of_inertia.vec.rvec, 0.001f);
|
|
vm_vec_scale(&pm->moment_of_inertia.vec.uvec, 0.001f);
|
|
vm_vec_scale(&pm->moment_of_inertia.vec.fvec, 0.001f);
|
|
}
|
|
|
|
// read in cross section info
|
|
pm->xc = NULL;
|
|
if ( pm->version >= 2014 ) {
|
|
pm->num_xc = cfread_int(fp);
|
|
if (pm->num_xc > 0) {
|
|
pm->xc = (cross_section*) vm_malloc(pm->num_xc*sizeof(cross_section));
|
|
for (i=0; i<pm->num_xc; i++) {
|
|
pm->xc[i].z = cfread_float(fp);
|
|
pm->xc[i].radius = cfread_float(fp);
|
|
}
|
|
}
|
|
} else {
|
|
pm->num_xc = 0;
|
|
}
|
|
|
|
if ( pm->version >= 2007 ) {
|
|
pm->num_lights = cfread_int(fp);
|
|
//mprintf(( "Found %d lights!\n", pm->num_lights ));
|
|
|
|
if (pm->num_lights > 0) {
|
|
pm->lights = (bsp_light *)vm_malloc( sizeof(bsp_light)*pm->num_lights );
|
|
for (i=0; i<pm->num_lights; i++ ) {
|
|
cfread_vector(&pm->lights[i].pos,fp);
|
|
pm->lights[i].type = cfread_int(fp);
|
|
pm->lights[i].value = 0.0f;
|
|
}
|
|
}
|
|
} else {
|
|
pm->num_lights = 0;
|
|
pm->lights = NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case ID_SOBJ: { //Subobject header
|
|
int n;
|
|
char *p, props[MAX_PROP_LEN];
|
|
// float d;
|
|
|
|
//mprintf(0,"Got chunk SOBJ, len=%d\n",len);
|
|
|
|
n = cfread_int(fp);
|
|
//mprintf(("SOBJ IDed itself as %d", n));
|
|
|
|
Assert(n < pm->n_models );
|
|
|
|
#if defined( FREESPACE2_FORMAT )
|
|
pm->submodel[n].rad = cfread_float(fp); //radius
|
|
#endif
|
|
|
|
pm->submodel[n].parent = cfread_int(fp);
|
|
|
|
// cfread_vector(&pm->submodel[n].norm,fp);
|
|
// d = cfread_float(fp);
|
|
// cfread_vector(&pm->submodel[n].pnt,fp);
|
|
cfread_vector(&pm->submodel[n].offset,fp);
|
|
|
|
// 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 ));
|
|
|
|
#if defined ( FREESPACE1_FORMAT )
|
|
pm->submodel[n].rad = cfread_float(fp); //radius
|
|
#endif
|
|
|
|
// pm->submodel[n].tree_offset = cfread_int(fp); //offset
|
|
// pm->submodel[n].data_offset = cfread_int(fp); //offset
|
|
|
|
cfread_vector(&pm->submodel[n].geometric_center,fp);
|
|
|
|
cfread_vector(&pm->submodel[n].min,fp);
|
|
cfread_vector(&pm->submodel[n].max,fp);
|
|
|
|
pm->submodel[n].name[0] = '\0';
|
|
|
|
cfread_string_len(pm->submodel[n].name, MAX_NAME_LEN, fp); // get the name
|
|
cfread_string_len(props, MAX_PROP_LEN, fp); // and the user properties
|
|
|
|
// sanity first!
|
|
if (maybe_swap_mins_maxs(&pm->submodel[n].min, &pm->submodel[n].max)) {
|
|
Warning(LOCATION, "Inverted bounding box on submodel '%s' of model '%s'! Swapping values to compensate.", pm->submodel[n].name, pm->filename);
|
|
}
|
|
model_calc_bound_box(pm->submodel[n].bounding_box, &pm->submodel[n].min, &pm->submodel[n].max);
|
|
|
|
pm->submodel[n].movement_type = cfread_int(fp);
|
|
pm->submodel[n].movement_axis = cfread_int(fp);
|
|
|
|
// change turret movement type to MOVEMENT_TYPE_ROT_SPECIAL
|
|
if (pm->submodel[n].movement_type == MOVEMENT_TYPE_ROT) {
|
|
if ( strstr(pm->submodel[n].name, "turret") || strstr(pm->submodel[n].name, "gun") || strstr(pm->submodel[n].name, "cannon")) {
|
|
pm->submodel[n].movement_type = MOVEMENT_TYPE_ROT_SPECIAL;
|
|
} else if (strstr(pm->submodel[n].name, "thruster")) {
|
|
// Int3();
|
|
pm->submodel[n].movement_type = MOVEMENT_TYPE_NONE;
|
|
pm->submodel[n].movement_axis = MOVEMENT_AXIS_NONE;
|
|
}else if(strstr(props, "$triggered:")){
|
|
pm->submodel[n].movement_type = MOVEMENT_TYPE_TRIGGERED;
|
|
}
|
|
}
|
|
|
|
if ( pm->submodel[n].name[0] == '\0' ) {
|
|
strcpy(pm->submodel[n].name, "unknown object name");
|
|
}
|
|
|
|
bool rotating_submodel_has_subsystem = !(pm->submodel[n].movement_type == MOVEMENT_TYPE_ROT);
|
|
if ( ( p = strstr(props, "$special"))!= NULL ) {
|
|
char type[64];
|
|
|
|
get_user_prop_value(p+9, type);
|
|
if ( !stricmp(type, "subsystem") ) { // if we have a subsystem, put it into the list!
|
|
do_new_subsystem( n_subsystems, subsystems, n, pm->submodel[n].rad, &pm->submodel[n].offset, props, pm->submodel[n].name, pm->id );
|
|
rotating_submodel_has_subsystem = true;
|
|
} else if ( !stricmp(type, "no_rotate") ) {
|
|
// mark those submodels which should not rotate - ie, those with no subsystem
|
|
pm->submodel[n].movement_type = MOVEMENT_TYPE_NONE;
|
|
pm->submodel[n].movement_axis = MOVEMENT_AXIS_NONE;
|
|
} else {
|
|
// if submodel rotates (via bspgen), then there is either a subsys or special=no_rotate
|
|
Assert( pm->submodel[n].movement_type != MOVEMENT_TYPE_ROT );
|
|
}
|
|
}
|
|
|
|
/* if ( strstr(props, "$nontargetable")!= NULL ) {
|
|
pm->submodel[n].targetable = 0;
|
|
}else{
|
|
pm->submodel[n].targetable = 1;
|
|
}
|
|
*/
|
|
// pm->submodel[n].n_triggers = 0;
|
|
// pm->submodel[n].triggers = NULL;
|
|
|
|
//parse_triggers(pm->submodel[n].n_triggers, &pm->submodel[n].triggers, &props[0]);
|
|
|
|
if (strstr(props, "$no_collisions") != NULL )
|
|
pm->submodel[n].no_collisions = true;
|
|
else
|
|
pm->submodel[n].no_collisions = false;
|
|
|
|
if ( (p = strstr(props, "$gun_rotation:")) == NULL )
|
|
pm->submodel[n].gun_rotation = true;
|
|
else
|
|
pm->submodel[n].gun_rotation = false;
|
|
|
|
if ( (p = strstr(props, "$detail_box:")) != NULL ) {
|
|
p += 13;
|
|
pm->submodel[n].use_render_box = atoi(p);
|
|
|
|
if ( (p = strstr(props, "$box_min:")) != NULL ) {
|
|
p += 10;
|
|
pm->submodel[n].render_box_min.xyz.x = (float)strtod(p, (char **)NULL);
|
|
while (*p != ',') p++;
|
|
pm->submodel[n].render_box_min.xyz.y = (float)strtod(++p, (char **)NULL);
|
|
while (*p != ',') p++;
|
|
pm->submodel[n].render_box_min.xyz.z = (float)strtod(++p, (char **)NULL);
|
|
} else {
|
|
pm->submodel[n].render_box_min = pm->submodel[n].min;
|
|
}
|
|
|
|
if ( (p = strstr(props, "$box_max:")) != NULL ) {
|
|
p += 10;
|
|
pm->submodel[n].render_box_max.xyz.x = (float)strtod(p, (char **)NULL);
|
|
while (*p != ',') p++;
|
|
pm->submodel[n].render_box_max.xyz.y = (float)strtod(++p, (char **)NULL);
|
|
while (*p != ',') p++;
|
|
pm->submodel[n].render_box_max.xyz.z = (float)strtod(++p, (char **)NULL);
|
|
} else {
|
|
pm->submodel[n].render_box_max = pm->submodel[n].max;
|
|
}
|
|
}
|
|
|
|
// Added for new handling of turret orientation - KeldorKatarn
|
|
matrix *orient = &pm->submodel[n].orientation;
|
|
|
|
if ( (p = strstr(props, "$uvec:")) != NULL ) {
|
|
p += 6;
|
|
|
|
char *parsed_string = p;
|
|
|
|
while (*parsed_string == ' ') {
|
|
parsed_string++; // Skip spaces
|
|
}
|
|
|
|
orient->vec.uvec.xyz.x = (float)(strtod(parsed_string, (char **)NULL));
|
|
|
|
// Find end of number
|
|
parsed_string = strchr(parsed_string, ',');
|
|
Assert(parsed_string != NULL);
|
|
parsed_string++;
|
|
|
|
while (*parsed_string == ' ') {
|
|
parsed_string++; // Skip spaces
|
|
}
|
|
|
|
orient->vec.uvec.xyz.y = (float)(strtod(parsed_string, (char **)NULL));
|
|
|
|
// Find end of number
|
|
parsed_string = strchr(parsed_string, ',');
|
|
Assert(parsed_string != NULL);
|
|
parsed_string++;
|
|
|
|
while (*parsed_string == ' ') {
|
|
parsed_string++; // Skip spaces
|
|
}
|
|
|
|
orient->vec.uvec.xyz.z = (float)(strtod(parsed_string, (char **)NULL));
|
|
|
|
if ( (p = strstr(props, "$fvec:")) != NULL ) {
|
|
parsed_string = p + 6;
|
|
|
|
while (*parsed_string == ' ') {
|
|
parsed_string++; // Skip spaces
|
|
}
|
|
|
|
orient->vec.fvec.xyz.x = (float)(strtod(parsed_string, (char **)NULL));
|
|
|
|
// Find end of number
|
|
parsed_string = strchr(parsed_string, ',');
|
|
Assert(parsed_string != NULL);
|
|
parsed_string++;
|
|
|
|
while (*parsed_string == ' ') {
|
|
parsed_string++; // Skip spaces
|
|
}
|
|
|
|
orient->vec.fvec.xyz.y = (float)(strtod(parsed_string, (char **)NULL));
|
|
|
|
// Find end of number
|
|
parsed_string = strchr(parsed_string, ',');
|
|
Assert(parsed_string != NULL);
|
|
parsed_string++;
|
|
|
|
while (*parsed_string == ' ') {
|
|
parsed_string++; // Skip spaces
|
|
}
|
|
|
|
orient->vec.fvec.xyz.z = (float)(strtod(parsed_string, (char **)NULL));
|
|
|
|
vm_vec_normalize(&orient->vec.uvec);
|
|
vm_vec_normalize(&orient->vec.fvec);
|
|
|
|
vm_vec_crossprod(&orient->vec.rvec, &orient->vec.uvec, &orient->vec.fvec);
|
|
vm_vec_crossprod(&orient->vec.fvec, &orient->vec.rvec, &orient->vec.uvec);
|
|
|
|
vm_vec_normalize(&orient->vec.fvec);
|
|
vm_vec_normalize(&orient->vec.rvec);
|
|
|
|
vm_orthogonalize_matrix(orient);
|
|
} else {
|
|
int parent_num = pm->submodel[n].parent;
|
|
|
|
if (parent_num > -1) {
|
|
*orient = pm->submodel[parent_num].orientation;
|
|
} else {
|
|
*orient = vmd_identity_matrix;
|
|
}
|
|
|
|
Warning( LOCATION, "Improper custom orientation matrix, you must define a up vector, then a forward vector");
|
|
}
|
|
} else {
|
|
int parent_num = pm->submodel[n].parent;
|
|
|
|
if (parent_num > -1) {
|
|
*orient = pm->submodel[parent_num].orientation;
|
|
} else {
|
|
*orient = vmd_identity_matrix;
|
|
}
|
|
|
|
if (strstr(props, "$fvec:") != NULL) {
|
|
Warning( LOCATION, "Improper custom orientation matrix, you must define a up vector, then a forward vector");
|
|
}
|
|
}
|
|
|
|
if ( !rotating_submodel_has_subsystem ) {
|
|
nprintf(("Model", "Model %s: Rotating Submodel without subsystem: %s\n", pm->filename, pm->submodel[n].name));
|
|
|
|
// mark those submodels which should not rotate - ie, those with no subsystem
|
|
pm->submodel[n].movement_type = MOVEMENT_TYPE_NONE;
|
|
pm->submodel[n].movement_axis = MOVEMENT_AXIS_NONE;
|
|
}
|
|
|
|
|
|
pm->submodel[n].angs.p = 0.0f;
|
|
pm->submodel[n].angs.b = 0.0f;
|
|
pm->submodel[n].angs.h = 0.0f;
|
|
|
|
{
|
|
int nchunks = cfread_int( fp ); // Throw away nchunks
|
|
if ( nchunks > 0 ) {
|
|
Error( LOCATION, "Model '%s' is chunked. See John or Adam!\n", pm->filename );
|
|
}
|
|
}
|
|
pm->submodel[n].bsp_data_size = cfread_int(fp);
|
|
if ( pm->submodel[n].bsp_data_size > 0 ) {
|
|
pm->submodel[n].bsp_data = (ubyte *)vm_malloc(pm->submodel[n].bsp_data_size);
|
|
cfread(pm->submodel[n].bsp_data,1,pm->submodel[n].bsp_data_size,fp);
|
|
swap_bsp_data( pm, pm->submodel[n].bsp_data );
|
|
} else {
|
|
pm->submodel[n].bsp_data = NULL;
|
|
}
|
|
|
|
if ( strstr( pm->submodel[n].name, "thruster") )
|
|
pm->submodel[n].is_thruster=1;
|
|
else
|
|
pm->submodel[n].is_thruster=0;
|
|
|
|
if ( strstr( pm->submodel[n].name, "-destroyed") )
|
|
pm->submodel[n].is_damaged=1;
|
|
else
|
|
pm->submodel[n].is_damaged=0;
|
|
|
|
//mprintf(( "Submodel %d, name '%s', parent = %d\n", n, pm->submodel[n].name, pm->submodel[n].parent ));
|
|
//key_getch();
|
|
|
|
//mprintf(( "Submodel %d, tree offset %d\n", n, pm->submodel[n].tree_offset ));
|
|
//mprintf(( "Submodel %d, data offset %d\n", n, pm->submodel[n].data_offset ));
|
|
//key_getch();
|
|
|
|
if(!Cmdline_nohtl) {
|
|
generate_vertex_buffers(&pm->submodel[n], pm);
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
case ID_SLDC: // kazan - Shield Collision tree
|
|
{
|
|
pm->sldc_size = cfread_int(fp);
|
|
pm->shield_collision_tree = (ubyte *)vm_malloc(pm->sldc_size);
|
|
cfread(pm->shield_collision_tree,1,pm->sldc_size,fp);
|
|
swap_sldc_data(pm->shield_collision_tree);
|
|
//mprintf(( "Shield Collision Tree, %d bytes in size\n", pm->sldc_size));
|
|
}
|
|
break;
|
|
|
|
case ID_SHLD:
|
|
{
|
|
pm->shield.nverts = cfread_int( fp ); // get the number of vertices in the list
|
|
|
|
if (pm->shield.nverts > 0) {
|
|
pm->shield.verts = (shield_vertex *)vm_malloc(pm->shield.nverts * sizeof(shield_vertex) );
|
|
Assert( pm->shield.verts );
|
|
for ( i = 0; i < pm->shield.nverts; i++ ) // read in the vertex list
|
|
cfread_vector( &(pm->shield.verts[i].pos), fp );
|
|
}
|
|
|
|
pm->shield.ntris = cfread_int( fp ); // get the number of triangles that compose the shield
|
|
|
|
if (pm->shield.ntris > 0) {
|
|
pm->shield.tris = (shield_tri *)vm_malloc(pm->shield.ntris * sizeof(shield_tri) );
|
|
Assert( pm->shield.tris );
|
|
for ( i = 0; i < pm->shield.ntris; i++ ) {
|
|
cfread_vector( &(pm->shield.tris[i].norm), fp );
|
|
for ( j = 0; j < 3; j++ ) {
|
|
pm->shield.tris[i].verts[j] = cfread_int( fp ); // read in the indices into the shield_vertex list
|
|
/*
|
|
#ifndef NDEBUG
|
|
if (pm->shield.tris[i].verts[j] >= nverts)
|
|
if (!warning_displayed) {
|
|
warning_displayed = 1;
|
|
Warning(LOCATION, "Ship %s has a bogus shield mesh.\nOnly %i vertices, index %i found.\n", filename, nverts, pm->shield.tris[i].verts[j]);
|
|
}
|
|
#endif
|
|
*/
|
|
}
|
|
for ( j = 0; j < 3; j++ )
|
|
pm->shield.tris[i].neighbors[j] = cfread_int( fp ); // read in the neighbor indices -- indexes into tri list
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ID_GPNT:
|
|
pm->n_guns = cfread_int(fp);
|
|
|
|
if (pm->n_guns > 0) {
|
|
pm->gun_banks = (w_bank *)vm_malloc(sizeof(w_bank) * pm->n_guns);
|
|
Assert( pm->gun_banks != NULL );
|
|
|
|
for (i = 0; i < pm->n_guns; i++ ) {
|
|
w_bank *bank = &pm->gun_banks[i];
|
|
|
|
bank->num_slots = cfread_int(fp);
|
|
Assert ( bank->num_slots < MAX_SLOTS );
|
|
for (j = 0; j < bank->num_slots; j++) {
|
|
cfread_vector( &(bank->pnt[j]), fp );
|
|
cfread_vector( &(bank->norm[j]), fp );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ID_MPNT:
|
|
pm->n_missiles = cfread_int(fp);
|
|
|
|
if (pm->n_missiles > 0) {
|
|
pm->missile_banks = (w_bank *)vm_malloc(sizeof(w_bank) * pm->n_missiles);
|
|
Assert( pm->missile_banks != NULL );
|
|
|
|
for (i = 0; i < pm->n_missiles; i++ ) {
|
|
w_bank *bank = &pm->missile_banks[i];
|
|
|
|
bank->num_slots = cfread_int(fp);
|
|
Assert ( bank->num_slots < MAX_SLOTS );
|
|
for (j = 0; j < bank->num_slots; j++) {
|
|
cfread_vector( &(bank->pnt[j]), fp );
|
|
cfread_vector( &(bank->norm[j]), fp );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ID_DOCK: {
|
|
char props[MAX_PROP_LEN];
|
|
|
|
pm->n_docks = cfread_int(fp);
|
|
|
|
if (pm->n_docks > 0) {
|
|
pm->docking_bays = (dock_bay *)vm_malloc(sizeof(dock_bay) * pm->n_docks);
|
|
Assert( pm->docking_bays != NULL );
|
|
|
|
for (i = 0; i < pm->n_docks; i++ ) {
|
|
char *p;
|
|
dock_bay *bay = &pm->docking_bays[i];
|
|
|
|
cfread_string_len( props, MAX_PROP_LEN, fp );
|
|
if ( (p = strstr(props, "$name"))!= NULL )
|
|
get_user_prop_value(p+5, bay->name);
|
|
else
|
|
sprintf(bay->name, "<unnamed bay %c>", 'A' + i);
|
|
bay->num_spline_paths = cfread_int( fp );
|
|
if ( bay->num_spline_paths > 0 ) {
|
|
bay->splines = (int *)vm_malloc(sizeof(int) * bay->num_spline_paths);
|
|
for ( j = 0; j < bay->num_spline_paths; j++ )
|
|
bay->splines[j] = cfread_int(fp);
|
|
} else {
|
|
bay->splines = NULL;
|
|
}
|
|
|
|
// determine what this docking bay can be used for
|
|
if ( !strnicmp(bay->name, "cargo", 5) )
|
|
bay->type_flags = DOCK_TYPE_CARGO;
|
|
else
|
|
bay->type_flags = (DOCK_TYPE_REARM | DOCK_TYPE_GENERIC);
|
|
|
|
bay->num_slots = cfread_int(fp);
|
|
|
|
if(bay->num_slots != 2) {
|
|
Warning(LOCATION, "Model '%s' has %d slots in dock point '%s'; models must have exactly %d slots per dock point.", filename, bay->num_slots, bay->name, 2);
|
|
}
|
|
|
|
for (j = 0; j < bay->num_slots; j++) {
|
|
cfread_vector( &(bay->pnt[j]), fp );
|
|
cfread_vector( &(bay->norm[j]), fp );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ID_GLOW: //start glow point reading -Bobboau
|
|
{
|
|
char props[MAX_PROP_LEN];
|
|
|
|
int gpb_num = cfread_int(fp);
|
|
|
|
pm->n_glow_point_banks = gpb_num;
|
|
pm->glow_point_banks = NULL;
|
|
|
|
if (gpb_num > 0)
|
|
{
|
|
pm->glow_point_banks = (glow_point_bank *) vm_malloc(sizeof(glow_point_bank) * gpb_num);
|
|
Assert(pm->glow_point_banks != NULL);
|
|
}
|
|
|
|
for (int gpb = 0; gpb < gpb_num; gpb++)
|
|
{
|
|
glow_point_bank *bank = &pm->glow_point_banks[gpb];
|
|
|
|
bank->is_on = 1;
|
|
bank->glow_timestamp = 0;
|
|
bank->disp_time = cfread_int(fp);
|
|
bank->on_time = cfread_int(fp);
|
|
bank->off_time = cfread_int(fp);
|
|
bank->submodel_parent = cfread_int(fp);
|
|
bank->LOD = cfread_int(fp);
|
|
bank->type = cfread_int(fp);
|
|
bank->num_points = cfread_int(fp);
|
|
bank->points = NULL;
|
|
|
|
if (bank->num_points > 0)
|
|
bank->points = (glow_point *) vm_malloc(sizeof(glow_point) * bank->num_points);
|
|
|
|
if((bank->off_time > 0) && (bank->disp_time > 0))
|
|
bank->is_on = 0;
|
|
|
|
cfread_string_len(props, MAX_PROP_LEN, fp);
|
|
// look for $glow_texture=xxx
|
|
int length = strlen(props);
|
|
|
|
if (length > 0)
|
|
{
|
|
int base_length = strlen("$glow_texture=");
|
|
Assert(strstr( (const char *)&props, "$glow_texture=") != NULL);
|
|
Assert(length > base_length);
|
|
char *glow_texture_name = props + base_length;
|
|
|
|
if (glow_texture_name[0] == '$')
|
|
glow_texture_name++;
|
|
|
|
bank->glow_bitmap = bm_load(glow_texture_name);
|
|
|
|
if (bank->glow_bitmap < 0)
|
|
{
|
|
Warning( LOCATION, "Couldn't open texture '%s'\nreferenced by model '%s'\n", glow_texture_name, pm->filename);
|
|
}
|
|
else
|
|
{
|
|
nprintf(( "Model", "Glow point bank %i texture num is %d for '%s'\n", gpb, bank->glow_bitmap, pm->filename));
|
|
}
|
|
|
|
strcat(glow_texture_name, "-neb");
|
|
bank->glow_neb_bitmap = bm_load(glow_texture_name);
|
|
|
|
if (bank->glow_neb_bitmap < 0)
|
|
{
|
|
bank->glow_neb_bitmap = bank->glow_bitmap;
|
|
nprintf(( "Model", "Glow point bank texture not found for '%s', setting as the normal one num\n", pm->filename));
|
|
// Error( LOCATION, "Couldn't open texture '%s'\nreferenced by model '%s'\n", glow_texture_name, pm->filename );
|
|
}
|
|
else
|
|
{
|
|
nprintf(( "Model", "Glow point bank %i nebula texture num is %d for '%s'\n", gpb, bank->glow_neb_bitmap, pm->filename));
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < bank->num_points; j++)
|
|
{
|
|
glow_point *p = &bank->points[j];
|
|
|
|
cfread_vector(&(p->pnt), fp);
|
|
cfread_vector(&(p->norm), fp);
|
|
p->radius = cfread_float( fp);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ID_FUEL:
|
|
char props[MAX_PROP_LEN];
|
|
pm->n_thrusters = cfread_int(fp);
|
|
|
|
if (pm->n_thrusters > 0) {
|
|
pm->thrusters = (thruster_bank *)vm_malloc(sizeof(thruster_bank) * pm->n_thrusters);
|
|
Assert( pm->thrusters != NULL );
|
|
|
|
for (i = 0; i < pm->n_thrusters; i++ ) {
|
|
thruster_bank *bank = &pm->thrusters[i];
|
|
|
|
bank->num_points = cfread_int(fp);
|
|
bank->points = NULL;
|
|
|
|
if (bank->num_points > 0)
|
|
bank->points = (glow_point *) vm_malloc(sizeof(glow_point) * bank->num_points);
|
|
|
|
bank ->obj_num = -1;
|
|
|
|
if (pm->version < 2117) {
|
|
bank->wash_info_pointer = NULL;
|
|
} else {
|
|
cfread_string_len( props, MAX_PROP_LEN, fp );
|
|
// look for $engine_subsystem=xxx
|
|
int length = strlen(props);
|
|
if (length > 0) {
|
|
int base_length = strlen("$engine_subsystem=");
|
|
Assert( strstr( (const char *)&props, "$engine_subsystem=") != NULL );
|
|
Assert( length > base_length );
|
|
char *engine_subsys_name = props + base_length;
|
|
if (engine_subsys_name[0] == '$') {
|
|
engine_subsys_name++;
|
|
}
|
|
|
|
nprintf(("wash", "Ship %s with engine wash associated with subsys %s\n", filename, engine_subsys_name));
|
|
|
|
// set wash_info_index to invalid
|
|
int table_error = 1;
|
|
bank->wash_info_pointer = NULL;
|
|
for (int k=0; k<n_subsystems; k++) {
|
|
if ( !subsystem_stricmp(subsystems[k].subobj_name, engine_subsys_name) ) {
|
|
bank->wash_info_pointer = subsystems[k].engine_wash_pointer;
|
|
if (bank->wash_info_pointer != NULL) {
|
|
table_error = 0;
|
|
}
|
|
// also set what subsystem this is attached to but not if we only have one thruster bank
|
|
// do this so that original :V: models still work like they used to
|
|
if (pm->n_thrusters > 1) {
|
|
bank->obj_num = k;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( (bank->wash_info_pointer == NULL) && (n_subsystems > 0) ) {
|
|
if (table_error) {
|
|
// Warning(LOCATION, "No engine wash table entry in ships.tbl for ship model %s", filename);
|
|
} else {
|
|
Warning(LOCATION, "Inconsistent model: Engine wash engine subsystem does not match any ship subsytem names for ship model %s", filename);
|
|
}
|
|
}
|
|
} else {
|
|
bank->wash_info_pointer = NULL;
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < bank->num_points; j++) {
|
|
glow_point *p = &bank->points[j];
|
|
|
|
cfread_vector( &(p->pnt), fp );
|
|
cfread_vector( &(p->norm), fp );
|
|
|
|
if ( pm->version > 2004 ) {
|
|
p->radius = cfread_float( fp );
|
|
//mprintf(( "Rad = %.2f\n", rad ));
|
|
} else {
|
|
p->radius = 1.0f;
|
|
}
|
|
}
|
|
//mprintf(( "Num slots = %d\n", bank->num_slots ));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ID_TGUN:
|
|
case ID_TMIS: {
|
|
int n_banks, n_slots, parent;
|
|
model_subsystem *subsystemp;
|
|
int snum=-1;
|
|
|
|
n_banks = cfread_int(fp); // number of turret points
|
|
for ( i = 0; i < n_banks; i++ ) {
|
|
int physical_parent; // who are we attached to?
|
|
parent = cfread_int( fp ); // get the turret parent of the object
|
|
|
|
physical_parent = cfread_int(fp); // The parent subobj that this is physically attached to
|
|
|
|
if ( subsystems ) {
|
|
for ( snum = 0; snum < n_subsystems; snum++ ) {
|
|
subsystemp = &subsystems[snum];
|
|
|
|
if ( parent == subsystemp->subobj_num ) {
|
|
cfread_vector( &subsystemp->turret_norm, fp );
|
|
vm_vector_2_matrix(&subsystemp->turret_matrix,&subsystemp->turret_norm,NULL,NULL);
|
|
|
|
n_slots = cfread_int( fp );
|
|
subsystemp->turret_gun_sobj = physical_parent;
|
|
if(n_slots > MAX_TFP) {
|
|
Warning(LOCATION, "Model %s has too many turret firing points on subsystem %s", subsystemp->name);
|
|
}
|
|
|
|
for (j = 0; j < n_slots; j++ ) {
|
|
if(j < MAX_TFP)
|
|
cfread_vector( &subsystemp->turret_firing_point[j], fp );
|
|
else
|
|
{
|
|
vec3d bogus;
|
|
cfread_vector(&bogus, fp);
|
|
}
|
|
}
|
|
Assert( n_slots > 0 );
|
|
|
|
subsystemp->turret_num_firing_points = n_slots;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//turret_gun_sobj
|
|
|
|
if ( (n_subsystems == 0) || (snum == n_subsystems) ) {
|
|
vec3d bogus;
|
|
|
|
nprintf(("Warning", "Turret object not found for turret firing point in model %s\n", model_filename));
|
|
cfread_vector( &bogus, fp );
|
|
n_slots = cfread_int( fp );
|
|
for (j = 0; j < n_slots; j++ )
|
|
cfread_vector( &bogus, fp );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ID_SPCL: {
|
|
char name[MAX_NAME_LEN], props[MAX_PROP_LEN], *p;
|
|
int n_specials;
|
|
float radius;
|
|
vec3d pnt;
|
|
|
|
n_specials = cfread_int(fp); // get the number of special subobjects we have
|
|
for (i = 0; i < n_specials; i++) {
|
|
|
|
// get the next free object of the subobject list. Flag error if no more room
|
|
|
|
cfread_string_len(name, MAX_NAME_LEN, fp); // get the name of this special polygon
|
|
|
|
cfread_string_len(props, MAX_PROP_LEN, fp); // will definately have properties as well!
|
|
cfread_vector( &pnt, fp );
|
|
radius = cfread_float( fp );
|
|
|
|
// check if $Split
|
|
p = strstr(name, "$split");
|
|
if (p != NULL) {
|
|
pm->split_plane[pm->num_split_plane] = pnt.xyz.z;
|
|
pm->num_split_plane++;
|
|
Assert(pm->num_split_plane <= MAX_SPLIT_PLANE);
|
|
} else if ( ( p = strstr(props, "$special"))!= NULL ) {
|
|
char type[64];
|
|
|
|
get_user_prop_value(p+9, type);
|
|
if ( !stricmp(type, "subsystem") ) // if we have a subsystem, put it into the list!
|
|
do_new_subsystem( n_subsystems, subsystems, -1, radius, &pnt, props, &name[1], pm->id ); // skip the first '$' character of the name
|
|
} else if ( strstr(name, "$enginelarge") || strstr(name, "$enginehuge") ){
|
|
do_new_subsystem( n_subsystems, subsystems, -1, radius, &pnt, props, &name[1], pm->id ); // skip the first '$' character of the name
|
|
} else {
|
|
nprintf(("Warning", "Unknown special object type %s while reading model %s\n", name, pm->filename));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ID_TXTR: { //Texture filename list
|
|
int n;
|
|
// char name_buf[128];
|
|
|
|
//mprintf(0,"Got chunk TXTR, len=%d\n",len);
|
|
|
|
|
|
n = cfread_int(fp);
|
|
pm->n_textures = n;
|
|
// Dont overwrite memory!!
|
|
Assert(n <= MAX_MODEL_TEXTURES);
|
|
//mprintf(0," num textures = %d\n",n);
|
|
for (i=0; i<n; i++ )
|
|
{
|
|
char tmp_name[256];
|
|
cfread_string_len(tmp_name,127,fp);
|
|
model_load_texture(pm, i, tmp_name);
|
|
//mprintf(0,"<%s>\n",name_buf);
|
|
}
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
/* case ID_IDTA: //Interpreter data
|
|
//mprintf(0,"Got chunk IDTA, len=%d\n",len);
|
|
|
|
pm->model_data = (ubyte *)vm_malloc(len);
|
|
pm->model_data_size = len;
|
|
Assert(pm->model_data != NULL );
|
|
|
|
cfread(pm->model_data,1,len,fp);
|
|
|
|
break;
|
|
*/
|
|
|
|
case ID_INFO: // don't need to do anything with info stuff
|
|
|
|
#ifndef NDEBUG
|
|
pm->debug_info_size = len;
|
|
pm->debug_info = (char *)vm_malloc(pm->debug_info_size+1);
|
|
Assert(pm->debug_info!=NULL);
|
|
memset(pm->debug_info,0,len+1);
|
|
cfread( pm->debug_info, 1, len, fp );
|
|
#endif
|
|
break;
|
|
|
|
case ID_GRID:
|
|
break;
|
|
|
|
case ID_PATH:
|
|
pm->n_paths = cfread_int( fp );
|
|
|
|
if (pm->n_paths <= 0) {
|
|
break;
|
|
}
|
|
|
|
pm->paths = (model_path *)vm_malloc(sizeof(model_path)*pm->n_paths);
|
|
Assert( pm->paths != NULL );
|
|
|
|
memset( pm->paths, 0, sizeof(model_path) * pm->n_paths );
|
|
|
|
for (i=0; i<pm->n_paths; i++ ) {
|
|
cfread_string_len(pm->paths[i].name , MAX_NAME_LEN-1, fp);
|
|
if ( pm->version >= 2002 ) {
|
|
// store the sub_model name number of the parent
|
|
cfread_string_len(pm->paths[i].parent_name , MAX_NAME_LEN-1, fp);
|
|
// get rid of leading '$' char in name
|
|
if ( pm->paths[i].parent_name[0] == '$' ) {
|
|
char tmpbuf[MAX_NAME_LEN];
|
|
strcpy(tmpbuf, pm->paths[i].parent_name+1);
|
|
strcpy(pm->paths[i].parent_name, tmpbuf);
|
|
}
|
|
// store the sub_model index (ie index into pm->submodel) of the parent
|
|
pm->paths[i].parent_submodel = -1;
|
|
for ( j = 0; j < pm->n_models; j++ ) {
|
|
if ( !stricmp( pm->submodel[j].name, pm->paths[i].parent_name) ) {
|
|
pm->paths[i].parent_submodel = j;
|
|
}
|
|
}
|
|
} else {
|
|
pm->paths[i].parent_name[0] = 0;
|
|
pm->paths[i].parent_submodel = -1;
|
|
}
|
|
|
|
pm->paths[i].nverts = cfread_int( fp );
|
|
pm->paths[i].verts = (mp_vert *)vm_malloc( sizeof(mp_vert) * pm->paths[i].nverts );
|
|
pm->paths[i].goal = pm->paths[i].nverts - 1;
|
|
pm->paths[i].type = MP_TYPE_UNUSED;
|
|
pm->paths[i].value = 0;
|
|
Assert(pm->paths[i].verts!=NULL);
|
|
memset( pm->paths[i].verts, 0, sizeof(mp_vert) * pm->paths[i].nverts );
|
|
|
|
for (j=0; j<pm->paths[i].nverts; j++ ) {
|
|
cfread_vector(&pm->paths[i].verts[j].pos,fp );
|
|
pm->paths[i].verts[j].radius = cfread_float( fp );
|
|
|
|
{ // version 1802 added turret stuff
|
|
int nturrets, k;
|
|
|
|
nturrets = cfread_int( fp );
|
|
pm->paths[i].verts[j].nturrets = nturrets;
|
|
|
|
if (nturrets > 0) {
|
|
pm->paths[i].verts[j].turret_ids = (int *)vm_malloc( sizeof(int) * nturrets );
|
|
for ( k = 0; k < nturrets; k++ )
|
|
pm->paths[i].verts[j].turret_ids[k] = cfread_int( fp );
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ID_EYE: // an eye position(s)
|
|
{
|
|
int num_eyes;
|
|
|
|
// all eyes points are stored simply as vectors and their normals.
|
|
// 0th element is used as usual player view position.
|
|
|
|
num_eyes = cfread_int( fp );
|
|
pm->n_view_positions = num_eyes;
|
|
Assert ( num_eyes < MAX_EYES );
|
|
for (i = 0; i < num_eyes; i++ ) {
|
|
pm->view_positions[i].parent = cfread_int( fp );
|
|
cfread_vector( &pm->view_positions[i].pnt, fp );
|
|
cfread_vector( &pm->view_positions[i].norm, fp );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ID_INSG:
|
|
int num_ins, num_verts, num_faces, idx, idx2, idx3;
|
|
|
|
// get the # of insignias
|
|
num_ins = cfread_int(fp);
|
|
pm->num_ins = num_ins;
|
|
|
|
// read in the insignias
|
|
for(idx=0; idx<num_ins; idx++){
|
|
// get the detail level
|
|
pm->ins[idx].detail_level = cfread_int(fp);
|
|
|
|
// # of faces
|
|
num_faces = cfread_int(fp);
|
|
pm->ins[idx].num_faces = num_faces;
|
|
Assert(num_faces <= MAX_INS_FACES);
|
|
|
|
// # of vertices
|
|
num_verts = cfread_int(fp);
|
|
Assert(num_verts <= MAX_INS_VECS);
|
|
|
|
// read in all the vertices
|
|
for(idx2=0; idx2<num_verts; idx2++){
|
|
cfread_vector(&pm->ins[idx].vecs[idx2], fp);
|
|
}
|
|
|
|
// read in world offset
|
|
cfread_vector(&pm->ins[idx].offset, fp);
|
|
|
|
// read in all the faces
|
|
for(idx2=0; idx2<pm->ins[idx].num_faces; idx2++){
|
|
// read in 3 vertices
|
|
for(idx3=0; idx3<3; idx3++){
|
|
pm->ins[idx].faces[idx2][idx3] = cfread_int(fp);
|
|
pm->ins[idx].u[idx2][idx3] = cfread_float(fp);
|
|
pm->ins[idx].v[idx2][idx3] = cfread_float(fp);
|
|
}
|
|
vec3d tempv;
|
|
|
|
//get three points (rotated) and compute normal
|
|
|
|
vm_vec_perp(&tempv,
|
|
&pm->ins[idx].vecs[pm->ins[idx].faces[idx2][0]],
|
|
&pm->ins[idx].vecs[pm->ins[idx].faces[idx2][1]],
|
|
&pm->ins[idx].vecs[pm->ins[idx].faces[idx2][2]]);
|
|
|
|
pm->ins[idx].norm[idx2] = tempv;
|
|
// mprintf(("insignorm %.2f %.2f %.2f\n",pm->ins[idx].norm[idx2].xyz.x, pm->ins[idx].norm[idx2].xyz.y, pm->ins[idx].norm[idx2].xyz.z));
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
// autocentering info
|
|
case ID_ACEN:
|
|
cfread_vector(&pm->autocenter, fp);
|
|
pm->flags |= PM_FLAG_AUTOCEN;
|
|
break;
|
|
|
|
default:
|
|
mprintf(("Unknown chunk <%c%c%c%c>, len = %d\n",id,id>>8,id>>16,id>>24,len));
|
|
cfseek(fp,len,SEEK_CUR);
|
|
break;
|
|
|
|
}
|
|
cfseek(fp,next_chunk,SEEK_SET);
|
|
|
|
id = cfread_int(fp);
|
|
len = cfread_int(fp);
|
|
next_chunk = cftell(fp) + len;
|
|
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
if ( ss_fp) {
|
|
int size;
|
|
|
|
cfclose(ss_fp);
|
|
ss_fp = cfopen(debug_name, "rb");
|
|
if ( ss_fp ) {
|
|
size = cfilelength(ss_fp);
|
|
cfclose(ss_fp);
|
|
if ( size <= 0 ) {
|
|
_unlink(debug_name);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
cfclose(fp);
|
|
|
|
// these must be reset to NULL for the tests to work correctly later
|
|
if ( ibuffer_info.read != NULL ) {
|
|
cfclose( ibuffer_info.read );
|
|
}
|
|
|
|
if ( ibuffer_info.write != NULL ) {
|
|
// if we switched to v2 at any point during IBX creation, make sure to update the header
|
|
if (ibuffer_info.version == 2) {
|
|
cfseek( ibuffer_info.write, 0, CF_SEEK_SET );
|
|
cfwrite_int( 0x58424932, ibuffer_info.write ); // "XBI2" - ("2IBX" in file)
|
|
}
|
|
|
|
cfclose( ibuffer_info.write );
|
|
}
|
|
|
|
memset( &ibuffer_info, 0, sizeof(IBX) );
|
|
|
|
// mprintf(("Done processing chunks\n"));
|
|
return 1;
|
|
}
|
|
|
|
void model_init_texture_map(texture_map *tmap)
|
|
{
|
|
if (tmap == NULL)
|
|
return;
|
|
|
|
memset(tmap, 0, sizeof(texture_map));
|
|
|
|
tmap->base_map.original_texture = -1;
|
|
tmap->base_map.texture = -1;
|
|
|
|
tmap->glow_map.original_texture = -1;
|
|
tmap->glow_map.texture = -1;
|
|
|
|
tmap->spec_map.original_texture = -1;
|
|
tmap->spec_map.texture = -1;
|
|
|
|
#ifdef BUMPMAPPING
|
|
tmap->bump_map.original_texture = -1;
|
|
tmap->bump_map.texture = -1;
|
|
#endif
|
|
}
|
|
|
|
//Goober
|
|
void model_load_texture(polymodel *pm, int i, char *file)
|
|
{
|
|
// NOTE: it doesn't help to use more than MAX_FILENAME_LEN here as bmpman will use that restriction
|
|
// we also have to make sure there is always a trailing NUL since overflow doesn't add it
|
|
char tmp_name[MAX_FILENAME_LEN];
|
|
int fps;
|
|
memset(tmp_name, 0, MAX_FILENAME_LEN);
|
|
strncpy(tmp_name, file, MAX_FILENAME_LEN-1);
|
|
strlwr(tmp_name);
|
|
|
|
texture_map *tmap = &pm->maps[i];
|
|
model_init_texture_map(tmap);
|
|
|
|
// base maps ---------------------------------------------------------------
|
|
if (strstr(tmp_name, "thruster") || strstr(tmp_name, "invisible") || strstr(tmp_name, "warpmap"))
|
|
{
|
|
// Don't load textures for thruster animations or invisible textures
|
|
// or warp models!-Bobboau
|
|
tmap->base_map.texture = -1;
|
|
}
|
|
else
|
|
{
|
|
// check if we should be transparent, include "-trans" but make sure to skip anything that might be "-transport"
|
|
if ( (strstr(tmp_name, "-trans") && !strstr(tmp_name, "-transpo")) || strstr(tmp_name, "shockwave") ) {
|
|
tmap->is_transparent = true;
|
|
}
|
|
|
|
if (strstr(tmp_name, "-amb")) {
|
|
tmap->is_ambient = true;
|
|
}
|
|
|
|
// try to load an ANI
|
|
tmap->base_map.texture = bm_load_animation(tmp_name, &tmap->base_map.anim.num_frames, &fps, 1, CF_TYPE_MAPS);
|
|
if (tmap->base_map.texture >= 0)
|
|
{
|
|
tmap->base_map.is_anim = true;
|
|
tmap->base_map.anim.total_time = (float) i2fl(tmap->base_map.anim.num_frames) / ((fps > 0) ? fps : 1);
|
|
}
|
|
else
|
|
{
|
|
nprintf(("Maps", "For \"%s\" I couldn't find %s.ani", pm->filename, tmp_name));
|
|
tmap->base_map.anim.num_frames = 1;
|
|
|
|
// try to load a non-ANI
|
|
tmap->base_map.texture = bm_load(tmp_name);
|
|
if (tmap->base_map.texture < 0)
|
|
{
|
|
Warning(LOCATION, "Couldn't open texture '%s'\nreferenced by model '%s'\n", tmp_name, pm->filename);
|
|
nprintf(("Maps", " or %s.pcx.", tmp_name));
|
|
}
|
|
|
|
nprintf(("Maps", "\n"));
|
|
}
|
|
}
|
|
tmap->base_map.original_texture = tmap->base_map.texture;
|
|
// -------------------------------------------------------------------------
|
|
|
|
// glow maps ---------------------------------------------------------------
|
|
if (Cmdline_noglow || (tmap->base_map.texture < 0))
|
|
{
|
|
tmap->glow_map.texture = -1;
|
|
}
|
|
else
|
|
{
|
|
memset(tmp_name, 0, MAX_FILENAME_LEN);
|
|
strncpy(tmp_name, file, MAX_FILENAME_LEN-1);
|
|
strncat(tmp_name, "-glow", MAX_FILENAME_LEN - strlen(tmp_name) - 1); // part of this may get chopped off if string is too long
|
|
strlwr(tmp_name);
|
|
|
|
// try to load an ANI
|
|
tmap->glow_map.texture = bm_load_animation(tmp_name, &tmap->glow_map.anim.num_frames, &fps, 1, CF_TYPE_MAPS);
|
|
if (tmap->glow_map.texture >= 0)
|
|
{
|
|
tmap->glow_map.is_anim = true;
|
|
tmap->glow_map.anim.total_time = (float) i2fl(tmap->glow_map.anim.num_frames) / ((fps > 0) ? fps : 1);
|
|
}
|
|
else
|
|
{
|
|
nprintf(("Maps", "For \"%s\" I couldn't find %s.ani", pm->filename, tmp_name));
|
|
tmap->glow_map.anim.num_frames = 1;
|
|
|
|
// try to load a non-ANI
|
|
tmap->glow_map.texture = bm_load(tmp_name);
|
|
if (tmap->glow_map.texture < 0)
|
|
{
|
|
nprintf(("Maps", " or %s.pcx.", tmp_name));
|
|
}
|
|
|
|
nprintf(("Maps", "\n"));
|
|
}
|
|
}
|
|
tmap->glow_map.original_texture = tmap->glow_map.texture;
|
|
// -------------------------------------------------------------------------
|
|
|
|
// specular maps -----------------------------------------------------------
|
|
if (Cmdline_nospec || (tmap->base_map.texture < 0))
|
|
{
|
|
tmap->spec_map.texture = -1;
|
|
}
|
|
else
|
|
{
|
|
memset(tmp_name, 0, MAX_FILENAME_LEN);
|
|
strncpy(tmp_name, file, MAX_FILENAME_LEN-1);
|
|
strncat(tmp_name, "-shine", MAX_FILENAME_LEN - strlen(tmp_name) - 1); // part of this may get chopped off if string is too long
|
|
strlwr(tmp_name);
|
|
|
|
// try to load an ANI
|
|
tmap->spec_map.texture = bm_load_animation(tmp_name, &tmap->spec_map.anim.num_frames, &fps, 1, CF_TYPE_MAPS);
|
|
if (tmap->spec_map.texture >= 0)
|
|
{
|
|
tmap->spec_map.is_anim = true;
|
|
tmap->spec_map.anim.total_time = (float) i2fl(tmap->spec_map.anim.num_frames) / ((fps > 0) ? fps : 1);
|
|
}
|
|
else
|
|
{
|
|
nprintf(("Maps", "For \"%s\" I couldn't find %s.ani", pm->filename, tmp_name));
|
|
tmap->spec_map.anim.num_frames = 1;
|
|
|
|
// try to load a non-ANI
|
|
tmap->spec_map.texture = bm_load(tmp_name);
|
|
if (tmap->spec_map.texture < 0)
|
|
{
|
|
nprintf(("Maps", " or %s.pcx.", tmp_name));
|
|
}
|
|
|
|
nprintf(("Maps", "\n"));
|
|
}
|
|
}
|
|
tmap->spec_map.original_texture = tmap->spec_map.texture;
|
|
// -------------------------------------------------------------------------
|
|
|
|
#ifdef BUMPMAPPING
|
|
// bump maps ---------------------------------------------------------------
|
|
if (true || (tmap->base_map.texture < 0))
|
|
{
|
|
tmap->bump_map.texture = -1;
|
|
}
|
|
else
|
|
{
|
|
memset(tmp_name, 0, MAX_FILENAME_LEN);
|
|
strncpy(tmp_name, file, MAX_FILENAME_LEN-1);
|
|
strncat(tmp_name, "-bump", MAX_FILENAME_LEN - strlen(tmp_name) - 1);
|
|
strlwr(tmp_name);
|
|
|
|
tmap->bump_map.texture = bm_load(tmp_name);
|
|
if (tmap->bump_map.texture < 0)
|
|
{
|
|
nprintf(("Maps", "For \"%s\" I couldn't find %s.pcx\n", pm->filename, tmp_name));
|
|
}
|
|
}
|
|
tmap->bump_map.original_texture = tmap->bump_map.texture;
|
|
// -------------------------------------------------------------------------
|
|
#endif
|
|
}
|
|
|
|
//returns the number of this model
|
|
int model_load(char *filename, int n_subsystems, model_subsystem *subsystems, int ferror, int duplicate)
|
|
{
|
|
int i, num, arc_idx;
|
|
polymodel *pm = NULL;
|
|
|
|
if ( !model_initted )
|
|
model_init();
|
|
|
|
// int Model_ram = 0;
|
|
|
|
#ifndef NDEBUG
|
|
int ram_before = TotalRam;
|
|
#endif
|
|
|
|
//Assert(strlen(filename) <= 12);
|
|
|
|
num = -1;
|
|
|
|
for (i=0; i< MAX_POLYGON_MODELS; i++) {
|
|
if ( Polygon_models[i] ) {
|
|
if (!stricmp(filename, Polygon_models[i]->filename) && !duplicate) {
|
|
// Model already loaded; just return.
|
|
Polygon_models[i]->used_this_mission++;
|
|
return Polygon_models[i]->id;
|
|
}
|
|
} else if ( num == -1 ) {
|
|
// This is the first empty slot
|
|
num = i;
|
|
}
|
|
}
|
|
|
|
// No empty slot
|
|
if ( num == -1 ) {
|
|
Error( LOCATION, "Too many models" );
|
|
return -1;
|
|
}
|
|
|
|
mprintf(( "Loading model '%s'\n", filename ));
|
|
|
|
pm = (polymodel *)vm_malloc( sizeof(polymodel) );
|
|
Assert( pm != NULL );
|
|
|
|
Polygon_models[num] = pm;
|
|
|
|
memset(pm, 0, sizeof(polymodel));
|
|
|
|
pm->n_paths = 0;
|
|
pm->paths = NULL;
|
|
|
|
int org_sig = Model_signature;
|
|
Model_signature+=MAX_POLYGON_MODELS;
|
|
if ( Model_signature < org_sig ) {
|
|
Model_signature = 0;
|
|
}
|
|
Assert( (Model_signature % MAX_POLYGON_MODELS) == 0 );
|
|
pm->id = Model_signature + num;
|
|
Assert( (pm->id % MAX_POLYGON_MODELS) == num );
|
|
|
|
extern int Parse_normal_problem_count;
|
|
Parse_normal_problem_count = 0;
|
|
|
|
pm->used_this_mission = 0;
|
|
|
|
#ifndef NDEBUG
|
|
char busy_text[60] = { '\0' };
|
|
|
|
SAFE_STRCAT( busy_text, "** ModelLoad: ", sizeof(busy_text) );
|
|
SAFE_STRCAT( busy_text, filename, sizeof(busy_text) );
|
|
SAFE_STRCAT( busy_text, " **", sizeof(busy_text) );
|
|
|
|
game_busy(busy_text);
|
|
#endif
|
|
|
|
if (read_model_file(pm, filename, n_subsystems, subsystems, ferror) < 0) {
|
|
if (pm != NULL) {
|
|
vm_free(pm);
|
|
pm = NULL;
|
|
}
|
|
|
|
Polygon_models[num] = NULL;
|
|
return -1;
|
|
}
|
|
|
|
pm->used_this_mission++;
|
|
|
|
#ifdef _DEBUG
|
|
if(Fred_running && Parse_normal_problem_count > 0)
|
|
{
|
|
char buffer[100];
|
|
sprintf(buffer,"Serious problem loading model %s, %d normals capped to zero",
|
|
filename, Parse_normal_problem_count);
|
|
MessageBox(NULL,buffer,"Error", MB_OK);
|
|
}
|
|
#endif
|
|
|
|
|
|
//mprintf(( "Loading model '%s'\n", filename ));
|
|
//key_getch();
|
|
|
|
//=============================
|
|
// Find the destroyed replacement models
|
|
|
|
// Set up the default values
|
|
for (i=0; i<pm->n_models; i++ ) {
|
|
pm->submodel[i].my_replacement = -1; // assume nothing replaces this
|
|
pm->submodel[i].i_replace = -1; // assume this doesn't replaces anything
|
|
}
|
|
|
|
// Search for models that have destroyed versions
|
|
for (i=0; i<pm->n_models; i++ ) {
|
|
int j;
|
|
char destroyed_name[128];
|
|
|
|
strcpy( destroyed_name, pm->submodel[i].name );
|
|
strcat( destroyed_name, "-destroyed" );
|
|
for (j=0; j<pm->n_models; j++ ) {
|
|
if ( !stricmp( pm->submodel[j].name, destroyed_name )) {
|
|
// mprintf(( "Found destroyed model for '%s'\n", pm->submodel[i].name ));
|
|
pm->submodel[i].my_replacement = j;
|
|
pm->submodel[j].i_replace = i;
|
|
}
|
|
}
|
|
|
|
// Search for models with live debris
|
|
// This debris comes from a destroyed subsystem when ship is still alive
|
|
char live_debris_name[128];
|
|
|
|
strcpy( live_debris_name, "debris-" );
|
|
strcat( live_debris_name, pm->submodel[i].name );
|
|
|
|
|
|
pm->submodel[i].num_live_debris = 0;
|
|
for (j=0; j<pm->n_models; j++ ) {
|
|
// check if current model name is substring of destroyed
|
|
if ( strstr( pm->submodel[j].name, live_debris_name )) {
|
|
mprintf(( "Found live debris model for '%s'\n", pm->submodel[i].name ));
|
|
Assert(pm->submodel[i].num_live_debris < MAX_LIVE_DEBRIS);
|
|
pm->submodel[i].live_debris[pm->submodel[i].num_live_debris++] = j;
|
|
pm->submodel[j].is_live_debris = 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
create_family_tree(pm);
|
|
// dump_object_tree(pm);
|
|
|
|
//==============================
|
|
// Find all the lower detail versions of the hires model
|
|
for (i=0; i<pm->n_models; i++ ) {
|
|
int j, l1;
|
|
bsp_info * sm1 = &pm->submodel[i];
|
|
|
|
// set all arc types to be default
|
|
for(arc_idx=0; arc_idx < MAX_ARC_EFFECTS; arc_idx++){
|
|
sm1->arc_type[arc_idx] = MARC_TYPE_NORMAL;
|
|
}
|
|
|
|
sm1->num_details = 0;
|
|
l1 = strlen(sm1->name);
|
|
|
|
for (j=0; j<pm->num_debris_objects;j++ ) {
|
|
if ( i == pm->debris_objects[j] ) {
|
|
sm1->is_damaged = 1;
|
|
}
|
|
}
|
|
|
|
|
|
for (j=0; j<MAX_MODEL_DETAIL_LEVELS; j++ ) {
|
|
sm1->details[j] = -1;
|
|
}
|
|
|
|
for (j=0; j<pm->n_models; j++ ) {
|
|
int k;
|
|
bsp_info * sm2 = &pm->submodel[j];
|
|
|
|
if ( i==j ) continue;
|
|
|
|
// set all arc types to be default
|
|
for(arc_idx=0; arc_idx < MAX_ARC_EFFECTS; arc_idx++){
|
|
sm2->arc_type[arc_idx] = MARC_TYPE_NORMAL;
|
|
}
|
|
|
|
// if sm2 is a detail of sm1 and sm1 is a high detail, then add it to sm1's list
|
|
if ((int)strlen(sm2->name)!=l1) continue;
|
|
|
|
int ndiff = 0;
|
|
int first_diff = 0;
|
|
for ( k=0; k<l1; k++) {
|
|
if (sm1->name[k] != sm2->name[k] ) {
|
|
if (ndiff==0) first_diff = k;
|
|
ndiff++;
|
|
}
|
|
}
|
|
if (ndiff==1) { // They only differ by one character!
|
|
int dl1, dl2;
|
|
dl1 = tolower(sm1->name[first_diff]) - 'a';
|
|
dl2 = tolower(sm2->name[first_diff]) - 'a';
|
|
|
|
if ( (dl1<0) || (dl2<0) || (dl1>=MAX_MODEL_DETAIL_LEVELS) || (dl2>=MAX_MODEL_DETAIL_LEVELS) ) continue; // invalid detail levels
|
|
|
|
if ( dl1 == 0 ) {
|
|
dl2--; // Start from 1 up...
|
|
if (dl2 >= sm1->num_details ) sm1->num_details = dl2+1;
|
|
sm1->details[dl2] = j;
|
|
//mprintf(( "Submodel '%s' is detail level %d of '%s'\n", sm2->name, dl2, sm1->name ));
|
|
}
|
|
}
|
|
}
|
|
|
|
for (j=0; j<sm1->num_details; j++ ) {
|
|
if ( sm1->details[j] == -1 ) {
|
|
// Warning( LOCATION, "Model '%s' could find all detail levels for submodel '%s'", pm->filename, sm1->name );
|
|
sm1->num_details = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
model_octant_create( pm );
|
|
|
|
// Find the core_radius... the minimum of
|
|
float rx, ry, rz;
|
|
rx = fl_abs( pm->submodel[pm->detail[0]].max.xyz.x - pm->submodel[pm->detail[0]].min.xyz.x );
|
|
ry = fl_abs( pm->submodel[pm->detail[0]].max.xyz.y - pm->submodel[pm->detail[0]].min.xyz.y );
|
|
rz = fl_abs( pm->submodel[pm->detail[0]].max.xyz.z - pm->submodel[pm->detail[0]].min.xyz.z );
|
|
|
|
pm->core_radius = MIN( rx, MIN(ry, rz) ) / 2.0f;
|
|
|
|
for (i=0; i<pm->n_view_positions; i++ ) {
|
|
if ( pm->view_positions[i].parent == pm->detail[0] ) {
|
|
float d = vm_vec_mag( &pm->view_positions[i].pnt );
|
|
|
|
d += 0.1f; // Make the eye 1/10th of a meter inside the sphere.
|
|
|
|
if ( d > pm->core_radius ) {
|
|
//mprintf(( "Model %s core radius increased from %.1f to %.1f to fit eye\n", pm->filename, pm->core_radius, d ));
|
|
pm->core_radius = d;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
int ram_after = TotalRam;
|
|
|
|
pm->ram_used = ram_after - ram_before;
|
|
Model_ram += pm->ram_used;
|
|
//mprintf(( "Model RAM = %d KB\n", Model_ram ));
|
|
#endif
|
|
|
|
// Goober5000 - originally done in ship_create for no apparent reason
|
|
model_set_subsys_path_nums(pm, n_subsystems, subsystems);
|
|
model_set_bay_path_nums(pm);
|
|
|
|
return pm->id;
|
|
}
|
|
|
|
// ensure that the subsys path is at least SUBSYS_PATH_DIST from the
|
|
// second last to last point.
|
|
void model_maybe_fixup_subsys_path(polymodel *pm, int path_num)
|
|
{
|
|
vec3d *v1, *v2, dir;
|
|
float dist;
|
|
int index_1, index_2;
|
|
|
|
Assert( (path_num >= 0) && (path_num < pm->n_paths) );
|
|
|
|
model_path *mp;
|
|
mp = &pm->paths[path_num];
|
|
|
|
Assert(mp != NULL);
|
|
Assert(mp->nverts > 1);
|
|
|
|
index_1 = 1;
|
|
index_2 = 0;
|
|
|
|
v1 = &mp->verts[index_1].pos;
|
|
v2 = &mp->verts[index_2].pos;
|
|
|
|
dist = vm_vec_dist(v1, v2);
|
|
if (dist < (SUBSYS_PATH_DIST - 10))
|
|
{
|
|
vm_vec_normalized_dir(&dir, v2, v1);
|
|
vm_vec_scale_add(v2, v1, &dir, SUBSYS_PATH_DIST);
|
|
}
|
|
}
|
|
|
|
// fill in the path_num field inside the model_subsystem struct. This is an index into
|
|
// the pm->paths[] array, which is a path that provides a frontal approach to a subsystem
|
|
// (used for attacking purposes)
|
|
//
|
|
// NOTE: path_num in model_subsystem has the follows the following convention:
|
|
// > 0 => index into pm->paths[] for model that subsystem sits on
|
|
// -1 => path is not yet determined (may or may not exist)
|
|
// -2 => path doesn't yet exist for this subsystem
|
|
void model_set_subsys_path_nums(polymodel *pm, int n_subsystems, model_subsystem *subsystems)
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0; i < n_subsystems; i++)
|
|
subsystems[i].path_num = -1;
|
|
|
|
for (i = 0; i < n_subsystems; i++)
|
|
{
|
|
for (j = 0; j < pm->n_paths; j++)
|
|
{
|
|
if ( ((subsystems[i].subobj_num != -1) && (subsystems[i].subobj_num == pm->paths[j].parent_submodel)) ||
|
|
(!subsystem_stricmp(subsystems[i].subobj_name, pm->paths[j].parent_name)) )
|
|
{
|
|
if (pm->n_paths > j)
|
|
{
|
|
subsystems[i].path_num = j;
|
|
model_maybe_fixup_subsys_path(pm, j);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If a path num wasn't located, then set value to -2
|
|
if (subsystems[i].path_num == -1)
|
|
subsystems[i].path_num = -2;
|
|
}
|
|
}
|
|
|
|
// Determine the path indices (indicies into pm->paths[]) for the paths used for approaching/departing
|
|
// a fighter bay on a capital ship.
|
|
void model_set_bay_path_nums(polymodel *pm)
|
|
{
|
|
int i;
|
|
|
|
if (pm->ship_bay != NULL)
|
|
{
|
|
vm_free(pm->ship_bay);
|
|
pm->ship_bay = NULL;
|
|
}
|
|
|
|
/*
|
|
// currently only capital ships have fighter bays
|
|
if ( !(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
|
|
return;
|
|
}
|
|
*/
|
|
|
|
// malloc out storage for the path information
|
|
pm->ship_bay = (ship_bay *) vm_malloc(sizeof(ship_bay));
|
|
Assert(pm->ship_bay != NULL);
|
|
|
|
pm->ship_bay->num_paths = 0;
|
|
// TODO: determine if zeroing out here is affecting any earlier initializations
|
|
pm->ship_bay->arrive_flags = 0; // bitfield, set to 1 when that path number is reserved for an arrival
|
|
pm->ship_bay->depart_flags = 0; // bitfield, set to 1 when that path number is reserved for a departure
|
|
|
|
|
|
// iterate through the paths that exist in the polymodel, searching for $bayN pathnames
|
|
for (i = 0; i < pm->n_paths; i++)
|
|
{
|
|
if (!strnicmp(pm->paths[i].name, NOX("$bay"), 4))
|
|
{
|
|
int bay_num;
|
|
char temp[3];
|
|
|
|
strncpy(temp, pm->paths[i].name + 4, 2);
|
|
temp[2] = 0;
|
|
bay_num = atoi(temp);
|
|
|
|
Assert(bay_num >= 1 && bay_num <= MAX_SHIP_BAY_PATHS);
|
|
if (bay_num < 1 || bay_num > MAX_SHIP_BAY_PATHS)
|
|
continue;
|
|
|
|
pm->ship_bay->path_indexes[bay_num - 1] = i;
|
|
pm->ship_bay->num_paths++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get "parent" submodel for live debris submodel
|
|
int model_get_parent_submodel_for_live_debris( int model_num, int live_debris_model_num )
|
|
{
|
|
polymodel *pm = model_get(model_num);
|
|
|
|
Assert(pm->submodel[live_debris_model_num].is_live_debris == 1);
|
|
|
|
int mn;
|
|
bsp_info *child;
|
|
|
|
// Start with the high level of detail hull
|
|
// Check all its children until we find the submodel to which the live debris belongs
|
|
child = &pm->submodel[pm->detail[0]];
|
|
mn = child->first_child;
|
|
|
|
while (mn > 0) {
|
|
child = &pm->submodel[mn];
|
|
|
|
if (child->num_live_debris > 0) {
|
|
// check all live debris submodels for the current child
|
|
for (int idx=0; idx<child->num_live_debris; idx++) {
|
|
if (child->live_debris[idx] == live_debris_model_num) {
|
|
return mn;
|
|
}
|
|
}
|
|
// DKA 5/26/99: can multiple live debris subsystems with each ship
|
|
// NO LONGER TRUE Can only be 1 submodel with live debris
|
|
// Error( LOCATION, "Could not find parent submodel for live debris. Possible model error");
|
|
}
|
|
|
|
// get next child
|
|
mn = child->next_sibling;
|
|
}
|
|
Error( LOCATION, "Could not find parent submodel for live debris");
|
|
return -1;
|
|
}
|
|
|
|
|
|
float model_get_radius( int modelnum )
|
|
{
|
|
polymodel *pm;
|
|
|
|
pm = model_get(modelnum);
|
|
|
|
return pm->rad;
|
|
}
|
|
|
|
float model_get_core_radius( int modelnum )
|
|
{
|
|
polymodel *pm;
|
|
|
|
pm = model_get(modelnum);
|
|
|
|
return pm->core_radius;
|
|
}
|
|
|
|
float submodel_get_radius( int modelnum, int submodelnum )
|
|
{
|
|
polymodel *pm;
|
|
|
|
pm = model_get(modelnum);
|
|
|
|
return pm->submodel[submodelnum].rad;
|
|
}
|
|
|
|
|
|
|
|
polymodel * model_get(int model_num)
|
|
{
|
|
Assert( model_num > -1 );
|
|
|
|
int num = model_num % MAX_POLYGON_MODELS;
|
|
|
|
Assert( num > -1 );
|
|
Assert( num < MAX_POLYGON_MODELS );
|
|
Assert( Polygon_models[num] );
|
|
Assert( Polygon_models[num]->id == model_num );
|
|
|
|
return Polygon_models[num];
|
|
}
|
|
|
|
|
|
/*
|
|
// Finds the 3d bounding box of a model. If submodel_num is -1,
|
|
// then it starts from the root object. If inc_children is non-zero,
|
|
// then this will recurse and find the bounding box for all children
|
|
// also.
|
|
void model_find_bound_box_3d(int model_num,int submodel_num, int inc_children, matrix *orient, vec3d * pos, vec3d * box )
|
|
{
|
|
polymodel * pm;
|
|
vec3d to_root_xlat;
|
|
matrix to_root_rotate;
|
|
int n_steps, steps[16];
|
|
int tmp_sobj;
|
|
|
|
if ( (model_num < 0) || (model_num >= N_polygon_models) ) return;
|
|
|
|
pm = &Polygon_models[model_num];
|
|
|
|
if ( submodel_num < 0 ) submodel_num = pm->detail[0];
|
|
|
|
// traverse up the model tree to a root object.
|
|
// Store this path in n_steps,
|
|
n_steps = 0;
|
|
tmp_sobj = submodel_num;
|
|
while( tmp_sobj > -1 ) {
|
|
steps[n_steps++] = tmp_sobj;
|
|
tmp_sobj = pm->submodel[tmp_sobj].parent;
|
|
}
|
|
|
|
|
|
|
|
// vm_copy_transpose_matrix(&to_world_rotate, orient );
|
|
// to_world_xlat = *pos;
|
|
|
|
}
|
|
*/
|
|
|
|
|
|
|
|
// Returns zero is x1,y1,x2,y2 are valid
|
|
// returns 1 for invalid model, 2 for point offscreen.
|
|
// note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates!
|
|
int model_find_2d_bound_min(int model_num,matrix *orient, vec3d * pos,int *x1, int *y1, int *x2, int *y2 )
|
|
{
|
|
polymodel * po;
|
|
int n_valid_pts;
|
|
int i, x,y,min_x, min_y, max_x, max_y;
|
|
int rval = 0;
|
|
|
|
po = model_get(model_num);
|
|
|
|
g3_start_instance_matrix(pos,orient,(gr_screen.mode == GR_DIRECT3D));
|
|
|
|
n_valid_pts = 0;
|
|
|
|
int hull = po->detail[0];
|
|
|
|
min_x = min_y = max_x = max_y = 0;
|
|
|
|
for (i=0; i<8; i++ ) {
|
|
vertex pt;
|
|
ubyte flags;
|
|
|
|
flags = g3_rotate_vertex(&pt,&po->submodel[hull].bounding_box[i]);
|
|
if ( !(flags&CC_BEHIND) ) {
|
|
g3_project_vertex(&pt);
|
|
|
|
if (!(pt.flags & PF_OVERFLOW)) {
|
|
x = fl2i(pt.sx);
|
|
y = fl2i(pt.sy);
|
|
if ( n_valid_pts == 0 ) {
|
|
min_x = x;
|
|
min_y = y;
|
|
max_x = x;
|
|
max_y = y;
|
|
} else {
|
|
if ( x < min_x ) min_x = x;
|
|
if ( y < min_y ) min_y = y;
|
|
|
|
if ( x > max_x ) max_x = x;
|
|
if ( y > max_y ) max_y = y;
|
|
}
|
|
n_valid_pts++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( n_valid_pts < 8 ) {
|
|
rval = 2;
|
|
}
|
|
|
|
if (x1) *x1 = min_x;
|
|
if (y1) *y1 = min_y;
|
|
|
|
if (x2) *x2 = max_x;
|
|
if (y2) *y2 = max_y;
|
|
|
|
g3_done_instance((gr_screen.mode == GR_DIRECT3D));
|
|
|
|
return rval;
|
|
}
|
|
|
|
|
|
// Returns zero is x1,y1,x2,y2 are valid
|
|
// returns 1 for invalid model, 2 for point offscreen.
|
|
// note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates!
|
|
int submodel_find_2d_bound_min(int model_num,int submodel, matrix *orient, vec3d * pos,int *x1, int *y1, int *x2, int *y2 )
|
|
{
|
|
polymodel * po;
|
|
int n_valid_pts;
|
|
int i, x,y,min_x, min_y, max_x, max_y;
|
|
bsp_info * sm;
|
|
|
|
po = model_get(model_num);
|
|
if ( (submodel < 0) || (submodel >= po->n_models ) ) return 1;
|
|
sm = &po->submodel[submodel];
|
|
|
|
g3_start_instance_matrix(pos,orient,(gr_screen.mode == GR_DIRECT3D));
|
|
|
|
n_valid_pts = 0;
|
|
|
|
min_x = min_y = max_x = max_y = 0;
|
|
|
|
for (i=0; i<8; i++ ) {
|
|
vertex pt;
|
|
ubyte flags;
|
|
|
|
flags = g3_rotate_vertex(&pt,&sm->bounding_box[i]);
|
|
if ( !(flags&CC_BEHIND) ) {
|
|
g3_project_vertex(&pt);
|
|
|
|
if (!(pt.flags & PF_OVERFLOW)) {
|
|
x = fl2i(pt.sx);
|
|
y = fl2i(pt.sy);
|
|
if ( n_valid_pts == 0 ) {
|
|
min_x = x;
|
|
min_y = y;
|
|
max_x = x;
|
|
max_y = y;
|
|
} else {
|
|
if ( x < min_x ) min_x = x;
|
|
if ( y < min_y ) min_y = y;
|
|
|
|
if ( x > max_x ) max_x = x;
|
|
if ( y > max_y ) max_y = y;
|
|
}
|
|
n_valid_pts++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( n_valid_pts == 0 ) {
|
|
return 2;
|
|
}
|
|
|
|
if (x1) *x1 = min_x;
|
|
if (y1) *y1 = min_y;
|
|
|
|
if (x2) *x2 = max_x;
|
|
if (y2) *y2 = max_y;
|
|
|
|
g3_done_instance((gr_screen.mode == GR_DIRECT3D));
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Returns zero is x1,y1,x2,y2 are valid
|
|
// returns 1 for invalid model, 2 for point offscreen.
|
|
// note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates!
|
|
int model_find_2d_bound(int model_num,matrix *orient, vec3d * pos,int *x1, int *y1, int *x2, int *y2 )
|
|
{
|
|
float t,w,h;
|
|
vertex pnt;
|
|
ubyte flags;
|
|
polymodel * po;
|
|
|
|
po = model_get(model_num);
|
|
float width = po->rad;
|
|
float height = po->rad;
|
|
|
|
flags = g3_rotate_vertex(&pnt,pos);
|
|
|
|
if ( pnt.flags & CC_BEHIND )
|
|
return 2;
|
|
|
|
if (!(pnt.flags&PF_PROJECTED))
|
|
g3_project_vertex(&pnt);
|
|
|
|
if (pnt.flags & PF_OVERFLOW)
|
|
return 2;
|
|
|
|
t = (width * Canv_w2)/pnt.z;
|
|
w = t*Matrix_scale.xyz.x;
|
|
|
|
t = (height*Canv_h2)/pnt.z;
|
|
h = t*Matrix_scale.xyz.y;
|
|
|
|
if (x1) *x1 = fl2i(pnt.sx - w);
|
|
if (y1) *y1 = fl2i(pnt.sy - h);
|
|
|
|
if (x2) *x2 = fl2i(pnt.sx + w);
|
|
if (y2) *y2 = fl2i(pnt.sy + h);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Returns zero is x1,y1,x2,y2 are valid
|
|
// returns 2 for point offscreen.
|
|
// note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates!
|
|
int subobj_find_2d_bound(float radius ,matrix *orient, vec3d * pos,int *x1, int *y1, int *x2, int *y2 )
|
|
{
|
|
float t,w,h;
|
|
vertex pnt;
|
|
ubyte flags;
|
|
|
|
float width = radius;
|
|
float height = radius;
|
|
|
|
flags = g3_rotate_vertex(&pnt,pos);
|
|
|
|
if ( pnt.flags & CC_BEHIND )
|
|
return 2;
|
|
|
|
if (!(pnt.flags&PF_PROJECTED))
|
|
g3_project_vertex(&pnt);
|
|
|
|
if (pnt.flags & PF_OVERFLOW)
|
|
return 2;
|
|
|
|
t = (width * Canv_w2)/pnt.z;
|
|
w = t*Matrix_scale.xyz.x;
|
|
|
|
t = (height*Canv_h2)/pnt.z;
|
|
h = t*Matrix_scale.xyz.y;
|
|
|
|
if (x1) *x1 = fl2i(pnt.sx - w);
|
|
if (y1) *y1 = fl2i(pnt.sy - h);
|
|
|
|
if (x2) *x2 = fl2i(pnt.sx + w);
|
|
if (y2) *y2 = fl2i(pnt.sy + h);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Given a vector that is in sub_model_num's frame of
|
|
// reference, and given the object's orient and position,
|
|
// return the vector in the model's frame of reference.
|
|
void model_find_obj_dir(vec3d *w_vec, vec3d *m_vec, object *ship_obj, int sub_model_num)
|
|
{
|
|
vec3d tvec, vec;
|
|
matrix m;
|
|
int mn;
|
|
|
|
Assert(ship_obj->type == OBJ_SHIP);
|
|
|
|
polymodel *pm = model_get(Ship_info[Ships[ship_obj->instance].ship_info_index].model_num);
|
|
vec = *m_vec;
|
|
mn = sub_model_num;
|
|
|
|
// instance up the tree for this point
|
|
while ( (mn >= 0) && (pm->submodel[mn].parent >= 0) ) {
|
|
// By using this kind of computation, the rotational angles can always
|
|
// be computed relative to the submodel itself, instead of relative
|
|
// to the parent - KeldorKatarn
|
|
matrix rotation_matrix = pm->submodel[mn].orientation;
|
|
vm_rotate_matrix_by_angles(&rotation_matrix, &pm->submodel[mn].angs);
|
|
|
|
matrix inv_orientation;
|
|
vm_copy_transpose_matrix(&inv_orientation, &pm->submodel[mn].orientation);
|
|
|
|
vm_matrix_x_matrix(&m, &rotation_matrix, &inv_orientation);
|
|
|
|
vm_vec_unrotate(&tvec, &vec, &m);
|
|
vec = tvec;
|
|
|
|
mn = pm->submodel[mn].parent;
|
|
}
|
|
|
|
// now instance for the entire object
|
|
vm_vec_unrotate(w_vec, &vec, &ship_obj->orient);
|
|
}
|
|
|
|
|
|
// Given a point (pnt) that is in sub_model_num's frame of
|
|
// reference, return the point in in the object's frame of reference
|
|
void model_rot_sub_into_obj(vec3d * outpnt, vec3d *mpnt,polymodel *pm, int sub_model_num)
|
|
{
|
|
vec3d pnt;
|
|
vec3d tpnt;
|
|
matrix m;
|
|
int mn;
|
|
|
|
pnt = *mpnt;
|
|
mn = sub_model_num;
|
|
|
|
//instance up the tree for this point
|
|
while ( (mn >= 0) && (pm->submodel[mn].parent >= 0) ) {
|
|
// By using this kind of computation, the rotational angles can always
|
|
// be computed relative to the submodel itself, instead of relative
|
|
// to the parent - KeldorKatarn
|
|
matrix rotation_matrix = pm->submodel[mn].orientation;
|
|
vm_rotate_matrix_by_angles(&rotation_matrix, &pm->submodel[mn].angs);
|
|
|
|
matrix inv_orientation;
|
|
vm_copy_transpose_matrix(&inv_orientation, &pm->submodel[mn].orientation);
|
|
|
|
vm_matrix_x_matrix(&m, &rotation_matrix, &inv_orientation);
|
|
|
|
vm_vec_unrotate(&tpnt, &pnt, &m);
|
|
vm_vec_add(&pnt, &tpnt, &pm->submodel[mn].offset);
|
|
|
|
mn = pm->submodel[mn].parent;
|
|
}
|
|
|
|
//now instance for the entire object
|
|
*outpnt = pnt;
|
|
}
|
|
|
|
|
|
// Given a rotating submodel, find the ship and world axes or rotatation.
|
|
void model_get_rotating_submodel_axis(vec3d *model_axis, vec3d *world_axis, int modelnum, int submodel_num, object *obj)
|
|
{
|
|
polymodel *pm = model_get(modelnum);
|
|
|
|
bsp_info *sm = &pm->submodel[submodel_num];
|
|
Assert(sm->movement_type == MOVEMENT_TYPE_ROT);
|
|
|
|
if (sm->movement_axis == MOVEMENT_AXIS_X) {
|
|
vm_vec_make(model_axis, 1.0f, 0.0f, 0.0f);
|
|
} else if (sm->movement_axis == MOVEMENT_AXIS_Y) {
|
|
vm_vec_make(model_axis, 0.0f, 1.0f, 0.0f);
|
|
} else {
|
|
Assert(sm->movement_axis == MOVEMENT_AXIS_Z);
|
|
vm_vec_make(model_axis, 0.0f, 0.0f, 1.0f);
|
|
}
|
|
|
|
model_find_obj_dir(world_axis, model_axis, obj, submodel_num);
|
|
}
|
|
|
|
|
|
// Does stepped rotation of a submodel
|
|
void submodel_stepped_rotate(model_subsystem *psub, submodel_instance_info *sii)
|
|
{
|
|
Assert(psub->flags & MSS_FLAG_STEPPED_ROTATE);
|
|
|
|
if ( psub->subobj_num < 0 ) return;
|
|
|
|
polymodel *pm = model_get(psub->model_num);
|
|
bsp_info *sm = &pm->submodel[psub->subobj_num];
|
|
|
|
if ( sm->movement_type != MOVEMENT_TYPE_ROT ) return;
|
|
|
|
// get active rotation time this frame
|
|
int end_stamp = timestamp();
|
|
float rotation_time = 0.001f * (end_stamp - sii->step_zero_timestamp);
|
|
Assert(rotation_time >= 0);
|
|
|
|
// save last angles
|
|
sii->prev_angs = sii->angs;
|
|
|
|
// float pointer into struct to get angle (either p,b,h)
|
|
float *ang_prev = NULL, *ang_next = NULL;
|
|
switch( sm->movement_axis ) {
|
|
case MOVEMENT_AXIS_X:
|
|
ang_prev = &sii->prev_angs.p;
|
|
ang_next = &sii->angs.p;
|
|
break;
|
|
|
|
case MOVEMENT_AXIS_Y:
|
|
ang_prev = &sii->prev_angs.h;
|
|
ang_next = &sii->angs.h;
|
|
break;
|
|
|
|
case MOVEMENT_AXIS_Z:
|
|
ang_prev = &sii->prev_angs.b;
|
|
ang_next = &sii->angs.b;
|
|
break;
|
|
}
|
|
|
|
// just in case we got through that switch statement in error
|
|
if ( (ang_prev == NULL) && (ang_next == NULL) )
|
|
return;
|
|
|
|
// angular displacement of one step
|
|
float step_size = (PI2 / psub->stepped_rotation->num_steps);
|
|
|
|
// get time to complete one step, including pause
|
|
float step_time = psub->stepped_rotation->t_transit + psub->stepped_rotation->t_pause;
|
|
|
|
// cur_step is step number relative to zero (0 - num_steps)
|
|
// step_offset_time is TIME into current step
|
|
float step_offset_time = (float)fmod(rotation_time, step_time);
|
|
// subtract off fractional step part, round up (ie, 1.999999 -> 2)
|
|
int cur_step = int( ((rotation_time - step_offset_time) / step_time) + 0.5f);
|
|
// mprintf(("cur step %d\n", cur_step));
|
|
// Assert(step_offset_time >= 0);
|
|
|
|
if (cur_step >= psub->stepped_rotation->num_steps) {
|
|
// I don;t know why, but removing this line makes it all good.
|
|
// sii->step_zero_timestamp += int(1000.0f * (psub->stepped_rotation->num_steps * step_time) + 0.5f);
|
|
|
|
// reset cur_step (use mod to handle physics/ai pause)
|
|
cur_step = cur_step % psub->stepped_rotation->num_steps;
|
|
}
|
|
|
|
// get base angle
|
|
*ang_next = cur_step * step_size;
|
|
|
|
// determine which phase of rotation we're in
|
|
float coast_start_time = psub->stepped_rotation->fraction * psub->stepped_rotation->t_transit;
|
|
float decel_start_time = psub->stepped_rotation->t_transit * (1.0f - psub->stepped_rotation->fraction);
|
|
float pause_start_time = psub->stepped_rotation->t_transit;
|
|
|
|
float start_coast_angle = 0.5f * psub->stepped_rotation->max_turn_accel * coast_start_time * coast_start_time;
|
|
|
|
if (step_offset_time < coast_start_time) {
|
|
// do accel
|
|
float accel_time = step_offset_time;
|
|
*ang_next += 0.5f * psub->stepped_rotation->max_turn_accel * accel_time * accel_time;
|
|
sii->cur_turn_rate = psub->stepped_rotation->max_turn_accel * accel_time;
|
|
} else if (step_offset_time < decel_start_time) {
|
|
// do coast
|
|
float coast_time = step_offset_time - coast_start_time;
|
|
*ang_next += start_coast_angle + psub->stepped_rotation->max_turn_rate * coast_time;
|
|
sii->cur_turn_rate = psub->stepped_rotation->max_turn_rate;
|
|
} else if (step_offset_time < pause_start_time) {
|
|
// do decel
|
|
float time_to_pause = psub->stepped_rotation->t_transit - step_offset_time;
|
|
*ang_next += (step_size - 0.5f * psub->stepped_rotation->max_turn_accel * time_to_pause * time_to_pause);
|
|
sii->cur_turn_rate = psub->stepped_rotation->max_turn_rate * time_to_pause;
|
|
} else {
|
|
// do pause
|
|
*ang_next += step_size;
|
|
sii->cur_turn_rate = 0.0f;
|
|
}
|
|
}
|
|
|
|
// Rotates the angle of a submodel. Use this so the right unlocked axis
|
|
// gets stuffed.
|
|
void submodel_rotate(model_subsystem *psub, submodel_instance_info *sii)
|
|
{
|
|
bsp_info * sm;
|
|
|
|
if ( psub->subobj_num < 0 ) return;
|
|
|
|
polymodel *pm = model_get(psub->model_num);
|
|
sm = &pm->submodel[psub->subobj_num];
|
|
|
|
if ( sm->movement_type != MOVEMENT_TYPE_ROT ) return;
|
|
|
|
// save last angles
|
|
sii->prev_angs = sii->angs;
|
|
|
|
// probably send in a calculated desired turn rate
|
|
float diff = sii->desired_turn_rate - sii->cur_turn_rate;
|
|
|
|
float final_turn_rate;
|
|
if (diff > 0) {
|
|
final_turn_rate = sii->cur_turn_rate + sii->turn_accel * flFrametime;
|
|
if (final_turn_rate > sii->desired_turn_rate) {
|
|
final_turn_rate = sii->desired_turn_rate;
|
|
}
|
|
} else if (diff < 0) {
|
|
final_turn_rate = sii->cur_turn_rate - sii->turn_accel * flFrametime;
|
|
if (final_turn_rate < sii->desired_turn_rate) {
|
|
final_turn_rate = sii->desired_turn_rate;
|
|
}
|
|
} else {
|
|
final_turn_rate = sii->desired_turn_rate;
|
|
}
|
|
|
|
float delta = (sii->cur_turn_rate + final_turn_rate) * 0.5f * flFrametime;
|
|
sii->cur_turn_rate = final_turn_rate;
|
|
|
|
|
|
//float delta = psub->turn_rate * flFrametime;
|
|
|
|
switch( sm->movement_axis ) {
|
|
case MOVEMENT_AXIS_X:
|
|
sii->angs.p += delta;
|
|
if (sii->angs.p > PI2 )
|
|
sii->angs.p -= PI2;
|
|
else if (sii->angs.p < 0.0f )
|
|
sii->angs.p += PI2;
|
|
break;
|
|
case MOVEMENT_AXIS_Y:
|
|
sii->angs.h += delta;
|
|
if (sii->angs.h > PI2 )
|
|
sii->angs.h -= PI2;
|
|
else if (sii->angs.h < 0.0f )
|
|
sii->angs.h += PI2;
|
|
break;
|
|
case MOVEMENT_AXIS_Z:
|
|
sii->angs.b += delta;
|
|
if (sii->angs.b > PI2 )
|
|
sii->angs.b -= PI2;
|
|
else if (sii->angs.b < 0.0f )
|
|
sii->angs.b += PI2;
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
void submodel_ai_rotate(model_subsystem *psub, submodel_instance_info *sii)
|
|
{
|
|
bsp_info * sm;
|
|
|
|
if ( psub->subobj_num < 0 ) return;
|
|
if(psub->ai_rotation.type = 0) return;
|
|
|
|
polymodel *pm = model_get(psub->model_num);
|
|
sm = &pm->submodel[psub->subobj_num];
|
|
|
|
if ( sm->movement_type != MOVEMENT_TYPE_ROT ) return;
|
|
|
|
|
|
// save last angles
|
|
sii->prev_angs = sii->angs;
|
|
|
|
// probably send in a calculated desired turn rate
|
|
float diff = sii->desired_turn_rate - sii->cur_turn_rate;
|
|
|
|
float final_turn_rate;
|
|
if (diff > 0) {
|
|
final_turn_rate = sii->cur_turn_rate + sii->turn_accel * flFrametime;
|
|
if (final_turn_rate > sii->desired_turn_rate) {
|
|
final_turn_rate = sii->desired_turn_rate;
|
|
}
|
|
} else if (diff < 0) {
|
|
final_turn_rate = sii->cur_turn_rate - sii->turn_accel * flFrametime;
|
|
if (final_turn_rate < sii->desired_turn_rate) {
|
|
final_turn_rate = sii->desired_turn_rate;
|
|
}
|
|
} else {
|
|
final_turn_rate = sii->desired_turn_rate;
|
|
}
|
|
|
|
float delta = (sii->cur_turn_rate + final_turn_rate) * 0.5f * flFrametime;
|
|
sii->cur_turn_rate = final_turn_rate;
|
|
|
|
|
|
//float delta = psub->turn_rate * flFrametime;
|
|
|
|
switch( sm->movement_axis ) {
|
|
case MOVEMENT_AXIS_X:
|
|
if (sii->angs.p + delta > psub->ai_rotation.max ){//if it will or has gone past it's max then set it to the max/min
|
|
sii->angs.p = psub->ai_rotation.max;
|
|
return;
|
|
} else if(sii->angs.p + delta < psub->ai_rotation.min){
|
|
sii->angs.p = psub->ai_rotation.min;
|
|
return;
|
|
}
|
|
sii->angs.p += delta;
|
|
if (sii->angs.p > PI2 )
|
|
sii->angs.p -= PI2;
|
|
else if (sii->angs.p < 0.0f )
|
|
sii->angs.p += PI2;
|
|
break;
|
|
case MOVEMENT_AXIS_Y:
|
|
sii->angs.h += delta;
|
|
if (sii->angs.h > PI2 )
|
|
sii->angs.h -= PI2;
|
|
else if (sii->angs.h < 0.0f )
|
|
sii->angs.h += PI2;
|
|
break;
|
|
case MOVEMENT_AXIS_Z:
|
|
sii->angs.b += delta;
|
|
if (sii->angs.b > PI2 )
|
|
sii->angs.b -= PI2;
|
|
else if (sii->angs.b < 0.0f )
|
|
sii->angs.b += PI2;
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
//=========================================================================
|
|
// Make a turret's correct orientation matrix. This should be done when
|
|
// the model is read, but I wasn't sure at what point all the data that I
|
|
// needed was read, so I just check a flag and call this routine when
|
|
// I determine I need the correct matrix. In this code, you can't use
|
|
// vm_vec_2_matrix or anything, since these turrets could be either
|
|
// right handed or left handed.
|
|
void model_make_turret_matrix(int model_num, model_subsystem * turret )
|
|
{
|
|
polymodel * pm;
|
|
vec3d fvec, uvec, rvec;
|
|
|
|
pm = model_get(model_num);
|
|
bsp_info * gun = &pm->submodel[turret->turret_gun_sobj];
|
|
bsp_info * base = &pm->submodel[turret->subobj_num];
|
|
float offset_base_h = 0.0f;
|
|
float offset_barrel_h = 0.0f;
|
|
#ifdef WMC_SIDE_TURRETS
|
|
offset_base_h = -PI_2;
|
|
offset_barrel_h = -PI_2;
|
|
#endif
|
|
|
|
model_clear_instance(model_num);
|
|
base->angs.h = offset_base_h;
|
|
gun->angs.h = offset_barrel_h;
|
|
model_find_world_dir(&fvec, &turret->turret_norm, model_num, turret->turret_gun_sobj, &vmd_identity_matrix, NULL );
|
|
|
|
base->angs.h = -PI_2 + offset_base_h;
|
|
gun->angs.p = -PI_2;
|
|
gun->angs.h = offset_barrel_h;
|
|
model_find_world_dir(&rvec, &turret->turret_norm, model_num, turret->turret_gun_sobj, &vmd_identity_matrix, NULL );
|
|
|
|
base->angs.h = 0.0f + offset_base_h;
|
|
gun->angs.p = -PI_2;
|
|
gun->angs.h = offset_barrel_h;
|
|
model_find_world_dir(&uvec, &turret->turret_norm, model_num, turret->turret_gun_sobj, &vmd_identity_matrix, NULL );
|
|
|
|
vm_vec_normalize(&fvec);
|
|
vm_vec_normalize(&rvec);
|
|
vm_vec_normalize(&uvec);
|
|
|
|
turret->turret_matrix.vec.fvec = fvec;
|
|
turret->turret_matrix.vec.rvec = rvec;
|
|
turret->turret_matrix.vec.uvec = uvec;
|
|
|
|
// vm_vector_2_matrix(&turret->turret_matrix,&turret->turret_norm,NULL,NULL);
|
|
|
|
// HACK!! WARNING!!!
|
|
// I'm doing nothing to verify that this matrix is orthogonal!!
|
|
// In other words, there's no guarantee that the vectors are 90 degrees
|
|
// from each other.
|
|
// I'm not doing this because I don't know how to do it without ruining
|
|
// the handedness of the matrix... however, I'm not too worried about
|
|
// this because I am creating these 3 vectors by making them 90 degrees
|
|
// apart, so this should be close enough. I think this will start
|
|
// causing weird errors when we view from turrets. -John
|
|
turret->flags |= MSS_FLAG_TURRET_MATRIX;
|
|
}
|
|
|
|
// Tries to move joints so that the turret points to the point dst.
|
|
// turret1 is the angles of the turret, turret2 is the angles of the gun from turret
|
|
// Returns 1 if rotated gun, 0 if no gun to rotate (rotation handled by AI)
|
|
int model_rotate_gun(int model_num, model_subsystem *turret, matrix *orient, angles *base_angles, angles *gun_angles, vec3d *pos, vec3d *dst)
|
|
{
|
|
polymodel * pm;
|
|
|
|
pm = model_get(model_num);
|
|
bsp_info * gun = &pm->submodel[turret->turret_gun_sobj];
|
|
bsp_info * base = &pm->submodel[turret->subobj_num];
|
|
|
|
// Check for a valid turret
|
|
Assert( turret->turret_num_firing_points > 0 );
|
|
|
|
//This should not happen
|
|
if ( base == gun ) {
|
|
return 0;
|
|
}
|
|
|
|
// Build the correct turret matrix if there isn't already one
|
|
if ( !(turret->flags & MSS_FLAG_TURRET_MATRIX) )
|
|
model_make_turret_matrix(model_num, turret );
|
|
|
|
Assert( turret->flags & MSS_FLAG_TURRET_MATRIX);
|
|
// Assert( gun->movement_axis == MOVEMENT_AXIS_X ); // Gun must be able to change pitch
|
|
// Assert( base->movement_axis == MOVEMENT_AXIS_Z ); // Parent must be able to change heading
|
|
|
|
//------------
|
|
// rotate the dest point into the turret gun normal's frame of
|
|
// reference, but not using the turret's angles.
|
|
// Call this vector of_dst
|
|
vec3d of_dst;
|
|
matrix world_to_turret_matrix; // converts world coordinates to turret's FOR
|
|
vec3d world_to_turret_translate; // converts world coordinates to turret's FOR
|
|
vec3d tempv;
|
|
|
|
vm_vec_unrotate( &tempv, &base->offset, orient);
|
|
vm_vec_add( &world_to_turret_translate, pos, &tempv );
|
|
|
|
vm_matrix_x_matrix( &world_to_turret_matrix, orient, &turret->turret_matrix );
|
|
|
|
vm_vec_sub( &tempv, dst, &world_to_turret_translate );
|
|
vm_vec_rotate( &of_dst, &tempv, &world_to_turret_matrix );
|
|
|
|
vm_vec_normalize(&of_dst);
|
|
|
|
//------------
|
|
// Find the heading and pitch that the gun needs to turn to
|
|
// by extracting them from the of_dst vector.
|
|
// Call this the desired_angles
|
|
angles desired_angles;
|
|
// vm_extract_angles_vector(&desired_angles, &of_dst);
|
|
|
|
desired_angles.p = (float)acos(of_dst.xyz.z);
|
|
desired_angles.h = PI - atan2_safe(of_dst.xyz.x, of_dst.xyz.y);
|
|
desired_angles.b = 0.0f;
|
|
|
|
// mprintf(( "Z = %.1f, atan= %.1f\n", of_dst.xyz.z, desired_angles.p ));
|
|
|
|
//------------
|
|
// Gradually turn the turret towards the desired angles
|
|
float step_size = turret->turret_turning_rate * flFrametime;
|
|
|
|
vm_interp_angle(&base_angles->h, desired_angles.h, step_size);
|
|
vm_interp_angle(&gun_angles->p, desired_angles.p, step_size);
|
|
|
|
// base_angles->h -= step_size*(key_down_timef(KEY_1)-key_down_timef(KEY_2) );
|
|
// gun_angles->p += step_size*(key_down_timef(KEY_3)-key_down_timef(KEY_4) );
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
// Goober5000
|
|
// For a submodel, return its overall offset from the main model.
|
|
void model_find_submodel_offset(vec3d *outpnt, int model_num, int sub_model_num)
|
|
{
|
|
int mn;
|
|
polymodel *pm = model_get(model_num);
|
|
|
|
vm_vec_zero(outpnt);
|
|
mn = sub_model_num;
|
|
|
|
//instance up the tree for this point
|
|
while ( (mn >= 0) && (pm->submodel[mn].parent >= 0) ) {
|
|
vm_vec_add2(outpnt, &pm->submodel[mn].offset);
|
|
|
|
mn = pm->submodel[mn].parent;
|
|
}
|
|
}
|
|
|
|
// Given a point (pnt) that is in sub_model_num's frame of
|
|
// reference, and given the object's orient and position,
|
|
// return the point in 3-space in outpnt.
|
|
void model_find_world_point(vec3d * outpnt, vec3d *mpnt,int model_num,int sub_model_num, matrix * objorient, vec3d * objpos )
|
|
{
|
|
vec3d pnt;
|
|
vec3d tpnt;
|
|
matrix m;
|
|
int mn;
|
|
polymodel *pm = model_get(model_num);
|
|
|
|
pnt = *mpnt;
|
|
mn = sub_model_num;
|
|
|
|
//instance up the tree for this point
|
|
while ( (mn >= 0) && (pm->submodel[mn].parent >= 0) ) {
|
|
// By using this kind of computation, the rotational angles can always
|
|
// be computed relative to the submodel itself, instead of relative
|
|
// to the parent - KeldorKatarn
|
|
matrix rotation_matrix = pm->submodel[mn].orientation;
|
|
vm_rotate_matrix_by_angles(&rotation_matrix, &pm->submodel[mn].angs);
|
|
|
|
matrix inv_orientation;
|
|
vm_copy_transpose_matrix(&inv_orientation, &pm->submodel[mn].orientation);
|
|
|
|
vm_matrix_x_matrix(&m, &rotation_matrix, &inv_orientation);
|
|
|
|
vm_vec_unrotate(&tpnt, &pnt, &m);
|
|
|
|
vm_vec_add(&pnt, &tpnt, &pm->submodel[mn].offset);
|
|
|
|
mn = pm->submodel[mn].parent;
|
|
}
|
|
|
|
//now instance for the entire object
|
|
vm_vec_unrotate(outpnt,&pnt,objorient);
|
|
vm_vec_add2(outpnt,objpos);
|
|
}
|
|
|
|
// Given a point in the world RF, find the corresponding point in the model RF.
|
|
// This is special purpose code, specific for model collision.
|
|
// NOTE - this code ASSUMES submodel is 1 level down from hull (detail[0])
|
|
//
|
|
// out - point in model RF
|
|
// world_pt - point in world RF
|
|
// pm - polygon model
|
|
// submodel_num - submodel in whose RF we're trying to find the corresponding world point
|
|
// orient - orient matrix of ship
|
|
// pos - pos vector of ship
|
|
void world_find_model_point(vec3d *out, vec3d *world_pt, polymodel *pm, int submodel_num, matrix *orient, vec3d *pos)
|
|
{
|
|
Assert( (pm->submodel[submodel_num].parent == pm->detail[0]) || (pm->submodel[submodel_num].parent == -1) );
|
|
|
|
vec3d tempv1, tempv2;
|
|
matrix m;
|
|
|
|
// get into ship RF
|
|
vm_vec_sub(&tempv1, world_pt, pos);
|
|
vm_vec_rotate(&tempv2, &tempv1, orient);
|
|
|
|
if (pm->submodel[submodel_num].parent == -1) {
|
|
*out = tempv2;
|
|
return;
|
|
}
|
|
|
|
// put into submodel RF
|
|
vm_vec_sub2(&tempv2, &pm->submodel[submodel_num].offset);
|
|
|
|
// By using this kind of computation, the rotational angles can always
|
|
// be computed relative to the submodel itself, instead of relative
|
|
// to the parent - KeldorKatarn
|
|
matrix rotation_matrix = pm->submodel[submodel_num].orientation;
|
|
vm_rotate_matrix_by_angles(&rotation_matrix, &pm->submodel[submodel_num].angs);
|
|
|
|
matrix inv_orientation;
|
|
vm_copy_transpose_matrix(&inv_orientation, &pm->submodel[submodel_num].orientation);
|
|
|
|
vm_matrix_x_matrix(&m, &rotation_matrix, &inv_orientation);
|
|
|
|
vm_vec_rotate(out, &tempv2, &m);
|
|
}
|
|
|
|
// Verify rotating submodel has corresponding ship subsystem -- info in which to store rotation angle
|
|
int rotating_submodel_has_ship_subsys(int submodel, ship *shipp)
|
|
{
|
|
model_subsystem *psub;
|
|
ship_subsys *pss;
|
|
|
|
int found = 0;
|
|
|
|
// Go through all subsystems and look for submodel
|
|
// the subsystems that need it.
|
|
for ( pss = GET_FIRST(&shipp->subsys_list); pss != END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
|
|
psub = pss->system_info;
|
|
if (psub->subobj_num == submodel) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
void model_get_rotating_submodel_list(int *submodel_list, int *num_rotating_submodels, object *objp)
|
|
{
|
|
Assert(objp->type == OBJ_SHIP);
|
|
|
|
// Check if not currently rotating - then treat as part of superstructure.
|
|
int modelnum = Ship_info[Ships[objp->instance].ship_info_index].model_num;
|
|
polymodel *pm = model_get(modelnum);
|
|
bsp_info *child_submodel;
|
|
|
|
*num_rotating_submodels = 0;
|
|
child_submodel = &pm->submodel[pm->detail[0]];
|
|
|
|
int i = child_submodel->first_child;
|
|
while ( i >= 0 ) {
|
|
child_submodel = &pm->submodel[i];
|
|
|
|
// Don't check it or its children if it is destroyed or it is a replacement (non-moving)
|
|
if ( !child_submodel->blown_off && (child_submodel->i_replace == -1) ) {
|
|
|
|
// Only look for submodels that rotate
|
|
if (child_submodel->movement_type == MOVEMENT_TYPE_ROT) {
|
|
|
|
// find ship subsys and check submodel rotation is less than max allowed.
|
|
ship *pship = &Ships[objp->instance];
|
|
ship_subsys *subsys;
|
|
|
|
for ( subsys = GET_FIRST(&pship->subsys_list); subsys !=END_OF_LIST(&pship->subsys_list); subsys = GET_NEXT(subsys) ) {
|
|
Assert(subsys->system_info->model_num == modelnum);
|
|
if (i == subsys->system_info->subobj_num) {
|
|
// found the correct subsystem - now check delta rotation angle not too large
|
|
float delta_angle = get_submodel_delta_angle(&subsys->submodel_info_1);
|
|
if (delta_angle < MAX_SUBMODEL_COLLISION_ROT_ANGLE) {
|
|
Assert(*num_rotating_submodels < MAX_ROTATING_SUBMODELS-1);
|
|
submodel_list[(*num_rotating_submodels)++] = i;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
i = child_submodel->next_sibling;
|
|
}
|
|
|
|
// error checking
|
|
//#define MODEL_CHECK
|
|
#ifdef MODEL_CHECK
|
|
ship *pship = &Ships[objp->instance];
|
|
for (int idx=0; idx<*num_rotating_submodels; idx++) {
|
|
int valid = rotating_submodel_has_ship_subsys(submodel_list[idx], pship);
|
|
// Assert( valid );
|
|
if ( !valid ) {
|
|
|
|
Warning( LOCATION, "Ship %s has rotating submodel [%s] without ship subsystem\n", pship->ship_name, pm->submodel[submodel_list[idx]].name );
|
|
pm->submodel[submodel_list[idx]].movement_type &= ~MOVEMENT_TYPE_ROT;
|
|
*num_rotating_submodels = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
// Given a direction (pnt) that is in sub_model_num's frame of
|
|
// reference, and given the object's orient and position,
|
|
// return the point in 3-space in outpnt.
|
|
void model_find_world_dir(vec3d * out_dir, vec3d *in_dir,int model_num, int sub_model_num, matrix * objorient, vec3d * objpos )
|
|
{
|
|
vec3d pnt;
|
|
vec3d tpnt;
|
|
matrix m;
|
|
int mn;
|
|
polymodel *pm = model_get(model_num);
|
|
|
|
pnt = *in_dir;
|
|
mn = sub_model_num;
|
|
|
|
//instance up the tree for this point
|
|
while ( (mn >= 0) && (pm->submodel[mn].parent >= 0) ) {
|
|
// By using this kind of computation, the rotational angles can always
|
|
// be computed relative to the submodel itself, instead of relative
|
|
// to the parent - KeldorKatarn
|
|
matrix rotation_matrix = pm->submodel[mn].orientation;
|
|
vm_rotate_matrix_by_angles(&rotation_matrix, &pm->submodel[mn].angs);
|
|
|
|
matrix inv_orientation;
|
|
vm_copy_transpose_matrix(&inv_orientation, &pm->submodel[mn].orientation);
|
|
|
|
vm_matrix_x_matrix(&m, &rotation_matrix, &inv_orientation);
|
|
|
|
vm_vec_unrotate(&tpnt, &pnt, &m);
|
|
pnt = tpnt;
|
|
|
|
mn = pm->submodel[mn].parent;
|
|
}
|
|
|
|
//now instance for the entire object
|
|
vm_vec_unrotate(out_dir,&pnt,objorient);
|
|
}
|
|
|
|
|
|
// Clears all the submodel instances stored in a model to their defaults.
|
|
void model_clear_instance(int model_num)
|
|
{
|
|
polymodel * pm;
|
|
int i;
|
|
|
|
pm = model_get(model_num);
|
|
|
|
pm->gun_submodel_rotation = 0.0f;
|
|
// reset textures to original ones
|
|
for (i=0; i<pm->n_textures; i++ ) {
|
|
pm->maps[i].base_map.texture = pm->maps[i].base_map.original_texture;
|
|
pm->maps[i].glow_map.texture = pm->maps[i].glow_map.original_texture;
|
|
pm->maps[i].spec_map.texture = pm->maps[i].spec_map.original_texture;
|
|
#ifdef BUMPMAPPING
|
|
pm->maps[i].bump_map.texture = pm->maps[i].bump_map.original_texture;
|
|
#endif
|
|
}
|
|
|
|
for (i=0; i<pm->n_models; i++ ) {
|
|
bsp_info *sm = &pm->submodel[i];
|
|
|
|
if ( pm->submodel[i].is_damaged ) {
|
|
sm->blown_off = 1;
|
|
} else {
|
|
sm->blown_off = 0;
|
|
}
|
|
sm->angs.p = 0.0f;
|
|
sm->angs.b = 0.0f;
|
|
sm->angs.h = 0.0f;
|
|
|
|
// set pointer to other ship subsystem info [turn rate, accel, moment, axis, ...]
|
|
sm->sii = NULL;
|
|
|
|
sm->num_arcs = 0; // Turn off any electric arcing effects
|
|
}
|
|
|
|
for (i=0; i<pm->num_lights; i++ ) {
|
|
pm->lights[i].value = 0.0f;
|
|
}
|
|
|
|
interp_clear_instance();
|
|
|
|
// if ( keyd_pressed[KEY_1] ) pm->lights[0].value = 1.0f/255.0f;
|
|
// if ( keyd_pressed[KEY_2] ) pm->lights[1].value = 1.0f/255.0f;
|
|
// if ( keyd_pressed[KEY_3] ) pm->lights[2].value = 1.0f/255.0f;
|
|
// if ( keyd_pressed[KEY_4] ) pm->lights[3].value = 1.0f/255.0f;
|
|
// if ( keyd_pressed[KEY_5] ) pm->lights[4].value = 1.0f/255.0f;
|
|
// if ( keyd_pressed[KEY_6] ) pm->lights[5].value = 1.0f/255.0f;
|
|
|
|
|
|
}
|
|
|
|
// initialization during ship set
|
|
void model_clear_instance_info( submodel_instance_info * sii )
|
|
{
|
|
sii->blown_off = 0;
|
|
sii->angs.p = 0.0f;
|
|
sii->angs.b = 0.0f;
|
|
sii->angs.h = 0.0f;
|
|
sii->prev_angs.p = 0.0f;
|
|
sii->prev_angs.b = 0.0f;
|
|
sii->prev_angs.h = 0.0f;
|
|
|
|
sii->cur_turn_rate = 0.0f;
|
|
sii->desired_turn_rate = 0.0f;
|
|
sii->turn_accel = 0.0f;
|
|
}
|
|
|
|
// initialization during ship set
|
|
void model_set_instance_info(submodel_instance_info *sii, float turn_rate, float turn_accel)
|
|
{
|
|
sii->blown_off = 0;
|
|
sii->angs.p = 0.0f;
|
|
sii->angs.b = 0.0f;
|
|
sii->angs.h = 0.0f;
|
|
sii->prev_angs.p = 0.0f;
|
|
sii->prev_angs.b = 0.0f;
|
|
sii->prev_angs.h = 0.0f;
|
|
|
|
sii->cur_turn_rate = turn_rate * 0.0f;
|
|
sii->desired_turn_rate = turn_rate;
|
|
sii->turn_accel = turn_accel;
|
|
sii->axis_set = 0;
|
|
sii->step_zero_timestamp = timestamp();
|
|
}
|
|
|
|
|
|
|
|
// Sets the submodel instance data in a submodel (for all detail levels)
|
|
void model_set_instance(int model_num, int sub_model_num, submodel_instance_info * sii)
|
|
{
|
|
int i;
|
|
polymodel * pm;
|
|
|
|
pm = model_get(model_num);
|
|
|
|
Assert( sub_model_num >= 0 );
|
|
Assert( sub_model_num < pm->n_models );
|
|
|
|
if ( sub_model_num < 0 ) return;
|
|
if ( sub_model_num >= pm->n_models ) return;
|
|
bsp_info *sm = &pm->submodel[sub_model_num];
|
|
|
|
// Set the "blown out" flags
|
|
sm->blown_off = sii->blown_off;
|
|
|
|
if ( sm->blown_off ) {
|
|
if ( sm->my_replacement > -1 ) {
|
|
pm->submodel[sm->my_replacement].blown_off = 0;
|
|
pm->submodel[sm->my_replacement].angs = sii->angs;
|
|
pm->submodel[sm->my_replacement].sii = sii;
|
|
}
|
|
} else {
|
|
if ( sm->my_replacement > -1 ) {
|
|
pm->submodel[sm->my_replacement].blown_off = 1;
|
|
}
|
|
}
|
|
|
|
// Set the angles
|
|
sm->angs = sii->angs;
|
|
sm->sii = sii;
|
|
|
|
// For all the detail levels of this submodel, set them also.
|
|
for (i=0; i<sm->num_details; i++ ) {
|
|
model_set_instance(model_num, sm->details[i], sii );
|
|
}
|
|
}
|
|
|
|
|
|
// Finds a point on the rotation axis of a submodel, used in collision, generally find rotational velocity
|
|
void model_init_submodel_axis_pt(submodel_instance_info *sii, int model_num, int submodel_num)
|
|
{
|
|
vec3d axis;
|
|
vec3d *mpoint1, *mpoint2;
|
|
vec3d p1, v1, p2, v2, int1;
|
|
|
|
polymodel *pm = model_get(model_num);
|
|
Assert(pm->submodel[submodel_num].movement_type == MOVEMENT_TYPE_ROT);
|
|
Assert(sii);
|
|
|
|
mpoint1 = NULL;
|
|
mpoint2 = NULL;
|
|
|
|
// find 2 fixed points in submodel RF
|
|
// these will be rotated to about the axis an angle of 0 and PI and we'll find the intersection of the
|
|
// two lines to find a point on the axis
|
|
if (pm->submodel[submodel_num].movement_axis == MOVEMENT_AXIS_X) {
|
|
axis = vmd_x_vector;
|
|
mpoint1 = &vmd_y_vector;
|
|
mpoint2 = &vmd_z_vector;
|
|
} else if (pm->submodel[submodel_num].movement_axis == MOVEMENT_AXIS_Y) {
|
|
mpoint1 = &vmd_x_vector;
|
|
axis = vmd_z_vector; // rotation about y is a change in heading (p,b,h), so we need z
|
|
mpoint2 = &vmd_z_vector;
|
|
} else if (pm->submodel[submodel_num].movement_axis == MOVEMENT_AXIS_Z) {
|
|
mpoint1 = &vmd_x_vector;
|
|
mpoint2 = &vmd_y_vector;
|
|
axis = vmd_y_vector; // rotation about z is a change in bank (p,b,h), so we need y
|
|
} else {
|
|
// must be one of these axes or submodel_rot_hit is incorrectly set
|
|
Int3();
|
|
}
|
|
|
|
// copy submodel angs
|
|
angles copy_angs = pm->submodel[submodel_num].angs;
|
|
|
|
// find two points rotated into model RF when angs set to 0
|
|
vm_vec_copy_scale((vec3d*)&pm->submodel[submodel_num].angs, &axis, 0.0f);
|
|
model_find_world_point(&p1, mpoint1, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector);
|
|
model_find_world_point(&p2, mpoint2, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector);
|
|
|
|
// find two points rotated into model RF when angs set to PI
|
|
vm_vec_copy_scale((vec3d*)&pm->submodel[submodel_num].angs, &axis, PI);
|
|
model_find_world_point(&v1, mpoint1, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector);
|
|
model_find_world_point(&v2, mpoint2, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector);
|
|
|
|
// reset submodel angs
|
|
pm->submodel[submodel_num].angs = copy_angs;
|
|
|
|
// find direction vectors of the two lines
|
|
vm_vec_sub2(&v1, &p1);
|
|
vm_vec_sub2(&v2, &p2);
|
|
|
|
// find the intersection of the two lines
|
|
float s, t;
|
|
fvi_two_lines_in_3space(&p1, &v1, &p2, &v2, &s, &t);
|
|
|
|
// find the actual intersection points
|
|
vm_vec_scale_add(&int1, &p1, &v1, s);
|
|
|
|
// set flag to init
|
|
sii->pt_on_axis = int1;
|
|
sii->axis_set = 1;
|
|
}
|
|
|
|
|
|
// Adds an electrical arcing effect to a submodel
|
|
void model_add_arc(int model_num, int sub_model_num, vec3d *v1, vec3d *v2, int arc_type )
|
|
{
|
|
polymodel * pm;
|
|
|
|
pm = model_get(model_num);
|
|
|
|
if ( sub_model_num == -1 ) {
|
|
sub_model_num = pm->detail[0];
|
|
}
|
|
|
|
Assert( sub_model_num >= 0 );
|
|
Assert( sub_model_num < pm->n_models );
|
|
|
|
if ( sub_model_num < 0 ) return;
|
|
if ( sub_model_num >= pm->n_models ) return;
|
|
bsp_info *sm = &pm->submodel[sub_model_num];
|
|
|
|
if ( sm->num_arcs < MAX_ARC_EFFECTS ) {
|
|
sm->arc_type[sm->num_arcs] = (ubyte)arc_type;
|
|
sm->arc_pts[sm->num_arcs][0] = *v1;
|
|
sm->arc_pts[sm->num_arcs][1] = *v2;
|
|
sm->num_arcs++;
|
|
}
|
|
}
|
|
|
|
// function to return an index into the docking_bays array which matches the criteria passed
|
|
// to this function. dock_type is one of the DOCK_TYPE_XXX defines in model.h
|
|
// Goober5000 - now finds more than one dockpoint of this type
|
|
int model_find_dock_index(int modelnum, int dock_type, int index_to_start_at)
|
|
{
|
|
int i;
|
|
polymodel *pm;
|
|
|
|
// get model and make sure it has dockpoints
|
|
pm = model_get(modelnum);
|
|
if ( pm->n_docks <= 0 )
|
|
return -1;
|
|
|
|
// look for a dockpoint of this type
|
|
for (i = index_to_start_at; i < pm->n_docks; i++ )
|
|
{
|
|
if ( dock_type & pm->docking_bays[i].type_flags )
|
|
return i;
|
|
}
|
|
|
|
// if we get here, type wasn't found -- return -1 and hope for the best
|
|
return -1;
|
|
}
|
|
|
|
// function to return an index into the docking_bays array which matches the string passed
|
|
// Fred uses strings to identify docking positions. This function also accepts generic strings
|
|
// so that a desginer doesn't have to know exact names if building a mission from hand.
|
|
int model_find_dock_name_index( int modelnum, char *name )
|
|
{
|
|
int i;
|
|
polymodel *pm;
|
|
|
|
// get model and make sure it has dockpoints
|
|
pm = model_get(modelnum);
|
|
if ( pm->n_docks <= 0 )
|
|
return -1;
|
|
|
|
// check the generic names and call previous function to find first dock point of
|
|
// the specified type
|
|
for(i = 0; i < Num_dock_type_names; i++)
|
|
{
|
|
if(!stricmp(name, Dock_type_names[i].name)) {
|
|
return model_find_dock_index(modelnum, Dock_type_names[i].def);
|
|
}
|
|
}
|
|
/*
|
|
if ( !stricmp(name, "cargo") )
|
|
return model_find_dock_index( modelnum, DOCK_TYPE_CARGO );
|
|
else if (!stricmp( name, "rearm") )
|
|
return model_find_dock_index( modelnum, DOCK_TYPE_REARM );
|
|
else if (!stricmp( name, "generic") )
|
|
return model_find_dock_index( modelnum, DOCK_TYPE_GENERIC );
|
|
*/
|
|
|
|
// look for a dockpoint with this name
|
|
for (i = 0; i < pm->n_docks; i++ )
|
|
{
|
|
if ( !stricmp(pm->docking_bays[i].name, name) )
|
|
return i;
|
|
}
|
|
|
|
// if the bay does not have a name in the model, the model loading code
|
|
// will assign it a default name... check for that here
|
|
if (!strnicmp(name, "<unnamed bay ", 13))
|
|
{
|
|
int index = (name[13] - 'A');
|
|
if (index >= 0 && index < pm->n_docks)
|
|
return index;
|
|
}
|
|
|
|
// if we get here, name wasn't found -- return -1 and hope for the best
|
|
return -1;
|
|
}
|
|
|
|
// returns the actual name of a docking point on a model, needed by Fred.
|
|
char *model_get_dock_name(int modelnum, int index)
|
|
{
|
|
polymodel *pm;
|
|
|
|
pm = model_get(modelnum);
|
|
Assert((index >= 0) && (index < pm->n_docks));
|
|
return pm->docking_bays[index].name;
|
|
}
|
|
|
|
int model_get_num_dock_points(int modelnum)
|
|
{
|
|
polymodel *pm;
|
|
|
|
pm = model_get(modelnum);
|
|
return pm->n_docks;
|
|
}
|
|
|
|
int model_get_dock_index_type(int modelnum, int index)
|
|
{
|
|
polymodel *pm = model_get(modelnum);
|
|
|
|
return pm->docking_bays[index].type_flags;
|
|
}
|
|
|
|
// get all the different docking point types on a model
|
|
int model_get_dock_types(int modelnum)
|
|
{
|
|
int i, type = 0;
|
|
polymodel *pm;
|
|
|
|
pm = model_get(modelnum);
|
|
for (i=0; i<pm->n_docks; i++)
|
|
type |= pm->docking_bays[i].type_flags;
|
|
|
|
return type;
|
|
}
|
|
|
|
// Goober5000
|
|
// returns index in [0, MAX_SHIP_BAY_PATHS)
|
|
int model_find_bay_path(int modelnum, char *bay_path_name)
|
|
{
|
|
int i;
|
|
polymodel *pm = model_get(modelnum);
|
|
|
|
if (pm->ship_bay == NULL)
|
|
return -1;
|
|
|
|
if (pm->ship_bay->num_paths <= 0)
|
|
return -1;
|
|
|
|
for (i = 0; i < pm->ship_bay->num_paths; i++)
|
|
{
|
|
if (!stricmp(pm->paths[pm->ship_bay->path_indexes[i]].name, bay_path_name))
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
extern void model_allocate_interp_data(int, int, int);
|
|
|
|
// tigital -
|
|
void swap_bsp_defpoints(ubyte * p)
|
|
{
|
|
int n, i;
|
|
int nverts = INTEL_INT( w(p+8) ); //tigital
|
|
int offset = INTEL_INT( w(p+16) );
|
|
int n_norms = INTEL_INT( w(p+12) );
|
|
|
|
w(p+8) = nverts;
|
|
w(p+16) = offset;
|
|
w(p+12) = n_norms;
|
|
|
|
ubyte * normcount = p+20;
|
|
vec3d *src = vp(p+offset);
|
|
|
|
model_allocate_interp_data(nverts, n_norms, 0);
|
|
|
|
for (n=0; n<nverts; n++ ) {
|
|
src->xyz.x = INTEL_FLOAT( &src->xyz.x ); //tigital
|
|
src->xyz.y = INTEL_FLOAT( &src->xyz.y );
|
|
src->xyz.z = INTEL_FLOAT( &src->xyz.z );
|
|
|
|
Interp_verts[n] = src;
|
|
src++; //tigital
|
|
|
|
for (i=0; i<normcount[n]; i++){
|
|
src->xyz.x = INTEL_FLOAT( &src->xyz.x ); //tigital
|
|
src->xyz.y = INTEL_FLOAT( &src->xyz.y );
|
|
src->xyz.z = INTEL_FLOAT( &src->xyz.z );
|
|
src++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void swap_bsp_tmappoly( polymodel * pm, ubyte * p )
|
|
{
|
|
int i, nv;
|
|
model_tmap_vert *verts;
|
|
vec3d * normal = vp(p+8); //tigital
|
|
vec3d * center = vp(p+20);
|
|
float radius = INTEL_FLOAT( &fl(p+32) );
|
|
|
|
fl(p+32) = radius;
|
|
|
|
normal->xyz.x = INTEL_FLOAT( &normal->xyz.x );
|
|
normal->xyz.y = INTEL_FLOAT( &normal->xyz.y );
|
|
normal->xyz.z = INTEL_FLOAT( &normal->xyz.z );
|
|
center->xyz.x = INTEL_FLOAT( ¢er->xyz.x );
|
|
center->xyz.y = INTEL_FLOAT( ¢er->xyz.y );
|
|
center->xyz.z = INTEL_FLOAT( ¢er->xyz.z );
|
|
|
|
nv = INTEL_INT( w(p+36)); //tigital
|
|
w(p+36) = nv;
|
|
|
|
int tmap_num = INTEL_INT( w(p+40) ); //tigital
|
|
w(p+40) = tmap_num;
|
|
|
|
if ( nv < 0 ) return;
|
|
|
|
verts = (model_tmap_vert *)(p+44);
|
|
for (i=0;i<nv;i++){
|
|
verts[i].vertnum = INTEL_SHORT( verts[i].vertnum );
|
|
verts[i].normnum = INTEL_SHORT( verts[i].normnum );
|
|
verts[i].u = INTEL_FLOAT( &verts[i].u );
|
|
verts[i].v = INTEL_FLOAT( &verts[i].v );
|
|
}
|
|
|
|
if ( pm->version < 2003 ) {
|
|
// Set the "normal_point" part of field to be the center of the polygon
|
|
vec3d center_point;
|
|
vm_vec_zero( ¢er_point );
|
|
|
|
for (i=0;i<nv;i++) {
|
|
vm_vec_add2( ¢er_point, Interp_verts[verts[i].vertnum] );
|
|
}
|
|
|
|
center_point.xyz.x /= nv;
|
|
center_point.xyz.y /= nv;
|
|
center_point.xyz.z /= nv;
|
|
|
|
*vp(p+20) = center_point;
|
|
|
|
float rad = 0.0f;
|
|
|
|
for (i=0;i<nv;i++) {
|
|
float dist = vm_vec_dist( ¢er_point, Interp_verts[verts[i].vertnum] );
|
|
if ( dist > rad ) {
|
|
rad = dist;
|
|
}
|
|
}
|
|
fl(p+32) = rad;
|
|
}
|
|
}
|
|
|
|
void swap_bsp_flatpoly( polymodel * pm, ubyte * p )
|
|
{
|
|
int i, nv;
|
|
short *verts;
|
|
vec3d * normal = vp(p+8); //tigital
|
|
vec3d * center = vp(p+20);
|
|
|
|
float radius = INTEL_FLOAT( &fl(p+32) );
|
|
|
|
fl(p+32) = radius;
|
|
|
|
normal->xyz.x = INTEL_FLOAT( &normal->xyz.x );
|
|
normal->xyz.y = INTEL_FLOAT( &normal->xyz.y );
|
|
normal->xyz.z = INTEL_FLOAT( &normal->xyz.z );
|
|
center->xyz.x = INTEL_FLOAT( ¢er->xyz.x );
|
|
center->xyz.y = INTEL_FLOAT( ¢er->xyz.y );
|
|
center->xyz.z = INTEL_FLOAT( ¢er->xyz.z );
|
|
|
|
nv = INTEL_INT( w(p+36)); //tigital
|
|
w(p+36) = nv;
|
|
|
|
if ( nv < 0 ) return;
|
|
|
|
verts = (short *)(p+44);
|
|
for (i=0; i<nv*2; i++){
|
|
verts[i] = INTEL_SHORT( verts[i] );
|
|
}
|
|
|
|
if ( pm->version < 2003 ) {
|
|
// Set the "normal_point" part of field to be the center of the polygon
|
|
vec3d center_point;
|
|
vm_vec_zero( ¢er_point );
|
|
|
|
for (i=0;i<nv;i++) {
|
|
vm_vec_add2( ¢er_point, Interp_verts[verts[i*2]] );
|
|
}
|
|
|
|
center_point.xyz.x /= nv;
|
|
center_point.xyz.y /= nv;
|
|
center_point.xyz.z /= nv;
|
|
|
|
*vp(p+20) = center_point;
|
|
|
|
float rad = 0.0f;
|
|
|
|
for (i=0;i<nv;i++) {
|
|
float dist = vm_vec_dist( ¢er_point, Interp_verts[verts[i*2]] );
|
|
if ( dist > rad ) {
|
|
rad = dist;
|
|
}
|
|
}
|
|
fl(p+32) = rad;
|
|
}
|
|
}
|
|
|
|
void swap_bsp_sortnorms( polymodel * pm, ubyte * p )
|
|
{
|
|
int frontlist = INTEL_INT( w(p+36) ); //tigital
|
|
int backlist = INTEL_INT( w(p+40) );
|
|
int prelist = INTEL_INT( w(p+44) );
|
|
int postlist = INTEL_INT( w(p+48) );
|
|
int onlist = INTEL_INT( w(p+52) );
|
|
|
|
w(p+36) = frontlist;
|
|
w(p+40) = backlist;
|
|
w(p+44) = prelist;
|
|
w(p+48) = postlist;
|
|
w(p+52) = onlist;
|
|
|
|
vec3d * normal = vp(p+8); //tigital
|
|
vec3d * center = vp(p+20);
|
|
int tmp = INTEL_INT( w(p+32) );
|
|
|
|
w(p+32) = tmp;
|
|
|
|
normal->xyz.x = INTEL_FLOAT( &normal->xyz.x );
|
|
normal->xyz.y = INTEL_FLOAT( &normal->xyz.y );
|
|
normal->xyz.z = INTEL_FLOAT( &normal->xyz.z );
|
|
center->xyz.x = INTEL_FLOAT( ¢er->xyz.x );
|
|
center->xyz.y = INTEL_FLOAT( ¢er->xyz.y );
|
|
center->xyz.z = INTEL_FLOAT( ¢er->xyz.z );
|
|
|
|
vec3d * bmin = vp(p+56); //tigital
|
|
vec3d * bmax = vp(p+68);
|
|
|
|
bmin->xyz.x = INTEL_FLOAT( &bmin->xyz.x );
|
|
bmin->xyz.y = INTEL_FLOAT( &bmin->xyz.y );
|
|
bmin->xyz.z = INTEL_FLOAT( &bmin->xyz.z );
|
|
bmax->xyz.x = INTEL_FLOAT( &bmax->xyz.x );
|
|
bmax->xyz.y = INTEL_FLOAT( &bmax->xyz.y );
|
|
bmax->xyz.z = INTEL_FLOAT( &bmax->xyz.z );
|
|
|
|
if (prelist) swap_bsp_data(pm,p+prelist);
|
|
if (backlist) swap_bsp_data(pm,p+backlist);
|
|
if (onlist) swap_bsp_data(pm,p+onlist);
|
|
if (frontlist) swap_bsp_data(pm,p+frontlist);
|
|
if (postlist) swap_bsp_data(pm,p+postlist);
|
|
}
|
|
#endif // BIG_ENDIAN
|
|
|
|
void swap_bsp_data( polymodel * pm, void *model_ptr )
|
|
{
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
ubyte *p = (ubyte *)model_ptr;
|
|
int chunk_type, chunk_size;
|
|
vec3d * min;
|
|
vec3d * max;
|
|
|
|
chunk_type = INTEL_INT( w(p) ); //tigital
|
|
chunk_size = INTEL_INT( w(p+4) );
|
|
w(p) = chunk_type;
|
|
w(p+4) = chunk_size;
|
|
|
|
while (chunk_type != OP_EOF) {
|
|
switch (chunk_type) {
|
|
case OP_EOF:
|
|
return;
|
|
case OP_DEFPOINTS:
|
|
swap_bsp_defpoints(p);
|
|
break;
|
|
case OP_FLATPOLY:
|
|
swap_bsp_flatpoly(pm, p);
|
|
break;
|
|
case OP_TMAPPOLY:
|
|
swap_bsp_tmappoly(pm, p);
|
|
break;
|
|
case OP_SORTNORM:
|
|
swap_bsp_sortnorms(pm, p);
|
|
break;
|
|
case OP_BOUNDBOX:
|
|
min = vp(p+8);
|
|
max = vp(p+20);
|
|
min->xyz.x = INTEL_FLOAT( &min->xyz.x );
|
|
min->xyz.y = INTEL_FLOAT( &min->xyz.y );
|
|
min->xyz.z = INTEL_FLOAT( &min->xyz.z );
|
|
max->xyz.x = INTEL_FLOAT( &max->xyz.x );
|
|
max->xyz.y = INTEL_FLOAT( &max->xyz.y );
|
|
max->xyz.z = INTEL_FLOAT( &max->xyz.z );
|
|
break;
|
|
default:
|
|
mprintf(( "Bad chunk type %d, len=%d in modelread:swap_bsp_data\n", chunk_type, chunk_size ));
|
|
Int3(); // Bad chunk type!
|
|
return;
|
|
}
|
|
|
|
p += chunk_size;
|
|
chunk_type = INTEL_INT( w(p)); //tigital
|
|
chunk_size = INTEL_INT( w(p+4) );
|
|
w(p) = chunk_type;
|
|
w(p+4) = chunk_size;
|
|
}
|
|
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
void swap_sldc_data(ubyte *buffer)
|
|
{
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
char *type_p = (char *)(buffer);
|
|
int *size_p = (int *)(buffer+1);
|
|
*size_p = INTEL_INT(*size_p);
|
|
|
|
// split and polygons
|
|
vec3d *minbox_p = (vec3d*)(buffer+5);
|
|
vec3d *maxbox_p = (vec3d*)(buffer+17);
|
|
|
|
minbox_p->xyz.x = INTEL_FLOAT(&minbox_p->xyz.x);
|
|
minbox_p->xyz.y = INTEL_FLOAT(&minbox_p->xyz.y);
|
|
minbox_p->xyz.z = INTEL_FLOAT(&minbox_p->xyz.z);
|
|
|
|
maxbox_p->xyz.x = INTEL_FLOAT(&maxbox_p->xyz.x);
|
|
maxbox_p->xyz.y = INTEL_FLOAT(&maxbox_p->xyz.y);
|
|
maxbox_p->xyz.z = INTEL_FLOAT(&maxbox_p->xyz.z);
|
|
|
|
|
|
// split
|
|
unsigned int *front_offset_p = (unsigned int*)(buffer+29);
|
|
unsigned int *back_offset_p = (unsigned int*)(buffer+33);
|
|
|
|
// polygons
|
|
unsigned int *num_polygons_p = (unsigned int*)(buffer+29);
|
|
|
|
unsigned int *shld_polys = (unsigned int*)(buffer+33);
|
|
|
|
if (*type_p == 0) // SPLIT
|
|
{
|
|
*front_offset_p = INTEL_INT(*front_offset_p);
|
|
*back_offset_p = INTEL_INT(*back_offset_p);
|
|
}
|
|
else
|
|
{
|
|
*num_polygons_p = INTEL_INT(*num_polygons_p);
|
|
for (unsigned int i = 0; i < *num_polygons_p; i++)
|
|
{
|
|
shld_polys[i] = INTEL_INT(shld_polys[i]);
|
|
}
|
|
}
|
|
#endif
|
|
}
|