mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-17 00:47:05 +00:00
Mass refactoring part 1/2: establishing multiple subprojects, moving source files to their new location, adding resources/templates to version control
This commit is contained in:
57
src/Core/Scene/CCollisionNode.cpp
Normal file
57
src/Core/Scene/CCollisionNode.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "CCollisionNode.h"
|
||||
#include <Core/CDrawUtil.h>
|
||||
#include <Core/CGraphics.h>
|
||||
#include <Core/CRenderer.h>
|
||||
|
||||
CCollisionNode::CCollisionNode(CSceneManager *pScene, CSceneNode *pParent, CCollisionMeshGroup *pCollision)
|
||||
: CSceneNode(pScene, pParent)
|
||||
{
|
||||
SetCollision(pCollision);
|
||||
SetName("Collision");
|
||||
}
|
||||
|
||||
ENodeType CCollisionNode::NodeType()
|
||||
{
|
||||
return eCollisionNode;
|
||||
}
|
||||
|
||||
void CCollisionNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
|
||||
{
|
||||
if (!mpCollision) return;
|
||||
if (!ViewInfo.ViewFrustum.BoxInFrustum(AABox())) return;
|
||||
if (ViewInfo.GameMode) return;
|
||||
|
||||
pRenderer->AddOpaqueMesh(this, -1, AABox(), eDrawMesh);
|
||||
|
||||
if (mSelected)
|
||||
pRenderer->AddOpaqueMesh(this, -1, AABox(), eDrawSelection);
|
||||
}
|
||||
|
||||
void CCollisionNode::Draw(ERenderOptions /*Options*/, int /*ComponentIndex*/, const SViewInfo& ViewInfo)
|
||||
{
|
||||
if (!mpCollision) return;
|
||||
|
||||
LoadModelMatrix();
|
||||
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
CDrawUtil::UseCollisionShader(TintColor(ViewInfo));
|
||||
mpCollision->Draw();
|
||||
CDrawUtil::UseColorShader(CColor::skTransparentBlack);
|
||||
mpCollision->DrawWireframe();
|
||||
}
|
||||
|
||||
SRayIntersection CCollisionNode::RayNodeIntersectTest(const CRay& /*Ray*/, u32 /*AssetID*/, const SViewInfo& /*ViewInfo*/)
|
||||
{
|
||||
// todo
|
||||
SRayIntersection Result;
|
||||
Result.Hit = false;
|
||||
return Result;
|
||||
}
|
||||
|
||||
void CCollisionNode::SetCollision(CCollisionMeshGroup *pCollision)
|
||||
{
|
||||
mpCollision = pCollision;
|
||||
}
|
||||
20
src/Core/Scene/CCollisionNode.h
Normal file
20
src/Core/Scene/CCollisionNode.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef CCOLLISIONNODE_H
|
||||
#define CCOLLISIONNODE_H
|
||||
|
||||
#include "CSceneNode.h"
|
||||
#include <Resource/CCollisionMeshGroup.h>
|
||||
|
||||
class CCollisionNode : public CSceneNode
|
||||
{
|
||||
TResPtr<CCollisionMeshGroup> mpCollision;
|
||||
|
||||
public:
|
||||
CCollisionNode(CSceneManager *pScene, CSceneNode *pParent = 0, CCollisionMeshGroup *pCollision = 0);
|
||||
ENodeType NodeType();
|
||||
void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo);
|
||||
void Draw(ERenderOptions Options, int ComponentIndex, const SViewInfo& ViewInfo);
|
||||
SRayIntersection RayNodeIntersectTest(const CRay &Ray, u32 AssetID, const SViewInfo& ViewInfo);
|
||||
void SetCollision(CCollisionMeshGroup *pCollision);
|
||||
};
|
||||
|
||||
#endif // CCOLLISIONNODE_H
|
||||
142
src/Core/Scene/CLightNode.cpp
Normal file
142
src/Core/Scene/CLightNode.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
#include "CLightNode.h"
|
||||
#include <Common/Math.h>
|
||||
#include <Core/CDrawUtil.h>
|
||||
#include <Core/CGraphics.h>
|
||||
#include <Core/CRenderer.h>
|
||||
|
||||
CLightNode::CLightNode(CSceneManager *pScene, CSceneNode *pParent, CLight *Light)
|
||||
: CSceneNode(pScene, pParent)
|
||||
{
|
||||
mpLight = Light;
|
||||
mLocalAABox = CAABox::skOne;
|
||||
mPosition = Light->GetPosition();
|
||||
|
||||
switch (Light->GetType())
|
||||
{
|
||||
case eLocalAmbient: SetName("Ambient Light"); break;
|
||||
case eDirectional: SetName("Directional Light"); break;
|
||||
case eSpot: SetName("Spot Light"); break;
|
||||
case eCustom: SetName("Custom Light"); break;
|
||||
}
|
||||
}
|
||||
|
||||
ENodeType CLightNode::NodeType()
|
||||
{
|
||||
return eLightNode;
|
||||
}
|
||||
|
||||
void CLightNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
|
||||
{
|
||||
if (ViewInfo.GameMode) return;
|
||||
|
||||
if (ViewInfo.ViewFrustum.BoxInFrustum(AABox()))
|
||||
pRenderer->AddOpaqueMesh(this, -1, AABox(), eDrawMesh);
|
||||
|
||||
if (IsSelected() && mpLight->GetType() == eCustom)
|
||||
{
|
||||
CAABox RadiusBox = (CAABox::skOne * 2.f * mpLight->GetRadius()) + mPosition;
|
||||
|
||||
if (ViewInfo.ViewFrustum.BoxInFrustum(RadiusBox))
|
||||
pRenderer->AddOpaqueMesh(this, -1, AABox(), eDrawSelection);
|
||||
}
|
||||
}
|
||||
|
||||
void CLightNode::Draw(ERenderOptions /*Options*/, int /*ComponentIndex*/, const SViewInfo& ViewInfo)
|
||||
{
|
||||
CDrawUtil::DrawLightBillboard(mpLight->GetType(), mpLight->GetColor(), mPosition, BillboardScale(), TintColor(ViewInfo));
|
||||
}
|
||||
|
||||
void CLightNode::DrawSelection()
|
||||
{
|
||||
CDrawUtil::DrawWireSphere(mPosition, mpLight->GetRadius(), mpLight->GetColor());
|
||||
}
|
||||
|
||||
void CLightNode::RayAABoxIntersectTest(CRayCollisionTester& Tester, const SViewInfo& /*ViewInfo*/)
|
||||
{
|
||||
CVector2f BillScale = BillboardScale();
|
||||
float ScaleXY = (BillScale.x > BillScale.y ? BillScale.x : BillScale.y);
|
||||
|
||||
CAABox BillBox = CAABox(mPosition + CVector3f(-ScaleXY, -ScaleXY, -BillScale.y),
|
||||
mPosition + CVector3f( ScaleXY, ScaleXY, BillScale.y));
|
||||
|
||||
std::pair<bool,float> BoxResult = BillBox.IntersectsRay(Tester.Ray());
|
||||
if (BoxResult.first) Tester.AddNode(this, 0, BoxResult.second);
|
||||
}
|
||||
|
||||
SRayIntersection CLightNode::RayNodeIntersectTest(const CRay& Ray, u32 AssetID, const SViewInfo& ViewInfo)
|
||||
{
|
||||
// todo: come up with a better way to share this code between CScriptNode and CLightNode
|
||||
SRayIntersection out;
|
||||
out.pNode = this;
|
||||
out.ComponentIndex = AssetID;
|
||||
|
||||
CTexture *pBillboard = CDrawUtil::GetLightTexture(mpLight->GetType());
|
||||
|
||||
if (!pBillboard)
|
||||
{
|
||||
out.Hit = false;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Step 1: check whether the ray intersects with the plane the billboard is on
|
||||
CPlane BillboardPlane(-ViewInfo.pCamera->Direction(), mPosition);
|
||||
std::pair<bool,float> PlaneTest = Math::RayPlaneIntersecton(Ray, BillboardPlane);
|
||||
|
||||
if (PlaneTest.first)
|
||||
{
|
||||
// Step 2: transform the hit point into the plane's local space
|
||||
CVector3f PlaneHitPoint = Ray.PointOnRay(PlaneTest.second);
|
||||
CVector3f RelHitPoint = PlaneHitPoint - mPosition;
|
||||
|
||||
CVector3f PlaneForward = -ViewInfo.pCamera->Direction();
|
||||
CVector3f PlaneRight = -ViewInfo.pCamera->RightVector();
|
||||
CVector3f PlaneUp = ViewInfo.pCamera->UpVector();
|
||||
CQuaternion PlaneRot = CQuaternion::FromAxes(PlaneRight, PlaneForward, PlaneUp);
|
||||
|
||||
CVector3f RotatedHitPoint = PlaneRot.Inverse() * RelHitPoint;
|
||||
CVector2f LocalHitPoint = RotatedHitPoint.xz() / BillboardScale();
|
||||
|
||||
// Step 3: check whether the transformed hit point is in the -1 to 1 range
|
||||
if ((LocalHitPoint.x >= -1.f) && (LocalHitPoint.x <= 1.f) && (LocalHitPoint.y >= -1.f) && (LocalHitPoint.y <= 1.f))
|
||||
{
|
||||
// Step 4: look up the hit texel and check whether it's transparent or opaque
|
||||
CVector2f TexCoord = (LocalHitPoint + CVector2f(1.f)) * 0.5f;
|
||||
TexCoord.x = -TexCoord.x + 1.f;
|
||||
float TexelAlpha = pBillboard->ReadTexelAlpha(TexCoord);
|
||||
|
||||
if (TexelAlpha < 0.25f)
|
||||
out.Hit = false;
|
||||
|
||||
else
|
||||
{
|
||||
// It's opaque... we have a hit!
|
||||
out.Hit = true;
|
||||
out.Distance = PlaneTest.second;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
out.Hit = false;
|
||||
}
|
||||
|
||||
else
|
||||
out.Hit = false;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
CLight* CLightNode::Light()
|
||||
{
|
||||
return mpLight;
|
||||
}
|
||||
|
||||
CVector2f CLightNode::BillboardScale()
|
||||
{
|
||||
return AbsoluteScale().xz() * 0.75f;
|
||||
}
|
||||
|
||||
void CLightNode::CalculateTransform(CTransform4f& rOut) const
|
||||
{
|
||||
// Billboards don't rotate and their scale is applied separately
|
||||
rOut.Translate(AbsolutePosition());
|
||||
}
|
||||
26
src/Core/Scene/CLightNode.h
Normal file
26
src/Core/Scene/CLightNode.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef CLIGHTNODE_H
|
||||
#define CLIGHTNODE_H
|
||||
|
||||
#include "CSceneNode.h"
|
||||
#include <Resource/CLight.h>
|
||||
|
||||
class CLightNode : public CSceneNode
|
||||
{
|
||||
CLight *mpLight;
|
||||
public:
|
||||
CLightNode(CSceneManager *pScene, CSceneNode *pParent = 0, CLight *Light = 0);
|
||||
ENodeType NodeType();
|
||||
void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo);
|
||||
void Draw(ERenderOptions Options, int ComponentIndex, const SViewInfo& ViewInfo);
|
||||
void DrawSelection();
|
||||
void RayAABoxIntersectTest(CRayCollisionTester& Tester, const SViewInfo& ViewInfo);
|
||||
SRayIntersection RayNodeIntersectTest(const CRay &Ray, u32 AssetID, const SViewInfo& ViewInfo);
|
||||
bool AllowsRotate() const { return false; }
|
||||
CLight* Light();
|
||||
CVector2f BillboardScale();
|
||||
|
||||
protected:
|
||||
void CalculateTransform(CTransform4f& rOut) const;
|
||||
};
|
||||
|
||||
#endif // CLIGHTNODE_H
|
||||
122
src/Core/Scene/CModelNode.cpp
Normal file
122
src/Core/Scene/CModelNode.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "CModelNode.h"
|
||||
#include <Common/Math.h>
|
||||
#include <Core/CDrawUtil.h>
|
||||
#include <Core/CRenderer.h>
|
||||
#include <Core/CGraphics.h>
|
||||
|
||||
CModelNode::CModelNode(CSceneManager *pScene, CSceneNode *pParent, CModel *pModel) : CSceneNode(pScene, pParent)
|
||||
{
|
||||
SetModel(pModel);
|
||||
mScale = CVector3f(1.f);
|
||||
mLightingEnabled = true;
|
||||
mForceAlphaOn = false;
|
||||
}
|
||||
|
||||
ENodeType CModelNode::NodeType()
|
||||
{
|
||||
return eModelNode;
|
||||
}
|
||||
|
||||
void CModelNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
|
||||
{
|
||||
if (!mpModel) return;
|
||||
if (!ViewInfo.ViewFrustum.BoxInFrustum(AABox())) return;
|
||||
if (ViewInfo.GameMode) return;
|
||||
|
||||
if (!mpModel->HasTransparency(mActiveMatSet))
|
||||
pRenderer->AddOpaqueMesh(this, -1, AABox(), eDrawMesh);
|
||||
else
|
||||
AddSurfacesToRenderer(pRenderer, mpModel, mActiveMatSet, ViewInfo);
|
||||
|
||||
if (mSelected)
|
||||
pRenderer->AddOpaqueMesh(this, -1, AABox(), eDrawSelection);
|
||||
}
|
||||
|
||||
void CModelNode::Draw(ERenderOptions Options, int ComponentIndex, const SViewInfo& ViewInfo)
|
||||
{
|
||||
if (!mpModel) return;
|
||||
if (mForceAlphaOn) Options = (ERenderOptions) (Options & ~eNoAlpha);
|
||||
|
||||
if (mLightingEnabled)
|
||||
{
|
||||
CGraphics::SetDefaultLighting();
|
||||
CGraphics::UpdateLightBlock();
|
||||
CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::skDefaultAmbientColor.ToVector4f();
|
||||
}
|
||||
else
|
||||
{
|
||||
CGraphics::sNumLights = 0;
|
||||
CGraphics::sVertexBlock.COLOR0_Amb = CColor::skBlack.ToVector4f();
|
||||
}
|
||||
|
||||
CGraphics::sPixelBlock.TevColor = CVector4f(1,1,1,1);
|
||||
CGraphics::sPixelBlock.TintColor = TintColor(ViewInfo).ToVector4f();
|
||||
LoadModelMatrix();
|
||||
|
||||
if (ComponentIndex < 0)
|
||||
mpModel->Draw(Options, mActiveMatSet);
|
||||
else
|
||||
mpModel->DrawSurface(Options, ComponentIndex, mActiveMatSet);
|
||||
}
|
||||
|
||||
void CModelNode::DrawSelection()
|
||||
{
|
||||
if (!mpModel) return;
|
||||
LoadModelMatrix();
|
||||
mpModel->DrawWireframe(eNoRenderOptions, WireframeColor());
|
||||
}
|
||||
|
||||
void CModelNode::RayAABoxIntersectTest(CRayCollisionTester& Tester, const SViewInfo& /*ViewInfo*/)
|
||||
{
|
||||
if (!mpModel) return;
|
||||
|
||||
const CRay& Ray = Tester.Ray();
|
||||
std::pair<bool,float> BoxResult = AABox().IntersectsRay(Ray);
|
||||
|
||||
if (BoxResult.first)
|
||||
Tester.AddNodeModel(this, mpModel);
|
||||
}
|
||||
|
||||
SRayIntersection CModelNode::RayNodeIntersectTest(const CRay &Ray, u32 AssetID, const SViewInfo& ViewInfo)
|
||||
{
|
||||
SRayIntersection out;
|
||||
out.pNode = this;
|
||||
out.ComponentIndex = AssetID;
|
||||
|
||||
CRay TransformedRay = Ray.Transformed(Transform().Inverse());
|
||||
ERenderOptions options = ViewInfo.pRenderer->RenderOptions();
|
||||
std::pair<bool,float> Result = mpModel->GetSurface(AssetID)->IntersectsRay(TransformedRay, ((options & eEnableBackfaceCull) == 0));
|
||||
|
||||
if (Result.first)
|
||||
{
|
||||
out.Hit = true;
|
||||
|
||||
CVector3f HitPoint = TransformedRay.PointOnRay(Result.second);
|
||||
CVector3f WorldHitPoint = Transform() * HitPoint;
|
||||
out.Distance = Math::Distance(Ray.Origin(), WorldHitPoint);
|
||||
}
|
||||
|
||||
else
|
||||
out.Hit = false;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void CModelNode::SetModel(CModel *pModel)
|
||||
{
|
||||
mpModel = pModel;
|
||||
mActiveMatSet = 0;
|
||||
|
||||
if (pModel)
|
||||
{
|
||||
SetName(pModel->Source());
|
||||
mLocalAABox = mpModel->AABox();
|
||||
}
|
||||
|
||||
MarkTransformChanged();
|
||||
}
|
||||
|
||||
void CModelNode::ForceAlphaEnabled(bool Enable)
|
||||
{
|
||||
mForceAlphaOn = Enable;
|
||||
}
|
||||
60
src/Core/Scene/CModelNode.h
Normal file
60
src/Core/Scene/CModelNode.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef CMODELNODE_H
|
||||
#define CMODELNODE_H
|
||||
|
||||
#include "CSceneNode.h"
|
||||
#include <Resource/model/CModel.h>
|
||||
|
||||
class CModelNode : public CSceneNode
|
||||
{
|
||||
TResPtr<CModel> mpModel;
|
||||
u32 mActiveMatSet;
|
||||
bool mLightingEnabled;
|
||||
bool mForceAlphaOn;
|
||||
|
||||
public:
|
||||
explicit CModelNode(CSceneManager *pScene, CSceneNode *pParent = 0, CModel *pModel = 0);
|
||||
|
||||
virtual ENodeType NodeType();
|
||||
virtual void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo);
|
||||
virtual void Draw(ERenderOptions Options, int ComponentIndex, const SViewInfo& ViewInfo);
|
||||
virtual void DrawSelection();
|
||||
virtual void RayAABoxIntersectTest(CRayCollisionTester& Tester, const SViewInfo& ViewInfo);
|
||||
virtual SRayIntersection RayNodeIntersectTest(const CRay &Ray, u32 AssetID, const SViewInfo& ViewInfo);
|
||||
|
||||
void SetModel(CModel *pModel);
|
||||
void SetMatSet(u32 MatSet);
|
||||
void SetDynamicLighting(bool Enable);
|
||||
void ForceAlphaEnabled(bool Enable);
|
||||
CModel* Model();
|
||||
u32 MatSet();
|
||||
bool IsDynamicLightingEnabled();
|
||||
};
|
||||
|
||||
|
||||
// ************ INLINE FUNCTIONS ************
|
||||
inline void CModelNode::SetMatSet(u32 MatSet)
|
||||
{
|
||||
mActiveMatSet = MatSet;
|
||||
}
|
||||
|
||||
inline void CModelNode::SetDynamicLighting(bool Enable)
|
||||
{
|
||||
mLightingEnabled = Enable;
|
||||
}
|
||||
|
||||
inline CModel* CModelNode::Model()
|
||||
{
|
||||
return mpModel;
|
||||
}
|
||||
|
||||
inline u32 CModelNode::MatSet()
|
||||
{
|
||||
return mActiveMatSet;
|
||||
}
|
||||
|
||||
inline bool CModelNode::IsDynamicLightingEnabled()
|
||||
{
|
||||
return mLightingEnabled;
|
||||
}
|
||||
|
||||
#endif // CMODELNODE_H
|
||||
27
src/Core/Scene/CRootNode.h
Normal file
27
src/Core/Scene/CRootNode.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef CROOTNODE_H
|
||||
#define CROOTNODE_H
|
||||
|
||||
#include "CSceneNode.h"
|
||||
#include <iostream>
|
||||
|
||||
// CRootNode's main purpose is to manage groups of other nodes as its children.
|
||||
class CRootNode : public CSceneNode
|
||||
{
|
||||
public:
|
||||
explicit CRootNode(CSceneManager *pScene, CSceneNode *pParent = 0) : CSceneNode(pScene, pParent) {}
|
||||
~CRootNode() {}
|
||||
|
||||
inline ENodeType NodeType() {
|
||||
return eRootNode;
|
||||
}
|
||||
|
||||
inline void RayAABoxIntersectTest(CRayCollisionTester&, const SViewInfo&) {}
|
||||
|
||||
inline SRayIntersection RayNodeIntersectTest(const CRay &, u32, const SViewInfo&) {
|
||||
return SRayIntersection(false, 0.f, nullptr, 0);
|
||||
}
|
||||
|
||||
inline void DrawSelection() {}
|
||||
};
|
||||
|
||||
#endif // CROOTNODE_H
|
||||
353
src/Core/Scene/CSceneManager.cpp
Normal file
353
src/Core/Scene/CSceneManager.cpp
Normal file
@@ -0,0 +1,353 @@
|
||||
#include "CSceneManager.h"
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include <Common/CRay.h>
|
||||
#include <Common/CRayCollisionTester.h>
|
||||
#include <Common/TString.h>
|
||||
#include <Core/CGraphics.h>
|
||||
#include <Core/CResCache.h>
|
||||
#include <FileIO/CFileInStream.h>
|
||||
#include <Resource/script/CScriptLayer.h>
|
||||
|
||||
/**
|
||||
* This class direly needs a rewrite
|
||||
* Future plan is to integrate a "scene layer" system, where nodes are grouped into layers
|
||||
* We would have terrain layer, lights layer, collision layer, multiple script layers, etc
|
||||
* Advantage of this is that I don't need to write separate functions for every single node type
|
||||
* They can all be tracked together and the code could be streamlined a lot.
|
||||
*/
|
||||
CSceneManager::CSceneManager()
|
||||
{
|
||||
mSplitTerrain = true;
|
||||
mNodeCount = 0;
|
||||
mpSceneRootNode = new CRootNode(this, nullptr);
|
||||
mpArea = nullptr;
|
||||
mpWorld = nullptr;
|
||||
mpAreaRootNode = nullptr;
|
||||
}
|
||||
|
||||
CSceneManager::~CSceneManager()
|
||||
{
|
||||
ClearScene();
|
||||
}
|
||||
|
||||
CModelNode* CSceneManager::AddModel(CModel *m)
|
||||
{
|
||||
if (m == nullptr) return nullptr;
|
||||
|
||||
CModelNode *node = new CModelNode(this, mpSceneRootNode, m);
|
||||
mModelNodes.push_back(node);
|
||||
mNodeCount++;
|
||||
return node;
|
||||
}
|
||||
|
||||
CStaticNode* CSceneManager::AddStaticModel(CStaticModel *mdl)
|
||||
{
|
||||
if (mdl == nullptr) return nullptr;
|
||||
|
||||
CStaticNode *node = new CStaticNode(this, mpAreaRootNode, mdl);
|
||||
mStaticNodes.push_back(node);
|
||||
mNodeCount++;
|
||||
return node;
|
||||
}
|
||||
|
||||
CCollisionNode* CSceneManager::AddCollision(CCollisionMeshGroup *mesh)
|
||||
{
|
||||
if (mesh == nullptr) return nullptr;
|
||||
|
||||
CCollisionNode *node = new CCollisionNode(this, mpAreaRootNode, mesh);
|
||||
mCollisionNodes.push_back(node);
|
||||
mNodeCount++;
|
||||
return node;
|
||||
}
|
||||
|
||||
CScriptNode* CSceneManager::AddScriptObject(CScriptObject *obj)
|
||||
{
|
||||
if (obj == nullptr) return nullptr;
|
||||
|
||||
CScriptNode *node = new CScriptNode(this, mpAreaRootNode, obj);
|
||||
mScriptNodes.push_back(node);
|
||||
mNodeCount++;
|
||||
return node;
|
||||
}
|
||||
|
||||
CLightNode* CSceneManager::AddLight(CLight *Light)
|
||||
{
|
||||
if (Light == nullptr) return nullptr;
|
||||
|
||||
CLightNode *node = new CLightNode(this, mpAreaRootNode, Light);
|
||||
mLightNodes.push_back(node);
|
||||
mNodeCount++;
|
||||
return node;
|
||||
}
|
||||
|
||||
void CSceneManager::SetActiveArea(CGameArea* _area)
|
||||
{
|
||||
// Clear existing area
|
||||
delete mpAreaRootNode;
|
||||
mModelNodes.clear();
|
||||
mStaticNodes.clear();
|
||||
mCollisionNodes.clear();
|
||||
mScriptNodes.clear();
|
||||
mLightNodes.clear();
|
||||
mAreaAttributesObjects.clear();
|
||||
mpActiveAreaAttributes = nullptr;
|
||||
mScriptNodeMap.clear();
|
||||
|
||||
// Create nodes for new area
|
||||
mpArea = _area;
|
||||
mpAreaRootNode = new CRootNode(this, mpSceneRootNode);
|
||||
|
||||
if (mSplitTerrain)
|
||||
{
|
||||
u32 count = mpArea->GetStaticModelCount();
|
||||
for (u32 m = 0; m < count; m++)
|
||||
AddStaticModel(mpArea->GetStaticModel(m));
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 count = mpArea->GetTerrainModelCount();
|
||||
for (u32 m = 0; m < count; m++)
|
||||
{
|
||||
CModel *mdl = mpArea->GetTerrainModel(m);
|
||||
CModelNode *node = AddModel(mdl);
|
||||
node->SetDynamicLighting(false);
|
||||
}
|
||||
}
|
||||
|
||||
AddCollision(mpArea->GetCollision());
|
||||
|
||||
u32 NumLayers = mpArea->GetScriptLayerCount();
|
||||
for (u32 l = 0; l < NumLayers; l++)
|
||||
{
|
||||
CScriptLayer *layer = mpArea->GetScriptLayer(l);
|
||||
u32 NumObjects = layer->GetNumObjects();
|
||||
mScriptNodes.reserve(mScriptNodes.size() + NumObjects);
|
||||
|
||||
for (u32 o = 0; o < NumObjects; o++)
|
||||
{
|
||||
CScriptObject *pObj = layer->ObjectByIndex(o);
|
||||
CScriptNode *Node = AddScriptObject( pObj );
|
||||
Node->BuildLightList(mpArea);
|
||||
|
||||
// Add to map
|
||||
mScriptNodeMap[pObj->InstanceID()] = Node;
|
||||
|
||||
// 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 o = 0; o < pGenLayer->GetNumObjects(); o++)
|
||||
{
|
||||
CScriptObject *pObj = pGenLayer->ObjectByIndex(o);
|
||||
CScriptNode *Node = AddScriptObject(pObj);
|
||||
|
||||
// Add to map
|
||||
mScriptNodeMap[pObj->InstanceID()] = Node;
|
||||
}
|
||||
}
|
||||
PickEnvironmentObjects();
|
||||
|
||||
// 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 ly = 0; ly < NumLightLayers; ly++)
|
||||
{
|
||||
u32 NumLights = mpArea->GetLightCount(ly);
|
||||
|
||||
for (u32 l = 0; l < NumLights; l++)
|
||||
{
|
||||
CLight *Light = mpArea->GetLight(ly, l);
|
||||
|
||||
if (Light->GetType() == eLocalAmbient)
|
||||
CGraphics::sAreaAmbientColor += Light->GetColor();
|
||||
|
||||
AddLight(Light);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << CSceneNode::NumNodes() << " nodes\n";
|
||||
}
|
||||
|
||||
void CSceneManager::SetActiveWorld(CWorld* _world)
|
||||
{
|
||||
mpWorld = _world;
|
||||
}
|
||||
|
||||
void CSceneManager::ClearScene()
|
||||
{
|
||||
if (mpAreaRootNode)
|
||||
{
|
||||
mpAreaRootNode->Unparent();
|
||||
delete mpAreaRootNode;
|
||||
}
|
||||
|
||||
mModelNodes.clear();
|
||||
mStaticNodes.clear();
|
||||
mCollisionNodes.clear();
|
||||
mScriptNodes.clear();
|
||||
mLightNodes.clear();
|
||||
|
||||
mpArea = nullptr;
|
||||
mpWorld = nullptr;
|
||||
mNodeCount = 0;
|
||||
}
|
||||
|
||||
void CSceneManager::AddSceneToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
|
||||
{
|
||||
ERenderOptions Options = pRenderer->RenderOptions();
|
||||
|
||||
if (Options & eDrawWorld || ViewInfo.GameMode)
|
||||
{
|
||||
for (u32 n = 0; n < mModelNodes.size(); n++)
|
||||
if (mModelNodes[n]->IsVisible())
|
||||
mModelNodes[n]->AddToRenderer(pRenderer, ViewInfo);
|
||||
|
||||
for (u32 n = 0; n < mStaticNodes.size(); n++)
|
||||
if (mStaticNodes[n]->IsVisible())
|
||||
mStaticNodes[n]->AddToRenderer(pRenderer, ViewInfo);
|
||||
}
|
||||
|
||||
if (Options & eDrawWorldCollision && !ViewInfo.GameMode)
|
||||
{
|
||||
for (u32 n = 0; n < mCollisionNodes.size(); n++)
|
||||
if (mCollisionNodes[n]->IsVisible())
|
||||
mCollisionNodes[n]->AddToRenderer(pRenderer, ViewInfo);
|
||||
}
|
||||
|
||||
if (Options & eDrawLights && !ViewInfo.GameMode)
|
||||
{
|
||||
for (u32 n = 0; n < mLightNodes.size(); n++)
|
||||
if (mLightNodes[n]->IsVisible())
|
||||
mLightNodes[n]->AddToRenderer(pRenderer, ViewInfo);
|
||||
}
|
||||
|
||||
if ((Options & eDrawObjects) || (Options & eDrawObjectCollision) || ViewInfo.GameMode)
|
||||
{
|
||||
for (u32 n = 0; n < mScriptNodes.size(); n++)
|
||||
if (mScriptNodes[n]->IsVisible())
|
||||
mScriptNodes[n]->AddToRenderer(pRenderer, ViewInfo);
|
||||
}
|
||||
}
|
||||
|
||||
SRayIntersection CSceneManager::SceneRayCast(const CRay& Ray, const SViewInfo& ViewInfo)
|
||||
{
|
||||
// Terribly hacky stuff to avoid having tons of redundant code
|
||||
// because I'm too lazy to rewrite CSceneManager right now and fix it
|
||||
// (I'm probably going to do it soon...)
|
||||
ERenderOptions renderOptions = ViewInfo.pRenderer->RenderOptions();
|
||||
|
||||
std::vector<CSceneNode*> *pNodeVectors[5] = {
|
||||
reinterpret_cast<std::vector<CSceneNode*>*>(&mModelNodes),
|
||||
reinterpret_cast<std::vector<CSceneNode*>*>(&mStaticNodes),
|
||||
reinterpret_cast<std::vector<CSceneNode*>*>(&mCollisionNodes),
|
||||
reinterpret_cast<std::vector<CSceneNode*>*>(&mScriptNodes),
|
||||
reinterpret_cast<std::vector<CSceneNode*>*>(&mLightNodes),
|
||||
};
|
||||
bool NodesVisible[5] = {
|
||||
true, ((renderOptions & eDrawWorld) != 0), ((renderOptions & eDrawWorldCollision) != 0),
|
||||
((renderOptions & ((ERenderOptions) (eDrawObjects | eDrawObjectCollision))) != 0), ((renderOptions & eDrawLights) != 0)
|
||||
};
|
||||
|
||||
// Override visibility for game mode
|
||||
if (ViewInfo.GameMode)
|
||||
{
|
||||
NodesVisible[0] = false;
|
||||
NodesVisible[1] = true;
|
||||
NodesVisible[2] = false;
|
||||
NodesVisible[3] = true;
|
||||
NodesVisible[4] = false;
|
||||
}
|
||||
|
||||
// Less hacky stuff
|
||||
CRayCollisionTester Tester(Ray);
|
||||
|
||||
for (u32 iVec = 0; iVec < 5; iVec++)
|
||||
{
|
||||
if (!NodesVisible[iVec]) continue;
|
||||
|
||||
std::vector<CSceneNode*>& vec = *pNodeVectors[iVec];
|
||||
|
||||
for (u32 iNode = 0; iNode < vec.size(); iNode++)
|
||||
if (vec[iNode]->IsVisible())
|
||||
vec[iNode]->RayAABoxIntersectTest(Tester, ViewInfo);
|
||||
}
|
||||
|
||||
return Tester.TestNodes(ViewInfo);
|
||||
}
|
||||
|
||||
void CSceneManager::PickEnvironmentObjects()
|
||||
{
|
||||
// Pick AreaAttributes
|
||||
for (auto it = mAreaAttributesObjects.begin(); it != mAreaAttributesObjects.end(); it++)
|
||||
{
|
||||
if ((*it).IsLayerEnabled())
|
||||
{
|
||||
mpActiveAreaAttributes = &(*it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CScriptNode* CSceneManager::ScriptNodeByID(u32 InstanceID)
|
||||
{
|
||||
auto it = mScriptNodeMap.find(InstanceID);
|
||||
|
||||
if (it != mScriptNodeMap.end()) return it->second;
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
CScriptNode* CSceneManager::NodeForObject(CScriptObject *pObj)
|
||||
{
|
||||
return ScriptNodeByID(pObj->InstanceID());
|
||||
}
|
||||
|
||||
CLightNode* CSceneManager::NodeForLight(CLight *pLight)
|
||||
{
|
||||
// Slow. Is there a better way to do this?
|
||||
for (auto it = mLightNodes.begin(); it != mLightNodes.end(); it++)
|
||||
if ((*it)->Light() == pLight) return *it;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CModel* CSceneManager::GetActiveSkybox()
|
||||
{
|
||||
if (mpActiveAreaAttributes)
|
||||
{
|
||||
if (mpActiveAreaAttributes->IsSkyEnabled())
|
||||
{
|
||||
CModel *pSky = mpActiveAreaAttributes->SkyModel();
|
||||
if (pSky) return pSky;
|
||||
else return mpWorld->GetDefaultSkybox();
|
||||
}
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
CGameArea* CSceneManager::GetActiveArea()
|
||||
{
|
||||
return mpArea;
|
||||
}
|
||||
71
src/Core/Scene/CSceneManager.h
Normal file
71
src/Core/Scene/CSceneManager.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#ifndef CSCENEMANAGER_h
|
||||
#define CSCENEMANAGER_h
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include "CAreaAttributes.h"
|
||||
#include "CRenderer.h"
|
||||
#include "SViewInfo.h"
|
||||
#include <Common/SRayIntersection.h>
|
||||
#include <Common/types.h>
|
||||
#include <Scene/CSceneNode.h>
|
||||
#include <Scene/CRootNode.h>
|
||||
#include <Scene/CLightNode.h>
|
||||
#include <Scene/CModelNode.h>
|
||||
#include <Scene/CScriptNode.h>
|
||||
#include <Scene/CStaticNode.h>
|
||||
#include <Scene/CCollisionNode.h>
|
||||
#include <Resource/CGameArea.h>
|
||||
#include <Resource/CWorld.h>
|
||||
|
||||
class CSceneManager
|
||||
{
|
||||
bool mSplitTerrain;
|
||||
|
||||
u32 mNodeCount;
|
||||
std::vector<CModelNode*> mModelNodes;
|
||||
std::vector<CStaticNode*> mStaticNodes;
|
||||
std::vector<CCollisionNode*> mCollisionNodes;
|
||||
std::vector<CScriptNode*> mScriptNodes;
|
||||
std::vector<CLightNode*> mLightNodes;
|
||||
CRootNode *mpSceneRootNode;
|
||||
|
||||
TResPtr<CGameArea> mpArea;
|
||||
TResPtr<CWorld> mpWorld;
|
||||
CRootNode *mpAreaRootNode;
|
||||
|
||||
// Environment
|
||||
std::vector<CAreaAttributes> mAreaAttributesObjects;
|
||||
CAreaAttributes *mpActiveAreaAttributes;
|
||||
|
||||
// Objects
|
||||
std::unordered_map<u32, CScriptNode*> mScriptNodeMap;
|
||||
|
||||
public:
|
||||
CSceneManager();
|
||||
~CSceneManager();
|
||||
|
||||
// Scene Management
|
||||
CModelNode* AddModel(CModel *mdl);
|
||||
CStaticNode* AddStaticModel(CStaticModel *mdl);
|
||||
CCollisionNode* AddCollision(CCollisionMeshGroup *mesh);
|
||||
CScriptNode* AddScriptObject(CScriptObject *obj);
|
||||
CLightNode* AddLight(CLight *Light);
|
||||
void SetActiveArea(CGameArea *_area);
|
||||
void SetActiveWorld(CWorld *_world);
|
||||
void ClearScene();
|
||||
void AddSceneToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo);
|
||||
SRayIntersection SceneRayCast(const CRay& Ray, const SViewInfo& ViewInfo);
|
||||
void PickEnvironmentObjects();
|
||||
CScriptNode* ScriptNodeByID(u32 InstanceID);
|
||||
CScriptNode* NodeForObject(CScriptObject *pObj);
|
||||
CLightNode* NodeForLight(CLight *pLight);
|
||||
|
||||
// Setters/Getters
|
||||
CModel* GetActiveSkybox();
|
||||
CGameArea* GetActiveArea();
|
||||
};
|
||||
|
||||
#endif // CSCENEMANAGER_H
|
||||
472
src/Core/Scene/CSceneNode.cpp
Normal file
472
src/Core/Scene/CSceneNode.cpp
Normal file
@@ -0,0 +1,472 @@
|
||||
#include "CSceneNode.h"
|
||||
#include <Core/CRenderer.h>
|
||||
#include <gtc/quaternion.hpp>
|
||||
#include <gtx/transform.hpp>
|
||||
#include <Common/AnimUtil.h>
|
||||
#include <Common/CTransform4f.h>
|
||||
#include <Resource/CGameArea.h>
|
||||
#include <Core/CGraphics.h>
|
||||
#include <Core/CDrawUtil.h>
|
||||
#include <algorithm>
|
||||
|
||||
u32 CSceneNode::smNumNodes = 0;
|
||||
CColor CSceneNode::skSelectionTint((u8) 39, 154, 167, 255);
|
||||
|
||||
CSceneNode::CSceneNode(CSceneManager *pScene, CSceneNode *pParent)
|
||||
{
|
||||
smNumNodes++;
|
||||
mpScene = pScene;
|
||||
mpParent = pParent;
|
||||
|
||||
mPosition = CVector3f::skZero;
|
||||
mRotation = CQuaternion::skIdentity;
|
||||
mScale = CVector3f::skOne;
|
||||
_mTransformDirty = true;
|
||||
|
||||
_mInheritsPosition = true;
|
||||
_mInheritsRotation = true;
|
||||
_mInheritsScale = true;
|
||||
|
||||
mLightLayerIndex = 0;
|
||||
mLightCount = 0;
|
||||
|
||||
mMouseHovering = false;
|
||||
mSelected = false;
|
||||
mVisible = true;
|
||||
|
||||
if (mpParent)
|
||||
mpParent->mChildren.push_back(this);
|
||||
}
|
||||
|
||||
CSceneNode::~CSceneNode()
|
||||
{
|
||||
smNumNodes--;
|
||||
for (auto it = mChildren.begin(); it != mChildren.end(); it++)
|
||||
delete (*it);
|
||||
}
|
||||
|
||||
// ************ VIRTUAL ************
|
||||
void CSceneNode::DrawSelection()
|
||||
{
|
||||
// Default implementation for virtual function
|
||||
CDrawUtil::DrawWireCube(AABox(), CColor::skWhite);
|
||||
}
|
||||
|
||||
void CSceneNode::RayAABoxIntersectTest(CRayCollisionTester& Tester, const SViewInfo& /*ViewInfo*/)
|
||||
{
|
||||
// Default implementation for virtual function
|
||||
std::pair<bool,float> result = AABox().IntersectsRay(Tester.Ray());
|
||||
|
||||
if (result.first)
|
||||
Tester.AddNode(this, -1, result.second);
|
||||
}
|
||||
|
||||
bool CSceneNode::IsVisible() const
|
||||
{
|
||||
// Default implementation for virtual function
|
||||
return mVisible;
|
||||
}
|
||||
|
||||
CColor CSceneNode::TintColor(const SViewInfo& ViewInfo) const
|
||||
{
|
||||
// Default implementation for virtual function
|
||||
return (IsSelected() && !ViewInfo.GameMode ? skSelectionTint : CColor::skWhite);
|
||||
}
|
||||
|
||||
CColor CSceneNode::WireframeColor() const
|
||||
{
|
||||
// Default implementation for virtual function
|
||||
return CColor::skWhite;
|
||||
}
|
||||
|
||||
// ************ MAIN FUNCTIONALITY ************
|
||||
void CSceneNode::Unparent()
|
||||
{
|
||||
// May eventually want to reset XForm so global position = local position
|
||||
// Seems like a waste performance wise for the time being though
|
||||
if (mpParent)
|
||||
mpParent->RemoveChild(this);
|
||||
|
||||
mpParent = nullptr;
|
||||
}
|
||||
|
||||
void CSceneNode::RemoveChild(CSceneNode *pChild)
|
||||
{
|
||||
for (auto it = mChildren.begin(); it != mChildren.end(); it++)
|
||||
{
|
||||
if (*it == pChild)
|
||||
{
|
||||
mChildren.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSceneNode::DeleteChildren()
|
||||
{
|
||||
for (auto it = mChildren.begin(); it != mChildren.end(); it++)
|
||||
delete *it;
|
||||
|
||||
mChildren.clear();
|
||||
}
|
||||
|
||||
void CSceneNode::SetInheritance(bool InheritPos, bool InheritRot, bool InheritScale)
|
||||
{
|
||||
_mInheritsPosition = InheritPos;
|
||||
_mInheritsRotation = InheritRot;
|
||||
_mInheritsScale = InheritScale;
|
||||
MarkTransformChanged();
|
||||
}
|
||||
|
||||
void CSceneNode::LoadModelMatrix()
|
||||
{
|
||||
CGraphics::sMVPBlock.ModelMatrix = Transform().ToMatrix4f();
|
||||
CGraphics::UpdateMVPBlock();
|
||||
}
|
||||
|
||||
void CSceneNode::BuildLightList(CGameArea *pArea)
|
||||
{
|
||||
mLightCount = 0;
|
||||
mAmbientColor = CColor::skBlack;
|
||||
|
||||
u32 index = mLightLayerIndex;
|
||||
if ((pArea->GetLightLayerCount() <= index) || (pArea->GetLightCount(index) == 0)) index = 0;
|
||||
|
||||
struct SLightEntry {
|
||||
CLight *pLight;
|
||||
float Distance;
|
||||
|
||||
SLightEntry(CLight *_pLight, float _Distance)
|
||||
: pLight(_pLight), Distance(_Distance) {}
|
||||
|
||||
bool operator<(const SLightEntry& other) {
|
||||
return (Distance < other.Distance);
|
||||
}
|
||||
};
|
||||
std::vector<SLightEntry> LightEntries;
|
||||
|
||||
// Default ambient color to white if there are no lights on the selected layer
|
||||
u32 numLights = pArea->GetLightCount(index);
|
||||
if (numLights == 0) mAmbientColor = CColor::skWhite;
|
||||
|
||||
for (u32 iLight = 0; iLight < numLights; iLight++)
|
||||
{
|
||||
CLight* pLight = pArea->GetLight(index, iLight);
|
||||
|
||||
// Ambient lights should only be present one per layer; need to check how the game deals with multiple ambients
|
||||
if (pLight->GetType() == eLocalAmbient)
|
||||
mAmbientColor = pLight->GetColor();
|
||||
|
||||
// Other lights will be used depending which are closest to the node
|
||||
else
|
||||
{
|
||||
bool IsInRange = AABox().IntersectsSphere(pLight->GetPosition(), pLight->GetRadius());
|
||||
|
||||
if (IsInRange)
|
||||
{
|
||||
float Dist = mPosition.Distance(pLight->GetPosition());
|
||||
LightEntries.push_back(SLightEntry(pLight, Dist));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine which lights are closest
|
||||
std::sort(LightEntries.begin(), LightEntries.end());
|
||||
mLightCount = (LightEntries.size() > 8) ? 8 : LightEntries.size();
|
||||
|
||||
for (u32 i = 0; i < mLightCount; i++)
|
||||
mLights[i] = LightEntries[i].pLight;
|
||||
}
|
||||
|
||||
void CSceneNode::LoadLights(const SViewInfo& ViewInfo)
|
||||
{
|
||||
CGraphics::sNumLights = 0;
|
||||
|
||||
if (CGraphics::sLightMode == CGraphics::eWorldLighting || ViewInfo.GameMode)
|
||||
{
|
||||
// World lighting: world ambient color, node dynamic lights
|
||||
CGraphics::sVertexBlock.COLOR0_Amb = mAmbientColor.ToVector4f();
|
||||
|
||||
for (u32 iLight = 0; iLight < mLightCount; iLight++)
|
||||
mLights[iLight]->Load();
|
||||
}
|
||||
|
||||
else if (CGraphics::sLightMode == CGraphics::eBasicLighting)
|
||||
{
|
||||
// Basic lighting: default ambient color, default dynamic lights
|
||||
CGraphics::SetDefaultLighting();
|
||||
CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::skDefaultAmbientColor.ToVector4f();
|
||||
}
|
||||
|
||||
else if (CGraphics::sLightMode == CGraphics::eNoLighting)
|
||||
{
|
||||
// No lighting: default ambient color, no dynamic lights
|
||||
CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::skDefaultAmbientColor.ToVector4f();
|
||||
}
|
||||
|
||||
CGraphics::UpdateLightBlock();
|
||||
}
|
||||
|
||||
void CSceneNode::DrawBoundingBox() const
|
||||
{
|
||||
CDrawUtil::DrawWireCube(AABox(), CColor::skWhite);
|
||||
}
|
||||
|
||||
void CSceneNode::AddSurfacesToRenderer(CRenderer *pRenderer, CModel *pModel, u32 MatSet, const SViewInfo& rkViewInfo)
|
||||
{
|
||||
u32 SurfaceCount = pModel->GetSurfaceCount();
|
||||
|
||||
for (u32 iSurf = 0; iSurf < SurfaceCount; iSurf++)
|
||||
{
|
||||
CAABox TransformedBox = pModel->GetSurfaceAABox(iSurf).Transformed(Transform());
|
||||
|
||||
if (rkViewInfo.ViewFrustum.BoxInFrustum(TransformedBox))
|
||||
{
|
||||
if (!pModel->IsSurfaceTransparent(iSurf, MatSet))
|
||||
pRenderer->AddOpaqueMesh(this, (int) iSurf, TransformedBox, eDrawMesh);
|
||||
else
|
||||
pRenderer->AddTransparentMesh(this, (int) iSurf, TransformedBox, eDrawMesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ************ TRANSFORM ************
|
||||
void CSceneNode::Translate(const CVector3f& translation, ETransformSpace transformSpace)
|
||||
{
|
||||
switch (transformSpace)
|
||||
{
|
||||
case eWorldTransform:
|
||||
mPosition += translation;
|
||||
break;
|
||||
case eLocalTransform:
|
||||
mPosition += mRotation * translation;
|
||||
break;
|
||||
}
|
||||
MarkTransformChanged();
|
||||
}
|
||||
|
||||
void CSceneNode::Rotate(const CQuaternion& rotation, ETransformSpace transformSpace)
|
||||
{
|
||||
switch (transformSpace)
|
||||
{
|
||||
case eWorldTransform:
|
||||
mRotation = rotation * mRotation;
|
||||
break;
|
||||
case eLocalTransform:
|
||||
mRotation *= rotation;
|
||||
break;
|
||||
}
|
||||
MarkTransformChanged();
|
||||
}
|
||||
|
||||
void CSceneNode::Scale(const CVector3f& scale)
|
||||
{
|
||||
// No support for stretch/skew world-space scaling; local only
|
||||
mScale *= scale;
|
||||
MarkTransformChanged();
|
||||
}
|
||||
|
||||
void CSceneNode::ForceRecalculateTransform() const
|
||||
{
|
||||
_mCachedTransform = CTransform4f::skIdentity;
|
||||
CalculateTransform(_mCachedTransform);
|
||||
_mCachedAABox = mLocalAABox.Transformed(_mCachedTransform);
|
||||
|
||||
// Sync with children - only needed if caller hasn't marked transform changed already
|
||||
// If so, the children will already be marked
|
||||
if (!_mTransformDirty)
|
||||
{
|
||||
for (auto it = mChildren.begin(); it != mChildren.end(); it++)
|
||||
(*it)->MarkTransformChanged();
|
||||
}
|
||||
_mTransformDirty = false;
|
||||
}
|
||||
|
||||
void CSceneNode::MarkTransformChanged() const
|
||||
{
|
||||
if (!_mTransformDirty)
|
||||
{
|
||||
for (auto it = mChildren.begin(); it != mChildren.end(); it++)
|
||||
(*it)->MarkTransformChanged();
|
||||
}
|
||||
|
||||
_mTransformDirty = true;
|
||||
}
|
||||
|
||||
const CTransform4f& CSceneNode::Transform() const
|
||||
{
|
||||
if (_mTransformDirty)
|
||||
ForceRecalculateTransform();
|
||||
|
||||
return _mCachedTransform;
|
||||
}
|
||||
|
||||
void CSceneNode::CalculateTransform(CTransform4f& rOut) const
|
||||
{
|
||||
// Default implementation for virtual function
|
||||
rOut.Scale(AbsoluteScale());
|
||||
rOut.Rotate(AbsoluteRotation());
|
||||
rOut.Translate(AbsolutePosition());
|
||||
}
|
||||
|
||||
// ************ GETTERS ************
|
||||
TString CSceneNode::Name() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
CSceneNode* CSceneNode::Parent() const
|
||||
{
|
||||
return mpParent;
|
||||
}
|
||||
|
||||
CSceneManager* CSceneNode::Scene() const
|
||||
{
|
||||
return mpScene;
|
||||
}
|
||||
|
||||
CVector3f CSceneNode::LocalPosition() const
|
||||
{
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
CVector3f CSceneNode::AbsolutePosition() const
|
||||
{
|
||||
CVector3f ret = mPosition;
|
||||
|
||||
if ((mpParent) && (InheritsPosition()))
|
||||
ret += mpParent->AbsolutePosition();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CQuaternion CSceneNode::LocalRotation() const
|
||||
{
|
||||
return mRotation;
|
||||
}
|
||||
|
||||
CQuaternion CSceneNode::AbsoluteRotation() const
|
||||
{
|
||||
CQuaternion ret = mRotation;
|
||||
|
||||
if ((mpParent) && (InheritsRotation()))
|
||||
ret *= mpParent->AbsoluteRotation();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CVector3f CSceneNode::LocalScale() const
|
||||
{
|
||||
return mScale;
|
||||
}
|
||||
|
||||
CVector3f CSceneNode::AbsoluteScale() const
|
||||
{
|
||||
CVector3f ret = mScale;
|
||||
|
||||
if ((mpParent) && (InheritsScale()))
|
||||
ret *= mpParent->AbsoluteScale();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CAABox CSceneNode::AABox() const
|
||||
{
|
||||
if (_mTransformDirty)
|
||||
ForceRecalculateTransform();
|
||||
|
||||
return _mCachedAABox;
|
||||
}
|
||||
|
||||
CVector3f CSceneNode::CenterPoint() const
|
||||
{
|
||||
return AABox().Center();
|
||||
}
|
||||
|
||||
u32 CSceneNode::LightLayerIndex() const
|
||||
{
|
||||
return mLightLayerIndex;
|
||||
}
|
||||
|
||||
bool CSceneNode::MarkedVisible() const
|
||||
{
|
||||
// The reason I have this function is because the instance view needs to know whether a node is marked
|
||||
// visible independently from other factors that may affect node visibility (as returned by IsVisible()).
|
||||
// It's a little confusing, so maybe there's a better way to set this up.
|
||||
return mVisible;
|
||||
}
|
||||
|
||||
bool CSceneNode::IsMouseHovering() const
|
||||
{
|
||||
return mMouseHovering;
|
||||
}
|
||||
|
||||
bool CSceneNode::IsSelected() const
|
||||
{
|
||||
return mSelected;
|
||||
}
|
||||
|
||||
bool CSceneNode::InheritsPosition() const
|
||||
{
|
||||
return _mInheritsPosition;
|
||||
}
|
||||
|
||||
bool CSceneNode::InheritsRotation() const
|
||||
{
|
||||
return _mInheritsRotation;
|
||||
}
|
||||
|
||||
bool CSceneNode::InheritsScale() const
|
||||
{
|
||||
return _mInheritsScale;
|
||||
}
|
||||
|
||||
// ************ SETTERS ************
|
||||
void CSceneNode::SetName(const TString& Name)
|
||||
{
|
||||
mName = Name;
|
||||
}
|
||||
|
||||
void CSceneNode::SetPosition(const CVector3f& position)
|
||||
{
|
||||
mPosition = position;
|
||||
MarkTransformChanged();
|
||||
}
|
||||
|
||||
void CSceneNode::SetRotation(const CQuaternion& rotation)
|
||||
{
|
||||
mRotation = rotation;
|
||||
MarkTransformChanged();
|
||||
}
|
||||
|
||||
void CSceneNode::SetRotation(const CVector3f& rotEuler)
|
||||
{
|
||||
mRotation = CQuaternion::FromEuler(rotEuler);
|
||||
MarkTransformChanged();
|
||||
}
|
||||
|
||||
void CSceneNode::SetScale(const CVector3f& scale)
|
||||
{
|
||||
mScale = scale;
|
||||
MarkTransformChanged();
|
||||
}
|
||||
|
||||
void CSceneNode::SetLightLayerIndex(u32 index)
|
||||
{
|
||||
mLightLayerIndex = index;
|
||||
}
|
||||
|
||||
void CSceneNode::SetMouseHovering(bool Hovering)
|
||||
{
|
||||
mMouseHovering = Hovering;
|
||||
}
|
||||
|
||||
void CSceneNode::SetSelected(bool Selected)
|
||||
{
|
||||
mSelected = Selected;
|
||||
}
|
||||
|
||||
void CSceneNode::SetVisible(bool Visible)
|
||||
{
|
||||
mVisible = Visible;
|
||||
}
|
||||
131
src/Core/Scene/CSceneNode.h
Normal file
131
src/Core/Scene/CSceneNode.h
Normal file
@@ -0,0 +1,131 @@
|
||||
#ifndef CSCENENODE_H
|
||||
#define CSCENENODE_H
|
||||
|
||||
#include <Core/IRenderable.h>
|
||||
#include "ENodeType.h"
|
||||
#include <Common/CAABox.h>
|
||||
#include <Common/CQuaternion.h>
|
||||
#include <Common/CRay.h>
|
||||
#include <Common/CRayCollisionTester.h>
|
||||
#include <Common/CTransform4f.h>
|
||||
#include <Common/CVector3f.h>
|
||||
#include <Common/ETransformSpace.h>
|
||||
#include <Common/types.h>
|
||||
#include <Core/ERenderOptions.h>
|
||||
#include <Resource/CLight.h>
|
||||
#include <Resource/CGameArea.h>
|
||||
|
||||
class CRenderer;
|
||||
class CSceneManager;
|
||||
|
||||
class CSceneNode : public IRenderable
|
||||
{
|
||||
private:
|
||||
mutable CTransform4f _mCachedTransform;
|
||||
mutable CAABox _mCachedAABox;
|
||||
mutable bool _mTransformDirty;
|
||||
|
||||
bool _mInheritsPosition;
|
||||
bool _mInheritsRotation;
|
||||
bool _mInheritsScale;
|
||||
|
||||
protected:
|
||||
static u32 smNumNodes;
|
||||
TString mName;
|
||||
CSceneNode *mpParent;
|
||||
CSceneManager *mpScene;
|
||||
|
||||
CVector3f mPosition;
|
||||
CQuaternion mRotation;
|
||||
CVector3f mScale;
|
||||
CAABox mLocalAABox;
|
||||
|
||||
bool mMouseHovering;
|
||||
bool mSelected;
|
||||
bool mVisible;
|
||||
std::list<CSceneNode*> mChildren;
|
||||
|
||||
u32 mLightLayerIndex;
|
||||
u32 mLightCount;
|
||||
CLight* mLights[8];
|
||||
CColor mAmbientColor;
|
||||
|
||||
public:
|
||||
explicit CSceneNode(CSceneManager *pScene, CSceneNode *pParent = 0);
|
||||
virtual ~CSceneNode();
|
||||
virtual ENodeType NodeType() = 0;
|
||||
virtual void AddToRenderer(CRenderer* /*pRenderer*/, const SViewInfo& /*ViewInfo*/) {}
|
||||
virtual void DrawSelection();
|
||||
virtual void RayAABoxIntersectTest(CRayCollisionTester& Tester, const SViewInfo& ViewInfo);
|
||||
virtual SRayIntersection RayNodeIntersectTest(const CRay& Ray, u32 AssetID, const SViewInfo& ViewInfo) = 0;
|
||||
virtual bool AllowsTranslate() const { return true; }
|
||||
virtual bool AllowsRotate() const { return true; }
|
||||
virtual bool AllowsScale() const { return true; }
|
||||
virtual bool IsVisible() const;
|
||||
virtual CColor TintColor(const SViewInfo& ViewInfo) const;
|
||||
virtual CColor WireframeColor() const;
|
||||
|
||||
void Unparent();
|
||||
void RemoveChild(CSceneNode *pChild);
|
||||
void DeleteChildren();
|
||||
void SetInheritance(bool InheritPos, bool InheritRot, bool InheritScale);
|
||||
void LoadModelMatrix();
|
||||
void BuildLightList(CGameArea *pArea);
|
||||
void LoadLights(const SViewInfo& ViewInfo);
|
||||
void DrawBoundingBox() const;
|
||||
void AddSurfacesToRenderer(CRenderer *pRenderer, CModel *pModel, u32 MatSet, const SViewInfo& ViewInfo);
|
||||
|
||||
// Transform
|
||||
void Translate(const CVector3f& translation, ETransformSpace transformSpace);
|
||||
void Rotate(const CQuaternion& rotation, ETransformSpace transformSpace);
|
||||
void Scale(const CVector3f& scale);
|
||||
const CTransform4f& Transform() const;
|
||||
protected:
|
||||
void MarkTransformChanged() const;
|
||||
void ForceRecalculateTransform() const;
|
||||
virtual void CalculateTransform(CTransform4f& rOut) const;
|
||||
|
||||
public:
|
||||
// Getters
|
||||
TString Name() const;
|
||||
CSceneNode* Parent() const;
|
||||
CSceneManager* Scene() const;
|
||||
CVector3f LocalPosition() const;
|
||||
CVector3f AbsolutePosition() const;
|
||||
CQuaternion LocalRotation() const;
|
||||
CQuaternion AbsoluteRotation() const;
|
||||
CVector3f LocalScale() const;
|
||||
CVector3f AbsoluteScale() const;
|
||||
CAABox AABox() const;
|
||||
CVector3f CenterPoint() const;
|
||||
u32 LightLayerIndex() const;
|
||||
bool MarkedVisible() const;
|
||||
bool IsMouseHovering() const;
|
||||
bool IsSelected() const;
|
||||
bool InheritsPosition() const;
|
||||
bool InheritsRotation() const;
|
||||
bool InheritsScale() const;
|
||||
|
||||
// Setters
|
||||
void SetName(const TString& Name);
|
||||
void SetPosition(const CVector3f& position);
|
||||
void SetRotation(const CQuaternion& rotation);
|
||||
void SetRotation(const CVector3f& rotEuler);
|
||||
void SetScale(const CVector3f& scale);
|
||||
void SetLightLayerIndex(u32 index);
|
||||
void SetMouseHovering(bool Hovering);
|
||||
void SetSelected(bool Selected);
|
||||
void SetVisible(bool Visible);
|
||||
|
||||
// Static
|
||||
static int NumNodes();
|
||||
static CColor skSelectionTint;
|
||||
};
|
||||
|
||||
// ************ INLINE FUNCTIONS ************
|
||||
inline int CSceneNode::NumNodes()
|
||||
{
|
||||
return smNumNodes;
|
||||
}
|
||||
|
||||
#endif // CSCENENODE_H
|
||||
503
src/Core/Scene/CScriptNode.cpp
Normal file
503
src/Core/Scene/CScriptNode.cpp
Normal file
@@ -0,0 +1,503 @@
|
||||
#include "CScriptNode.h"
|
||||
#include "script/CScriptExtra.h"
|
||||
|
||||
#include <Common/AnimUtil.h>
|
||||
#include <Common/Math.h>
|
||||
#include <Core/CDrawUtil.h>
|
||||
#include <Core/CGraphics.h>
|
||||
#include <Core/CRenderer.h>
|
||||
#include <Core/CResCache.h>
|
||||
#include <Core/CSceneManager.h>
|
||||
#include <Resource/script/CMasterTemplate.h>
|
||||
#include <Resource/script/CScriptLayer.h>
|
||||
|
||||
CScriptNode::CScriptNode(CSceneManager *pScene, CSceneNode *pParent, CScriptObject *pObject)
|
||||
: CSceneNode(pScene, pParent)
|
||||
{
|
||||
mpVolumePreviewNode = nullptr;
|
||||
|
||||
// Evaluate instance
|
||||
mpInstance = pObject;
|
||||
mpActiveModel = nullptr;
|
||||
mpBillboard = nullptr;
|
||||
mpCollisionNode = new CCollisionNode(pScene, this);
|
||||
mpCollisionNode->SetInheritance(true, true, false);
|
||||
|
||||
if (mpInstance)
|
||||
{
|
||||
CScriptTemplate *pTemp = mpInstance->Template();
|
||||
|
||||
// Determine transform
|
||||
mPosition = mpInstance->Position();
|
||||
mRotation = CQuaternion::FromEuler(mpInstance->Rotation());
|
||||
mScale = mpInstance->Scale();
|
||||
mScaleMultiplier = mpInstance->Template()->PreviewScale();
|
||||
MarkTransformChanged();
|
||||
|
||||
SetName("[" + pTemp->TemplateName(mpInstance->NumProperties()) + "] " + mpInstance->InstanceName());
|
||||
|
||||
// Determine display assets
|
||||
mpActiveModel = mpInstance->GetDisplayModel();
|
||||
mpBillboard = mpInstance->GetBillboard();
|
||||
mpCollisionNode->SetCollision(mpInstance->GetCollision());
|
||||
|
||||
// Create preview volume node
|
||||
mHasValidPosition = pTemp->HasPosition();
|
||||
mHasVolumePreview = (pTemp->ScaleType() == CScriptTemplate::eScaleVolume);
|
||||
|
||||
if (mHasVolumePreview)
|
||||
{
|
||||
EVolumeShape shape = mpInstance->VolumeShape();
|
||||
TResPtr<CModel> pVolumeModel = nullptr;
|
||||
|
||||
if ((shape == eAxisAlignedBoxShape) || (shape == eBoxShape))
|
||||
pVolumeModel = gResCache.GetResource("../resources/VolumeBox.cmdl");
|
||||
|
||||
else if (shape == eEllipsoidShape)
|
||||
pVolumeModel = gResCache.GetResource("../resources/VolumeSphere.cmdl");
|
||||
|
||||
else if (shape == eCylinderShape)
|
||||
pVolumeModel = gResCache.GetResource("../resources/VolumeCylinder.cmdl");
|
||||
|
||||
else if (shape == eCylinderLargeShape)
|
||||
pVolumeModel = gResCache.GetResource("../resources/VolumeCylinderLarge.cmdl");
|
||||
|
||||
if (pVolumeModel)
|
||||
{
|
||||
mpVolumePreviewNode = new CModelNode(pScene, this, pVolumeModel);
|
||||
mpVolumePreviewNode->SetInheritance(true, (shape != eAxisAlignedBoxShape), true);
|
||||
mpVolumePreviewNode->ForceAlphaEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch LightParameters
|
||||
mpLightParameters = new CLightParameters(mpInstance->LightParameters(), mpInstance->MasterTemplate()->GetGame());
|
||||
SetLightLayerIndex(mpLightParameters->LightLayerIndex());
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Shouldn't ever happen
|
||||
SetName("ScriptNode - NO INSTANCE");
|
||||
}
|
||||
|
||||
if (mpActiveModel)
|
||||
mLocalAABox = mpActiveModel->AABox();
|
||||
else
|
||||
mLocalAABox = CAABox::skOne;
|
||||
|
||||
mpExtra = CScriptExtra::CreateExtra(this);
|
||||
}
|
||||
|
||||
ENodeType CScriptNode::NodeType()
|
||||
{
|
||||
return eScriptNode;
|
||||
}
|
||||
|
||||
void CScriptNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
|
||||
{
|
||||
if (!mpInstance) return;
|
||||
|
||||
// Add script extra to renderer first
|
||||
if (mpExtra) mpExtra->AddToRenderer(pRenderer, ViewInfo);
|
||||
|
||||
// If we're in game mode, then override other visibility settings.
|
||||
if (ViewInfo.GameMode)
|
||||
{
|
||||
if (!mpInstance->IsActive() || !mpInstance->HasInGameModel())
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether the script extra wants us to render before we render.
|
||||
bool ShouldDraw = (!mpExtra || mpExtra->ShouldDrawNormalAssets());
|
||||
|
||||
if (ShouldDraw)
|
||||
{
|
||||
// Otherwise, we proceed as normal
|
||||
ERenderOptions options = pRenderer->RenderOptions();
|
||||
|
||||
if ((options & eDrawObjectCollision) && (!ViewInfo.GameMode))
|
||||
mpCollisionNode->AddToRenderer(pRenderer, ViewInfo);
|
||||
|
||||
if (options & eDrawObjects || ViewInfo.GameMode)
|
||||
{
|
||||
if (ViewInfo.ViewFrustum.BoxInFrustum(AABox()))
|
||||
{
|
||||
if (!mpActiveModel)
|
||||
pRenderer->AddOpaqueMesh(this, -1, AABox(), eDrawMesh);
|
||||
|
||||
else
|
||||
{
|
||||
if (!mpActiveModel->HasTransparency(0))
|
||||
pRenderer->AddOpaqueMesh(this, -1, AABox(), eDrawMesh);
|
||||
else
|
||||
AddSurfacesToRenderer(pRenderer, mpActiveModel, 0, ViewInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsSelected() && !ViewInfo.GameMode)
|
||||
{
|
||||
// Script nodes always draw their selections regardless of frustum planes
|
||||
// in order to ensure that script connection lines don't get improperly culled.
|
||||
if (ShouldDraw)
|
||||
pRenderer->AddOpaqueMesh(this, -1, AABox(), eDrawSelection);
|
||||
|
||||
if (mHasVolumePreview && (!mpExtra || mpExtra->ShouldDrawVolume()))
|
||||
mpVolumePreviewNode->AddToRenderer(pRenderer, ViewInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void CScriptNode::Draw(ERenderOptions Options, int ComponentIndex, const SViewInfo& ViewInfo)
|
||||
{
|
||||
if (!mpInstance) return;
|
||||
|
||||
// Draw model
|
||||
if (UsesModel())
|
||||
{
|
||||
CGraphics::SetupAmbientColor();
|
||||
CGraphics::UpdateVertexBlock();
|
||||
LoadModelMatrix();
|
||||
LoadLights(ViewInfo);
|
||||
|
||||
// Draw model if possible!
|
||||
if (mpActiveModel)
|
||||
{
|
||||
if (mpExtra) CGraphics::sPixelBlock.TevColor = mpExtra->TevColor().ToVector4f();
|
||||
else CGraphics::sPixelBlock.TevColor = CColor::skWhite.ToVector4f();
|
||||
CGraphics::sPixelBlock.TintColor = TintColor(ViewInfo).ToVector4f();
|
||||
CGraphics::UpdatePixelBlock();
|
||||
|
||||
if (ComponentIndex < 0)
|
||||
mpActiveModel->Draw(Options, 0);
|
||||
else
|
||||
mpActiveModel->DrawSurface(Options, ComponentIndex, 0);
|
||||
}
|
||||
|
||||
// If no model or billboard, default to drawing a purple box
|
||||
else
|
||||
{
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ZERO);
|
||||
glDepthMask(GL_TRUE);
|
||||
CDrawUtil::DrawShadedCube(CColor::skTransparentPurple * TintColor(ViewInfo));
|
||||
}
|
||||
}
|
||||
|
||||
// Draw billboard
|
||||
else if (mpBillboard)
|
||||
{
|
||||
CDrawUtil::DrawBillboard(mpBillboard, mPosition, BillboardScale(), TintColor(ViewInfo));
|
||||
}
|
||||
}
|
||||
|
||||
void CScriptNode::DrawSelection()
|
||||
{
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
|
||||
// Draw wireframe for models; billboards only get tinted
|
||||
if (UsesModel())
|
||||
{
|
||||
LoadModelMatrix();
|
||||
CModel *pModel = (mpActiveModel ? mpActiveModel : CDrawUtil::GetCubeModel());
|
||||
pModel->DrawWireframe(eNoRenderOptions, WireframeColor());
|
||||
}
|
||||
|
||||
if (mpInstance)
|
||||
{
|
||||
CGraphics::sMVPBlock.ModelMatrix = CMatrix4f::skIdentity;
|
||||
CGraphics::UpdateMVPBlock();
|
||||
|
||||
for (u32 iIn = 0; iIn < mpInstance->NumInLinks(); iIn++)
|
||||
{
|
||||
// Don't draw in links if the other object is selected.
|
||||
const SLink& con = mpInstance->InLink(iIn);
|
||||
CScriptNode *pLinkNode = mpScene->ScriptNodeByID(con.ObjectID);
|
||||
if (pLinkNode && !pLinkNode->IsSelected()) CDrawUtil::DrawLine(CenterPoint(), pLinkNode->CenterPoint(), CColor::skTransparentRed);
|
||||
}
|
||||
|
||||
for (u32 iOut = 0; iOut < mpInstance->NumOutLinks(); iOut++)
|
||||
{
|
||||
const SLink& con = mpInstance->OutLink(iOut);
|
||||
CScriptNode *pLinkNode = mpScene->ScriptNodeByID(con.ObjectID);
|
||||
if (pLinkNode) CDrawUtil::DrawLine(CenterPoint(), pLinkNode->CenterPoint(), CColor::skTransparentGreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CScriptNode::RayAABoxIntersectTest(CRayCollisionTester& Tester, const SViewInfo& ViewInfo)
|
||||
{
|
||||
if (!mpInstance)
|
||||
return;
|
||||
|
||||
// Let script extra do ray check first
|
||||
if (mpExtra)
|
||||
{
|
||||
mpExtra->RayAABoxIntersectTest(Tester, ViewInfo);
|
||||
|
||||
// If the extra doesn't want us rendering, then don't do the ray test either
|
||||
if (!mpExtra->ShouldDrawNormalAssets())
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're in game mode, then check whether we're visible before proceeding with the ray test.
|
||||
if (ViewInfo.GameMode)
|
||||
{
|
||||
if (!mpInstance->IsActive() || !mpInstance->HasInGameModel())
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, proceed with the ray test as normal...
|
||||
const CRay& Ray = Tester.Ray();
|
||||
|
||||
if (UsesModel())
|
||||
{
|
||||
std::pair<bool,float> BoxResult = AABox().IntersectsRay(Ray);
|
||||
|
||||
if (BoxResult.first)
|
||||
{
|
||||
if (mpActiveModel) Tester.AddNodeModel(this, mpActiveModel);
|
||||
else Tester.AddNode(this, 0, BoxResult.second);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Because the billboard rotates a lot, expand the AABox on the X/Y axes to cover any possible orientation
|
||||
CVector2f BillScale = BillboardScale();
|
||||
float ScaleXY = (BillScale.x > BillScale.y ? BillScale.x : BillScale.y);
|
||||
|
||||
CAABox BillBox = CAABox(mPosition + CVector3f(-ScaleXY, -ScaleXY, -BillScale.y),
|
||||
mPosition + CVector3f( ScaleXY, ScaleXY, BillScale.y));
|
||||
|
||||
std::pair<bool,float> BoxResult = BillBox.IntersectsRay(Ray);
|
||||
if (BoxResult.first) Tester.AddNode(this, 0, BoxResult.second);
|
||||
}
|
||||
}
|
||||
|
||||
SRayIntersection CScriptNode::RayNodeIntersectTest(const CRay& Ray, u32 AssetID, const SViewInfo& ViewInfo)
|
||||
{
|
||||
ERenderOptions options = ViewInfo.pRenderer->RenderOptions();
|
||||
|
||||
SRayIntersection out;
|
||||
out.pNode = this;
|
||||
out.ComponentIndex = AssetID;
|
||||
|
||||
if (options & eDrawObjects || ViewInfo.GameMode)
|
||||
{
|
||||
// Model test
|
||||
if (UsesModel())
|
||||
{
|
||||
CModel *pModel = (mpActiveModel ? mpActiveModel : CDrawUtil::GetCubeModel());
|
||||
|
||||
CRay TransformedRay = Ray.Transformed(Transform().Inverse());
|
||||
std::pair<bool,float> Result = pModel->GetSurface(AssetID)->IntersectsRay(TransformedRay, ((options & eEnableBackfaceCull) == 0));
|
||||
|
||||
if (Result.first)
|
||||
{
|
||||
out.Hit = true;
|
||||
|
||||
CVector3f HitPoint = TransformedRay.PointOnRay(Result.second);
|
||||
CVector3f WorldHitPoint = Transform() * HitPoint;
|
||||
out.Distance = Math::Distance(Ray.Origin(), WorldHitPoint);
|
||||
}
|
||||
|
||||
else
|
||||
out.Hit = false;
|
||||
}
|
||||
|
||||
// Billboard test
|
||||
// todo: come up with a better way to share this code between CScriptNode and CLightNode
|
||||
else
|
||||
{
|
||||
// Step 1: check whether the ray intersects with the plane the billboard is on
|
||||
CPlane BillboardPlane(-ViewInfo.pCamera->Direction(), mPosition);
|
||||
std::pair<bool,float> PlaneTest = Math::RayPlaneIntersecton(Ray, BillboardPlane);
|
||||
|
||||
if (PlaneTest.first)
|
||||
{
|
||||
// Step 2: transform the hit point into the plane's local space
|
||||
CVector3f PlaneHitPoint = Ray.PointOnRay(PlaneTest.second);
|
||||
CVector3f RelHitPoint = PlaneHitPoint - mPosition;
|
||||
|
||||
CVector3f PlaneForward = -ViewInfo.pCamera->Direction();
|
||||
CVector3f PlaneRight = -ViewInfo.pCamera->RightVector();
|
||||
CVector3f PlaneUp = ViewInfo.pCamera->UpVector();
|
||||
CQuaternion PlaneRot = CQuaternion::FromAxes(PlaneRight, PlaneForward, PlaneUp);
|
||||
|
||||
CVector3f RotatedHitPoint = PlaneRot.Inverse() * RelHitPoint;
|
||||
CVector2f LocalHitPoint = RotatedHitPoint.xz() / BillboardScale();
|
||||
|
||||
// Step 3: check whether the transformed hit point is in the -1 to 1 range
|
||||
if ((LocalHitPoint.x >= -1.f) && (LocalHitPoint.x <= 1.f) && (LocalHitPoint.y >= -1.f) && (LocalHitPoint.y <= 1.f))
|
||||
{
|
||||
// Step 4: look up the hit texel and check whether it's transparent or opaque
|
||||
CVector2f TexCoord = (LocalHitPoint + CVector2f(1.f)) * 0.5f;
|
||||
TexCoord.x = -TexCoord.x + 1.f;
|
||||
float TexelAlpha = mpBillboard->ReadTexelAlpha(TexCoord);
|
||||
|
||||
if (TexelAlpha < 0.25f)
|
||||
out.Hit = false;
|
||||
|
||||
else
|
||||
{
|
||||
// It's opaque... we have a hit!
|
||||
out.Hit = true;
|
||||
out.Distance = PlaneTest.second;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
out.Hit = false;
|
||||
}
|
||||
|
||||
else
|
||||
out.Hit = false;
|
||||
}
|
||||
}
|
||||
else out.Hit = false;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
bool CScriptNode::AllowsRotate() const
|
||||
{
|
||||
CScriptTemplate *pTemp = mpInstance->Template();
|
||||
return (pTemp->RotationType() == CScriptTemplate::eRotationEnabled);
|
||||
}
|
||||
|
||||
bool CScriptNode::AllowsScale() const
|
||||
{
|
||||
CScriptTemplate *pTemp = mpInstance->Template();
|
||||
return (pTemp->ScaleType() != CScriptTemplate::eScaleDisabled);
|
||||
}
|
||||
|
||||
bool CScriptNode::IsVisible() const
|
||||
{
|
||||
// Reimplementation of CSceneNode::IsVisible() to allow for layer and template visiblity to be taken into account
|
||||
return (mVisible && mpInstance->Layer()->IsVisible() && mpInstance->Template()->IsVisible());
|
||||
}
|
||||
|
||||
CColor CScriptNode::TintColor(const SViewInfo &ViewInfo) const
|
||||
{
|
||||
CColor BaseColor = CSceneNode::TintColor(ViewInfo);
|
||||
if (mpExtra) mpExtra->ModifyTintColor(BaseColor);
|
||||
return BaseColor;
|
||||
}
|
||||
|
||||
void CScriptNode::GeneratePosition()
|
||||
{
|
||||
if (!mHasValidPosition)
|
||||
{
|
||||
// Default to center of the active area; this is to preven recursion issues
|
||||
CTransform4f& AreaTransform = mpScene->GetActiveArea()->GetTransform();
|
||||
mPosition = CVector3f(AreaTransform[0][3], AreaTransform[1][3], AreaTransform[2][3]);
|
||||
mHasValidPosition = true;
|
||||
MarkTransformChanged();
|
||||
|
||||
// Ideal way to generate the position is to find a spot close to where it's being used.
|
||||
// To do this I check the location of the objects that this one is linked to.
|
||||
u32 NumLinks = mpInstance->NumInLinks() + mpInstance->NumOutLinks();
|
||||
|
||||
// In the case of one link, apply an offset so the new position isn't the same place as the object it's linked to
|
||||
if (NumLinks == 1)
|
||||
{
|
||||
const SLink& link = (mpInstance->NumInLinks() > 0 ? mpInstance->InLink(0) : mpInstance->OutLink(0));
|
||||
CScriptNode *pNode = mpScene->ScriptNodeByID(link.ObjectID);
|
||||
pNode->GeneratePosition();
|
||||
mPosition = pNode->AbsolutePosition();
|
||||
mPosition.z += (pNode->AABox().Size().z / 2.f);
|
||||
mPosition.z += (AABox().Size().z / 2.f);
|
||||
mPosition.z += 2.f;
|
||||
}
|
||||
|
||||
// For two or more links, average out the position of the connected objects.
|
||||
else if (NumLinks >= 2)
|
||||
{
|
||||
CVector3f NewPos = CVector3f::skZero;
|
||||
|
||||
for (u32 iIn = 0; iIn < mpInstance->NumInLinks(); iIn++)
|
||||
{
|
||||
CScriptNode *pNode = mpScene->ScriptNodeByID(mpInstance->InLink(iIn).ObjectID);
|
||||
|
||||
if (pNode)
|
||||
{
|
||||
pNode->GeneratePosition();
|
||||
NewPos += pNode->AABox().Center();
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 iOut = 0; iOut < mpInstance->NumOutLinks(); iOut++)
|
||||
{
|
||||
CScriptNode *pNode = mpScene->ScriptNodeByID(mpInstance->OutLink(iOut).ObjectID);
|
||||
|
||||
if (pNode)
|
||||
{
|
||||
pNode->GeneratePosition();
|
||||
NewPos += pNode->AABox().Center();
|
||||
}
|
||||
}
|
||||
|
||||
mPosition = NewPos / (float) NumLinks;
|
||||
mPosition.x += 2.f;
|
||||
}
|
||||
|
||||
MarkTransformChanged();
|
||||
}
|
||||
}
|
||||
|
||||
CColor CScriptNode::WireframeColor() const
|
||||
{
|
||||
return CColor((u8) 12, 135, 194, 255);
|
||||
}
|
||||
|
||||
CScriptObject* CScriptNode::Object() const
|
||||
{
|
||||
return mpInstance;
|
||||
}
|
||||
|
||||
CModel* CScriptNode::ActiveModel() const
|
||||
{
|
||||
return mpActiveModel;
|
||||
}
|
||||
|
||||
bool CScriptNode::UsesModel() const
|
||||
{
|
||||
return ((mpActiveModel != nullptr) || (mpBillboard == nullptr));
|
||||
}
|
||||
|
||||
bool CScriptNode::HasPreviewVolume() const
|
||||
{
|
||||
return mHasVolumePreview;
|
||||
}
|
||||
|
||||
CAABox CScriptNode::PreviewVolumeAABox() const
|
||||
{
|
||||
if (!mHasVolumePreview)
|
||||
return CAABox::skZero;
|
||||
else
|
||||
return mpVolumePreviewNode->AABox();
|
||||
}
|
||||
|
||||
CVector2f CScriptNode::BillboardScale() const
|
||||
{
|
||||
CVector2f out = (mpInstance->Template()->ScaleType() == CScriptTemplate::eScaleEnabled ? AbsoluteScale().xz() : CVector2f(1.f));
|
||||
return out * 0.5f;
|
||||
}
|
||||
|
||||
// ************ PROTECTED ************
|
||||
void CScriptNode::CalculateTransform(CTransform4f& rOut) const
|
||||
{
|
||||
CScriptTemplate *pTemp = mpInstance->Template();
|
||||
|
||||
if (pTemp->ScaleType() != CScriptTemplate::eScaleDisabled)
|
||||
{
|
||||
CVector3f Scale = (HasPreviewVolume() ? CVector3f::skOne : AbsoluteScale());
|
||||
rOut.Scale(Scale * mScaleMultiplier);
|
||||
}
|
||||
|
||||
if (UsesModel() && pTemp->RotationType() == CScriptTemplate::eRotationEnabled)
|
||||
rOut.Rotate(AbsoluteRotation());
|
||||
|
||||
rOut.Translate(AbsolutePosition());
|
||||
}
|
||||
52
src/Core/Scene/CScriptNode.h
Normal file
52
src/Core/Scene/CScriptNode.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef CSCRIPTNODE_H
|
||||
#define CSCRIPTNODE_H
|
||||
|
||||
#include "CSceneNode.h"
|
||||
#include "CModelNode.h"
|
||||
#include "CCollisionNode.h"
|
||||
#include <Core/CLightParameters.h>
|
||||
#include <Resource/script/CScriptObject.h>
|
||||
|
||||
class CScriptNode : public CSceneNode
|
||||
{
|
||||
CScriptObject *mpInstance;
|
||||
class CScriptExtra *mpExtra;
|
||||
|
||||
TResPtr<CModel> mpActiveModel;
|
||||
TResPtr<CTexture> mpBillboard;
|
||||
CCollisionNode *mpCollisionNode;
|
||||
|
||||
bool mHasValidPosition;
|
||||
bool mHasVolumePreview;
|
||||
float mScaleMultiplier;
|
||||
CModelNode *mpVolumePreviewNode;
|
||||
|
||||
CLightParameters *mpLightParameters;
|
||||
|
||||
public:
|
||||
CScriptNode(CSceneManager *pScene, CSceneNode *pParent = 0, CScriptObject *pObject = 0);
|
||||
ENodeType NodeType();
|
||||
void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo);
|
||||
void Draw(ERenderOptions Options, int ComponentIndex, const SViewInfo& ViewInfo);
|
||||
void DrawSelection();
|
||||
void RayAABoxIntersectTest(CRayCollisionTester& Tester, const SViewInfo& ViewInfo);
|
||||
SRayIntersection RayNodeIntersectTest(const CRay &Ray, u32 AssetID, const SViewInfo& ViewInfo);
|
||||
bool AllowsRotate() const;
|
||||
bool AllowsScale() const;
|
||||
bool IsVisible() const;
|
||||
CColor TintColor(const SViewInfo &ViewInfo) const;
|
||||
CColor WireframeColor() const;
|
||||
|
||||
void GeneratePosition();
|
||||
CScriptObject* Object() const;
|
||||
CModel* ActiveModel() const;
|
||||
bool UsesModel() const;
|
||||
bool HasPreviewVolume() const;
|
||||
CAABox PreviewVolumeAABox() const;
|
||||
CVector2f BillboardScale() const;
|
||||
|
||||
protected:
|
||||
void CalculateTransform(CTransform4f& rOut) const;
|
||||
};
|
||||
|
||||
#endif // CSCRIPTNODE_H
|
||||
115
src/Core/Scene/CStaticNode.cpp
Normal file
115
src/Core/Scene/CStaticNode.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "CStaticNode.h"
|
||||
#include <Core/CRenderer.h>
|
||||
#include <Core/CGraphics.h>
|
||||
#include <Common/AnimUtil.h>
|
||||
#include <Common/Math.h>
|
||||
#include <Core/CDrawUtil.h>
|
||||
|
||||
CStaticNode::CStaticNode(CSceneManager *pScene, CSceneNode *pParent, CStaticModel *pModel)
|
||||
: CSceneNode(pScene, pParent)
|
||||
{
|
||||
mpModel = pModel;
|
||||
mLocalAABox = mpModel->AABox();
|
||||
mScale = CVector3f(1.f, 1.f, 1.f);
|
||||
SetName("Static Node");
|
||||
}
|
||||
|
||||
ENodeType CStaticNode::NodeType()
|
||||
{
|
||||
return eStaticNode;
|
||||
}
|
||||
|
||||
void CStaticNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
|
||||
{
|
||||
if (!mpModel) return;
|
||||
if (mpModel->IsOccluder()) return;
|
||||
if (!ViewInfo.ViewFrustum.BoxInFrustum(AABox())) return;
|
||||
|
||||
if (!mpModel->IsTransparent())
|
||||
pRenderer->AddOpaqueMesh(this, -1, AABox(), eDrawMesh);
|
||||
|
||||
else
|
||||
{
|
||||
u32 NumSurfaces = mpModel->GetSurfaceCount();
|
||||
for (u32 iSurf = 0; iSurf < NumSurfaces; iSurf++)
|
||||
{
|
||||
CAABox TransformedBox = mpModel->GetSurfaceAABox(iSurf).Transformed(Transform());
|
||||
|
||||
if (ViewInfo.ViewFrustum.BoxInFrustum(TransformedBox))
|
||||
pRenderer->AddTransparentMesh(this, iSurf, TransformedBox, eDrawMesh);
|
||||
}
|
||||
}
|
||||
|
||||
if (mSelected && !ViewInfo.GameMode)
|
||||
pRenderer->AddOpaqueMesh(this, -1, AABox(), eDrawSelection);
|
||||
}
|
||||
|
||||
void CStaticNode::Draw(ERenderOptions Options, int ComponentIndex, const SViewInfo& ViewInfo)
|
||||
{
|
||||
if (!mpModel) return;
|
||||
|
||||
CGraphics::sVertexBlock.COLOR0_Amb = CVector4f(0, 0, 0, 1);
|
||||
float Multiplier = CGraphics::sWorldLightMultiplier;
|
||||
CGraphics::sPixelBlock.TevColor = CVector4f(Multiplier,Multiplier,Multiplier,1);
|
||||
CGraphics::sPixelBlock.TintColor = TintColor(ViewInfo).ToVector4f();
|
||||
CGraphics::sNumLights = 0;
|
||||
CGraphics::UpdateLightBlock();
|
||||
LoadModelMatrix();
|
||||
|
||||
if (ComponentIndex < 0)
|
||||
mpModel->Draw(Options);
|
||||
else
|
||||
mpModel->DrawSurface(Options, ComponentIndex);
|
||||
}
|
||||
|
||||
void CStaticNode::DrawSelection()
|
||||
{
|
||||
if (!mpModel) return;
|
||||
LoadModelMatrix();
|
||||
mpModel->DrawWireframe(eNoRenderOptions, WireframeColor());
|
||||
}
|
||||
|
||||
void CStaticNode::RayAABoxIntersectTest(CRayCollisionTester& Tester, const SViewInfo& /*ViewInfo*/)
|
||||
{
|
||||
if ((!mpModel) || (mpModel->IsOccluder()))
|
||||
return;
|
||||
|
||||
const CRay& Ray = Tester.Ray();
|
||||
std::pair<bool,float> BoxResult = AABox().IntersectsRay(Ray);
|
||||
|
||||
if (BoxResult.first)
|
||||
{
|
||||
for (u32 iSurf = 0; iSurf < mpModel->GetSurfaceCount(); iSurf++)
|
||||
{
|
||||
std::pair<bool,float> SurfResult = mpModel->GetSurfaceAABox(iSurf).Transformed(Transform()).IntersectsRay(Ray);
|
||||
|
||||
if (SurfResult.first)
|
||||
Tester.AddNode(this, iSurf, SurfResult.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SRayIntersection CStaticNode::RayNodeIntersectTest(const CRay &Ray, u32 AssetID, const SViewInfo& ViewInfo)
|
||||
{
|
||||
SRayIntersection out;
|
||||
out.pNode = this;
|
||||
out.ComponentIndex = AssetID;
|
||||
|
||||
CRay TransformedRay = Ray.Transformed(Transform().Inverse());
|
||||
ERenderOptions options = ViewInfo.pRenderer->RenderOptions();
|
||||
std::pair<bool,float> Result = mpModel->GetSurface(AssetID)->IntersectsRay(TransformedRay, ((options & eEnableBackfaceCull) == 0));
|
||||
|
||||
if (Result.first)
|
||||
{
|
||||
out.Hit = true;
|
||||
|
||||
CVector3f HitPoint = TransformedRay.PointOnRay(Result.second);
|
||||
CVector3f WorldHitPoint = Transform() * HitPoint;
|
||||
out.Distance = Math::Distance(Ray.Origin(), WorldHitPoint);
|
||||
}
|
||||
|
||||
else
|
||||
out.Hit = false;
|
||||
|
||||
return out;
|
||||
}
|
||||
21
src/Core/Scene/CStaticNode.h
Normal file
21
src/Core/Scene/CStaticNode.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef CSTATICNODE_H
|
||||
#define CSTATICNODE_H
|
||||
|
||||
#include "CSceneNode.h"
|
||||
#include <Resource/model/CStaticModel.h>
|
||||
|
||||
class CStaticNode : public CSceneNode
|
||||
{
|
||||
CStaticModel *mpModel;
|
||||
|
||||
public:
|
||||
CStaticNode(CSceneManager *pScene, CSceneNode *pParent = 0, CStaticModel *pModel = 0);
|
||||
ENodeType NodeType();
|
||||
void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo);
|
||||
void Draw(ERenderOptions Options, int ComponentIndex, const SViewInfo& ViewInfo);
|
||||
void DrawSelection();
|
||||
void RayAABoxIntersectTest(CRayCollisionTester& Tester, const SViewInfo& ViewInfo);
|
||||
SRayIntersection RayNodeIntersectTest(const CRay &Ray, u32 AssetID, const SViewInfo& ViewInfo);
|
||||
};
|
||||
|
||||
#endif // CSTATICNODE_H
|
||||
16
src/Core/Scene/ENodeType.h
Normal file
16
src/Core/Scene/ENodeType.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef ENODETYPE
|
||||
#define ENODETYPE
|
||||
|
||||
enum ENodeType
|
||||
{
|
||||
eRootNode,
|
||||
eModelNode,
|
||||
eStaticNode,
|
||||
eCollisionNode,
|
||||
eScriptNode,
|
||||
eScriptExtraNode,
|
||||
eLightNode
|
||||
};
|
||||
|
||||
#endif // ENODETYPE
|
||||
|
||||
Reference in New Issue
Block a user