/*
**	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 <http://www.gnu.org/licenses/>.
*/

/***********************************************************************************************
 ***              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<AABoxClass> * 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