#include "CScene.h" #include "Core/Render/CGraphics.h" #include "Core/Resource/CResCache.h" #include "Core/Resource/CPoiToWorld.h" #include "Core/Resource/Script/CScriptLayer.h" #include "Core/CRayCollisionTester.h" #include #include #include #include #include CScene::CScene() : mSplitTerrain(true) , mNumNodes(0) , mpSceneRootNode(new CRootNode(this, nullptr)) , mpArea(nullptr) , mpWorld(nullptr) , mpAreaRootNode(nullptr) { } CScene::~CScene() { ClearScene(); } CModelNode* CScene::CreateModelNode(CModel *pModel) { if (pModel == nullptr) return nullptr; CModelNode *pNode = new CModelNode(this, mpSceneRootNode, pModel); mNodes[eModelNode].push_back(pNode); mNumNodes++; return pNode; } CStaticNode* CScene::CreateStaticNode(CStaticModel *pModel) { if (pModel == nullptr) return nullptr; CStaticNode *pNode = new CStaticNode(this, mpAreaRootNode, pModel); mNodes[eStaticNode].push_back(pNode); mNumNodes++; return pNode; } CCollisionNode* CScene::CreateCollisionNode(CCollisionMeshGroup *pMesh) { if (pMesh == nullptr) return nullptr; CCollisionNode *pNode = new CCollisionNode(this, mpAreaRootNode, pMesh); mNodes[eCollisionNode].push_back(pNode); mNumNodes++; return pNode; } CScriptNode* CScene::CreateScriptNode(CScriptObject *pObj) { if (pObj == nullptr) return nullptr; CScriptNode *pNode = new CScriptNode(this, mpAreaRootNode, pObj); mNodes[eScriptNode].push_back(pNode); mNumNodes++; return pNode; } CLightNode* CScene::CreateLightNode(CLight *pLight) { if (pLight == nullptr) return nullptr; CLightNode *pNode = new CLightNode(this, mpAreaRootNode, pLight); mNodes[eLightNode].push_back(pNode); mNumNodes++; return pNode; } void CScene::SetActiveArea(CGameArea *pArea) { // Clear existing area delete mpAreaRootNode; mNodes.clear(); mAreaAttributesObjects.clear(); mScriptNodeMap.clear(); // Create nodes for new area mpArea = pArea; mpAreaRootNode = new CRootNode(this, mpSceneRootNode); // Create static nodes u32 Count = mpArea->GetStaticModelCount(); for (u32 iMdl = 0; iMdl < Count; iMdl++) { CStaticNode *pNode = CreateStaticNode(mpArea->GetStaticModel(iMdl)); pNode->SetName("Static World Model " + TString::FromInt32(iMdl, 0, 10)); } // Create model nodes Count = mpArea->GetTerrainModelCount(); for (u32 iMdl = 0; iMdl < Count; iMdl++) { CModel *pModel = mpArea->GetTerrainModel(iMdl); CModelNode *pNode = CreateModelNode(pModel); pNode->SetName("World Model " + TString::FromInt32(iMdl, 0, 10)); pNode->SetDynamicLighting(false); } CreateCollisionNode(mpArea->GetCollision()); u32 NumLayers = mpArea->GetScriptLayerCount(); for (u32 iLyr = 0; iLyr < NumLayers; iLyr++) { CScriptLayer *pLayer = mpArea->GetScriptLayer(iLyr); u32 NumObjects = pLayer->GetNumObjects(); mNodes[eScriptNode].reserve(mNodes[eScriptNode].size() + NumObjects); for (u32 iObj = 0; iObj < NumObjects; iObj++) { CScriptObject *pObj = pLayer->ObjectByIndex(iObj); CScriptNode *pNode = CreateScriptNode(pObj); pNode->BuildLightList(mpArea); // Add to map mScriptNodeMap[pObj->InstanceID()] = pNode; // AreaAttributes check switch (pObj->ObjectTypeID()) { case 0x4E: // MP1 AreaAttributes ID case 0x52454141: // MP2/MP3/DKCR AreaAttributes ID ("REAA") mAreaAttributesObjects.emplace_back( CAreaAttributes(pObj) ); break; } } } CScriptLayer *pGenLayer = mpArea->GetGeneratorLayer(); if (pGenLayer) { for (u32 iObj = 0; iObj < pGenLayer->GetNumObjects(); iObj++) { CScriptObject *pObj = pGenLayer->ObjectByIndex(iObj); CScriptNode *pNode = CreateScriptNode(pObj); // Add to map mScriptNodeMap[pObj->InstanceID()] = pNode; } } // Ensure script nodes have valid positions + build light lists for (auto it = mScriptNodeMap.begin(); it != mScriptNodeMap.end(); it++) { it->second->GeneratePosition(); it->second->BuildLightList(mpArea); } u32 NumLightLayers = mpArea->GetLightLayerCount(); CGraphics::sAreaAmbientColor = CColor::skBlack; for (u32 iLyr = 0; iLyr < NumLightLayers; iLyr++) { u32 NumLights = mpArea->GetLightCount(iLyr); for (u32 iLit = 0; iLit < NumLights; iLit++) { CLight *pLight = mpArea->GetLight(iLyr, iLit); if (pLight->GetType() == eLocalAmbient) CGraphics::sAreaAmbientColor += pLight->GetColor(); CreateLightNode(pLight); } } Log::Write( TString::FromInt32(CSceneNode::NumNodes()) + " nodes" ); } void CScene::SetActiveWorld(CWorld* pWorld) { mpWorld = pWorld; } void CScene::ClearScene() { if (mpAreaRootNode) { mpAreaRootNode->Unparent(); delete mpAreaRootNode; } mNodes.clear(); mNumNodes = 0; mpArea = nullptr; mpWorld = nullptr; } void CScene::AddSceneToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo) { // Override show flags in game mode FShowFlags ShowFlags = (ViewInfo.GameMode ? gkGameModeShowFlags : ViewInfo.ShowFlags); FNodeFlags NodeFlags = NodeFlagsForShowFlags(ShowFlags); for (auto it = mNodes.begin(); it != mNodes.end(); it++) { if (NodeFlags & it->first) { std::vector& rNodeVec = it->second; for (u32 iNode = 0; iNode < rNodeVec.size(); iNode++) if (ViewInfo.GameMode || rNodeVec[iNode]->IsVisible()) rNodeVec[iNode]->AddToRenderer(pRenderer, ViewInfo); } } } SRayIntersection CScene::SceneRayCast(const CRay& Ray, const SViewInfo& ViewInfo) { FShowFlags ShowFlags = (ViewInfo.GameMode ? gkGameModeShowFlags : ViewInfo.ShowFlags); FNodeFlags NodeFlags = NodeFlagsForShowFlags(ShowFlags); CRayCollisionTester Tester(Ray); for (auto it = mNodes.begin(); it != mNodes.end(); it++) { if (NodeFlags & it->first) { std::vector& rNodeVec = it->second; for (u32 iNode = 0; iNode < rNodeVec.size(); iNode++) if (rNodeVec[iNode]->IsVisible()) rNodeVec[iNode]->RayAABoxIntersectTest(Tester, ViewInfo); } } return Tester.TestNodes(ViewInfo); } CScriptNode* CScene::ScriptNodeByID(u32 InstanceID) { auto it = mScriptNodeMap.find(InstanceID); if (it != mScriptNodeMap.end()) return it->second; else return nullptr; } CScriptNode* CScene::NodeForObject(CScriptObject *pObj) { return ScriptNodeByID(pObj->InstanceID()); } CLightNode* CScene::NodeForLight(CLight *pLight) { // Slow. Is there a better way to do this? std::vector& rLights = mNodes[eLightNode]; for (auto it = rLights.begin(); it != rLights.end(); it++) { CLightNode *pNode = static_cast(*it); if (pNode->Light() == pLight) return pNode; } return nullptr; } CModel* CScene::GetActiveSkybox() { bool SkyEnabled = false; for (u32 iAtt = 0; iAtt < mAreaAttributesObjects.size(); iAtt++) { const CAreaAttributes& rkAttributes = mAreaAttributesObjects[iAtt]; if (rkAttributes.IsSkyEnabled()) SkyEnabled = true; if (rkAttributes.IsLayerEnabled()) { if (rkAttributes.IsSkyEnabled()) { SkyEnabled = true; CModel *pSky = rkAttributes.SkyModel(); if (pSky) return pSky; } } } if (SkyEnabled) return mpWorld->GetDefaultSkybox(); else return nullptr; } CGameArea* CScene::GetActiveArea() { return mpArea; } // ************ STATIC ************ FShowFlags CScene::ShowFlagsForNodeFlags(FNodeFlags NodeFlags) { FShowFlags Out; if (NodeFlags & eModelNode) Out |= eShowSplitWorld; if (NodeFlags & eStaticNode) Out |= eShowMergedWorld; if (NodeFlags & eScriptNode) Out |= eShowObjects; if (NodeFlags & eCollisionNode) Out |= eShowWorldCollision; if (NodeFlags & eLightNode) Out |= eShowLights; return Out; } FNodeFlags CScene::NodeFlagsForShowFlags(FShowFlags ShowFlags) { FNodeFlags Out = eRootNode; if (ShowFlags & eShowSplitWorld) Out |= eModelNode; if (ShowFlags & eShowMergedWorld) Out |= eStaticNode; if (ShowFlags & eShowWorldCollision) Out |= eCollisionNode; if (ShowFlags & eShowObjects) Out |= eScriptNode | eScriptExtraNode; if (ShowFlags & eShowLights) Out |= eLightNode; return Out; }