This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/ww3d2/scene.cpp

662 lines
34 KiB
C++

/*
** 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 : WW3D *
* *
* $Archive:: /VSS_Sync/ww3d2/scene.cpp $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 8/29/01 7:29p $*
* *
* $Revision:: 22 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* SceneClass::SceneClass -- constructor *
* SceneClass::~SceneClass -- destructor *
* SceneClass::Render -- preps the scene for rendering, derived classes should override *
* SceneClass::Add_Render_Object -- base add function *
* SceneClass::Remove_Render_Object -- base remove function *
* SceneClass::Set_Depth_Cue -- set the depth cue values *
* SceneClass::Set_Fog_Range -- set the fog range *
* SceneClass::Get_Fog_Range -- get the fog range *
* SceneClass::Render -- preps the scene for rendering, derived classes should add functional*
* SimpleSceneClass -- Constructor *
* SimpleSceneClass::~SimpleSceneClass -- destructor *
* SimpleSceneClass::Add_Render_Object -- add a render object to the scene *
* SimpleSceneClass::Remove_Render_Object -- remove a render object from this scene *
* SimpleSceneClass::Visiblity_Check -- set the visiblity status of the render objects *
* SimpleSceneClass::Render -- internal scene rendering function *
* SimpleSceneClass::Render -- Render this scene *
* SimpleSceneClass::Create_Iterator -- create an iterator for this scene *
* SimpleSceneClass::Destroy_Iterator -- destroy an iterater of this scene *
* SceneClass::Save -- saves scene settings into a chunk *
* SceneClass::Load -- loads scene settings from a chunk *
* SimpleSceneClass::Compute_Point_Visibility -- returns visibility of a point *
* SimpleSceneClass::Remove_All_Render_Objects -- Removes all render objects from the scene *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "scene.h"
#include "plane.h"
#include "camera.h"
#include "ww3d.h"
#include "rinfo.h"
#include "chunkio.h"
#include "dx8renderer.h"
#include "dx8wrapper.h"
#include "sortingrenderer.h"
#include "coltest.h"
/*
** Chunk ID's used by SceneClass
*/
enum
{
SCENECLASS_CHUNK_VARIABLES = 0x00042300,
SCENECLASS_VARIABLE_AMBIENTLIGHT = 0x00,
SCENECLASS_VARIABLE_POLYRENDERMODE,
SCENECLASS_VARIABLE_FOGCOLOR,
SCENECLASS_VARIABLE_FOGENABLED,
SCENECLASS_VARIABLE_FOGSTART,
SCENECLASS_VARIABLE_FOGEND,
};
/*
** SimpleSceneIterator
** This iterator is used by the SimpleSceneClass to allow
** the user to iterate through its render objects.
*/
class SimpleSceneIterator : public SceneIterator
{
public:
virtual void First(void);
virtual void Next(void);
virtual bool Is_Done(void);
virtual RenderObjClass * Current_Item(void);
protected:
SimpleSceneIterator(RefRenderObjListClass * renderlist,bool onlyvis);
RefRenderObjListIterator RobjIterator;
SimpleSceneClass * Scene;
bool OnlyVis;
friend class SimpleSceneClass;
};
/***********************************************************************************************
* SceneClass::SceneClass -- constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/10/98 GTH : Created. *
*=============================================================================================*/
SceneClass::SceneClass(void) :
AmbientLight(0.5f,0.5f,0.5f),
PolyRenderMode(FILL),
ExtraPassPolyRenderMode(EXTRA_PASS_DISABLE),
FogEnabled(false),
FogColor(0,0,0),
FogStart(0.0f),
FogEnd(1000.0f) // Arbitrary default value
{
}
/***********************************************************************************************
* SceneClass::~SceneClass -- destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/10/98 GTH : Created. *
*=============================================================================================*/
SceneClass::~SceneClass(void)
{
}
/***********************************************************************************************
* SceneClass::Add_Render_Object -- base add function *
* *
* This function only sets the render object's scene pointer and calls the notify method. *
* Derived classes must add the functionality of actually storing a pointer to the object *
* and doing something with it :) *
* *
* INPUT: *
* obj - pointer to the object to add *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/25/99 GTH : Created. *
*=============================================================================================*/
void SceneClass::Add_Render_Object(RenderObjClass * obj)
{
obj->Notify_Added(this);
}
/***********************************************************************************************
* SceneClass::Remove_Render_Object -- base remove function *
* *
* Similar to the add function; concrete derived classes must override and actually remove *
* the object... *
* *
* INPUT: *
* obj - pointer to the object to remove *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/25/99 GTH : Created. *
*=============================================================================================*/
void SceneClass::Remove_Render_Object(RenderObjClass * obj)
{
obj->Notify_Removed(this);
}
/***********************************************************************************************
* SceneClass::Render -- preps the scene for rendering, derived classes should add functionalit*
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/10/98 GTH : Created. *
*=============================================================================================*/
void SceneClass::Render(RenderInfoClass & rinfo)
{
DX8Wrapper::Set_Fog(FogEnabled, FogColor, FogStart, FogEnd);
if (Get_Extra_Pass_Polygon_Mode()==EXTRA_PASS_DISABLE) {
Customized_Render(rinfo);
}
else {
bool old_enable=WW3D::Is_Texturing_Enabled();
DX8Wrapper::Set_DX8_Render_State (D3DRS_ZBIAS, 0);
Customized_Render(rinfo);
switch (Get_Extra_Pass_Polygon_Mode()) {
case EXTRA_PASS_LINE:
WW3D::Enable_Texturing(false);
DX8Wrapper::Set_DX8_Render_State(D3DRS_FILLMODE,D3DFILL_WIREFRAME);
DX8Wrapper::Set_DX8_Render_State (D3DRS_ZBIAS, 7);
Customized_Render(rinfo);
break;
case EXTRA_PASS_CLEAR_LINE:
DX8Wrapper::Clear(true, false, Vector3(0.0f,0.0f,0.0f)); // Clear color but not z
WW3D::Enable_Texturing(false);
DX8Wrapper::Set_DX8_Render_State(D3DRS_FILLMODE,D3DFILL_WIREFRAME);
DX8Wrapper::Set_DX8_Render_State (D3DRS_ZBIAS, 7);
Customized_Render(rinfo);
break;
}
WW3D::Enable_Texturing(old_enable);
}
}
/***********************************************************************************************
* SceneClass::Save -- saves scene settings into a chunk *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*=============================================================================================*/
void SceneClass::Save(ChunkSaveClass & csave)
{
csave.Begin_Chunk(SCENECLASS_CHUNK_VARIABLES);
WRITE_MICRO_CHUNK(csave,SCENECLASS_VARIABLE_AMBIENTLIGHT,AmbientLight);
WRITE_MICRO_CHUNK(csave,SCENECLASS_VARIABLE_POLYRENDERMODE,PolyRenderMode);
WRITE_MICRO_CHUNK(csave,SCENECLASS_VARIABLE_FOGCOLOR,FogColor);
WRITE_MICRO_CHUNK(csave,SCENECLASS_VARIABLE_FOGENABLED,FogEnabled);
WRITE_MICRO_CHUNK(csave,SCENECLASS_VARIABLE_FOGSTART,FogStart);
WRITE_MICRO_CHUNK(csave,SCENECLASS_VARIABLE_FOGEND,FogEnd);
csave.End_Chunk();
}
/***********************************************************************************************
* SceneClass::Load -- loads scene settings from a chunk *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*=============================================================================================*/
void SceneClass::Load(ChunkLoadClass & cload)
{
cload.Open_Chunk();
if (cload.Cur_Chunk_ID() == SCENECLASS_CHUNK_VARIABLES) {
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK(cload,SCENECLASS_VARIABLE_AMBIENTLIGHT,AmbientLight);
READ_MICRO_CHUNK(cload,SCENECLASS_VARIABLE_POLYRENDERMODE,PolyRenderMode);
READ_MICRO_CHUNK(cload,SCENECLASS_VARIABLE_FOGCOLOR,FogColor);
READ_MICRO_CHUNK(cload,SCENECLASS_VARIABLE_FOGENABLED,FogEnabled);
READ_MICRO_CHUNK(cload,SCENECLASS_VARIABLE_FOGSTART,FogStart);
READ_MICRO_CHUNK(cload,SCENECLASS_VARIABLE_FOGEND,FogEnd);
}
cload.Close_Micro_Chunk();
}
} else {
WWDEBUG_SAY(("Unhandled Chunk: 0x%X in file: %s line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
}
cload.Close_Chunk();
}
/***********************************************************************************************
* SimpleSceneClass -- Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/24/98 GTH : Created. *
* 9/10/99 GTH : Created. *
*=============================================================================================*/
SimpleSceneClass::SimpleSceneClass(void) :
Visibility_Checked(false)
{
}
/***********************************************************************************************
* SimpleSceneClass::~SimpleSceneClass -- destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/24/98 GTH : Created. *
*=============================================================================================*/
SimpleSceneClass::~SimpleSceneClass(void)
{
Remove_All_Render_Objects();
}
/***********************************************************************************************
* SimpleSceneClass::Remove_All_Render_Objects -- Removes all render objects from the scene *
* *
* *
* *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/27/2001 hy : Created. *
*=============================================================================================*/
void SimpleSceneClass::Remove_All_Render_Objects(void)
{
RenderObjClass * obj;
while ( ( obj = RenderList.Remove_Head() ) != NULL ) {
SceneClass::Remove_Render_Object(obj);
obj->Release_Ref(); // remove head gets a ref
}
}
/***********************************************************************************************
* SimpleSceneClass::Add_Render_Object -- add a render object to the scene *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/24/98 GTH : Created. *
*=============================================================================================*/
void SimpleSceneClass::Add_Render_Object(RenderObjClass * obj)
{
SceneClass::Add_Render_Object(obj);
RenderList.Add(obj);
}
/***********************************************************************************************
* SimpleSceneClass::Remove_Render_Object -- remove a render object from this scene *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/24/98 GTH : Created. *
*=============================================================================================*/
void SimpleSceneClass::Remove_Render_Object(RenderObjClass * obj)
{
SceneClass::Remove_Render_Object(obj);
// HY
// this line must come last because otherwise it might cause
// a premature release ref and cause a crash
RenderList.Remove(obj);
}
void SimpleSceneClass::Register(RenderObjClass * obj,RegType for_what)
{
switch (for_what) {
case ON_FRAME_UPDATE:
UpdateList.Add(obj);
break;
case LIGHT:
LightList.Add(obj);
break;
case RELEASE:
ReleaseList.Add(obj);
break;
};
}
void SimpleSceneClass::Unregister(RenderObjClass * obj,RegType for_what)
{
switch (for_what) {
case ON_FRAME_UPDATE:
UpdateList.Remove(obj);
break;
case LIGHT:
LightList.Remove(obj);
break;
case RELEASE:
ReleaseList.Remove(obj);
break;
}
}
/***********************************************************************************************
* SimpleSceneClass::Visiblity_Check -- set the visiblity status of the render objects *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/24/98 GTH : Created. *
* 4/13/98 NH : Added non-trivial checking (sphere vs. frustum). *
*=============================================================================================*/
void SimpleSceneClass::Visibility_Check(CameraClass * camera)
{
RefRenderObjListIterator it(&RenderList);
// Loop over all top-level RenderObjects in this scene. If the bounding sphere is not in front
// of all the frustum planes, it is invisible.
for (it.First(); !it.Is_Done(); it.Next()) {
RenderObjClass * robj = it.Peek_Obj();
if (robj->Is_Force_Visible()) {
robj->Set_Visible(true);
} else {
robj->Set_Visible(!camera->Cull_Sphere(robj->Get_Bounding_Sphere()));
}
// Prepare visible objects for LOD:
if (robj->Is_Really_Visible()) {
robj->Prepare_LOD(*camera);
}
}
Visibility_Checked = true;
}
/***********************************************************************************************
* SimpleSceneClass::Compute_Point_Visibility -- returns visibility of a point *
* *
* This function is used by the default dazzle behavior. SimpleSceneClass's implementation *
* casts a ray against every object in the scene to see if the dazzle is occluded. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/13/2001 gth : Created. *
*=============================================================================================*/
float SimpleSceneClass::Compute_Point_Visibility
(
RenderInfoClass & rinfo,
const Vector3 & point
)
{
CastResultStruct res;
LineSegClass ray(rinfo.Camera.Get_Position(),point);
RayCollisionTestClass raytest(ray,&res,COLLISION_TYPE_PROJECTILE);
RefRenderObjListIterator it(&RenderList);
for (it.First(); !it.Is_Done(); it.Next()) {
RenderObjClass * robj = it.Peek_Obj();
robj->Cast_Ray(raytest);
}
if (res.Fraction == 1.0f) {
return 1.0f;
} else {
return 0.0f;
}
}
/***********************************************************************************************
* SimpleSceneClass::Render -- Render this scene *
* *
* passes the gerd and camera to all render objects for rendering... *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/10/98 GTH : Created. *
*=============================================================================================*/
void SimpleSceneClass::Customized_Render(RenderInfoClass & rinfo)
{
// SceneClass::Render(rinfo);
// If visibility has not been checked for this scene since the last
// Render() call, check it (set/clear the visibility bit in all render
// objects in the scene).
if (!Visibility_Checked) {
// set the visibility bit in all render objects in all layers.
Visibility_Check(&rinfo.Camera);
}
Visibility_Checked = false;
RefRenderObjListIterator it(&UpdateList);
// allow all objects in the update list to do their "every frame" processing
for (it.First(); !it.Is_Done(); it.Next()) {
it.Peek_Obj()->On_Frame_Update();
}
// apply only the first four lights in the scene
// derived classes should use light environment
WWASSERT(rinfo.light_environment==NULL);
int count=0;
// Turn off lights in case we have none
DX8Wrapper::Set_Light(0,NULL);
DX8Wrapper::Set_Light(1,NULL);
DX8Wrapper::Set_Light(2,NULL);
DX8Wrapper::Set_Light(3,NULL);
for (it.First(&LightList); !it.Is_Done(); it.Next())
{
if (count<4)
{
DX8Wrapper::Set_Light(count,*(LightClass*)it.Peek_Obj());
} else
{
// Simple scene only supports 4 global lights
WWDEBUG_SAY(("Light %d ignored\n",count));
}
count++;
}
// loop through all render objects in the list:
for (it.First(&RenderList); !it.Is_Done(); it.Next()) {
// get the render object
RenderObjClass * robj = it.Peek_Obj();
if (robj->Is_Really_Visible()) {
robj->Render(rinfo);
}
}
}
void SimpleSceneClass::Post_Render_Processing(RenderInfoClass& rinfo)
{
// process the 'Release' list. These are objects that have notified us that they
// want to be released. We have to walk this list twice, first un-linking the
// object from the scene or its container. And then removing them all from
// the list. (this last removal will destroy any auto-created objects)
RefRenderObjListIterator it(&ReleaseList);
for (it.First(&ReleaseList); !it.Is_Done(); it.Next()) {
RenderObjClass * robj = it.Peek_Obj();
if (robj->Get_Container()) {
robj->Get_Container()->Remove_Sub_Object(robj);
} else {
robj->Remove();
}
}
while(!ReleaseList.Is_Empty()) {
ReleaseList.Release_Head();
}
}
/***********************************************************************************************
* SimpleSceneClass::Create_Iterator -- create an iterator for this scene *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/27/98 GTH : Created. *
*=============================================================================================*/
SceneIterator * SimpleSceneClass::Create_Iterator(bool onlyvisible)
{
SimpleSceneIterator * it = new SimpleSceneIterator(&RenderList,onlyvisible);
return it;
}
/***********************************************************************************************
* SimpleSceneClass::Destroy_Iterator -- destroy an iterater of this scene *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/27/98 GTH : Created. *
*=============================================================================================*/
void SimpleSceneClass::Destroy_Iterator(SceneIterator * it)
{
delete it;
}
SimpleSceneIterator::SimpleSceneIterator(RefRenderObjListClass * list,bool onlyvis) :
RobjIterator(list)
{
// TODO: make SimpleSceneIterator able to iterate through only the visible nodes.
OnlyVis = onlyvis;
}
void SimpleSceneIterator::First(void)
{
RobjIterator.First();
}
void SimpleSceneIterator::Next(void)
{
RobjIterator.Next();
}
bool SimpleSceneIterator::Is_Done(void)
{
return RobjIterator.Is_Done();
}
RenderObjClass * SimpleSceneIterator::Current_Item(void)
{
return RobjIterator.Peek_Obj();
}