/*
** 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.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 3/28/02 6:48p $*
* *
* $Revision:: 218 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* PhysicsSceneClass::PhysicsSceneClass -- Constructor *
* PhysicsSceneClass::~PhysicsSceneClass -- Destructor *
* PhysicsSceneClass::Update -- Simulates the entire scene forward one timestep *
* PhysicsSceneClass::Add_Dynamic_Object -- Adds a dynamic object to the scene *
* PhysicsSceneClass::Internal_Add_Dynamic_Object -- internal function finishes adding a dyn *
* PhysicsSceneClass::Add_Static_Object -- Adds a static object to the scene *
* PhysicsSceneClass::Internal_Add_Static_Object -- internal add static object... *
* PhysicsSceneClass::Add_Static_Light -- Adds a static light to the system *
* PhysicsSceneClass::Internal_Add_Static_Light -- internal add static light... *
* PhysicsSceneClass::Delayed_Remove_Object -- registers an object for removal *
* PhysicsSceneClass::Process_Release_List -- Removes all objects in the ReleaseList *
* PhysicsSceneClass::Remove_Object -- Remove an object from the scene *
* PhysicsSceneClass::Remove_All -- Removes all objects from the scene *
* PhysicsSceneClass::Contains -- Tests if the scene contains the given object *
* PhysicsSceneClass::Get_Dynamic_Object_Iterator -- returns an iterator for the dynamic obj *
* PhysicsSceneClass::Get_Static_Object_Iterator -- return an iterator for the static objs *
* PhysicsSceneClass::Get_Static_Anim_Object_Iterator -- returns an iterator for static anim *
* PhysicsSceneClass::Get_Static_Light_Iterator -- returns an iterator for static lights *
* PhysicsSceneClass::Get_Static_Projector_Iterator -- returns an iterator for static projec *
* PhysicsSceneClass::Get_Dynamic_Projector_Iterator -- returns an iterator for dynamic proj *
* PhysicsSceneClass::Add_To_Dirty_Cull_List -- adds something to the "dirty cull" list *
* PhysicsSceneClass::Remove_From_Dirty_Cull_List -- removes an object from the dirty cull l *
* PhysicsSceneClass::Is_In_Dirty_Cull_List -- tests whether an object is in the dirty cull *
* PhysicsSceneClass::Add_Render_Object -- Adds a render object to the scene *
* PhysicsSceneClass::Remove_Render_Object -- removes a render object from the scene *
* PhysicsSceneClass::Register -- rengisters given render object *
* PhysicsSceneClass::Unregister -- Unregisters the given render object *
* PhysicsSceneClass::Set_Vis_Sample_Point -- Set the current vis sample point *
* PhysicsSceneClass::Pre_Render_Processing -- processing which occurs prior to rendering *
* PhysicsSceneClass::Post_Render_Processing -- processing that occurs after rendering *
* PhysicsSceneClass::Optimize_LODs -- Set the LOD level for each object *
* PhysicsSceneClass::Render -- Render the scene *
* PhysicsSceneClass::Render_Objects -- Render the visible objects *
* PhysicsSceneClass::Render_Object -- Render an individual object *
* PhysicsSceneClass::Render_Backface_Occluders -- Render backfaces of all occluders *
* PhysicsSceneClass::Re_Partition_Static_Objects -- partition the static objects *
* PhysicsSceneClass::Re_Partition_Static_Lights -- partition the static lights *
* PhysicsSceneClass::Re_Partition_Dynamic_Culling_System -- partition the dynamic culling s *
* PhysicsSceneClass::Re_Partition_Static_Projectors -- partition the static projectors *
* PhysicsSceneClass::Update_Culling_System_Bounding_Boxes -- updates the cull systems *
* PhysicsSceneClass::Get_Level_Extents -- returns the bounds of the level *
* PhysicsSceneClass::Set_Polygon_Budgets -- set the budgets for the LOD system *
* PhysicsSceneClass::Get_Polygon_Budgets -- returns the budgets for the LOD system *
* PhysicsSceneClass::Per_Frame_Statistics_Update -- statistics tracking *
* PhysicsSceneClass::Get_Statistics -- returns statistics *
* PhysicsSceneClass::Find_Static_Object -- Find static object with the given ID *
* PhysicsSceneClass::Shatter_Mesh -- Clips a mesh into pieces and scatters them *
* PhysicsSceneClass::Verify_Culling_Systems -- Verify the culling systems *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "pscene.h"
#include "rinfo.h"
#include "light.h"
#include "wwdebug.h"
#include "wwhack.h"
#include "wwprofile.h"
#include "wwmemlog.h"
#include "physgridcull.h"
#include "staticaabtreecull.h"
#include "dynamicaabtreecull.h"
#include "lightcull.h"
#include "camera.h"
#include "wwphystrig.h"
#include "vertmaterial.h"
#include "predlod.h"
#include "widgets.h"
#include "renderobjphys.h"
#include "pathfind.h"
#include "lightexclude.h"
#include "physdecalsys.h"
#include "quat.h"
#include "shader.h"
#include "lookuptable.h"
#include "shattersystem.h"
#include "projectile.h"
#include "staticphys.h"
#include "vistable.h"
#include "meshmdl.h"
#include "camerashakesystem.h"
#include "lightenvironment.h"
#include "dx8wrapper.h"
#include "physresourcemgr.h"
#include "phys3.h"
#include "umbrasupport.h"
#define STATISTICS_FRAMES 20 // number of frames to average statistics across
#define SHATTER_DEBUG 0 // debugging, freeze shattered particles in place for 60sec
/*
** Static members of PhysicsSceneClass
*/
bool PhysicsSceneClass::AllowCollisionFlags[NUM_COLLISION_FLAGS];
PhysicsSceneClass * PhysicsSceneClass::TheScene = NULL;
/*
** Constants
*/
const float MAX_TIMESTEP = 1.0f / 15.0f;
const Vector3 MIN_GRID_CELL_SIZE(15.0f,15.0f,15.0f);
const int MAX_GRID_CELL_COUNT = 8192;
const float MAX_DYNAMIC_OBJ_RADIUS = 5.0f;
const int DEFAULT_DYNAMIC_LOD_BUDGET = 4000;
const int DEFAULT_STATIC_LOD_BUDGET = 4000;
/******************************************************************************************
**
**
** PhysicsSceneClass Implementation
**
**
******************************************************************************************/
/***********************************************************************************************
* PhysicsSceneClass::PhysicsSceneClass -- Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
PhysicsSceneClass::PhysicsSceneClass(void) :
FrameNum(0),
LastCameraPosition(0,0,0),
LastValidVisId(-1),
DebugDisplayEnabled(false),
ProjectorDebugDisplayEnabled(false),
DirtyCullDebugDisplayEnabled(false),
LightingDebugDisplayEnabled(false),
StaticCullingSystem(NULL),
DynamicCullingSystem(NULL),
StaticLightingSystem(NULL),
StaticProjectorCullingSystem(NULL),
DynamicProjectorCullingSystem(NULL),
DynamicPolyBudget(DEFAULT_DYNAMIC_LOD_BUDGET),
StaticPolyBudget(DEFAULT_STATIC_LOD_BUDGET),
LightingMode(LIGHTING_MODE_CHEAP),
SceneAmbientLight(0.5f,0.5f,0.5f),
UseSun(false),
SunLight(NULL),
VisEnabled(true),
VisInverted(false),
VisQuickAndDirty(false),
VisResetNeeded(false),
VisSectorDisplayEnabled(false),
VisSectorHistoryEnabled(false),
VisGridDisplayMode(VIS_GRID_DISPLAY_NONE),
VisSectorMissing(false),
VisSectorFallbackEnabled(true),
BackfaceDebugEnabled(false),
VisSamplePointLocked(false),
LockedVisSamplePoint(0,0,0),
VisCamera(NULL),
CurrentVisTable(NULL),
StaticProjectorsEnabled(false),
DynamicProjectorsEnabled(false),
ShadowMode(SHADOW_MODE_NONE),
ShadowAttenStart(25.0f),
ShadowAttenEnd(40.0f),
ShadowNormalIntensity(0.45f),
ShadowBlobTexture(NULL),
ShadowCamera(NULL),
ShadowRenderContext(NULL),
ShadowMaterialPass(NULL),
ShadowResWidth(128),
ShadowResHeight(128),
DecalSystem(NULL),
Pathfinder(NULL),
CameraShakeSystem(NULL),
HighlightMaterialPass(NULL),
UpdateOnlyVisibleObjects(false),
CurrentFrameNumber(0)
{
WWASSERT_PRINT(TheScene == NULL,"Only one instance of the PhysicsSceneClass is allowed.\r\n");
WWMEMLOG(MEM_PHYSICSDATA);
TheScene = this;
/*
** Initialize Umbra
*/
#if (UMBRASUPPORT)
UmbraSupport::Init();
#endif
/*
** Clear the collision flags
*/
memset(AllowCollisionFlags,0,NUM_COLLISION_FLAGS);
/*
** Allocate culling systems
*/
StaticCullingSystem = new StaticAABTreeCullClass(this);
DynamicCullingSystem = new PhysGridCullClass(this);
DynamicObjVisSystem = new DynamicAABTreeCullClass(this);
StaticLightingSystem = new StaticLightCullClass;
StaticProjectorCullingSystem = new TypedAABTreeCullSystemClass;
DynamicProjectorCullingSystem = new TypedGridCullSystemClass;
/*
** Allocate pathfind object
*/
Pathfinder = new PathfindClass;
/*
** Allocate the camera shake system
*/
CameraShakeSystem = new CameraShakeSystemClass;
/*
** Allocate the sun light
*/
SunLight = NEW_REF(LightClass,(LightClass::DIRECTIONAL));
Reset_Sun_Light();
/*
** Initialize the debug code
*/
WidgetSystem::Init_Debug_Widgets();
/*
** Allocate decal resources
*/
Allocate_Decal_Resources();
}
/***********************************************************************************************
* PhysicsSceneClass::~PhysicsSceneClass -- Destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
PhysicsSceneClass::~PhysicsSceneClass(void)
{
Remove_All();
delete StaticCullingSystem;
delete DynamicCullingSystem;
delete DynamicObjVisSystem;
delete StaticLightingSystem;
delete StaticProjectorCullingSystem;
delete DynamicProjectorCullingSystem;
delete Pathfinder;
delete CameraShakeSystem;
REF_PTR_RELEASE(SunLight);
Release_Vis_Resources();
Release_Projector_Resources();
Release_Decal_Resources();
WidgetSystem::Release_Debug_Widgets();
WWASSERT(TheScene == this);
TheScene = NULL;
/*
** Shutdown UMBRA
*/
#if (UMBRASUPPORT)
UmbraSupport::Shutdown();
#endif
}
/***********************************************************************************************
* PhysicsSceneClass::Update -- Simulates the entire scene forward one timestep *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Update(float dt,int frameid)
{
WWPROFILE("PhysicsScene::Update");
Generate_Static_Shadow_Projectors();
FrameNum = frameid;
if (dt == 0.0f) {
return;
}
/*
** Timestep all of the physics objects
*/
{
WWPROFILE("Timestep");
float remaining = dt;
while (remaining > 0) {
float step = min(remaining,MAX_TIMESTEP);
/*
** Loop through each object telling each to time-step itself.
*/
RefPhysListIterator it(&TimestepList);
for (it.First(); !it.Is_Done(); it.Next()) {
PhysClass* phys_obj=it.Peek_Obj();
// Little optimization hack - only update vehicles that are visible (for now update all other physics
// objects regardless of the visibility to avoid problems, vehicles are the most expensive anyway).
// This same thing is done to Post Timestep couple lines lower.
if (phys_obj->Is_Object_Simulating()) {
if (!UpdateOnlyVisibleObjects ||
phys_obj->Get_Last_Visible_Frame()==CurrentFrameNumber ||
!phys_obj->As_VehiclePhysClass()) {
phys_obj->Timestep(step);
}
}
}
remaining -= step;
}
}
{
WWPROFILE("Post Timestep");
RefPhysListIterator it(&TimestepList);
for (it.First(); !it.Is_Done(); it.Next()) {
// if (it.Peek_Obj()->Is_Object_Simulating()) {
// if (!UpdateOnlyVisibleObjects || it.Peek_Obj()->Get_Last_Visible_Frame()==CurrentFrameNumber) {
PhysClass* phys_obj=it.Peek_Obj();
if (phys_obj->Is_Object_Simulating()) {
if (!UpdateOnlyVisibleObjects ||
phys_obj->Get_Last_Visible_Frame()==CurrentFrameNumber ||
!phys_obj->As_VehiclePhysClass()) {
phys_obj->Post_Timestep_Process();
}
}
}
}
/*
** Timestep the camera shakers
*/
{
WWPROFILE("CameraShakeSystem");
CameraShakeSystem->Timestep(dt);
}
/*
** Timestep the material effects
*/
{
WWPROFILE("MaterialEffects");
MaterialEffectClass::Timestep_All_Effects(dt);
}
/*
** Process pending release requests
** Put here prior to rendering to insure items won't draw too many times.
*/
Process_Release_List();
CurrentFrameNumber++;
}
/***********************************************************************************************
* PhysicsSceneClass::Add_Dynamic_Object -- Adds a dynamic object to the scene *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Add_Dynamic_Object(PhysClass * newobj)
{
WWASSERT(newobj != NULL);
WWASSERT(newobj->Peek_Model() != NULL);
WWASSERT(newobj->Get_Culling_System() == NULL);
// Add the object to the dynamic culling system
DynamicCullingSystem->Add_Object(newobj);
// Add the object to the lists in the physics scene
Internal_Add_Dynamic_Object(newobj);
// Clean up any cached visibility data that may have been in the object
DynamicPhysClass * dynobj = newobj->As_DynamicPhysClass();
WWASSERT(dynobj != NULL);
if (dynobj != NULL) {
dynobj->Update_Visibility_Status();
}
}
/***********************************************************************************************
* PhysicsSceneClass::Internal_Add_Dynamic_Object -- internal function finishes adding a dynam *
* *
* This function is called both by the loading code and the Add_Dynamic_Object function. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Internal_Add_Dynamic_Object(PhysClass * newobj)
{
// Add the object to the appropriate lists
ObjList.Add(newobj);
SceneClass::Add_Render_Object(newobj->Peek_Model());
if (newobj->Needs_Timestep()) {
TimestepList.Add(newobj);
}
}
/***********************************************************************************************
* PhysicsSceneClass::Add_Static_Object -- Adds a static object to the scene *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Add_Static_Object(StaticPhysClass * newtile,int cull_node_id/*=-1*/)
{
WWASSERT(newtile != NULL);
WWASSERT(newtile->Peek_Model() != NULL);
WWASSERT(newtile->Get_Culling_System() == NULL);
// Add the object to the static culling system
StaticCullingSystem->Add_Object(newtile,cull_node_id);
// Add the object to the lists in the physics scene
Internal_Add_Static_Object(newtile);
}
/***********************************************************************************************
* PhysicsSceneClass::Internal_Add_Static_Object -- internal add static object... *
* *
* This function is called by both the loading code and the Add_Static_Object function *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Internal_Add_Static_Object(StaticPhysClass * newtile)
{
// Add the object to the appropriate lists
StaticObjList.Add(newtile);
SceneClass::Add_Render_Object(newtile->Peek_Model());
if (newtile->Needs_Timestep()) {
TimestepList.Add(newtile);
}
if (newtile->As_StaticAnimPhysClass() != NULL) {
StaticAnimList.Add(newtile);
}
}
/***********************************************************************************************
* PhysicsSceneClass::Add_Static_Light -- Adds a static light to the system *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Add_Static_Light(LightPhysClass * newlight,int cull_node_id/*=-1*/)
{
WWASSERT(newlight != NULL);
WWASSERT(newlight->Peek_Model() != NULL);
WWASSERT(newlight->Get_Culling_System() == NULL);
// Add the light to the static light culling system
StaticLightingSystem->Add_Object(newlight);
// Add the light to all of the appropriate lists
Internal_Add_Static_Light(newlight);
}
/***********************************************************************************************
* PhysicsSceneClass::Internal_Add_Static_Light -- internal add static light... *
* *
* This function is called by both the loading code and the Add_Static_Light function *
* *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Internal_Add_Static_Light(LightPhysClass * newlight)
{
// Add the object to the appropriate lists
StaticLightList.Add(newlight);
//(gth) hack, I don't want static lights to get registered as vertex processors.
//so don't let the scene class know about them ;-) I should probably come up with
//a better mechanism for this...
//SceneClass::Add_Render_Object(newlight->Peek_Model());
if (newlight->Needs_Timestep()) {
TimestepList.Add(newlight);
}
}
/***********************************************************************************************
* PhysicsSceneClass::Delayed_Remove_Object -- registers an object for removal *
* *
* The object will be released by the scene at the end of the next frame. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Delayed_Remove_Object(PhysClass * obj)
{
if (!ReleaseList.Contains(obj)) {
ReleaseList.Add(obj);
}
}
/***********************************************************************************************
* PhysicsSceneClass::Process_Release_List -- Removes all objects in the ReleaseList *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Process_Release_List(void)
{
while(!ReleaseList.Is_Empty()) {
PhysClass * obj = ReleaseList.Remove_Head();
Remove_Object(obj);
obj->Release_Ref();
}
}
/***********************************************************************************************
* PhysicsSceneClass::Remove_Object -- Remove an object from the scene *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Remove_Object(PhysClass * obj)
{
// NOTE: sometimes the game tries to 'remove' objects that havent been added...
if (!Contains(obj)) return;
// Assert that the object is valid
WWASSERT(obj != NULL);
WWASSERT(obj->Peek_Model() != NULL);
// Notify the model that it is being removed from the scene
SceneClass::Remove_Render_Object(obj->Peek_Model());
// Notify the observer (if it has one)
if (obj->Get_Observer() != NULL) {
obj->Get_Observer()->Object_Removed_From_Scene(obj);
}
// Pull the object out of any of the "extra-processing" lists
Remove_From_Dirty_Cull_List(obj);
TimestepList.Remove(obj);
StaticAnimList.Remove(obj);
// Pull the physics object out of whatever system it is in
CullSystemClass * cullsys = obj->Get_Culling_System();
if (cullsys == DynamicCullingSystem) {
DynamicCullingSystem->Remove_Object(obj);
ObjList.Remove(obj);
} else if (cullsys == StaticCullingSystem) {
WWASSERT(obj->As_StaticPhysClass() != NULL);
StaticCullingSystem->Remove_Object(obj->As_StaticPhysClass());
StaticObjList.Remove(obj);
} else if (cullsys == StaticLightingSystem) {
WWASSERT(obj->As_LightPhysClass() != NULL);
StaticLightingSystem->Remove_Object(obj->As_LightPhysClass());
StaticLightList.Remove(obj);
} else {
WWASSERT(0); // should never happen!
}
}
/***********************************************************************************************
* PhysicsSceneClass::Remove_All -- Removes all objects from the scene *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Remove_All(void)
{
PhysClass * obj = ObjList.Peek_Head();
while (obj) {
Remove_Object(obj);
obj = ObjList.Peek_Head();
}
StaticPhysClass * tile = (StaticPhysClass *)StaticObjList.Peek_Head();
while (tile) {
Remove_Object(tile);
tile = (StaticPhysClass *)StaticObjList.Peek_Head();
}
LightPhysClass * light = (LightPhysClass *)StaticLightList.Peek_Head();
while(light) {
Remove_Object(light);
light = (LightPhysClass *)StaticLightList.Peek_Head();
}
TexProjectClass * static_proj = StaticProjectorList.Peek_Head();
while(static_proj) {
Remove_Static_Texture_Projector(static_proj);
static_proj = StaticProjectorList.Peek_Head();
}
TexProjectClass * dynamic_proj = DynamicProjectorList.Peek_Head();
while(dynamic_proj) {
Remove_Dynamic_Texture_Projector(dynamic_proj);
dynamic_proj = DynamicProjectorList.Peek_Head();
}
Pathfinder->Reset_Sectors ();
}
/***********************************************************************************************
* PhysicsSceneClass::Contains -- Tests if the scene contains the given object *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
bool PhysicsSceneClass::Contains(PhysClass * obj)
{
if (ObjList.Is_In_List(obj)) return true;
if (StaticObjList.Is_In_List(obj)) return true;
if (StaticLightList.Is_In_List(obj)) return true;
return false;
}
/***********************************************************************************************
* PhysicsSceneClass::Get_Dynamic_Object_Iterator -- returns an iterator for the dynamic objec *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/29/2000 gth : Created. *
*=============================================================================================*/
RefPhysListIterator PhysicsSceneClass::Get_Dynamic_Object_Iterator(void)
{
return RefPhysListIterator(&ObjList);
}
/***********************************************************************************************
* PhysicsSceneClass::Get_Static_Object_Iterator -- return an iterator for the static objs *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/29/2000 gth : Created. *
*=============================================================================================*/
RefPhysListIterator PhysicsSceneClass::Get_Static_Object_Iterator(void)
{
return RefPhysListIterator(&StaticObjList);
}
/***********************************************************************************************
* PhysicsSceneClass::Get_Static_Anim_Object_Iterator -- returns an iterator for static anim o *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/29/2000 gth : Created. *
*=============================================================================================*/
RefPhysListIterator PhysicsSceneClass::Get_Static_Anim_Object_Iterator(void)
{
return RefPhysListIterator(&StaticAnimList);
}
/***********************************************************************************************
* PhysicsSceneClass::Get_Static_Light_Iterator -- returns an iterator for static lights *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/29/2000 gth : Created. *
*=============================================================================================*/
RefPhysListIterator PhysicsSceneClass::Get_Static_Light_Iterator(void)
{
return RefPhysListIterator(&StaticLightList);
}
/***********************************************************************************************
* PhysicsSceneClass::Get_Static_Projector_Iterator -- returns an iterator for static projecto *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/29/2000 gth : Created. *
*=============================================================================================*/
TexProjListIterator PhysicsSceneClass::Get_Static_Projector_Iterator(void)
{
return TexProjListIterator(&StaticProjectorList);
}
/***********************************************************************************************
* PhysicsSceneClass::Get_Dynamic_Projector_Iterator -- returns an iterator for dynamic projec *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/29/2000 gth : Created. *
*=============================================================================================*/
TexProjListIterator PhysicsSceneClass::Get_Dynamic_Projector_Iterator(void)
{
return TexProjListIterator(&DynamicProjectorList);
}
/***********************************************************************************************
* PhysicsSceneClass::Add_To_Dirty_Cull_List -- adds something to the "dirty cull" list *
* *
* objects in the dirty cull list have their visiblity and cull box re-computed each *
* frame. We should try to keep things like this to a minimum. Particle buffers are *
* an example of something that would end up in the "dirty cull" list *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/29/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Add_To_Dirty_Cull_List(PhysClass *obj)
{
DirtyCullList.Add (obj);
}
/***********************************************************************************************
* PhysicsSceneClass::Remove_From_Dirty_Cull_List -- removes an object from the dirty cull lis *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/29/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Remove_From_Dirty_Cull_List(PhysClass *obj)
{
DirtyCullList.Remove (obj);
}
/***********************************************************************************************
* PhysicsSceneClass::Is_In_Dirty_Cull_List -- tests whether an object is in the dirty cull li *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/29/2000 gth : Created. *
*=============================================================================================*/
bool PhysicsSceneClass::Is_In_Dirty_Cull_List(PhysClass *obj)
{
return DirtyCullList.Contains (obj);
}
/***********************************************************************************************
* PhysicsSceneClass::Add_Render_Object -- Adds a render object to the scene *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* This interface is less efficient than wrapping your render objects in some kind of physics *
* object. *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Add_Render_Object(RenderObjClass * obj)
{
WWMEMLOG(MEM_PHYSICSDATA); // Right category?
// NOTE: The *ONLY* way this code should get activated is when the user
// or some deep dark w3d code is directly adding and removing render objects
// in the physics scene. In this case, I wrap the render objects with
// RenderObjPhysClass's and set the UserData pointer to point back to this
// wrapper.
WWASSERT(obj != NULL);
RenderObjPhysClass * cullnode = NEW_REF(RenderObjPhysClass,());
cullnode->Set_Model(obj);
Add_Dynamic_Object(cullnode);
Add_To_Dirty_Cull_List(cullnode);
//
// Make sure we don't save particle buffers or lines
//
if ((obj->Class_ID () == RenderObjClass::CLASSID_PARTICLEBUFFER) ||
(obj->Class_ID () == RenderObjClass::CLASSID_SEGLINE))
{
cullnode->Enable_Dont_Save(true);
}
cullnode->Release_Ref();
SceneClass::Add_Render_Object(obj);
}
/***********************************************************************************************
* PhysicsSceneClass::Remove_Render_Object -- removes a render object from the scene *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Remove_Render_Object(RenderObjClass * obj)
{
// NOTE: The *ONLY* way this code should get activated is when the user
// or some deep dark w3d code is directly adding and removing render objects
// in the physics scene. In this case, I wrap the render objects with
// RenderObjPhysClass's and set the UserData pointer to point back to this
// wrapper.
SceneClass::Remove_Render_Object(obj);
WWASSERT(obj != NULL);
PhysClass * cullnode = (PhysClass *)obj->Get_User_Data();
WWASSERT(cullnode != NULL);
WWASSERT(cullnode->As_RenderObjPhysClass() != NULL);
Remove_Object(cullnode);
}
/***********************************************************************************************
* PhysicsSceneClass::Register -- rengisters given render object *
* *
* Render objects can be registered for On_Frame_Update calls, added to the vertex *
* processor list, or added to the release list. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Register(RenderObjClass * obj,RegType for_what)
{
WWASSERT(obj != NULL);
switch (for_what)
{
case ON_FRAME_UPDATE:
UpdateList.Add(obj);
break;
case LIGHT:
VertexProcList.Add(obj);
break;
case RELEASE:
{
if (obj->Get_Container() != NULL) {
obj->Get_Container()->Remove_Sub_Object(obj);
} else {
PhysClass * wrapper = (PhysClass *)obj->Get_User_Data();
/*
** If there is no wrapper, the object isn't actually in the scene so do nothing
*/
if (wrapper != NULL) {
Delayed_Remove_Object(wrapper);
}
}
}
break;
}
}
/***********************************************************************************************
* PhysicsSceneClass::Unregister -- Unregisters the given render object *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Unregister(RenderObjClass * obj,RegType for_what)
{
WWASSERT(obj != NULL);
switch (for_what)
{
case ON_FRAME_UPDATE:
UpdateList.Remove(obj);
break;
case LIGHT:
VertexProcList.Remove(obj);
break;
case RELEASE:
WWASSERT_PRINT(0,("Error! Object %s tried to un-register from the release list\r\n",obj->Get_Name()));
break;
}
}
/***********************************************************************************************
* PhysicsSceneClass::Pre_Render_Processing -- processing which occurs prior to rendering *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Pre_Render_Processing(CameraClass & camera)
{
WWPROFILE("Pre_Render_Processing");
LastCameraPosition = camera.Get_Position();
DecalSystem->Update_Decal_Fade_Distances(camera);
// Do the needed 'On_Frame_Update's
RefRenderObjListIterator rit(&UpdateList);
for (rit.First(); !rit.Is_Done(); rit.Next()) {
rit.Peek_Obj()->On_Frame_Update();
}
// Update culling info for all of the objects in the "dirty cull" list (these are
// objects which were added to the scene as pure render objects so I don't assume
// that the I have control over when their transform or bounding box is changed...)
RefPhysListIterator it(&DirtyCullList);
for (it.First(); !it.Is_Done(); it.Next()) {
PhysClass * obj = it.Peek_Obj();
obj->Update_Cull_Box();
if (obj->As_DynamicPhysClass()) {
obj->As_DynamicPhysClass()->Update_Visibility_Status();
}
if (DirtyCullDebugDisplayEnabled) {
const AABoxClass & box = obj->Get_Cull_Box();
if (box.Extent.Length2() > 0.0f) {
DEBUG_RENDER_AABOX(obj->Get_Cull_Box(),Vector3(1,1,1),0.25f);
}
}
}
// Get the pvs
VisTableClass * pvs = Get_Vis_Table_For_Rendering(camera);
// Collect the visible objects
bool use_umbra = false;
#if (UMBRASUPPORT)
use_umbra = UmbraSupport::Is_Umbra_Enabled();
#endif
if (!use_umbra) {
// Get the lists of visible objects
{
WWPROFILE( "Collect Static Objs" );
StaticCullingSystem->Collect_Visible_Objects(camera.Get_Frustum(),pvs,VisibleStaticObjectList,VisibleWSMeshList);
}
// Collect list of the visible dynamic objects
{
WWPROFILE( "Collect Dynamic Objs" );
DynamicCullingSystem->Collect_Visible_Objects(camera.Get_Frustum(),pvs,VisibleDynamicObjectList);
}
// LOD processing
Optimize_LODs(camera,&VisibleDynamicObjectList,&VisibleStaticObjectList,&VisibleWSMeshList);
// Texture projectors
Apply_Projectors(camera);
} else {
#if (UMBRASUPPORT)
RefPhysListClass vis_obj_list;
{
WWPROFILE("Umbra!");
UmbraSupport::Collect_Visible_Objects(camera,VisibleDynamicObjectList);
}
#endif
}
// release the visibility table
REF_PTR_RELEASE(pvs);
}
/***********************************************************************************************
* PhysicsSceneClass::Post_Render_Processing -- processing that occurs after rendering *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Post_Render_Processing(void)
{
WWPROFILE("Post_Render_Processing");
// Release the visible object lists, release the shadow textures
VisibleDynamicObjectList.Reset_List();
VisibleStaticObjectList.Reset_List();
VisibleWSMeshList.Reset_List();
// Update statistics
Per_Frame_Statistics_Update();
}
/***********************************************************************************************
* PhysicsSceneClass::Optimize_LODs -- Set the LOD level for each object *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Optimize_LODs
(
CameraClass & camera,
RefPhysListClass * dyn_objs,
RefPhysListClass * static_objs,
RefPhysListClass * static_ws_meshes
)
{
WWPROFILE("Optimize_LODs");
// process the dynamic objects
PredictiveLODOptimizerClass::Clear();
RefPhysListIterator it(dyn_objs);
for (it.First(); !it.Is_Done(); it.Next()) {
it.Peek_Obj()->Peek_Model()->Prepare_LOD(camera);
it.Peek_Obj()->Set_Last_Visible_Frame(CurrentFrameNumber);
}
PredictiveLODOptimizerClass::Optimize_LODs(DynamicPolyBudget);
// process the static objects
PredictiveLODOptimizerClass::Clear();
for (it.First(static_objs); !it.Is_Done(); it.Next()) {
it.Peek_Obj()->Peek_Model()->Prepare_LOD(camera);
it.Peek_Obj()->Set_Last_Visible_Frame(CurrentFrameNumber);
}
for (it.First(static_ws_meshes); !it.Is_Done(); it.Next()) {
it.Peek_Obj()->Peek_Model()->Prepare_LOD(camera);
it.Peek_Obj()->Set_Last_Visible_Frame(CurrentFrameNumber);
}
PredictiveLODOptimizerClass::Optimize_LODs(StaticPolyBudget);
}
/***********************************************************************************************
* PhysicsSceneClass::Render -- Render the scene *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Customized_Render(RenderInfoClass & rinfo)
{
WWPROFILE("PhysicsSceneClass::Render");
/*
** Render the normal models
*/
Render_Objects(rinfo,&VisibleWSMeshList,&VisibleStaticObjectList,&VisibleDynamicObjectList);
/*
** Backface Debug rendering
*/
#ifdef WWDEBUG
Render_Backface_Occluders(rinfo,&VisibleWSMeshList,&VisibleStaticObjectList);
#endif
/*
** Debug widget rendering
*/
{
static LightEnvironmentClass _defaultlightenvironment;
_defaultlightenvironment.Reset(Vector3(0,0,0),Get_Ambient_Light());
_defaultlightenvironment.Add_Light(*SunLight);
rinfo.light_environment = &_defaultlightenvironment;
Render_Debug_Widgets(rinfo);
Reset_Debug_Widget_List();
rinfo.light_environment = NULL;
}
if (Pathfinder != NULL) {
Pathfinder->Render_Debug_Widgets(rinfo);
}
/*
** Vis Sector Debugging
*/
if (VisSectorDisplayEnabled || VisSectorHistoryEnabled) {
// list of previous vis sectors so we can render them
static StaticPhysClass * old_vis_sectors[3] = { NULL, NULL, NULL };
static int old_vis_index = 0;
Vector3 vis_sample_point;
Compute_Vis_Sample_Point(rinfo.Camera,&vis_sample_point);
StaticPhysClass * vis_sector = StaticCullingSystem->Find_Vis_Tile(vis_sample_point);
if (vis_sector != NULL) {
MaterialPassClass * matpass = PhysResourceMgrClass::Get_Highlight_Material_Pass();
if (matpass) {
// flush all rendering, set wireframe render mode
WW3D::Flush(rinfo);
DX8Wrapper::Set_DX8_Render_State(D3DRS_FILLMODE,D3DFILL_WIREFRAME);
// set wireframe mode and draw the vis sector
rinfo.Push_Material_Pass(matpass);
rinfo.Push_Override_Flags(RenderInfoClass::RINFO_OVERRIDE_ADDITIONAL_PASSES_ONLY);
if (VisSectorHistoryEnabled) {
// render the previous vis sectors
for (int i=0; i<3; i++) {
if (old_vis_sectors[i] != NULL) {
old_vis_sectors[i]->Render_Vis_Meshes(rinfo);
}
}
// add this one to the history if it is new
int prev_index = (old_vis_index + 2) % 3;
if (old_vis_sectors[prev_index] != vis_sector) {
old_vis_sectors[old_vis_index] = vis_sector;
old_vis_index = (old_vis_index + 1) % 3;
}
} else {
// Just draw the current vis sector
vis_sector->Render_Vis_Meshes(rinfo);
}
WW3D::Flush(rinfo);
rinfo.Pop_Material_Pass();
rinfo.Pop_Override_Flags();
// restore previous render mode
switch(Get_Polygon_Mode()) {
case POINT:
DX8Wrapper::Set_DX8_Render_State(D3DRS_FILLMODE,D3DFILL_POINT);
break;
case LINE:
DX8Wrapper::Set_DX8_Render_State(D3DRS_FILLMODE,D3DFILL_WIREFRAME);
break;
case FILL:
DX8Wrapper::Set_DX8_Render_State(D3DRS_FILLMODE,D3DFILL_SOLID);
break;
}
// release resources
REF_PTR_RELEASE(matpass);
}
}
}
/*
** Dynamic Vis, Virtual Occludee Debugging
*/
if (VisGridDisplayMode == VIS_GRID_DISPLAY_ACTUAL_BOXES) {
VisTableClass * pvs = Get_Vis_Table_For_Rendering(rinfo.Camera);
if ((pvs != NULL) && (DynamicObjVisSystem != NULL)) {
DynamicObjVisSystem->Render_Visible_Cells(rinfo,pvs,DynamicAABTreeCullClass::DISPLAY_ACTUAL_BOXES);
}
REF_PTR_RELEASE(pvs);
}
/*
** Light Source Debugging, render debug widgets at each light transform
*/
static bool ACTIVE_ONLY = true;
if (LightingDebugDisplayEnabled) {
StaticLightingSystem->Reset_Collection();
StaticLightingSystem->Collect_Objects(rinfo.Camera.Get_Frustum());
LightPhysClass * light = StaticLightingSystem->Peek_First_Collected_Object();
while (light != NULL) {
if ((!light->Is_Disabled()) || (!ACTIVE_ONLY)) {
DEBUG_RENDER_AXES(light->Get_Transform(),Vector3(1.0f,1.0f,1.0f));
}
light = StaticLightingSystem->Peek_Next_Collected_Object(light);
}
}
}
/***********************************************************************************************
* PhysicsSceneClass::Render_Objects -- Render the visible objects *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/24/2001 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Render_Objects(
RenderInfoClass& rinfo,
RefPhysListClass * static_ws_list,
RefPhysListClass * static_list,
RefPhysListClass * dyn_list)
{
WWPROFILE("Render_Meshes");
RefPhysListIterator it(static_ws_list);
if (WW3D::Get_Mesh_Draw_Mode()!=WW3D::MESH_DRAW_MODE_NONE) {
if (Is_Backface_Occluder_Debug_Enabled()) {
// render the static world-space meshes
{
WWPROFILE("world-space meshes");
for (it.First(static_ws_list); !it.Is_Done(); it.Next()) {
// Only render occluders when backface debug is on
if (it.Peek_Obj()->As_StaticPhysClass()->Is_Occluder() != 0) {
Render_Object(rinfo,it.Peek_Obj());
}
}
}
// render the other static objects
{
WWPROFILE("static objects");
for (it.First(static_list); !it.Is_Done(); it.Next()) {
// Only render occluders when backface debug is on
if (it.Peek_Obj()->As_StaticPhysClass()->Is_Occluder() != 0) {
Render_Object(rinfo,it.Peek_Obj());
}
}
}
} else {
// render the static world-space meshes
{
WWPROFILE("world-space meshes");
for (it.First(static_ws_list); !it.Is_Done(); it.Next()) {
Render_Object(rinfo,it.Peek_Obj());
}
}
// render the other static objects
{
WWPROFILE("static objects");
for (it.First(static_list); !it.Is_Done(); it.Next()) {
Render_Object(rinfo,it.Peek_Obj());
}
}
}
}
// render the dynamic objects
// render dynamic objects even if the render type is "NONE"
{
WWPROFILE("dynamic objects");
for (it.First(dyn_list); !it.Is_Done(); it.Next()) {
Render_Object(rinfo,it.Peek_Obj());
}
}
}
/***********************************************************************************************
* PhysicsSceneClass::Render_Object -- Render an individual object *
* *
* This function installs the lighting environment and renders the object. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/24/2001 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Render_Object(RenderInfoClass & context,PhysClass * obj)
{
if ((obj->Peek_Model() == NULL) || (obj->Is_Rendering_Disabled())) {
return;
}
/*
** Set up the lighting environment for this object
*/
bool do_lighting = ( (obj->Is_Pre_Lit() == false) &&
(obj->Peek_Model()->Is_Not_Hidden_At_All()) );
if (do_lighting) {
WWPROFILE("setup lights");
LightEnvironmentClass & light_env = *(obj->Get_Static_Lighting_Environment());
light_env.Pre_Render_Update(context.Camera.Get_Transform());
/*
** If lighting debugging is enabled, display a vector to each light source
*/
#if WWDEBUG
Vector3 pos = obj->Peek_Model()->Get_Bounding_Box().Center;
if (LightingDebugDisplayEnabled) {
if ((pos - context.Camera.Get_Position()).Length2() < 30.0f * 30.0f) {
for (int i=0; iRender(context);
}
/*
** Remove the lighting environment
*/
if (do_lighting) {
context.light_environment = NULL;
}
}
/***********************************************************************************************
* PhysicsSceneClass::Render_Backface_Occluders -- Render backfaces of all occluders *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/24/2001 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Render_Backface_Occluders
(
RenderInfoClass & context,
RefPhysListClass * static_ws_list,
RefPhysListClass * static_list
)
{
static LightEnvironmentClass lenv;
if (BackfaceDebugEnabled) {
MaterialPassClass * matpass = PhysResourceMgrClass::Get_Highlight_Material_Pass();
if (matpass != NULL) {
/*
** Flush the system and invert the backface culling check
*/
WW3D::Flush(context);
ShaderClass::Invert_Backface_Culling(true);
/*
** Set up the render context to render everything bright green
*/
ShaderClass shader = matpass->Peek_Shader();
shader.Set_Depth_Compare(ShaderClass::PASS_LESS);
matpass->Set_Shader(shader);
context.Push_Material_Pass(matpass);
context.Push_Override_Flags(RenderInfoClass::RINFO_OVERRIDE_ADDITIONAL_PASSES_ONLY);
lenv.Reset(Vector3(0,0,0),Vector3(1,1,1));
context.light_environment = &lenv;
/*
** Render the world-space mesh that are occluders
*/
RefPhysListIterator it(static_ws_list);
while (!it.Is_Done()) {
StaticPhysClass * sphys = it.Peek_Obj()->As_StaticPhysClass();
if (sphys && sphys->Is_Occluder()) {
sphys->Render(context);
}
it.Next();
}
/*
** Render the static objects that are occluders
*/
it.First(static_list);
while (!it.Is_Done()) {
StaticPhysClass * sphys = it.Peek_Obj()->As_StaticPhysClass();
if (sphys && sphys->Is_Occluder()) {
sphys->Render(context);
}
it.Next();
}
/*
** Flush all rendering and restore the normal backface culling state
*/
WW3D::Flush(context);
ShaderClass::Invert_Backface_Culling(false);
context.Pop_Material_Pass();
context.Pop_Override_Flags();
REF_PTR_RELEASE(matpass);
}
}
}
/***********************************************************************************************
* PhysicsSceneClass::Re_Partition_Static_Objects -- partition the static objects *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* This should be done in the editor. VIS data is invalidated when you do this... *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Re_Partition_Static_Objects(void)
{
StaticCullingSystem->Re_Partition();
}
/***********************************************************************************************
* PhysicsSceneClass::Re_Partition_Static_Lights -- partition the static lights *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* This should be done in the editor. VIS data is invalidated when you do this... *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Re_Partition_Static_Lights(void)
{
StaticLightingSystem->Re_Partition();
}
/***********************************************************************************************
* PhysicsSceneClass::Re_Partition_Dynamic_Culling_System -- partition the dynamic culling sys *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* This should be done in the editor. VIS data is invalidated when you do this... *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Re_Partition_Dynamic_Culling_System(void)
{
Vector3 wmin,wmax;
Get_Level_Extents(wmin,wmax);
DynamicCullingSystem->Re_Partition( wmin,wmax,
MAX_DYNAMIC_OBJ_RADIUS );
DynamicObjVisSystem->Re_Partition( NULL,
wmin,wmax,
MIN_GRID_CELL_SIZE,
MAX_GRID_CELL_COUNT,
MAX_DYNAMIC_OBJ_RADIUS );
Reset_Vis();
}
/***********************************************************************************************
* PhysicsSceneClass::Re_Partition_Dynamic_Culling_System -- partition the dynamic culling sys *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* This should be done in the editor. VIS data is invalidated when you do this... *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Re_Partition_Dynamic_Culling_System(DynamicVectorClass & virtual_occludees)
{
Vector3 wmin,wmax;
Get_Level_Extents(wmin,wmax);
DynamicCullingSystem->Re_Partition( wmin,wmax,
MAX_DYNAMIC_OBJ_RADIUS );
AABoxClass bounds(virtual_occludees[0]);
for (int i=0; iRe_Partition( &virtual_occludees,
bounds.Center - bounds.Extent,
bounds.Center + bounds.Extent,
MIN_GRID_CELL_SIZE,
MAX_GRID_CELL_COUNT,
MAX_DYNAMIC_OBJ_RADIUS );
Reset_Vis();
}
/***********************************************************************************************
* PhysicsSceneClass::Re_Partition_Static_Projectors -- partition the static projectors *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Re_Partition_Static_Projectors(void)
{
StaticProjectorCullingSystem->Re_Partition();
}
/***********************************************************************************************
* PhysicsSceneClass::Update_Culling_System_Bounding_Boxes -- updates the cull systems *
* *
* This should be performed by the level editor when geometry may have changed but we do not *
* want to do a re-partition due to the loss of hierarchical VIS data. Basically, the editor *
* will call this every time it loads an LVL in case the user has changed the geometry. It *
* causes all bounding boxes in the static culling systems to be validated. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/3/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Update_Culling_System_Bounding_Boxes(void)
{
StaticCullingSystem->Update_Bounding_Boxes();
StaticLightingSystem->Update_Bounding_Boxes();
}
/***********************************************************************************************
* PhysicsSceneClass::Verify_Culling_Systems -- Verify the culling systems *
* *
* This function asks the culling systems to verify that all of thier bounding boxes *
* are properly constructed. I.e. they contain the objects they claim to contain, etc. *
* *
* INPUT: *
* error_report - will be filled in with a description of any errors encountered *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/28/2000 gth : Created. *
*=============================================================================================*/
bool PhysicsSceneClass::Verify_Culling_Systems(StringClass & set_error_report)
{
return StaticCullingSystem->Verify(set_error_report);
}
/***********************************************************************************************
* PhysicsSceneClass::Get_Level_Extents -- returns the bounds of the level *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Get_Level_Extents(Vector3 &min, Vector3 &max)
{
const AABoxClass & level_bounds = StaticCullingSystem->Get_Bounding_Box();
min = level_bounds.Center - level_bounds.Extent;
max = level_bounds.Center + level_bounds.Extent;
}
/***********************************************************************************************
* PhysicsSceneClass::Set_Polygon_Budgets -- set the budgets for the LOD system *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Set_Polygon_Budgets(int static_count,int dynamic_count)
{
StaticPolyBudget = static_count;
DynamicPolyBudget = dynamic_count;
}
/***********************************************************************************************
* PhysicsSceneClass::Get_Polygon_Budgets -- returns the budgets for the LOD system *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Get_Polygon_Budgets(int * static_count,int * dynamic_count)
{
*static_count = StaticPolyBudget;
*dynamic_count = DynamicPolyBudget;
}
/***********************************************************************************************
* PhysicsSceneClass::Per_Frame_Statistics_Update -- statistics tracking *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Per_Frame_Statistics_Update(void)
{
CurrentStats.FrameCount ++;
if (CurrentStats.FrameCount >= STATISTICS_FRAMES) {
/*
** Collect the culling system stats
*/
const GridCullSystemClass::StatsStruct & dyn_stats = DynamicCullingSystem->Get_Statistics();
const AABTreeCullSystemClass::StatsStruct & static_stats = StaticCullingSystem->Get_Statistics();
const AABTreeCullSystemClass::StatsStruct & light_stats = StaticLightingSystem->Get_Statistics();
CurrentStats.CullNodeCount = dyn_stats.NodeCount + static_stats.NodeCount + light_stats.NodeCount;
CurrentStats.CullNodesAccepted = dyn_stats.NodesAccepted + static_stats.NodesAccepted + light_stats.NodesAccepted;
CurrentStats.CullNodesTriviallyAccepted = dyn_stats.NodesTriviallyAccepted + static_stats.NodesTriviallyAccepted + light_stats.NodesTriviallyAccepted;
CurrentStats.CullNodesRejected = dyn_stats.NodesRejected + static_stats.NodesRejected + light_stats.NodesRejected;
DynamicCullingSystem->Reset_Statistics();
StaticCullingSystem->Reset_Statistics();
StaticLightingSystem->Reset_Statistics();
/*
** Copy over LastValidStats, reset
*/
LastValidStats = CurrentStats;
CurrentStats.Reset();
}
}
/***********************************************************************************************
* PhysicsSceneClass::Get_Statistics -- returns statistics *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
const PhysicsSceneClass::StatsStruct & PhysicsSceneClass::Get_Statistics(void)
{
return LastValidStats;
}
/***********************************************************************************************
* PhysicsSceneClass::Find_Static_Object -- Find static object with the given ID *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
StaticPhysClass * PhysicsSceneClass::Find_Static_Object( int instance_id )
{
RefPhysListIterator it(&StaticObjList);
for (it.First(); !it.Is_Done(); it.Next()) {
if ( (int)(it.Peek_Obj()->Get_ID()) == instance_id ) {
return (StaticPhysClass*)it.Peek_Obj();
}
}
return NULL;
}
/***********************************************************************************************
* PhysicsSceneClass::Shatter_Mesh -- Clips a mesh into pieces and scatters them *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/7/2000 gth : Created. *
*=============================================================================================*/
void PhysicsSceneClass::Shatter_Mesh
(
MeshClass * mesh,
const Vector3 & impact_point,
const Vector3 & impact_normal,
const Vector3 & impact_velocity
)
{
LookupTableClass * vel_table = LookupTableMgrClass::Get_Table("ShatterVel.tbl");
LookupTableClass * avel_table = LookupTableMgrClass::Get_Table("ShatterAVel.tbl");
/*
** Clip the mesh into fragments
*/
ShatterSystem::Shatter_Mesh(mesh,impact_point,impact_normal);
/*
** Wake up any dynamic objects in the area
*/
if (mesh->Get_Collision_Type() & COLLISION_TYPE_PHYSICAL) {
AABoxClass bounds = mesh->Get_Bounding_Box();
bounds.Extent *= 1.05f;
Force_Dynamic_Objects_Awake(bounds);
}
/*
** Disable the original mesh so that it doesn't shatter repeatedly
*/
mesh->Set_Hidden(true);
mesh->Set_Collision_Type(0);
/*
** Now create new auto-destructing projectiles for each fragment
*/
for (int i=0; iGet_Transform();
frag->Set_Model(frag_model);
frag->Set_Transform(frag_tm);
frag->Set_Orientation_Mode_Tumbling();
frag->Set_Lifetime(3.0f);
frag->Set_Gravity_Multiplier(2.0f);
frag->Set_Bounce_Count(1);
/*
** Vector from the impact to the center of the fragment is
** used in generating the initial velocity and rotation of the fragment
*/
Vector3 dc = frag_tm.Get_Translation() - impact_point;
float dclen = dc.Length();
dc /= dclen;
/*
** Generate a suitable velocity for the fragment
*/
Vector3 frag_vel = impact_velocity;
frag_vel.Normalize();
if (rand() & 0x00000100) {
frag_vel *= -1.0f;
}
frag_vel += 0.2f*dc;
frag_vel *= vel_table->Get_Value(dclen);
frag->Set_Velocity(frag_vel);
/*
** Generate a rotation for the fragment. The axis it rotates about
** will be in the plane of the impact, perpendicular to a vector from
** the impact to the CM
*/
Vector3 axis;
Vector3::Cross_Product(impact_velocity,dc,&axis);
frag->Set_Orientation_Mode_Tumbling(axis,avel_table->Get_Value(dclen));
/*
** We cannot allow these object to get saved since their model was
** procedurally generated and it will not get loaded...
*/
frag->Enable_Dont_Save(true);
/*
** Debugging, Stop the velocity, set life to infinite, move along initial velocity to offset pieces
*/
#if (SHATTER_DEBUG)
Matrix3D tm = frag->Get_Transform();
Vector3 pos,vel;
tm.Get_Translation(&pos);
frag->Get_Velocity(&vel);
tm.Set_Translation(pos+vel*0.1f);
frag->Set_Transform(tm);
frag->Set_Velocity(Vector3(0,0,0));
frag->Set_Lifetime(60.0f);
frag->Set_Gravity_Multiplier(0.0f);
frag->Set_Bounce_Count(100);
frag->Set_Orientation_Mode_Fixed();
#endif
/*
** Add the fragment to the scene
*/
Add_Dynamic_Object(frag);
REF_PTR_RELEASE(frag);
}
/*
** Done, have the shatter system release the fragments
*/
ShatterSystem::Release_Fragments();
vel_table->Release_Ref();
avel_table->Release_Ref();
}
void PhysicsSceneClass::Add_Camera_Shake
(
const Vector3 & position,
float radius,
float duration,
float power
)
{
WWASSERT(CameraShakeSystem != NULL);
CameraShakeSystem->Add_Camera_Shake(position,radius,duration,power);
}
void PhysicsSceneClass::Apply_Camera_Shakes(CameraClass & camera)
{
CameraShakeSystem->Update_Camera(camera);
}
float PhysicsSceneClass::Compute_Vis_Mesh_Ram(void)
{
float total = 0.0f;
RefPhysListIterator it(&StaticObjList);
for (it.First(); !it.Is_Done(); it.Next()) {
total += ((StaticPhysClass *)it.Peek_Obj())->Compute_Vis_Mesh_Ram();
}
return total;
}
void PhysicsSceneClass::Force_Dynamic_Objects_Awake(const AABoxClass & box)
{
/*
** Collect the objects that touch this box and wake them up.
*/
NonRefPhysListClass list;
Collect_Objects(box,false,true,&list);
NonRefPhysListIterator it(&list);
while (!it.Is_Done()) {
PhysClass * obj = it.Peek_Obj();
obj->Force_Awake();
if (obj->As_Phys3Class() != NULL) {
obj->As_Phys3Class()->Invalidate_Ground_State();
}
it.Next();
}
}
/******************************************************************************************
**
**
** PhysicsSceneClass::StatsStruct Implementation
**
**
******************************************************************************************/
PhysicsSceneClass::StatsStruct::StatsStruct(void)
{
Reset();
}
void PhysicsSceneClass::StatsStruct::Reset(void)
{
FrameCount = 0;
CullNodeCount = 0;
CullNodesAccepted = 0;
CullNodesTriviallyAccepted = 0;
CullNodesRejected = 0;
}
/*
** Force-Link relevant modules from WWPhys
*/
void Force_Link_Modules(void)
{
FORCE_LINK(decophys);
FORCE_LINK(humanphys);
FORCE_LINK(lightphys);
FORCE_LINK(motorcycle);
FORCE_LINK(movephys);
FORCE_LINK(phys3);
FORCE_LINK(rbody);
FORCE_LINK(staticphys);
FORCE_LINK(staticanimphys);
FORCE_LINK(wheelvehicle);
FORCE_LINK(projectile);
FORCE_LINK(timeddecophys);
FORCE_LINK(trackedvehicle);
FORCE_LINK(vtolvehicle);
FORCE_LINK(waypath);
FORCE_LINK(waypoint);
FORCE_LINK(dynamicanimphys);
FORCE_LINK(shakeablestaticphys);
FORCE_LINK(RenegadeTerrainPatch);
}