Files
GTASource/game/renderer/HorizonObjects.cpp
T
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

441 lines
14 KiB
C++

//
// game/renderer/HorizonObjects.cpp
//
// Copyright (C) 1999-2014 Rockstar Games. All Rights Reserved.
//
#include "HorizonObjects.h"
#include "grmodel/shader.h"
#include "grcore/allocscope.h"
#include "grcore/light.h"
#include "grcore/debugdraw.h"
#include "fwsys/gameskeleton.h"
#include "fwmaths/vectorutil.h"
#include "file/asset.h"
#include "streaming/streaming.h"
#include "vectormath/classfreefuncsv.h"
#include "renderer/GtaDrawable.h"
#include "renderer/Water.h"
#include "shaders/ShaderLib.h"
#if __BANK
#include "bank/widget.h"
#include "bank/bkmgr.h"
#include "debug/TiledScreenCapture.h"
#endif
RENDER_OPTIMISATIONS()
//OPTIMISATIONS_OFF()
CHorizonObjects g_HorizonObjects;
static const strStreamingObjectName HORIZON_OBJECTS_FILENAME("platform:/models/farlods",0xA1FA0AB3);
CHorizonObjects::CHorizonObjects() : m_bEnable(true), m_bCullEverything(false), m_pSilhoutteShader(NULL), m_pHorizonObjectsDictionary(NULL), m_fScaleFactor(10.0f), m_fDepthRangePercentage(0.032f), m_fFarPlane(25000.0f)
BANK_ONLY(, m_bDrawBounds(false), m_bSkipAABBTest(false), m_bDebugShader(false), m_iTotalObjects(0), m_iTotalVisibleObjects(0), m_iObjectSelection(-1), m_MemUsage(0), m_bSkipFarDistanceSearch(false),
m_bSortMapObjects(true), m_bSkipMapObjects(false), m_bSkipWaterObjects(false)
PS3_ONLY(, m_bDisableEdgeCulling(false)))
{
}
CHorizonObjects::~CHorizonObjects()
{
}
// Sort function for the models - sorts based on height (z component)
int CompareFunc(gtaDrawable* const* a, gtaDrawable* const* b)
{
Vector3 boxMin;
Vector3 boxMax;
float heightA;
(*a)->GetLodGroup().GetBoundingBox(boxMin, boxMax);
heightA = boxMax.z;
float heightB;
(*b)->GetLodGroup().GetBoundingBox(boxMin, boxMax);
heightB = boxMax.z;
return heightA > heightB ? 1 : (heightA < heightB ? -1 : 0);
}
void CHorizonObjects::Init(unsigned initMode)
{
#if !__FINAL
XPARAM(level);
const char* levelName = NULL;
if (PARAM_level.Get(levelName))
{
m_bEnable = false;
if (atStringHash(levelName) == atStringHash("gta5") || atStringHash(levelName) == atStringHash("gta5_liberty"))
{
m_bEnable = true;
}
}
#endif
if (initMode == INIT_CORE)
{
// Create model and setup shaders
ASSET.PushFolder("common:/shaders");
m_pSilhoutteShader = grmShaderFactory::GetInstance().Create();
m_pSilhoutteShader->Load("silhouettelayer");
ASSET.PopFolder();
m_SilhoutteDrawTech = m_pSilhoutteShader->LookupTechnique("draw", true);
m_SilhoutteDrawWaterReflectionTech = m_pSilhoutteShader->LookupTechnique("waterreflection_draw", true);
// Prepare the depth stencil state
grcDepthStencilStateDesc DSDesc;
DSDesc.StencilEnable = TRUE;
DSDesc.FrontFace.StencilPassOp = grcRSV::STENCILOP_REPLACE;
DSDesc.BackFace.StencilPassOp = grcRSV::STENCILOP_REPLACE;
DSDesc.FrontFace.StencilFunc = grcRSV::CMP_ALWAYS;
DSDesc.BackFace.StencilFunc = grcRSV::CMP_ALWAYS;
DSDesc.DepthWriteMask = true;
DSDesc.DepthFunc = grcRSV::CMP_LESSEQUAL;
m_DSS_Water = grcStateBlock::CreateDepthStencilState(DSDesc, "WaterHorizonObjectsDepthStencil");
DSDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESSEQUAL);
m_DSS_Scene = grcStateBlock::CreateDepthStencilState(DSDesc, "SceneHorizonObjectsDepthStencil");
// Load the models
BANK_ONLY(m_MemUsage = 0;)
strLocalIndex modelSlot = g_DwdStore.FindSlot(HORIZON_OBJECTS_FILENAME);
if(modelSlot.Get() >= 0)
{
bool modelLoaded = CStreaming::LoadObject(modelSlot, g_DwdStore.GetStreamingModuleId());
if (modelLoaded)
{
#if __BANK
strIndex streamIndex = strStreamingEngine::GetInfo().GetModuleMgr().GetModule(g_DwdStore.GetStreamingModuleId())->GetStreamingIndex(modelSlot);
strStreamingInfo& info = *strStreamingEngine::GetInfo().GetStreamingInfo(streamIndex);
m_MemUsage = (info.ComputePhysicalSize(streamIndex, true) + info.ComputeVirtualSize(streamIndex, true))/1024;
#endif // __BANK
g_DwdStore.AddRef(modelSlot, REF_OTHER);
m_pHorizonObjectsDictionary = g_DwdStore.Get(modelSlot);
Assert(m_pHorizonObjectsDictionary);
m_arrDrawables.Reserve(m_pHorizonObjectsDictionary->GetCount());
for(int iCurModel = 0; iCurModel < m_pHorizonObjectsDictionary->GetCount(); iCurModel++)
{
gtaDrawable* pDrawable = static_cast<gtaDrawable*>(m_pHorizonObjectsDictionary->GetEntry(iCurModel));
if(pDrawable)
{
m_arrDrawables.Append() = pDrawable;
}
}
BANK_ONLY(m_iTotalObjects = m_arrDrawables.GetCount());
Assertf(m_arrDrawables.GetCount() == m_pHorizonObjectsDictionary->GetCount(), "Horizon Objects ouldn't load all the models");
m_arrVisible.Reserve(m_arrDrawables.GetCount());
// Sort the models based on their height to avoid water getting rendered on top of terrain
m_arrDrawables.QSort(0, -1, CompareFunc);
}
}
}
}
void CHorizonObjects::Shutdown(unsigned shutdownMode)
{
if (shutdownMode == INIT_CORE)
{
if(m_pSilhoutteShader)
{
// Delete shader
delete m_pSilhoutteShader;
m_pSilhoutteShader = NULL;
// Delete drawables
for(int iCurDrawable = 0; iCurDrawable < m_arrDrawables.GetCount(); iCurDrawable++)
{
if (m_arrDrawables[iCurDrawable] != NULL)
{
delete m_arrDrawables[iCurDrawable];
}
}
m_arrDrawables.clear();
// Delete ref and dictionary
if(m_pHorizonObjectsDictionary)
{
g_DwdStore.RemoveRef(strLocalIndex(g_DwdStore.FindSlot(HORIZON_OBJECTS_FILENAME)), REF_OTHER);
m_pHorizonObjectsDictionary = NULL;
}
}
}
}
void CHorizonObjects::Render()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
g_HorizonObjects.RenderInternal(g_HorizonObjects.m_fDepthRangePercentage, true, false, -1, -1, -1, -1);
}
void CHorizonObjects::RenderEx(float depthRange, bool renderWater, bool waterReflections, int scissorX, int scissorY, int scissorW, int scissorH)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
g_HorizonObjects.RenderInternal(depthRange, renderWater, waterReflections, scissorX, scissorY, scissorW, scissorH);
}
void CHorizonObjects::CullEverything(bool bEnabled)
{
g_HorizonObjects.m_bCullEverything = bEnabled;
}
void CHorizonObjects::RenderInternal(float depthRange, bool renderWater, bool waterReflections, int scissorX, int scissorY, int scissorW, int scissorH)
{
GRC_ALLOC_SCOPE_AUTO_PUSH_POP()
// First figure which models are visible
m_arrVisible.clear();
if(!m_bEnable || m_bCullEverything BANK_ONLY(|| TiledScreenCapture::IsEnabled()))
{
return;
}
#if __BANK && __PS3
// This is just for testing - we never want to disable EDGE
bool edgeCullPrevEnabled;
if(m_bDisableEdgeCulling)
{
edgeCullPrevEnabled = grcEffect::SetEdgeViewportCullEnable(false);
}
#endif // __BANK && __PS3
// Prepare a scale matrix for the models
Mat34V worldMtx = Mat34V(V_IDENTITY);
Vec3V vScale = Vec3V(ScalarV(m_fScaleFactor));
Scale(worldMtx, vScale, worldMtx);
// Move the near plane just a little in front of where the far plane is during GBuffer stage
// This will cull all the triangles that are inside the GBuffer visible range
grcViewport vp = *grcViewport::GetCurrent();
float fPrevNear = vp.GetNearClip();
float fPrevFar = vp.GetFarClip();
const float fNewNearPlane = Max(fPrevNear, fPrevFar - fPrevFar * depthRange);
const float fNewFarPlane = m_fFarPlane;
vp.SetNearFarClip(fNewNearPlane, fNewFarPlane);
float fClosestObjFarPlane = 0.0;
Vec3V boxMin;
Vec3V boxMax;
for(int iCurDrawable = 0; iCurDrawable < m_arrDrawables.GetCount(); iCurDrawable++)
{
// Get the bounding box and scale it
m_arrDrawables[iCurDrawable]->GetLodGroup().GetBoundingBox(RC_VECTOR3(boxMin), RC_VECTOR3(boxMax));
boxMin *= vScale;
boxMax *= vScale;
#if __BANK
if(m_bDrawBounds)
{
grcDebugDraw::BoxAxisAligned(boxMin, boxMax, Color_red, false);
}
if(m_bSkipAABBTest)
{
m_arrVisible.Append() = iCurDrawable;
}
else
#endif // __BANK
{
if (grcViewport::IsAABBVisible(boxMin.GetIntrin128ConstRef(), boxMax.GetIntrin128ConstRef(), vp.GetFrustumNFNF()))
{
m_arrVisible.Append() = iCurDrawable;
BANK_ONLY(if(!m_bSkipFarDistanceSearch))
{
// TODO -- this is terribly inefficient code ..
Vector4 nearclip = VEC4V_TO_VECTOR4(vp.GetFrustumClipPlane(grcViewport::CLIP_PLANE_NEAR));
Vector3 vBoxMin = RCC_VECTOR3(boxMin);
Vector3 vBoxMax = RCC_VECTOR3(boxMax);
Vector3 arrCorners[8] = {
Vector3(vBoxMin.x, vBoxMin.y, vBoxMin.z),
Vector3(vBoxMax.x, vBoxMin.y, vBoxMin.z),
Vector3(vBoxMin.x, vBoxMax.y, vBoxMin.z),
Vector3(vBoxMin.x, vBoxMin.y, vBoxMax.z),
Vector3(vBoxMax.x, vBoxMax.y, vBoxMin.z),
Vector3(vBoxMin.x, vBoxMax.y, vBoxMax.z),
Vector3(vBoxMax.x, vBoxMin.y, vBoxMax.z),
Vector3(vBoxMax.x, vBoxMax.y, vBoxMax.z)
};
float fFurthestDist = 0.0f;
for(int i = 0; i < 8; i++)
{
float dist = nearclip.Dot3(arrCorners[i]) + nearclip.w;
fFurthestDist = Max(dist, fFurthestDist);
}
fFurthestDist += vp.GetNearClip();
fClosestObjFarPlane = Max(fClosestObjFarPlane, fFurthestDist);
}
}
}
}
BANK_ONLY(m_iTotalVisibleObjects = m_arrVisible.GetCount());
// Don't bother if nothing is visible
grcViewport *prevVP= grcViewport::GetCurrent();
grcViewport curVP = *prevVP;
grcViewport* pCurVP = &curVP;
grcViewport::SetCurrent(pCurVP);
#if USE_INVERTED_PROJECTION_ONLY
pCurVP->SetInvertZInProjectionMatrix(!waterReflections);
#endif // USE_INVERTED_PROJECTION_ONLY
pCurVP->SetNearFarClip(fNewNearPlane, fNewFarPlane); // Set the far plane to a very high distance to avoid culling
grcWindow winPrev = pCurVP->GetWindow();
// Set initial state
grcBindTexture(NULL);
grcLightState::SetEnabled(false);
// Set the states
grcStateBlock::SetBlendState(grcStateBlock::BS_Default);
grcStateBlock::SetDepthStencilState(waterReflections ? m_DSS_Water : m_DSS_Scene, DEFERRED_MATERIAL_DEFAULT);
grcStateBlock::SetRasterizerState(grcStateBlock::RS_Default);
// This is the last depth value before the far plane
// Unfortunately the PS3 is the problematic boy as usual
#if USE_INVERTED_PROJECTION_ONLY
static float fZMax = waterReflections ? 0.99999994f : 1.0f - 0.99999994f;
#else
static float fZMax = 0.99999994f;
#endif
if(BANK_ONLY(!m_bSkipWaterObjects &&) renderWater)
{
// Clamp the water depth value to the last value before the far plane
pCurVP->SetWindow(winPrev.GetX(), winPrev.GetY(), winPrev.GetWidth(), winPrev.GetHeight(), fZMax, fZMax);
// Render the water
Water::RenderWaterFLOD();
}
if(m_arrVisible.GetCount() > 0)
{
BANK_ONLY(if(m_bSortMapObjects))
{
m_arrVisible.QSort(0,m_arrVisible.GetCount(),CHorizonObjects::Compare);
}
// Check if we want to use the closest far plane value
if(fClosestObjFarPlane >= fNewFarPlane BANK_ONLY( || m_bSkipFarDistanceSearch ))
{
// Use the fixed far plane value
pCurVP->SetNearFarClip(fNewNearPlane, fNewFarPlane);
}
else
{
// USe the closest far plane value
pCurVP->SetNearFarClip(fNewNearPlane, fClosestObjFarPlane);
}
// Use the window depth range to force the depths into the end of the range
#if USE_INVERTED_PROJECTION_ONLY
static float fHorizonZMin = waterReflections ? 0.99999976f : fZMax;
static float fHorizonZMax = waterReflections ? fZMax : 1.0f - 0.99999976f;
#else
static float fHorizonZMin = 0.99999976f;
static float fHorizonZMax = fZMax;
#endif
pCurVP->SetWindow(winPrev.GetX(), winPrev.GetY(), winPrev.GetWidth(), winPrev.GetHeight(), fHorizonZMin, fHorizonZMax);
if (scissorX > -1)
{
GRCDEVICE.SetScissor(scissorX, scissorY, scissorW, scissorH);
}
BANK_ONLY(if(!m_bSkipMapObjects))
if (m_pSilhoutteShader->BeginDraw(grmShader::RMC_DRAW, true, waterReflections ? m_SilhoutteDrawWaterReflectionTech : m_SilhoutteDrawTech))
{
m_pSilhoutteShader->Bind(BANK_SWITCH(m_bDebugShader ? 1 : 0, 0));
for(int iCurDrawable = 0; iCurDrawable < m_arrVisible.GetCount(); iCurDrawable++)
{
#if __BANK
if(m_iObjectSelection > -1 && m_iObjectSelection != iCurDrawable)
{
continue;
}
#endif // __BANK
m_arrDrawables[m_arrVisible[iCurDrawable]]->DrawNoShaders(RCC_MATRIX34(worldMtx), CRenderer::GenerateBucketMask(CRenderer::RB_OPAQUE), 0);
}
m_pSilhoutteShader->UnBind();
m_pSilhoutteShader->EndDraw();
}
}
// Restore the states
grcStateBlock::SetDepthStencilState(CShaderLib::DSS_Default_Invert);
// Restore viewport
grcViewport::SetCurrent(prevVP);
#if __BANK && __PS3
// Restore EDGE as needed
if(m_bDisableEdgeCulling)
{
grcEffect::SetEdgeViewportCullEnable(edgeCullPrevEnabled);
}
#endif // __BANK && __PS3
}
s32 CHorizonObjects::Compare(const int* a, const int* b)
{
const gtaDrawable *dblA = g_HorizonObjects.m_arrDrawables[*a];
const Vec3V ctrA = dblA->GetLodGroup().GetCullSphereV().GetCenter();
const gtaDrawable *dblB = g_HorizonObjects.m_arrDrawables[*b];
const Vec3V ctrB = dblB->GetLodGroup().GetCullSphereV().GetCenter();
const Vec3V camPos = grcViewport::GetCurrent()->GetCameraMtx().d();
const ScalarV distA = MagSquared(camPos - ctrA);
const ScalarV distB = MagSquared(camPos - ctrB);
return (IsGreaterThan(distA,distB).Getb() ? -1 : 1);
}
#if __BANK
void CHorizonObjects::ClampSelection()
{
m_iObjectSelection = Clamp(m_iObjectSelection, -1, m_arrDrawables.GetCount()-1);
}
void CHorizonObjects::InitWidgets(bkBank* pBank)
{
pBank->PushGroup("Horizon Objects");
pBank->AddSlider("Total Horizon Objects", &m_iTotalObjects, 0, 1000, -1);
pBank->AddSlider("Total Visible", &m_iTotalVisibleObjects, 0, 1000, -1);
pBank->AddSlider("Total Memory Used (in Kb)", &m_MemUsage, 0, 5000, 1);
pBank->AddToggle("Enable", &m_bEnable);
pBank->AddToggle("Cull Everything", &m_bCullEverything);
pBank->AddToggle("Sort Map Objects", &m_bSortMapObjects);
pBank->AddToggle("Skip Map Objects", &m_bSkipMapObjects);
pBank->AddToggle("Skip Water Objects", &m_bSkipWaterObjects);
pBank->AddToggle("Skip AABB Test", &m_bSkipAABBTest);
pBank->AddSlider("Object selection", &m_iObjectSelection, -1, 50, 1, datCallback(MFA2(CHorizonObjects::ClampSelection), (datBase*)this));
pBank->AddToggle("Draw Bounds", &m_bDrawBounds);
pBank->AddToggle("Use Debug Shader", &m_bDebugShader);
pBank->AddSlider("Model Scale Factor", &m_fScaleFactor, 0.1f, 15.0f, 0.1f);
pBank->AddSlider("Depth Range Used", &m_fDepthRangePercentage, 0.001f, 1.0f, 0.001f);
pBank->AddSlider("Far Plane Distance", &m_fFarPlane, 5000.0, 30000.0f, 1.0f);
pBank->AddToggle("Skip Far Distance Search", &m_bSkipFarDistanceSearch);
#if __PS3
pBank->AddToggle("Disable EDGE Culling", &m_bDisableEdgeCulling);
#endif // __PS3
pBank->PopGroup();
}
#endif // __BANK