/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : WWPhys * * * * $Archive:: /Commando/Code/wwphys/pscene.h $* * * * Author:: Greg Hjelstrom * * * * $Modtime:: 2/28/02 11:48a $* * * * $Revision:: 125 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #if defined(_MSC_VER) #pragma once #endif #ifndef PSCENE_H #define PSCENE_H #include "always.h" #include "scene.h" #include "widgetuser.h" #include "physlist.h" #include "aabtreecull.h" #include "gridcull.h" #include "vector.h" #include "shader.h" #include "vissample.h" #include "vistablemgr.h" #include "phystexproject.h" #include "simplevec.h" #include "vissectorstats.h" class Matrix3D; class ChunkLoadClass; class ChunkSaveClass; class PhysClass; class LightPhysClass; class LightClass; class StaticPhysClass; class RenderInfoClass; class SpecialRenderInfoClass; class VisRenderContextClass; class VisOptimizationContextClass; class AABoxClass; class VertexMaterialClass; class PathfindClass; class PhysDecalSysClass; class MeshClass; class VisOptProgressClass; class CameraShakeSystemClass; class LightEnvironmentClass; class StaticAnimPhysClass; class StringClass; // forward referencing the collision detection queries class PhysRayCollisionTestClass; class PhysAABoxCollisionTestClass; class PhysOBBoxCollisionTestClass; class PhysAABoxIntersectionTestClass; class PhysOBBoxIntersectionTestClass; class PhysMeshIntersectionTestClass; // forward referencing the culling system types. class PhysAABTreeCullClass; class StaticAABTreeCullClass; class DynamicAABTreeCullClass; class PhysGridCullClass; class StaticLightCullClass; // Lighting solver class LightSolveContextClass; /** Physics system All objects in the world which want to interact physically (as opposed to being simply decorations) will register a "PhysicalObject" with the physics sytem. The ABC PhysicalObjectClass is used by the physics manager for the following things: - checking for collisions between all objects; methods for collision detection will be pure virtual methods of the base class - computing forces between any "linked" objects - applying global gravity and damping to all of the objects - telling all physical objects to timestep themselves. Initializing the system - Physics system will init itself by clearing all of its lists and tables. - Each game object asks the physics system to create an instance of a physics object and specifies all of the important parameters... - Whenever an object is added to the system, it is added in sorted order to the x,y, and z "neighbor" lists. Each frame the following things will happen: - All game objects will exert their influence over their PhysicalObjects using methods provided. Game objects will usually know exactly what PhysicalObject type they have. I.e. a HummVee will know that its physical object is a Wheel4PhysClass and will be able to directly call methods of that class. - One at a time, each physical object will take its "move" for the frame. Moves will be calculated in a framerate-independent way. A single "move" for an object may consist of rotation and translation. Each move, the following things will happen: - The object will rotate in place according to its angular velocity and the amount of time which has passed for this frame - Collision detection code will "push" the object into a valid position in the world. If it is unable to push the object into a valid position, the rotation state will be reverted. - The object will then compute a translational move for the frame based on its velocity and the time which has passed. - Collision detection code will test the final position for penetration with the world. If penetration has occured, the object will be pushed out of the polygons it is penetrating. If this is not possible, we revert to the previous state. Brainstorming: - Collision between any pair of objects can be disabled or enabled as desired? - Multiple different types of collision objects or everything is a box? Or everything is three different sets of 3d boxes? - A separate sorted list for the x axis, y axis and z axis will be maintained. It will be used to optimize the collision detection algorithm. A single, overall bounding sphere / axis aligned box should be used for this quick rejection. - How will all of the separate physical object types interact??? The papers all handle everything as a rigid body, no special cases... - Different types of objects need to interact whenever a collision occurs. The physics manager should just detect collisions, then the objects should ask for any info they need to resolve the collision. Whenever a value is computed for a particular collision, it should be cached so that if the second object also needs the same value, it is not re-computed. - "real" collision resolution requires the velocity of the collision point. - How do we generalize the dy/dt function for the ODE solver since we have multiple different types of objects (with different state vectors) * each object will be responsible for: (1) adding itself to the state vector (2) getting its new state out of the state vector and (3) adding its derivatives to the derivative vector. Each of these functions will return the number of values which were added to the vector. This should allow us to "dumb-down" the physics for objects which are far away too... - Can each object be asked for its dy/dt separately? * yes, each object will add its dy/dt values to a given array. - All of this copying into the gigantic state vector sounds really in-efficient. Find a better system. What if we only decide to use Euler's method, it could be easily hard coded into each object type... * The giganitic state vector approach is overkill. We will not use it. 12/03/97 - Each object will be "timestepped" individually. Each object type will be free to use whatever integration method it wants to (again, no monolithic state vector) - One at a time, each object will be told to update their state. They will be given an elapsed time to ensure that we are frame-rate independent. - The steps a typical physical object will need to take in order to update itself: - Store its current state so that we can revert if we have to - Compute a desired state for the end of the timestep. This can be done using the high-tech integrator and all of the rigid body dynamics equations. - Do the rotation portion of that move in place. - "Push" the object into a valid position (world polys and object polys) If push goes into an infinite loop, revert and kill the angular velocity. - Subdivide the linear move into steps which are ~half the characteristic length of the bounding box. Store this vector - Loop while we haven't eaten the entire translation: - Add the sub-move vector - "Push" the object into a valid position, modify velocity by normal of the polygon collided with. If push goes into an infinite loop, revert to previous sub-move and exit Feb 9, 1998 Nope, try again, simplify, simplify, simplify! - For objects which do not care about rotation causing collision (e.g. characters), *use a collision object which doesn't care either!* So, for characters and many of the objects in the game, we will use either Axis-Aligned boxes or cylinders. Since I have the math for casting an axis-aligned box, I will probably use that. These objects will rotate within thier box so to the physics system, rotation / orientation will be trivial. - Jello Physics! For objects like the vehicles which were going to be done with rigid body dynamics I'm going to try a spring-mass model. There are many problems with rigid body dynamics. It is very difficult and expensive to compute correct contact forces, and it is difficult to find contact points. Assuming the bodies are rigid requires collision detection or at least penetration depth calculations for solids like Oriented Boxes or Convex Polyhedra. I have solved "volume-sweeping" for oriented boxes but not "volume- rotation" or the penetration depth calculation. The spring mass model avoids these problems and it comes down to testing line-segments for intersection with the world polygons. - Each object will still be moved one at a time. At the beginning of a move, an object must be in a valid position. Its move will be "cast" through the world and the object will only actually move up to the first collision. At that point, the velocity will be modified to slide along whatever it it (if possible). Repeat until the entire move vector is eaten or the object is stopped completely. April 14, 1998 - Finally getting back to the physics system. Month of march was spent converting code to the new version of surrender. So, not all of the items in the February notes were implemented. At this point, I have the beginnings of the character collision system. It relies on the Cast_AABox function which sweeps an axis-aligned box through the world. currently it only checks for collisions with the terrain polygons. Also, the "move-one- at-a-time" system was implemented and the old simulation style system removed. - I have hidden the implementation of the terrain collision from the game engine. Any terrain implementation must simply support the Cast_Ray, Cast_AABox, Cast_OBBox functions and be able to give the user a pointer to a render object which can be added to the scene in which the terrain is to be rendered. This should allow some freedom to experiment with octrees or bsp trees to speed up the cast functions... - The next step is to use the existing "partitioned" mesh to implement the rest of the needed collision detection functionality so that the rest of the team can work with it while I experiment with more efficient systems. - Cast_AABox must be made to also check against the objects. - Cast_Ray should be hooked in and a physics object for projectiles which is based on Cast_Ray should be created. - Communication system between the game engine and collision system should be tested. This is going to be a virtual class which simply defines a virtual callback method, game objects will multiply inherit this class and get messages from the system whenever a collision occurs. June 1, 1998 - Working on collision detection again. Spent the past two weeks or so writing some bpt code which turns the terrain mesh into a combination bsp/box tree (I simply call it a Binary Partition Tree). Each node contains a bounding box which bounds all polygons in the tree below that node. Building an efficient bpt is becoming a priority since we're getting a lot of splits and also nodes with gobs of polys in them due to the way our terrains are constructed. Should be easy to take advantage of our grid lines. - Collision system has been hooked into the game and a very simple projectile physics class written which uses rays instead of full blown boxes. The collision system communicates back to the game through multiple inheritance of a "CollisionObserverClass". The projectile physics work pretty well when you plug in RK4 or Midpoint as the integrator; same trajectory no matter what the framerate (within reason). (Mar2000 note: this was pretty stupid since you can directly calculate where the projectile would end up if the only force is gravity!) - Changing all of the internal collision functions to pass around "collision test classes" which wrap the primitive, its movement vector, a results structure, and any pre-calculatable variables into one structure. The main reason for this was just so I have a place to put the info that can be precalculated and re-used. (e.g. put a min,max along with the AABoxClass) - Now writing the recursive AABox Cast function which casts the box against the bpt. Once this is in place, we should start getting a significant speedup from using the bpt. - Thinking again about using a hybrid between the "every object moves on its own" and the "every object puts itself into the monolithic state vector" ideas. I do need some objects to be simulated together if we want to completely avoid being tied to fixed timesteps. For example, I need to simulate the camera along with the object its tied to... If we do true contact... all things touching each other will need to be simulated together. July 13, 1998 - Removing the distinction between physical objects and terrain. New engine idea will use a combination of a heightfield and tilemap. This means we're going to have multiple "terrains" and we will also be having meshes which don't move but are collideable such as guard towers, etc. August 31, 1998 CURRENT SITUATION: several sub-systems need efficient culling: - rendering system needs to be able to only render objects which are in the view frustum - collision system needs to quickly narrow down the number of objects checked for each collision query made. - AI/Game Logic needs to be able to quickly find "nearby" objects, objects within certain radii, objects which are in a given cone etc... PROBLEMS: - current code has no culling except for the TileMap object which is itself a huge render object spanning the entire level that performs culling internally in its render call. - currently, the "SceneClass" is expected to only deal with RenderObj derived classes. - collision queries and the like must go through physics object interfaces so that some physics objects can use simple primitives - deriving physobj from renderobj doesn't make a lot of sense because render obj is a relatively "heavy" class. I don't want to make another wrapper (and have the associated duplicated transforms, etc...) - Render objects can have their transforms changed at the programmers whim, there is no built-in provision for telling the scene to refresh its culling information SOLUTION: - make a physics "SceneClass" with culling datastructures for both static and dynamic culling - physics scene deals only with physics objects - therefore all objects in the "game world" will be represented by a physics object - when a physics object changes position, the culling database can be refreshed - physobj needs its linkage information built in. class PhysicsSceneClass : public SceneClass { void Add_Render_Obj(...) { assert(0); } void Remove_Render_Obj(...) { assert(0); } void Add_Physical_Object(PhysObjClass * pobj,hint = MOVING); void Remove_Physical_Object(PhysObjClass * pobj); void Render(CameraClass & cam); bool Cast_Ray( ); bool Cast_AABox( ); bool Cast_OBBox( ); PhysObjListClass * Get_Spatial_Object_List(pos,radius); PhysObjListClass * Get_Spatial_Object_List(min,max); PhysObjListClass * Get_Spatial_Object_List(frustum); PhysObjListClass * Get_Spatial_Object_List(raytest); PhysObjListClass * Get_Spatial_Object_List(raytest); } September 15, 1998 Finished my work on the initial prototype of the level editor. In the process of writing the level editor, I implemented the new PhysicsSceneClass which replaces the old classes: PhysicsSystem PrivatePhysicsSystem and CollisionSystem and also takes over the job of the main 3D game scene for rendering. This caused some major re-organization and as a result most of WWPhys was #if 0'd... A new base class PhysClass was implemented along with a derived DecorationPhysClass which can be inserted into this scene and rendered, collided with, etc. The AABTree culling system for all of the static meshes was written to be dynamically re-partitionable. The Grid culling system was only implemented in terms of an interface. Currently it just contains a linked list of PhysClasses. Next job is to add all of the functionality of the old system (Phys4, CharPhys, Tilemap, etc) into this system and get Commando running on top of it. NOTES: - New system contains provisions for updating the culling datastructures when an object moves. I should also add a "safe teleport" function which asks the system if it is ok for an object to be in a given position and if not, move the object to a safe position. This could get rid of the possibility of "startbad's" on level load. - TileMapClass is now obsolete but much of its functionality will have to go into game code. The PhysicsSceneClass contains an axis-aligned bounding box tree which serves the purpose that TileMap used to. Other "TerrainPhysClass's" are still going to be single objects though. November 30, 1998 Just completed another massive port to the latest version of surrender (1.4x). Also had to rewrite much of the plugin this time. Adding occlusion culling for all physics objects in the "static" scene. I still have some questions in my mind as to how this is going to work but the basic idea is that there will be "vis-sectors". Each vis-sector will contain a bit-vector with one bit for every piece of static geometry which indicates whether that piece of geometry can be seen from this "vis-sector". These bit arrays will be created by rendering the scene from each sector in a way that writes the node's id rather than texturing, lighting, etc, and then reading the framebuffer to see which nodes were not occluded. Currently I have two questions: How are these vis sectors defined? and How can I cull dynamic objects using this information? Now working on the occlusion bit vectors. Currently implementing it with a full bit vector for each object which contains a bit for every other object. If we compress this, we could at the start of each frame, decompress the vis bits for the current active node (or even only decompress it when the active node changes). Only problem is in implementation, editor stuff will want an uncompressed one that is easily modified; game will want tight compressed vis table. December 21, 1998 Converted W3D to render directly to the gerd (bypass the sr scene graph). This was the easiest way to implement the vis-detection stuff and opens the door for us to hierarchically cull lights. Vis detection is implemented now. Need to implement a file format for the map. - What is in the file format? - Who reads/writes it? - If pscene just loads stuff directly, how does Byon get ahold of the objects? - VisBits are independent of culling system! They only depend on the IDs not changing! - Its a lot simpler if this system *only* contains completely static meshes! (not walls, etc) + none of them *need* game logic + none of them change or move + byon doesn't need a game object wrapped around them - is there no pre-calculated lighting on the non static stuff? - want as much as we can get into this since it should be faster and has no size restrictions File format is going to be chunk based. Parsing of the top-levels of the file will occur in Commando. Certain chunks will be passed down to the PhysicsSceneClass to be handled. These chunks will include a chunk which wraps all of the culling data, a chunk which wraps each static object or dynamic object, chunks for zones and lights, etc. Jan 15, 1999 Working on the lighting management system. For the static lights in the level, I want to use an efficient culling system like the AABTree. All of these lights will be entered into the scene through a custom call in the physics scene. All dynamic lights will be added through the normal Add_Render_Object function or by being added as sub-objects of a model in the scene. Add_Render_Obj and Remove_Render_Obj will be implemented by having the physics scene automatically create a cullable object for the render object and storing the pointer to that object in the UserData field. In addition, the cullable object created for them will need to be a "dirty culler" that assumes its bounding box or transform could change at any time since the user is obviously working with a render object and could change the transform underneath us (normally this is a no-no). These will be added to a "dirty culler" list and caused to re-insert themselves into the dynamic culling system each frame. These objects will never cause collisions either (just for sanity) they are only visual... Render function will look like this: Render() { update the "dirty cullers" collect dynamic objects and vertex processors (dy_obj_list,dy_vp_list) push dynamic vertex processors render static terrain global optimization of LOD levels for the dynamic objects for each dynamic object push static lights which may affect it render pop static lights that were pushed pop dynamic vertex processors May 27, 1999 Moving the Visibility system into PhysicsSceneClass so that it can incorporate pre-calculated visibility for other things like dynamic objects. Previously it was pretty nicely encapsulated inside of the static culling system. October 26, 1999 Adding shadow generation code Jan 2000 Pluging in the decal system, texture projection system is mostly done except for optimizations and static projector generation (masses of tree shadows and stuff). Feb 21, 2000 Decals are done, Animated Static objects with collision are working now (translation only). Adding code which generates the static projectors (tree shadows, window projections and things like that). Optimized the view-frustum culling code to propogate the plane masks and perform trivial acceptance of entire sub-trees. Junk 28, 1000 Major VIS update. Now the dynamic culling system is a pre-generated AABTree with precalculated visiblity for each node in the tree. In addition, the static object culling system contains hierarchical visibility data and each light will have a pvs for what static objects and dynamic object cells it can "see". The generation of the dynamic object AAB-Tree is "seeded" with a pathfind solve and a coarse voxelization of the level. This tree is "optimized" after VIS is computed; redundant leaves of the tree are deleted and the vis tables collapsed. Aug 9, 2000 Implementing a new lighting system which collects the lights affecting each object which is not pre-lit and creates temporary directional lights for them. This should solve the problem of having a level with thousands of lights but only a few that actually need to be active. We'll still need to reduce the number of lights in some levels though. The code for this lighting algorithm can be found in lightenvironment.cpp. Sept 24, 2000 Making the lighting system coherent across frames. If an object doesn't move, it will have its local static lighting environment cached and can just re-use the data. Whenever physics object move, they invalidate their internal cache and will ask the physics scene to recompute the lighting environment next time its needed. This change involved adding the Compute_Static_Lighting function to pscene and the pair of functions: Invalidate_Static_Lighting_Cache and Get_Static_Lighting_Cache to PhysClass. These caches must also be invalidated when lights in the scene change state (such as when the power goes out in a building...) April 2001 Installed the new vis renderer, it seems to simplify a lot of code! Also converting the shadow rendering code to work under DX8. I plan to add a new shadow mode which uses hardware to render-to-texture. */ /** ** PhysicsSceneClass ** Drop all of the objects into the world in here, represented by physics objects, then ** you can do fun things like rendering and collision detection. */ class PhysicsSceneClass : public SceneClass , public WidgetUserClass { public: PhysicsSceneClass(void); virtual ~PhysicsSceneClass(void); /* ** For now, making the Physics Scene a singleton. If we want the feature of ** instantiating multiple physics scenes, we will need to add a scene pointer ** to the base PhysClass and resolve all cases where we use this function. */ static PhysicsSceneClass * Get_Instance(void) { return TheScene; } /* ** Timestep the world */ void Update(float dt,int frameid); void Pre_Render_Processing(CameraClass & camera); void Post_Render_Processing(void); /* ** Methods to add and remove physical objects in the system */ void Add_Dynamic_Object(PhysClass * newobj); void Add_Static_Object(StaticPhysClass * newtile,int cull_node_id = -1); void Add_Dynamic_Light(LightPhysClass * light); void Add_Static_Light(LightPhysClass * newlight,int cull_node_id = -1); void Remove_Object(PhysClass * obj); void Remove_All(void); void Delayed_Remove_Object(PhysClass * obj); void Process_Release_List(void); bool Contains(PhysClass * obj); RefPhysListIterator Get_Dynamic_Object_Iterator(void); RefPhysListIterator Get_Static_Object_Iterator(void); RefPhysListIterator Get_Static_Anim_Object_Iterator(void); RefPhysListIterator Get_Static_Light_Iterator(void); TexProjListIterator Get_Static_Projector_Iterator(void); TexProjListIterator Get_Dynamic_Projector_Iterator(void); StaticPhysClass * Get_Static_Object_By_ID(uint32 id); /* ** Dirty Cull List ** ** The objects in this list update their culling every frame. ** An example of this is a cinematic with an animated bounding ** box. ** */ void Add_To_Dirty_Cull_List(PhysClass *obj); void Remove_From_Dirty_Cull_List(PhysClass *obj); bool Is_In_Dirty_Cull_List(PhysClass *obj); /* ** Lighting Controls ** ** Lighting Mode: You can disable all lighting calculations, use the surrender lighting code, ** or use the el-cheapo lighting code which turns all lights into direction lights with no ** per-vertex attenuation or direction calculations. ** ** Scene Ambient Light: The physics scene takes over the ambient light (is also implemented ** in SceneClass) so that I can tweak the ambient light on a per-object basis. This will ** probably be done to make dynamic objects blend in with the light-mapped environment better. ** ** Lighting LOD: Set the cut-off intensity where point light sources are turned into pure ** ambient lights. For example, if this is set to 0.5f, then any light whose intensity ** is less that 0.5 will be converted into a slightly dimmer ball of ambient and thus not ** incur the directional lighting calculation cost. NOTE: this lighting LOD is only ** used when using LIGHTING_MODE_CHEAP lighting model ** ** Sun Light: This controls the direction of the sun. For Renegade, we probably won't ever ** change this in-game. However, if we did not use light-maps and we re-calculated all of ** the static projectors each time the sun moved, we could have a moving sun. ** ** NOTE: elevation is the angle that the sun makes with the horizontal, ** rotation is the angle between the +X axis and the line of the sun projected onto the X-Y plane. ** these variables are chose to match the ones in LightScape. */ enum { LIGHTING_MODE_NONE = 0, LIGHTING_MODE_SURRENDER, LIGHTING_MODE_CHEAP }; int Get_Lighting_Mode(void) { return LightingMode; } void Set_Lighting_Mode(int mode) { LightingMode = mode; } virtual void Set_Ambient_Light(const Vector3 & color) { SceneAmbientLight = color; } virtual const Vector3 & Get_Ambient_Light(void) { return SceneAmbientLight; } void Set_Lighting_LOD_Cutoff(float intensity); float Get_Lighting_LOD_Cutoff(void); bool Is_Sun_Light_Enabled(void); void Enable_Sun_Light(bool onoff); LightClass * Get_Sun_Light(void); void Set_Sun_Light_Orientation(float rotation,float elevation); void Get_Sun_Light_Orientation(float * set_rotation,float * set_elevation); void Get_Sun_Light_Vector(Vector3 * set_vector); void Compute_Static_Lighting(LightEnvironmentClass * light_env,const Vector3 & obj_center,bool use_sun,int vis_object_id); void Invalidate_Lighting_Caches(const AABoxClass & bounds); /* ** Collision Detection Methods ** Set_Collision_Region - collects objects in the given region into a list for subsequent collision checks ** this is useful if you're going to do a lot of checks in the same general area (e.g. RigidBody) ** Release_Collision_Region - releases the collision region list ** Cast_Ray - casts a ray into the world, returning information about what was collided and at what point along the ray ** Cast_AABox - casts an axis aligned box, returning information about what was collided and at what point ** Cast_OBBox - casts an oriented box, returning information about what was collided and at what point ** Intersection_Test - tests the given primitive for intersection with anything else in the system */ void Set_Collision_Region(const AABoxClass & bounds,int colgroup); void Release_Collision_Region(void); bool Cast_Ray(PhysRayCollisionTestClass & raytest,bool use_collision_region = false); bool Cast_AABox(PhysAABoxCollisionTestClass & boxtest,bool use_collision_region = false); bool Cast_OBBox(PhysOBBoxCollisionTestClass & boxtest,bool use_collision_region = false); bool Intersection_Test(PhysAABoxIntersectionTestClass & boxtest,bool use_collision_region = false); bool Intersection_Test(PhysOBBoxIntersectionTestClass & boxtest,bool use_collision_region = false); bool Intersection_Test(PhysMeshIntersectionTestClass & meshtest,bool use_collision_region = false); bool Intersection_Test(const AABoxClass & box,int collision_group,int collision_type,bool use_collision_region = false); bool Intersection_Test(const OBBoxClass & box,int collision_group,int collision_type,bool use_collision_region = false); void Force_Dynamic_Objects_Awake(const AABoxClass & box); /* ** Collection Methods ** Collect lists of objects which meet certain requirements. ** TODO: is this the interface we want? maybe the user can give a callback object? ** maybe more filters over which objects are added to the list? */ void Collect_Objects(const Vector3 & point,bool static_objs, bool dynamic_objs,NonRefPhysListClass * list); void Collect_Objects(const AABoxClass & box,bool static_objs, bool dynamic_objs,NonRefPhysListClass * list); void Collect_Objects(const OBBoxClass & box,bool static_objs, bool dynamic_objs,NonRefPhysListClass * list); void Collect_Objects(const FrustumClass & frustum,bool static_objs, bool dynamic_objs,NonRefPhysListClass * list); void Collect_Collideable_Objects(const AABoxClass & box,int colgroup,bool static_objs, bool dynamic_objs,NonRefPhysListClass * list); void Collect_Collideable_Objects(const OBBoxClass & box,int colgroup,bool static_objs, bool dynamic_objs,NonRefPhysListClass * list); void Collect_Lights(const Vector3 & point,bool static_lights,bool dynamic_lights,NonRefPhysListClass * list); void Collect_Lights(const AABoxClass & bounds,bool static_lights,bool dynamic_lights,NonRefPhysListClass * list); StaticPhysClass * Find_Static_Object( int instance_id ); /* ** Collision Groups: ** ** Each physical object can be assigned to a collision group. Then ** collision detection between any pair of groups can be enabled or ** disabled. An object will completely ignore objects in collision ** groups that are disabled relative to its collision group. ** ** Collision groups are numbered 0x0 - 0xF */ enum { COLLISION_GROUP_WORLD = 0x0F, MAX_COLLISION_GROUP = 0x0F, NUM_COLLISION_FLAGS = 256, COLLISION_FLAG_SHIFT = 4 }; void Enable_Collision_Detection(int group0,int group1); void Disable_Collision_Detection(int group0,int group1); void Enable_All_Collision_Detections(int group); void Disable_All_Collision_Detections(int group); bool Do_Groups_Collide(int group0,int group1); /* ** Cause the culling systems to re-partition themselves. (Typically only done in the EDITOR!) ** The Update_Culling_System_Bounding_Boxes can be used by the EDITOR to refresh the ** boxes in the culling systems without re-partitioning (and losing vis data...) */ void Re_Partition_Static_Objects(void); void Re_Partition_Static_Lights(void); void Re_Partition_Static_Projectors(void); void Re_Partition_Dynamic_Culling_System(void); void Re_Partition_Dynamic_Culling_System(DynamicVectorClass & virtual_occludees); void Update_Culling_System_Bounding_Boxes(void); bool Verify_Culling_Systems(StringClass & set_error_report); /* ** Visibility System. ** Enable_Vis - turn the vis system on or off ** Invert_Vis - invert the logic of vis, draw what would be occulded ** Set_Vis_Quick_And_Dirty - when generating, make non-occluders always visible (only used in preprocessing) ** Enable_Vis_Sector_Display - highlight the current vis sector in wireframe ** Enable_Vis_Sector_History_Display - highlight the last few vis sectors ** Disable_Vis_Sector_Fallback - don't fall back to the previous sector when there is no sector for the current position */ void Enable_Vis(bool onoff); // on by default void Invert_Vis(bool onoff); // off by default void Set_Vis_Quick_And_Dirty(bool onoff); // off by default void Enable_Vis_Sector_Display(bool onoff); // off by default void Enable_Vis_Sector_History_Display(bool onoff); // off by default void Enable_Vis_Sector_Fallback(bool onoff); // on by default void Enable_Backface_Occluder_Debug(bool onoff); bool Is_Vis_Enabled(void); bool Is_Vis_Inverted(void); bool Is_Vis_Quick_And_Dirty(void); bool Is_Vis_Sector_Display_Enabled(void); bool Is_Vis_Sector_History_Display_Enabled(void); bool Is_Vis_Sector_Missing(void); bool Is_Vis_Sector_Fallback_Enabled(void); bool Is_Backface_Occluder_Debug_Enabled(void); void Lock_Vis_Sample_Point(bool onoff); bool Is_Vis_Sample_Point_Locked(void); void Compute_Vis_Sample_Point(const CameraClass & camera,Vector3 * set_point); VisTableClass * Get_Vis_Table(int vis_sector_id); VisTableClass * Get_Vis_Table(const Vector3 & point); VisTableClass * Get_Vis_Table(const CameraClass & camera); VisTableClass * Get_Vis_Table_For_Rendering(const CameraClass & camera); virtual void On_Vis_Occluders_Rendered(VisRenderContextClass & context,VisSampleClass & sample) {} /* ** Dynamic object visibility system debugging. This system is no longer implemented ** as a "grid" but rather an AABTree... */ enum { VIS_GRID_DISPLAY_NONE = 0, VIS_GRID_DISPLAY_ACTUAL_BOXES, VIS_GRID_DISPLAY_CENTERS, VIS_GRID_DISPLAY_OCCUPIED }; void Set_Vis_Grid_Display_Mode(int mode); int Get_Vis_Grid_Display_Mode(void); void Vis_Grid_Debug_Reset_Node(void); bool Vis_Grid_Debug_Enter_Parent(void); bool Vis_Grid_Debug_Enter_Sibling(void); bool Vis_Grid_Debug_Enter_Front_Child(void); bool Vis_Grid_Debug_Enter_Back_Child(void); /* ** Dynamic objects look up their vis-ID throught this method. Note that if you pass in a ** pointer to your previous node_id, the lookup will be faster and this variable will be modified ** with the updated node id. The first time this method is called, initialize the node_id to zero. */ uint32 Get_Dynamic_Object_Vis_ID(const AABoxClass & obj_bounds,int * node_id = NULL); void Debug_Display_Dynamic_Vis_Node(int node_id); /* ** Visibility Pre-Processing System. (Typically only used in the EDITOR!) ** Reset_Vis - marks the visibility data as dirty, to be discarded at the next opportunity ** Validate_Vis - manually validates the visibility data (used after loading for example) ** Allocate_Vis_Object_Id - reserve id(s) for objects whose visibility is to be tracked ** Allocate_Vis_Sector_Id - reserve id(s) for vis sectors (view cells). ** Get_Vis_Table_Size - returns the number of Vis Object ID's reserved ** Get_Vis_Table_Count - returns the number of Vis Sector ID's reserved ** Update_Vis - performs a vis-sample from the given position or camera ** Export_Vis_Data - saves just the visibility data to a file ** Import_Vis_Data - loads the visibility data from a file ** Show_Vis_Window - enable display of the vis render window ** Is_Vis_Window_Visibile - test whether the vis render window is being displayed ** Generate_Vis_Statistics_Report - generates a report giving the amount of polygons and textures ** visible from each sector in the system. ** Optimize_Visibility_Data - compresses vis by removing redundant vis-id's and pruning the dynamic culling ** system. This should only be called *after* all vis samples have been completed. */ virtual void Reset_Vis(bool doitnow = false); void Validate_Vis(void); int Allocate_Vis_Object_ID(int count = 1); int Allocate_Vis_Sector_ID(int count = 1); int Get_Vis_Table_Size(void); // also max vis *object* ID and vis object count. int Get_Vis_Table_Count(void); // also max vis *sector* ID and vis sector count. VisSampleClass Update_Vis(const Matrix3D & camera,VisDirBitsType direction_bits = VIS_ALL); VisSampleClass Update_Vis(const Vector3 & sample_point,const Matrix3D & camera,VisDirBitsType direction_bits = VIS_ALL,CameraClass * alternate_camera = NULL,int user_vis_id = -1); int Get_Static_Light_Count(void); void Generate_Vis_For_Light(int light_index); void Export_Vis_Data(ChunkSaveClass & csave); void Import_Vis_Data(ChunkLoadClass & cload); void Show_Vis_Window(bool onoff); bool Is_Vis_Window_Visible(void); void Generate_Vis_Statistics_Report(DynamicVectorClass & report); void Optimize_Visibility_Data(VisOptProgressClass & progress_status); /* ** Texture Projection System. */ void Enable_Static_Projectors(bool onoff); bool Are_Static_Projectors_Enabled(void); void Enable_Dynamic_Projectors(bool onoff); bool Are_Dynamic_Projectors_Enabled(void); void Add_Static_Texture_Projector(TexProjectClass * newprojector); void Remove_Static_Texture_Projector(TexProjectClass * projector); void Add_Dynamic_Texture_Projector(TexProjectClass * newprojector); void Remove_Dynamic_Texture_Projector(TexProjectClass * projector); void Remove_Texture_Projector(TexProjectClass * projector); bool Contains(TexProjectClass * projector); /* ** Shadow system, built on top of the Texture Projection system. */ enum ShadowEnum { SHADOW_MODE_NONE = 0, // no shadows at all SHADOW_MODE_BLOBS, // projected blob shadows SHADOW_MODE_BLOBS_PLUS, // projected blobs with main character having a rendered shadow SHADOW_MODE_HARDWARE, // use render-to-texture hardware SHADOW_MODE_COUNT, }; void Set_Shadow_Mode(ShadowEnum shadow_mode); ShadowEnum Get_Shadow_Mode(void); void Set_Shadow_Attenuation(float znear,float zfar); void Get_Shadow_Attenuation(float * set_znear,float * set_zfar); void Set_Shadow_Normal_Intensity(float normal_intensity); float Get_Shadow_Normal_Intensity(void); void Set_Shadow_Resolution(unsigned int res); unsigned int Get_Shadow_Resolution(void); void Set_Max_Simultaneous_Shadows(unsigned int count); unsigned int Get_Max_Simultaneous_Shadows(void); CameraClass * Get_Shadow_Camera(void); SpecialRenderInfoClass *Get_Shadow_Render_Context(int width,int height); MaterialPassClass * Get_Shadow_Material_Pass(void); /* ** Generate (or re-generate) all static shadows. Invalidate must be called before calling Generate. */ void Invalidate_Static_Shadow_Projectors(void); void Generate_Static_Shadow_Projectors(void); void Setup_Static_Directional_Shadow(StaticAnimPhysClass & obj,const Vector3 & light_dir,TextureClass * render_target); /* ** Decal system ** Project a decal onto the geometry in the vicinity of the given coordinate system */ int Create_Decal( const Matrix3D & tm, const char * texture_name, float radius, bool is_permanent = false, bool apply_to_translucent_polys = false, PhysClass * only_this_obj = NULL ); bool Remove_Decal(uint32 id); /* ** Shatter system ** Shatter a mesh into many pieces and send them flying. */ void Shatter_Mesh( MeshClass * mesh, const Vector3 & impact_point, const Vector3 & impact_normal, const Vector3 & impact_velocity ); /* ** Camera Shake System ** Drop in camera shake spheres and any camera in the vicinity will automatically be affected. ** Prior to calling WW3D::Render(scene...) you need to pass your camera into Apply_Camera_Shakes */ void Add_Camera_Shake( const Vector3 & position, float radius = 50.0f, float duration = 1.5f, float power = 1.0f ); void Apply_Camera_Shakes( CameraClass & camera ); /* ** Temporary accessors for the level editor. */ bool Is_Vis_Reset_Needed (void) const { return VisResetNeeded; } void Vis_Reset_Needed(bool needed) { VisResetNeeded = needed; } /* ** Save-Load system. There are three save-load sub-systems in the physics library. ** PhysStaticDataSaveSystem - saves static data like pathfind, vis, and static culling data. ** PhysStaticObjectsSaveSystem - saves static objects like terrain pieces which have no dynamic state. ** PhysDynamicDataSaveSystem - saves dynamic data, settings, and objects in the physics scene */ void Save_Level_Static_Data(ChunkSaveClass & csave); void Load_Level_Static_Data(ChunkLoadClass & cload); void Post_Load_Level_Static_Data(void); void Save_Level_Static_Objects(ChunkSaveClass & csave); void Load_Level_Static_Objects(ChunkLoadClass & cload); void Post_Load_Level_Static_Objects(void); void Save_Level_Dynamic_Data(ChunkSaveClass & csave); void Load_Level_Dynamic_Data(ChunkLoadClass & cload); void Post_Load_Level_Dynamic_Data(void); /* ** Control over the polygon budget */ void Set_Polygon_Budgets(int static_count,int dynamic_count); void Get_Polygon_Budgets(int * static_count,int * dynamic_count); void Set_Update_Only_Visible_Objects(bool b) { UpdateOnlyVisibleObjects=b; } bool Get_Update_Only_Visible_Objects() { return UpdateOnlyVisibleObjects; } /* ** Scene Class methods. These should *only* be used when absolutely necessary since ** it is more efficient to operate through the physics interface (I can keep track ** of whether an object has moved and update its culling etc). If you insert a render ** object through this interface, a DecoPhys object will be created for it and added to ** a "Dirty Cullers" list. The "Dirty Cullers" list will assume that those objects ** have changed shape or moved every frame and always re-insert the object into the ** culling system. This code is needed in order to support render objects that ** add other render objects to the scene like particle emitters. ** Still don't support the iterator interface... maybe will some day... */ virtual void Add_Render_Object(RenderObjClass * obj); virtual void Remove_Render_Object(RenderObjClass * obj); virtual SceneIterator * Create_Iterator(bool /*onlyvisible = false*/) { assert(0); return NULL; } virtual void Destroy_Iterator(SceneIterator * /*it*/) { assert(0); } virtual void Register(RenderObjClass * obj,RegType for_what); virtual void Unregister(RenderObjClass * obj,RegType for_what); /* ** Accessors */ void Get_Level_Extents (Vector3 &min, Vector3 &max); /* ** Debugging control. Turn on rendering of debug vectors, points, bounding boxes, etc. */ void Enable_Debug_Display(bool onoff) { DebugDisplayEnabled = onoff; } bool Is_Debug_Display_Enabled(void) { return DebugDisplayEnabled; } void Enable_Projector_Debug_Display(bool onoff) { ProjectorDebugDisplayEnabled = onoff; } bool Is_Projector_Debug_Display_Enabled(void) { return ProjectorDebugDisplayEnabled; } void Enable_Dirty_Cull_Debug_Display(bool onoff) { DirtyCullDebugDisplayEnabled = onoff; } bool Is_Dirty_Cull_Debug_Display_Enabled(void) { return DirtyCullDebugDisplayEnabled; } void Enable_Lighting_Debug_Display(bool onoff) { LightingDebugDisplayEnabled = onoff; } bool Is_Lighting_Debug_Display_Enabled(void) { return LightingDebugDisplayEnabled; } const Vector3 & Get_Last_Camera_Position(void) { return LastCameraPosition; } /* ** Performance Statistics */ struct StatsStruct { StatsStruct(void); void Reset(void); int FrameCount; int CullNodeCount; int CullNodesAccepted; int CullNodesTriviallyAccepted; int CullNodesRejected; }; void Per_Frame_Statistics_Update(void); const StatsStruct & Get_Statistics(void); float Compute_Vis_Mesh_Ram(void); protected: /* ** Rendering functions */ virtual void Customized_Render(RenderInfoClass & rinfo); void Render_Objects(RenderInfoClass& rinfo,RefPhysListClass * static_ws_list,RefPhysListClass * static_list,RefPhysListClass * dyn_list); void Render_Object(RenderInfoClass & context,PhysClass * obj); void Render_Backface_Occluders(RenderInfoClass & context,RefPhysListClass * static_ws_list,RefPhysListClass * static_list); void Optimize_LODs( CameraClass & camera, RefPhysListClass * dyn_obj_list, RefPhysListClass * static_obj_list, RefPhysListClass * static_ws_mesh_list ); /* ** Internal Vis functions. These are used to generate the VIS data. */ void Release_Vis_Resources(void); virtual void Internal_Vis_Reset(void); CameraClass * Get_Vis_Camera(void); void Vis_Render_And_Scan(VisRenderContextClass & context,VisSampleClass & sample); void Merge_Vis_Sector_IDs(uint32 id0,uint32 id1); void Merge_Vis_Object_IDs(uint32 id0,uint32 id1); /* ** Internal texture-projection functions */ public: void Release_Projector_Resources(void); protected: void Apply_Projectors(const CameraClass & camera); void Apply_Projector_To_Objects(TexProjectClass * tex_proj,const CameraClass & camera); float Compute_Projector_Attenuation(TexProjectClass * dynamic_projector,const Vector3 & view_pos,const Vector3 & view_dir); /* ** Internal decal functions */ void Allocate_Decal_Resources(void); void Release_Decal_Resources(void); /* ** Internal save-load code */ void Save_LDD_Variables(ChunkSaveClass & csave); void Save_Static_Objects(ChunkSaveClass & csave); void Save_Dynamic_Objects(ChunkSaveClass & csave); void Save_Static_Lights(ChunkSaveClass & csave); void Save_Static_Object_States(ChunkSaveClass & csave); void Load_LDD_Variables(ChunkLoadClass & cload); void Load_Static_Objects(ChunkLoadClass & cload); void Load_Dynamic_Objects(ChunkLoadClass & cload); void Load_Static_Lights(ChunkLoadClass & cload); void Load_Static_Object_States(ChunkLoadClass & cload); /* ** Misc */ void Internal_Add_Dynamic_Object(PhysClass * newobj); void Internal_Add_Static_Object(StaticPhysClass * newtile); void Internal_Add_Static_Light(LightPhysClass * newlight); void Reset_Sun_Light(void); void Load_Sun_Light(ChunkLoadClass & cload); void Save_Sun_Light(ChunkSaveClass & csave); void Add_Collected_Objects_To_List(bool static_objs,bool dynamic_objs,NonRefPhysListClass * list); void Add_Collected_Collideable_Objects_To_List(int colgroup,bool static_objs,bool dynamic_objs,NonRefPhysListClass * list); void Add_Collected_Lights_To_List(bool static_lights,bool dynamic_lights,NonRefPhysListClass * list); //- Volatile or Constant Member Variables ------------------------------------------------------------- // // These variables do not need to be saved or loaded for one of the following reasons: // - They are created/initialized when the physics scene is constructed and never change // - They are simple flags or variables that just store the last thing the user set // //----------------------------------------------------------------------------------------------------- /* ** Cached Data during the Pre_Render, Render, and Post_Render sequence */ RefPhysListClass VisibleStaticObjectList; RefPhysListClass VisibleWSMeshList; RefPhysListClass VisibleDynamicObjectList; TexProjListClass ActiveTextureProjectors; /* ** Current frame number, last camera position, etc */ int FrameNum; Vector3 LastCameraPosition; int LastValidVisId; // id of the last valid vis sector bool DebugDisplayEnabled; bool ProjectorDebugDisplayEnabled; bool DirtyCullDebugDisplayEnabled; bool LightingDebugDisplayEnabled; StatsStruct LastValidStats; // last complete statistics sample StatsStruct CurrentStats; // currently accumulating statistics sample bool StaticProjectorsDirty; /* ** Visibility system variables */ enum { VIS_RENDER_WIDTH = 256, // resolution width for the vis render window VIS_RENDER_HEIGHT = 256 // resolution height for the vis render window }; bool VisEnabled; // use vis data if present? (default true) bool VisInverted; // invert vis data if present? (default false) bool VisQuickAndDirty; // ignore non-occluders in generating vis? (quick-n-dirty vis) bool VisResetNeeded; // indicates that the vis system is to be reset bool VisSectorDisplayEnabled;// debugging, highlight the vis sector mesh bool VisSectorHistoryEnabled;// debugging, highlight the previous 3 vis sectors. int VisGridDisplayMode; // debugging, display the visible grid cells bool VisSectorMissing; // debugging, true when no vis sector was found! bool VisSectorFallbackEnabled; // Use the last valid vis sector when no sector is found bool BackfaceDebugEnabled; // is backface debugging enabled. bool VisSamplePointLocked; // is the sample point for vis being over-ridden? Vector3 LockedVisSamplePoint; // position to sample vis from when locked/overridden. CameraClass * VisCamera; // camera set up for vis-rendering VisTableClass * CurrentVisTable; // current active vis table /* ** Shadow system variables */ bool StaticProjectorsEnabled; // toggle static shadows (shadows cast by static objs onto dynamic objs) bool DynamicProjectorsEnabled; // toggle dynamic shadows (shadows cast by dynamic objs onto everything) ShadowEnum ShadowMode; // current shadow mode float ShadowAttenStart; // distance to start of shadow attenuation float ShadowAttenEnd; // distance to end of shadow attenuation float ShadowNormalIntensity; // "normal" non attenuated shadow intensity TextureClass * ShadowBlobTexture; // texture to use for fake "blob" shadows SpecialRenderInfoClass *ShadowRenderContext; // render context for shadows CameraClass * ShadowCamera; // camera for rendering shadow textures MaterialPassClass * ShadowMaterialPass; // material pass for shadows int ShadowResWidth; int ShadowResHeight; /* ** Decal System */ PhysDecalSysClass * DecalSystem; /* ** Pathfinding */ PathfindClass * Pathfinder; /* ** Camera Shaking */ CameraShakeSystemClass *CameraShakeSystem; /* ** Render objects processing lists */ RefRenderObjListClass UpdateList; RefRenderObjListClass VertexProcList; RefPhysListClass ReleaseList; /* ** Miscellaneous resources */ MaterialPassClass * HighlightMaterialPass; //- Level Static Data --------------------------------------------------------------------------------- // // This data is constant throughout the course of a level and therefore is stored in the // LSD (L)evel (S)tatic (D)ata file. // - The structure of the Static Object AABTree // - The structure of the Static Light AABTree // - The structure of the Dynamic Object culling grid // - All of the StaticPhysClasses and StaticLightClasses (in StaticObjList and StaticLightList) // //----------------------------------------------------------------------------------------------------- /* ** Culling systems. These will have a structure which is based on the ** static terrain for the level and do not change during the course of a level (though ** the objects may link and unlink from them) */ StaticAABTreeCullClass * StaticCullingSystem; StaticLightCullClass * StaticLightingSystem; PhysGridCullClass * DynamicCullingSystem; DynamicAABTreeCullClass * DynamicObjVisSystem; TypedAABTreeCullSystemClass * StaticProjectorCullingSystem; TypedGridCullSystemClass * DynamicProjectorCullingSystem; /* ** Visibility Tables */ VisTableMgrClass VisTableManager; /* ** Lighting: ** Sun Light. The sun is special cased to generate shadows and a lens ** flare. It should not be changed unless you are in the level editor. */ int LightingMode; Vector3 SceneAmbientLight; bool UseSun; float SunPitch; float SunYaw; LightClass * SunLight; //- Level Dynamic Data -------------------------------------------------------------------------------- // // This is the dynamic data/objects which all need to be saved in either the LDD file or SAV file. // //----------------------------------------------------------------------------------------------------- /* ** Collision group tables */ static bool AllowCollisionFlags[NUM_COLLISION_FLAGS]; /* ** Polygon budget for the LOD system */ int DynamicPolyBudget; int StaticPolyBudget; /* ** Master lists of the objects in the system. Each object will be in *one* of the ** following lists. */ RefPhysListClass ObjList; RefPhysListClass StaticObjList; RefPhysListClass StaticLightList; TexProjListClass StaticProjectorList; TexProjListClass DynamicProjectorList; /* ** Auxiliary lists. These lists are used to perform certain special ** processing on certain objects or to avoid performing certain operations ** on objects. A given object can be in any or all of these lists... */ RefPhysListClass DirtyCullList; // objects that have 'dirty culling' must be re-inserted each frame... RefPhysListClass TimestepList; // objects which need to be time-stepped go in here RefPhysListClass StaticAnimList; // list of the StaticAnim objects, these can cast shadows, change states, etc NonRefPhysListClass CollisionRegionList; // cached list of objects in the current collision region bool UpdateOnlyVisibleObjects; unsigned CurrentFrameNumber; private: /* ** The physics scene is a singleton. If we want to support multiple ** physics scenes, we would need to add a scene pointer to each physics object ** and everywhere they are calling Get_Instance() they can just use their pointer */ static PhysicsSceneClass * TheScene; /* ** WW3D is a friend because we let it call our protected Render function (which no ** one else is allowed to call... trying to "contain" surrender :-) */ friend class WW3D; /* ** VisOptimizationContextClass is a friend so that it can merge vis-id's. */ friend class VisOptimizationContextClass; }; #endif