verus/Verus/src/Scene/SceneManager.cpp

476 lines
11 KiB
C++

#include "verus.h"
using namespace verus;
using namespace verus::Scene;
SceneManager::SceneManager()
{
VERUS_ZERO_MEM(_visibleCountPerType);
}
SceneManager::~SceneManager()
{
Done();
}
void SceneManager::Init(RcDesc desc)
{
VERUS_INIT();
SetCamera(desc._pMainCamera);
const float hf = desc._mapSide * 0.5f;
Math::Bounds bounds;
bounds.Set(
Vector3(-hf, -hf, -hf),
Vector3(+hf, +hf, +hf));
const Vector3 limit(hf / 14, hf / 14, hf / 14);
_octree.Done();
_octree.Init(bounds, limit);
_octree.SetDelegate(this);
}
void SceneManager::Done()
{
{ // PBLE:
//DeleteAllPrefabs();
DeleteAllBlocks();
DeleteAllLights();
//DeleteAllEmitters();
}
DeleteAllModels();
VERUS_DONE(SceneManager);
}
void SceneManager::ResetInstanceCount()
{
for (auto& x : TStoreModels::_map)
x.second.GetMesh().ResetInstanceCount();
}
void SceneManager::Update()
{
VERUS_UPDATE_ONCE_CHECK;
//for (auto& x : TStoreSites::_map)
// x.second.Update();
{ // PBLE:
//for (auto& x : TStorePrefabs::_list)
// x.Update();
for (auto& x : TStoreBlocks::_list)
x.Update();
for (auto& x : TStoreLights::_list)
x.Update();
//for (auto& x : TStoreEmitters::_list)
// x.Update();
}
//for (auto& x : TStoreSceneParticles::_map)
// x.second.Update();
}
void SceneManager::UpdateParts()
{
const RcPoint3 eyePos = _pMainCamera->GetEyePosition();
for (auto& block : TStoreBlocks::_list)
{
const float distSq = VMath::distSqr(block.GetPosition(), eyePos);
const float part = MaterialManager::ComputePart(distSq, block.GetBounds().GetAverageSize() * 0.5f);
block.GetMaterial()->IncludePart(part);
}
}
void SceneManager::Layout()
{
VERUS_QREF_CONST_SETTINGS;
VERUS_QREF_ATMO;
VERUS_QREF_SM;
// Allocate enough space:
int countAll = 0;
//countAll += TStorePrefabs::_list.size();
countAll += TStoreBlocks::_list.size();
countAll += TStoreLights::_list.size();
//countAll += TStoreSites::_map.size();
if (_vVisibleNodes.size() != countAll)
_vVisibleNodes.resize(countAll);
_visibleCount = 0;
VERUS_ZERO_MEM(_visibleCountPerType);
PCamera pPrevCamera = nullptr;
// For CSM we need to create geometry beyond the view frustum (1st slice):
if (settings._sceneShadowQuality >= App::Settings::ShadowQuality::cascaded && atmo.GetShadowMap().IsRendering())
{
PCamera pCameraCSM = atmo.GetShadowMap().GetCameraCSM();
if (pCameraCSM)
pPrevCamera = sm.SetCamera(pCameraCSM);
}
_octree.TraverseVisible(_pCamera->GetFrustum());
// Back to original camera:
if (pPrevCamera)
SetCamera(pPrevCamera);
VERUS_RT_ASSERT(!_visibleCountPerType[+NodeType::unknown]);
std::sort(_vVisibleNodes.begin(), _vVisibleNodes.begin() + _visibleCount, [](PSceneNode pA, PSceneNode pB)
{
// Sort by node type?
const NodeType typeA = pA->GetType();
const NodeType typeB = pB->GetType();
if (typeA != typeB)
return typeA < typeB;
// Both are blocks?
if (NodeType::block == typeA)
{
PBlock pBlockA = static_cast<PBlock>(pA);
PBlock pBlockB = static_cast<PBlock>(pB);
MaterialPtr materialA = pBlockA->GetMaterial();
MaterialPtr materialB = pBlockB->GetMaterial();
if (materialA && materialB) // A and B have materials, compare them.
{
const bool ab = *materialA < *materialB;
const bool ba = *materialB < *materialA;
if (ab || ba)
return ab;
}
else if (materialA) // A is with material, B is without, so A goes first.
{
return true;
}
else if (materialB) // A is without material, B is with, so B goes first.
{
return false;
}
// Equal materials or none have any material, compare models used:
ModelPtr modelA = pBlockA->GetModel();
ModelPtr modelB = pBlockB->GetModel();
if (modelA != modelB)
return modelA < modelB;
}
// Both are lights?
if (NodeType::light == typeA)
{
PLight pLightA = static_cast<PLight>(pA);
PLight pLightB = static_cast<PLight>(pB);
const CGI::LightType lightTypeA = pLightA->GetLightType();
const CGI::LightType lightTypeB = pLightB->GetLightType();
if (lightTypeA != lightTypeB)
return lightTypeA < lightTypeB;
}
// Draw same node types front-to-back:
return pA->GetDistToEyeSq() < pB->GetDistToEyeSq();
});
}
void SceneManager::Draw()
{
if (!_visibleCountPerType[+NodeType::block])
return;
VERUS_QREF_RENDERER;
ModelPtr model;
MaterialPtr material;
bool bindPipeline = true;
auto cb = renderer.GetCommandBuffer();
auto shader = Scene::Mesh::GetShader();
const int begin = 0;
const int end = _visibleCountPerType[+NodeType::block];
shader->BeginBindDescriptors();
for (int i = begin; i <= end; ++i)
{
if (i == end)
{
if (model)
model->Draw(cb);
break;
}
PSceneNode pSN = _vVisibleNodes[i];
PBlock pBlock = static_cast<PBlock>(pSN);
ModelPtr nextModel = pBlock->GetModel();
MaterialPtr nextMaterial = pBlock->GetMaterial();
if (!nextModel->IsLoaded() || !nextMaterial->IsLoaded())
continue;
if (nextModel != model)
{
if (model)
model->Draw(cb);
model = nextModel;
model->MarkFirstInstance();
if (bindPipeline)
{
bindPipeline = false;
model->BindPipeline(cb);
cb->BindDescriptors(shader, 0);
}
model->BindGeo(cb);
cb->BindDescriptors(shader, 2);
}
if (nextMaterial != material)
{
material = nextMaterial;
material->UpdateMeshUniformBuffer();
cb->BindDescriptors(shader, 1, material->GetComplexSetHandle());
}
if (model)
model->PushInstance(pBlock->GetTransform(), pBlock->GetColor());
}
shader->EndBindDescriptors();
}
void SceneManager::DrawLights()
{
if (!_visibleCountPerType[+NodeType::light])
return;
VERUS_QREF_RENDERER;
VERUS_QREF_HELPERS;
CGI::LightType type = CGI::LightType::none;
PMesh pMesh = nullptr;
bool bindPipeline = true;
auto& ds = renderer.GetDS();
auto cb = renderer.GetCommandBuffer();
auto DrawMesh = [cb](PMesh pMesh)
{
if (pMesh && !pMesh->IsInstanceBufferEmpty(true))
{
pMesh->UpdateInstanceBuffer();
cb->DrawIndexed(pMesh->GetIndexCount(), pMesh->GetInstanceCount(true), 0, 0, pMesh->GetFirstInstance());
}
};
const int begin = _visibleCountPerType[+NodeType::block];
const int end = _visibleCountPerType[+NodeType::block] + _visibleCountPerType[+NodeType::light];
for (int i = begin; i <= end; ++i)
{
if (i == end)
{
DrawMesh(pMesh);
break;
}
PSceneNode pSN = _vVisibleNodes[i];
PLight pLight = static_cast<PLight>(pSN);
const CGI::LightType nextType = pLight->GetLightType();
if (nextType != type)
{
DrawMesh(pMesh);
type = nextType;
ds.OnNewLightType(cb, type);
pMesh = (type != CGI::LightType::none) ? &helpers.GetDeferredLights().Get(type) : nullptr;
if (pMesh)
{
pMesh->MarkFirstInstance();
pMesh->BindGeo(cb, (1 << 0) | (1 << 4));
pMesh->CopyPosDeqScale(&ds.GetUbPerMeshVS()._posDeqScale.x);
pMesh->CopyPosDeqBias(&ds.GetUbPerMeshVS()._posDeqBias.x);
ds.BindDescriptorsPerMeshVS(cb);
}
}
#ifdef VERUS_RELEASE_DEBUG
if (CGI::LightType::dir == type) // Must not be scaled!
{
RcTransform3 matW = pLight->GetTransform();
const Vector3 s = matW.GetScale();
VERUS_RT_ASSERT(glm::all(glm::epsilonEqual(s.GLM(), glm::vec3(1), glm::vec3(VERUS_FLOAT_THRESHOLD))));
}
#endif
if (pMesh)
pMesh->PushInstance(pLight->GetTransform(), pLight->GetInstData());
}
}
bool SceneManager::IsDrawingDepth(DrawDepth dd)
{
if (DrawDepth::automatic == dd)
{
VERUS_QREF_ATMO;
return atmo.GetShadowMap().IsRendering();
}
else
return DrawDepth::yes == dd;
}
String SceneManager::EnsureUniqueName(CSZ name)
{
// Compact name:
const char* s = strrchr(name, ':');
s = s ? s + 1 : name;
const char* e = strrchr(name, '.');
if (!e)
e = name + strlen(name);
// Unique name:
int id = -1;
String test;
do
{
test = (id < 0) ? String(s, e) : String(s, e) + "_" + std::to_string(id);
//for (auto& it : TStorePrefabs::_list) if (it.GetName() == _C(test)) test.clear();
for (auto& it : TStoreBlocks::_list) if (it.GetName() == _C(test)) test.clear();
for (auto& it : TStoreLights::_list) if (it.GetName() == _C(test)) test.clear();
//for (auto& it : TStoreEmitters::_list) if (it.GetName() == _C(test)) test.clear();
id++;
} while (test.empty());
return test;
}
PModel SceneManager::InsertModel(CSZ url)
{
return TStoreModels::Insert(url);
}
PModel SceneManager::FindModel(CSZ url)
{
return TStoreModels::Find(url);
}
void SceneManager::DeleteModel(CSZ url)
{
TStoreModels::Delete(url);
}
void SceneManager::DeleteAllModels()
{
TStoreModels::DeleteAll();
}
PBlock SceneManager::InsertBlock()
{
return TStoreBlocks::Insert();
}
void SceneManager::DeleteBlock(PBlock p)
{
TStoreBlocks::Delete(p);
}
void SceneManager::DeleteAllBlocks()
{
TStoreBlocks::DeleteAll();
}
PLight SceneManager::InsertLight()
{
return TStoreLights::Insert();
}
void SceneManager::DeleteLight(PLight p)
{
TStoreLights::Delete(p);
}
void SceneManager::DeleteAllLights()
{
TStoreLights::DeleteAll();
}
Continue SceneManager::Octree_ProcessNode(void* pToken, void* pUser)
{
PSceneNode pSN = static_cast<PSceneNode>(pToken);
if (pSN->IsHidden())
return Continue::yes;
_vVisibleNodes[_visibleCount++] = pSN;
_visibleCountPerType[+pSN->GetType()]++;
return Continue::yes;
}
bool SceneManager::RayCastingTest(RcPoint3 pointA, RcPoint3 pointB, PBlockPtr pBlock,
PPoint3 pPoint, PVector3 pNormal, const float* pRadius, Physics::Group mask)
{
VERUS_RT_ASSERT(!pBlock || *pBlock);
VERUS_QREF_BULLET;
btVector3 from(pointA.Bullet()), to(pointB.Bullet());
if (pPoint || pNormal)
{
if (pRadius)
{
btSphereShape sphere(*pRadius);
btTransform trA, trB;
trA.setIdentity();
trB.setIdentity();
trA.setOrigin(pointA.Bullet());
trB.setOrigin(pointB.Bullet());
btCollisionWorld::ClosestConvexResultCallback ccrc(from, to);
ccrc.m_collisionFilterMask = +mask;
bullet.GetWorld()->convexSweepTest(&sphere, trA, trB, ccrc);
if (ccrc.hasHit())
{
Physics::PUserPtr p = static_cast<Physics::PUserPtr>(ccrc.m_hitCollisionObject->getUserPointer());
if (pBlock && p->UserPtr_GetType() == +NodeType::block)
pBlock->Attach(static_cast<PBlock>(p));
if (pPoint)
*pPoint = ccrc.m_hitPointWorld;
if (pNormal)
*pNormal = ccrc.m_hitNormalWorld;
return true;
}
else
return false;
}
else
{
btCollisionWorld::ClosestRayResultCallback crrc(from, to);
crrc.m_collisionFilterMask = +mask;
bullet.GetWorld()->rayTest(from, to, crrc);
if (crrc.hasHit())
{
Physics::PUserPtr p = static_cast<Physics::PUserPtr>(crrc.m_collisionObject->getUserPointer());
if (pBlock && p->UserPtr_GetType() == +NodeType::block)
pBlock->Attach(static_cast<PBlock>(p));
if (pPoint)
*pPoint = crrc.m_hitPointWorld;
if (pNormal)
*pNormal = crrc.m_hitNormalWorld;
return true;
}
else
return false;
}
}
else // Point/normal not required?
{
struct AnyRayResultCallback : public btCollisionWorld::RayResultCallback
{
btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace)
{
m_closestHitFraction = 0;
m_collisionObject = rayResult.m_collisionObject;
return 0;
}
} arrc;
arrc.m_collisionFilterMask = +mask;
bullet.GetWorld()->rayTest(from, to, arrc);
if (arrc.hasHit())
{
Physics::PUserPtr p = static_cast<Physics::PUserPtr>(arrc.m_collisionObject->getUserPointer());
if (pBlock && p->UserPtr_GetType() == +NodeType::block)
pBlock->Attach(static_cast<PBlock>(p));
return true;
}
else
return false;
}
return false;
}