mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-08-02 18:25:53 +00:00
321 lines
8.7 KiB
C++
321 lines
8.7 KiB
C++
#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 <FileIO/CFileInStream.h>
|
|
#include <Common/TString.h>
|
|
#include <Math/CRay.h>
|
|
|
|
#include <list>
|
|
#include <string>
|
|
|
|
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<CSceneNode*>& 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<CSceneNode*>& 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<CSceneNode*>& rLights = mNodes[eLightNode];
|
|
|
|
for (auto it = rLights.begin(); it != rLights.end(); it++)
|
|
{
|
|
CLightNode *pNode = static_cast<CLightNode*>(*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;
|
|
}
|