/*
** 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/dynamicaabtreecull.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 7/24/01 1:26p $*
* *
* $Revision:: 7 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef DYNAMICAABTREECULL_H
#define DYNAMICAABTREECULL_H
#include "always.h"
#include "physaabtreecull.h"
#include "physlist.h"
class VisTableClass;
class VisRenderContextClass;
class VisOptimizationContextClass;
class AABoxRenderObjClass;
/**
** DynamicAABTreeCullClass
** This is a specialized AABTree which is designed to be used with dynamic objects. It
** contains both a uniform voxelization of the level and special "seed" boxes which
** are generated from a pathfind floodfill. Whenever a dynamic object moves, it is
** re-inserted into the smallest leaf node in the tree. So this is basically an AAB-Tree
** that partitions the level paying special attention to the areas that you seed
** it with. All nodes in the tree are assigned a visiblity id and are tracked in the
** vis system.
**
** This AABTree also over-rides the Add_Object and Update_Culling function to behave differently.
** The tree is constructed with a set of boxes which cover all of the space in the level.
** These boxes are inflated by the MaxObjRadius parameter. When inserting objects into the
** tree we test their center-point against the non-inflated boxes and insert the object into
** the smallest leaf node that we can find.
*/
class DynamicAABTreeCullClass : public PhysAABTreeCullClass
{
public:
DynamicAABTreeCullClass(PhysicsSceneClass * pscene);
~DynamicAABTreeCullClass(void);
/*
** DynamicAABTreeCullClass over-rides Add_Object and Update_Culling because it
** uses custom logic for determining which cell an object should end up in.
*/
virtual void Add_Object(PhysClass * obj);
virtual void Update_Culling(CullableClass * obj);
/*
** Objects will request the visibility ID from this system.
** 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);
/*
** DynamicObjCullClass adds a new re-partitioning interface to AABTreeCullSystemClass
** This interface allows the user to supply the grid parameters and the virtual occludees
*/
void Re_Partition( DynamicVectorClass * virtual_occludees,
const Vector3 & grid_min,
const Vector3 & grid_max,
const Vector3 & min_grid_cell_size,
int max_grid_cell_count,
float max_obj_radius );
/*
** DynamicObjCullClass adds a new collect function which takes the pvs data
** into account. It also puts objects into one of two lists, separating
** world-space-meshes from the other objects.
*/
void Collect_Visible_Objects( const FrustumClass & frustum,
VisTableClass * pvs,
RefPhysListClass & visobjlist );
/*
** Debugging
** Render the visible leaf cells.
** Tree visualization, walk the tree, rendering all leaf cells of current node.
*/
enum DisplayModeType {
DISPLAY_NONE = 0,
DISPLAY_ACTUAL_BOXES,
DISPLAY_CENTERS,
DISPLAY_OCCUPIED,
};
void Render_Visible_Cells(RenderInfoClass & rinfo,VisTableClass * pvs,DisplayModeType mode);
void Debug_Reset_Node(void) { DebugIterator.Reset(); }
bool Debug_Enter_Parent(void) { return DebugIterator.Enter_Parent(); }
bool Debug_Enter_Sibling(void) { return DebugIterator.Enter_Sibling(); }
bool Debug_Enter_Front_Child(void) { return DebugIterator.Enter_Front_Child(); }
bool Debug_Enter_Back_Child(void) { return DebugIterator.Enter_Back_Child(); }
/*
** Visibility Preprocesing. For this culling system, visibility of all of the nodes is tracked
** while the objects inside the culling system are ignored because they are assumed to be
** dynamic. For this reason, there are no occluders in the dynamic object culling system.
*/
void Assign_Vis_IDs(void);
void Evaluate_Non_Occluder_Visibility(VisRenderContextClass & context);
void Prune_Redundant_Leaf_Nodes(VisOptimizationContextClass & context);
void Merge_Vis_Object_IDs(uint32 id0,uint32 id1);
/*
** Save/Load system.
*/
virtual void Save_Static_Data(ChunkSaveClass & csave);
virtual void Load_Static_Data(ChunkLoadClass & cload);
protected:
/*
** VisObjCollectContextClass - this object is passed into the recursive function that collects
** the visible objects each frame.
*/
class VisObjCollectContextClass
{
public:
VisObjCollectContextClass(const FrustumClass & frustum,VisTableClass & pvs,RefPhysListClass & visobjlist) :
Frustum(frustum),
PVS(pvs),
VisObjList(visobjlist),
PlanesPassed(0)
{
}
const FrustumClass & Frustum;
VisTableClass & PVS;
RefPhysListClass & VisObjList;
int PlanesPassed;
};
/*
** Internal functions
*/
void collect_visible_objects_recursive(AABTreeNodeClass * node,VisObjCollectContextClass & context);
void collect_visible_objects_no_hvis_recursive(AABTreeNodeClass * node,VisObjCollectContextClass & context);
void render_visible_cells_recursive(AABTreeNodeClass * node,RenderInfoClass & rinfo,VisTableClass * pvs,DisplayModeType mode);
void evaluate_non_occluder_visibility_recursive(AABTreeNodeClass * node,VisRenderContextClass & context);
bool subtree_is_visible(AABTreeNodeClass * node,VisRenderContextClass & context);
void set_tree_visibility(AABTreeNodeClass * node,VisRenderContextClass & context,bool onoff);
int find_insertion_node(CullableClass * obj);
void find_optimal_node(AABTreeNodeClass * node,const AABoxClass & box,int & node_index);
void find_optimal_deflated_node(AABTreeNodeClass * node,const Vector3 & center,int & node_index);
bool deflated_box_contains_point(const AABoxClass & box,const Vector3 & point);
void prune_redundant_leaf_nodes_recursive(AABTreeNodeClass * node,VisOptimizationContextClass & context);
void prune_child(AABTreeNodeClass * parent,AABTreeNodeClass * child,VisOptimizationContextClass & context);
AABoxRenderObjClass * get_render_box(void);
/*
** Members
*/
float MaxObjRadius; // Object "inflation" radius used when the tree was created.
AABoxRenderObjClass * RenderBox; // Used to render nodes when debugging
AABTreeIterator DebugIterator; // Used to walk the tree while debugging. Render_Visible.. starts from this node
/*
** Making Physics Scene a friend so that some of the more ugly vis-system interfaces
** can be accessed by it without exposing them to everyone else...
*/
friend class PhysicsSceneClass;
};
/*
** Inlines
*/
inline bool DynamicAABTreeCullClass::deflated_box_contains_point
(
const AABoxClass & box,
const Vector3 & point
)
{
if (WWMath::Fabs(point.X - box.Center.X) > box.Extent.X - MaxObjRadius) return false;
if (WWMath::Fabs(point.Y - box.Center.Y) > box.Extent.Y - MaxObjRadius) return false;
if (WWMath::Fabs(point.Z - box.Center.Z) > box.Extent.Z - MaxObjRadius) return false;
return true;
}
#endif //DYNAMICAABTREECULL_H