mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-18 01:15:26 +00:00
Completely overhauled resource loading in preparation for projects
This commit is contained in:
222
src/Core/Resource/Area/CGameArea.cpp
Normal file
222
src/Core/Resource/Area/CGameArea.cpp
Normal file
@@ -0,0 +1,222 @@
|
||||
#include "CGameArea.h"
|
||||
#include "Core/Resource/Script/CScriptLayer.h"
|
||||
#include "Core/Render/CRenderer.h"
|
||||
|
||||
CGameArea::CGameArea(CResourceEntry *pEntry /*= 0*/)
|
||||
: CResource(pEntry)
|
||||
, mWorldIndex(-1)
|
||||
, mVertexCount(0)
|
||||
, mTriangleCount(0)
|
||||
, mTerrainMerged(false)
|
||||
, mOriginalWorldMeshCount(0)
|
||||
, mUsesCompression(false)
|
||||
, mMaterialSet(nullptr)
|
||||
, mpGeneratorLayer(nullptr)
|
||||
, mpCollision(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
CGameArea::~CGameArea()
|
||||
{
|
||||
ClearTerrain();
|
||||
|
||||
delete mpCollision;
|
||||
delete mpGeneratorLayer;
|
||||
|
||||
for (u32 iSCLY = 0; iSCLY < mScriptLayers.size(); iSCLY++)
|
||||
delete mScriptLayers[iSCLY];
|
||||
|
||||
for (u32 iLyr = 0; iLyr < mLightLayers.size(); iLyr++)
|
||||
for (u32 iLight = 0; iLight < mLightLayers[iLyr].size(); iLight++)
|
||||
delete mLightLayers[iLyr][iLight];
|
||||
}
|
||||
|
||||
void CGameArea::AddWorldModel(CModel *pModel)
|
||||
{
|
||||
mWorldModels.push_back(pModel);
|
||||
mVertexCount += pModel->GetVertexCount();
|
||||
mTriangleCount += pModel->GetTriangleCount();
|
||||
mAABox.ExpandBounds(pModel->AABox());
|
||||
}
|
||||
|
||||
void CGameArea::MergeTerrain()
|
||||
{
|
||||
if (mTerrainMerged) return;
|
||||
|
||||
// Nothing really complicated here - iterate through every terrain submesh, add each to a static model
|
||||
for (u32 iMdl = 0; iMdl < mWorldModels.size(); iMdl++)
|
||||
{
|
||||
CModel *pMdl = mWorldModels[iMdl];
|
||||
u32 SubmeshCount = pMdl->GetSurfaceCount();
|
||||
|
||||
for (u32 iSurf = 0; iSurf < SubmeshCount; iSurf++)
|
||||
{
|
||||
SSurface *pSurf = pMdl->GetSurface(iSurf);
|
||||
CMaterial *pMat = mMaterialSet->MaterialByIndex(pSurf->MaterialID);
|
||||
|
||||
bool NewMat = true;
|
||||
for (std::vector<CStaticModel*>::iterator it = mStaticWorldModels.begin(); it != mStaticWorldModels.end(); it++)
|
||||
{
|
||||
if ((*it)->GetMaterial() == pMat)
|
||||
{
|
||||
// When we append a new submesh to an existing static model, we bump it to the back of the vector.
|
||||
// This is because mesh ordering actually matters sometimes
|
||||
// (particularly with multi-layered transparent meshes)
|
||||
// so we need to at least try to maintain it.
|
||||
// This is maybe not the most efficient way to do this, but it works.
|
||||
CStaticModel *pStatic = *it;
|
||||
pStatic->AddSurface(pSurf);
|
||||
mStaticWorldModels.erase(it);
|
||||
mStaticWorldModels.push_back(pStatic);
|
||||
NewMat = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (NewMat)
|
||||
{
|
||||
CStaticModel *pStatic = new CStaticModel(pMat);
|
||||
pStatic->AddSurface(pSurf);
|
||||
mStaticWorldModels.push_back(pStatic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGameArea::ClearTerrain()
|
||||
{
|
||||
for (u32 iModel = 0; iModel < mWorldModels.size(); iModel++)
|
||||
delete mWorldModels[iModel];
|
||||
mWorldModels.clear();
|
||||
|
||||
for (u32 iStatic = 0; iStatic < mStaticWorldModels.size(); iStatic++)
|
||||
delete mStaticWorldModels[iStatic];
|
||||
mStaticWorldModels.clear();
|
||||
|
||||
if (mMaterialSet) delete mMaterialSet;
|
||||
|
||||
mVertexCount = 0;
|
||||
mTriangleCount = 0;
|
||||
mTerrainMerged = false;
|
||||
mAABox = CAABox::skInfinite;
|
||||
}
|
||||
|
||||
void CGameArea::ClearScriptLayers()
|
||||
{
|
||||
for (auto it = mScriptLayers.begin(); it != mScriptLayers.end(); it++)
|
||||
delete *it;
|
||||
mScriptLayers.clear();
|
||||
delete mpGeneratorLayer;
|
||||
mpGeneratorLayer = nullptr;
|
||||
}
|
||||
|
||||
u32 CGameArea::TotalInstanceCount() const
|
||||
{
|
||||
u32 Num = 0;
|
||||
|
||||
for (u32 iLyr = 0; iLyr < mScriptLayers.size(); iLyr++)
|
||||
Num += mScriptLayers[iLyr]->NumInstances();
|
||||
|
||||
return Num;
|
||||
}
|
||||
|
||||
CScriptObject* CGameArea::InstanceByID(u32 InstanceID)
|
||||
{
|
||||
auto it = mObjectMap.find(InstanceID);
|
||||
if (it != mObjectMap.end()) return it->second;
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
u32 CGameArea::FindUnusedInstanceID(CScriptLayer *pLayer) const
|
||||
{
|
||||
u32 InstanceID = (pLayer->AreaIndex() << 26) | (mWorldIndex << 16) | 1;
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto it = mObjectMap.find(InstanceID);
|
||||
|
||||
if (it == mObjectMap.end())
|
||||
break;
|
||||
else
|
||||
InstanceID++;
|
||||
}
|
||||
|
||||
return InstanceID;
|
||||
}
|
||||
|
||||
CScriptObject* CGameArea::SpawnInstance(CScriptTemplate *pTemplate,
|
||||
CScriptLayer *pLayer,
|
||||
const CVector3f& rkPosition /*= CVector3f::skZero*/,
|
||||
const CQuaternion& rkRotation /*= CQuaternion::skIdentity*/,
|
||||
const CVector3f& rkScale /*= CVector3f::skOne*/,
|
||||
u32 SuggestedID /*= -1*/,
|
||||
u32 SuggestedLayerIndex /*= -1*/ )
|
||||
{
|
||||
// Verify we can fit another instance in this area.
|
||||
u32 NumInstances = TotalInstanceCount();
|
||||
|
||||
if (NumInstances >= 0xFFFF)
|
||||
{
|
||||
Log::Error("Unable to spawn a new script instance; too many instances in area (" + TString::FromInt32(NumInstances, 0, 10) + ")");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check whether the suggested instance ID is valid
|
||||
u32 InstanceID = SuggestedID;
|
||||
|
||||
if (InstanceID != -1)
|
||||
{
|
||||
if (mObjectMap.find(InstanceID) == mObjectMap.end())
|
||||
InstanceID = -1;
|
||||
}
|
||||
|
||||
// If not valid (or if there's no suggested ID) then determine a new instance ID
|
||||
if (InstanceID == -1)
|
||||
{
|
||||
// Determine layer index
|
||||
u32 LayerIndex = pLayer->AreaIndex();
|
||||
|
||||
if (LayerIndex == -1)
|
||||
{
|
||||
Log::Error("Unable to spawn a new script instance; invalid script layer passed in");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Look for a valid instance ID
|
||||
InstanceID = FindUnusedInstanceID(pLayer);
|
||||
}
|
||||
|
||||
// Spawn instance
|
||||
CScriptObject *pInstance = new CScriptObject(InstanceID, this, pLayer, pTemplate);
|
||||
pInstance->EvaluateProperties();
|
||||
pInstance->SetPosition(rkPosition);
|
||||
pInstance->SetRotation(rkRotation.ToEuler());
|
||||
pInstance->SetScale(rkScale);
|
||||
pInstance->SetName(pTemplate->Name());
|
||||
if (pTemplate->Game() < eEchoesDemo) pInstance->SetActive(true);
|
||||
pLayer->AddInstance(pInstance, SuggestedLayerIndex);
|
||||
mObjectMap[InstanceID] = pInstance;
|
||||
return pInstance;
|
||||
}
|
||||
|
||||
void CGameArea::AddInstanceToArea(CScriptObject *pInstance)
|
||||
{
|
||||
// Used for undo after deleting an instance.
|
||||
// In the future the script loader should go through SpawnInstance to avoid the need for this function.
|
||||
mObjectMap[pInstance->InstanceID()] = pInstance;
|
||||
}
|
||||
|
||||
void CGameArea::DeleteInstance(CScriptObject *pInstance)
|
||||
{
|
||||
pInstance->BreakAllLinks();
|
||||
pInstance->Layer()->RemoveInstance(pInstance);
|
||||
pInstance->Template()->RemoveObject(pInstance);
|
||||
|
||||
auto it = mObjectMap.find(pInstance->InstanceID());
|
||||
if (it != mObjectMap.end()) mObjectMap.erase(it);
|
||||
|
||||
if (mpPoiToWorldMap && mpPoiToWorldMap->HasPoiMappings(pInstance->InstanceID()))
|
||||
mpPoiToWorldMap->RemovePoi(pInstance->InstanceID());
|
||||
|
||||
delete pInstance;
|
||||
}
|
||||
102
src/Core/Resource/Area/CGameArea.h
Normal file
102
src/Core/Resource/Area/CGameArea.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#ifndef CGAMEAREA_H
|
||||
#define CGAMEAREA_H
|
||||
|
||||
#include "Core/Resource/CResource.h"
|
||||
#include "Core/Resource/CCollisionMeshGroup.h"
|
||||
#include "Core/Resource/CLight.h"
|
||||
#include "Core/Resource/CMaterialSet.h"
|
||||
#include "Core/Resource/CPoiToWorld.h"
|
||||
#include "Core/Resource/Model/CModel.h"
|
||||
#include "Core/Resource/Model/CStaticModel.h"
|
||||
#include <Common/types.h>
|
||||
#include <Math/CQuaternion.h>
|
||||
#include <Math/CTransform4f.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
class CScriptLayer;
|
||||
class CScriptObject;
|
||||
class CScriptTemplate;
|
||||
|
||||
class CGameArea : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eArea)
|
||||
friend class CAreaLoader;
|
||||
friend class CAreaCooker;
|
||||
|
||||
EGame mVersion;
|
||||
u32 mWorldIndex;
|
||||
u32 mVertexCount;
|
||||
u32 mTriangleCount;
|
||||
bool mTerrainMerged;
|
||||
CTransform4f mTransform;
|
||||
CAABox mAABox;
|
||||
|
||||
// Data saved from the original file to help on recook
|
||||
std::vector<std::vector<u8>> mSectionDataBuffers;
|
||||
u32 mOriginalWorldMeshCount;
|
||||
bool mUsesCompression;
|
||||
|
||||
struct SSectionNumber
|
||||
{
|
||||
CFourCC SectionID;
|
||||
u32 Index;
|
||||
};
|
||||
std::vector<SSectionNumber> mSectionNumbers;
|
||||
|
||||
// Geometry
|
||||
CMaterialSet *mMaterialSet;
|
||||
std::vector<CModel*> mWorldModels; // TerrainModels is the original version of each model; this is currently mainly used in the POI map editor
|
||||
std::vector<CStaticModel*> mStaticWorldModels; // StaticTerrainModels is the merged terrain for faster rendering in the world editor
|
||||
// Script
|
||||
std::vector<CScriptLayer*> mScriptLayers;
|
||||
CScriptLayer *mpGeneratorLayer;
|
||||
std::unordered_map<u32, CScriptObject*> mObjectMap;
|
||||
// Collision
|
||||
CCollisionMeshGroup *mpCollision;
|
||||
// Lights
|
||||
std::vector<std::vector<CLight*>> mLightLayers;
|
||||
// Object to Static Geometry Map
|
||||
TResPtr<CPoiToWorld> mpPoiToWorldMap;
|
||||
|
||||
public:
|
||||
CGameArea(CResourceEntry *pEntry = 0);
|
||||
~CGameArea();
|
||||
|
||||
void AddWorldModel(CModel *pModel);
|
||||
void MergeTerrain();
|
||||
void ClearTerrain();
|
||||
void ClearScriptLayers();
|
||||
u32 TotalInstanceCount() const;
|
||||
CScriptObject* InstanceByID(u32 InstanceID);
|
||||
u32 FindUnusedInstanceID(CScriptLayer *pLayer) const;
|
||||
CScriptObject* SpawnInstance(CScriptTemplate *pTemplate, CScriptLayer *pLayer,
|
||||
const CVector3f& rkPosition = CVector3f::skZero,
|
||||
const CQuaternion& rkRotation = CQuaternion::skIdentity,
|
||||
const CVector3f& rkScale = CVector3f::skOne,
|
||||
u32 SuggestedID = -1, u32 SuggestedLayerIndex = -1);
|
||||
void AddInstanceToArea(CScriptObject *pInstance);
|
||||
void DeleteInstance(CScriptObject *pInstance);
|
||||
|
||||
// Inline Accessors
|
||||
inline EGame Version() const { return mVersion; }
|
||||
inline u32 WorldIndex() const { return mWorldIndex; }
|
||||
inline CTransform4f Transform() const { return mTransform; }
|
||||
inline u32 NumWorldModels() const { return mWorldModels.size(); }
|
||||
inline u32 NumStaticModels() const { return mStaticWorldModels.size(); }
|
||||
inline CModel* TerrainModel(u32 iMdl) const { return mWorldModels[iMdl]; }
|
||||
inline CStaticModel* StaticModel(u32 iMdl) const { return mStaticWorldModels[iMdl]; }
|
||||
inline CCollisionMeshGroup* Collision() const { return mpCollision; }
|
||||
inline u32 NumScriptLayers() const { return mScriptLayers.size(); }
|
||||
inline CScriptLayer* ScriptLayer(u32 Index) const { return mScriptLayers[Index]; }
|
||||
inline CScriptLayer* GeneratedObjectsLayer() const { return mpGeneratorLayer; }
|
||||
inline u32 NumLightLayers() const { return mLightLayers.size(); }
|
||||
inline u32 NumLights(u32 LayerIndex) const { return (LayerIndex < mLightLayers.size() ? mLightLayers[LayerIndex].size() : 0); }
|
||||
inline CLight* Light(u32 LayerIndex, u32 LightIndex) const { return mLightLayers[LayerIndex][LightIndex]; }
|
||||
inline CPoiToWorld* PoiToWorldMap() const { return mpPoiToWorldMap; }
|
||||
inline CAABox AABox() const { return mAABox; }
|
||||
|
||||
inline void SetWorldIndex(u32 NewWorldIndex) { mWorldIndex = NewWorldIndex; }
|
||||
};
|
||||
|
||||
#endif // CGAMEAREA_H
|
||||
Reference in New Issue
Block a user