Added the ability to do accurate raycasts against billboards

This commit is contained in:
parax0 2015-11-25 14:37:34 -07:00
parent 1fedc8f616
commit 373426a98f
39 changed files with 431 additions and 176 deletions

View File

@ -1,7 +1,8 @@
#include "CQuaternion.h" #include "CQuaternion.h"
#include <cmath> #include <cmath>
#include <math.h> #include <math.h>
#include <Common/Math.h> #include "Math.h"
#include "CMatrix4f.h"
CQuaternion::CQuaternion() CQuaternion::CQuaternion()
{ {
@ -137,5 +138,58 @@ CQuaternion CQuaternion::FromAxisAngle(float angle, CVector3f axis)
} }
CQuaternion CQuaternion::FromRotationMatrix(const CMatrix4f& RotMtx)
{
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
CQuaternion out;
float trace = RotMtx[0][0] + RotMtx[1][1] + RotMtx[2][2];
if (trace > 0.f)
{
float s = Math::Sqrt(trace + 1.f) * 2; // s = 4*w
out.w = 0.25f * s;
out.x = (RotMtx[2][1] - RotMtx[1][2]) / s;
out.y = (RotMtx[0][2] - RotMtx[2][0]) / s;
out.z = (RotMtx[1][0] - RotMtx[0][1]) / s;
}
else if ((RotMtx[0][0] > RotMtx[1][1]) && (RotMtx[0][0] > RotMtx[2][2]))
{
float s = Math::Sqrt(1.f + RotMtx[0][0] - RotMtx[1][1] - RotMtx[2][2]) * 2; // s = 4*x
out.w = (RotMtx[2][1] - RotMtx[1][2]) / s;
out.x = 0.25f * s;
out.y = (RotMtx[0][1] + RotMtx[1][0]) / s;
out.z = (RotMtx[0][2] + RotMtx[2][0]) / s;
}
else if (RotMtx[1][1] > RotMtx[2][2]) {
float s = Math::Sqrt(1.f + RotMtx[1][1] - RotMtx[0][0] - RotMtx[2][2]) * 2; // s = 4*y
out.w = (RotMtx[0][2] - RotMtx[2][0]) / s;
out.x = (RotMtx[0][1] + RotMtx[1][0]) / s;
out.y = 0.25f * s;
out.z = (RotMtx[1][2] + RotMtx[2][1]) / s;
}
else {
float s = Math::Sqrt(1.f + RotMtx[2][2] - RotMtx[0][0] - RotMtx[1][1]) * 2; // S=4*qz
out.w = (RotMtx[1][0] - RotMtx[0][1]) / s;
out.x = (RotMtx[0][2] + RotMtx[2][0]) / s;
out.y = (RotMtx[1][2] + RotMtx[2][1]) / s;
out.z = 0.25f * s;
}
return out;
}
CQuaternion CQuaternion::FromAxes(const CVector3f& X, const CVector3f& Y, const CVector3f& Z)
{
CMatrix4f RotMtx(X.x, Y.x, Z.x, 0.f,
X.y, Y.y, Z.y, 0.f,
X.z, Y.z, Z.z, 0.f,
0.f, 0.f, 0.f, 1.f);
return CQuaternion::FromRotationMatrix(RotMtx);
}
CQuaternion CQuaternion::skIdentity = CQuaternion(1.f, 0.f, 0.f, 0.f); CQuaternion CQuaternion::skIdentity = CQuaternion(1.f, 0.f, 0.f, 0.f);
CQuaternion CQuaternion::skZero = CQuaternion(0.f, 0.f, 0.f, 0.f); CQuaternion CQuaternion::skZero = CQuaternion(0.f, 0.f, 0.f, 0.f);

View File

@ -25,6 +25,8 @@ public:
// Static // Static
static CQuaternion FromEuler(CVector3f euler); static CQuaternion FromEuler(CVector3f euler);
static CQuaternion FromAxisAngle(float angle, CVector3f axis); static CQuaternion FromAxisAngle(float angle, CVector3f axis);
static CQuaternion FromRotationMatrix(const CMatrix4f& RotMtx);
static CQuaternion FromAxes(const CVector3f& X, const CVector3f& Y, const CVector3f& Z);
static CQuaternion skIdentity; static CQuaternion skIdentity;
static CQuaternion skZero; static CQuaternion skZero;

View File

@ -19,7 +19,7 @@ void CRayCollisionTester::AddNode(CSceneNode *pNode, u32 AssetIndex, float Dista
Intersection.Distance = Distance; Intersection.Distance = Distance;
} }
SRayIntersection CRayCollisionTester::TestNodes(ERenderOptions options) SRayIntersection CRayCollisionTester::TestNodes(const SViewInfo& ViewInfo)
{ {
// Sort nodes by distance from ray // Sort nodes by distance from ray
mBoxIntersectList.sort( mBoxIntersectList.sort(
@ -43,7 +43,7 @@ SRayIntersection CRayCollisionTester::TestNodes(ERenderOptions options)
// Otherwise, more intersection tests... // Otherwise, more intersection tests...
CSceneNode *pNode = Intersection.pNode; CSceneNode *pNode = Intersection.pNode;
SRayIntersection MidResult = pNode->RayNodeIntersectTest(mRay, Intersection.AssetIndex, options); SRayIntersection MidResult = pNode->RayNodeIntersectTest(mRay, Intersection.AssetIndex, ViewInfo);
if (MidResult.Hit) if (MidResult.Hit)
{ {

View File

@ -6,7 +6,7 @@
#include "CVector3f.h" #include "CVector3f.h"
#include "SRayIntersection.h" #include "SRayIntersection.h"
#include "types.h" #include "types.h"
#include <Core/ERenderOptions.h> #include <Core/SViewInfo.h>
#include <list> #include <list>
@ -22,7 +22,7 @@ public:
~CRayCollisionTester(); ~CRayCollisionTester();
const CRay& Ray() const; const CRay& Ray() const;
void AddNode(CSceneNode *pNode, u32 AssetIndex, float Distance); void AddNode(CSceneNode *pNode, u32 AssetIndex, float Distance);
SRayIntersection TestNodes(ERenderOptions options); SRayIntersection TestNodes(const SViewInfo& ViewInfo);
}; };
inline const CRay& CRayCollisionTester::Ray() const inline const CRay& CRayCollisionTester::Ray() const

View File

@ -15,6 +15,11 @@ float Pow(float Base, float Exponent)
return pow(Base, Exponent); return pow(Base, Exponent);
} }
float Sqrt(float v)
{
return sqrtf(v);
}
float Distance(const CVector3f& A, const CVector3f& B) float Distance(const CVector3f& A, const CVector3f& B)
{ {
return sqrtf( Pow(B.x - A.x, 2.f) + return sqrtf( Pow(B.x - A.x, 2.f) +

View File

@ -15,6 +15,8 @@ float Abs(float v);
float Pow(float Base, float Exponent); float Pow(float Base, float Exponent);
float Sqrt(float v);
float Distance(const CVector3f& A, const CVector3f& B); float Distance(const CVector3f& A, const CVector3f& B);
float DegreesToRadians(float deg); float DegreesToRadians(float deg);

View File

@ -163,13 +163,6 @@ void CCamera::LoadMatrices()
CGraphics::UpdateMVPBlock(); CGraphics::UpdateMVPBlock();
} }
void CCamera::LoadRotationOnlyMatrices()
{
CGraphics::sMVPBlock.ViewMatrix = RotationOnlyViewMatrix();
CGraphics::sMVPBlock.ProjectionMatrix = ProjectionMatrix();
CGraphics::UpdateMVPBlock();
}
// ************ GETTERS ************ // ************ GETTERS ************
CVector3f CCamera::Position() const CVector3f CCamera::Position() const
{ {
@ -181,6 +174,16 @@ CVector3f CCamera::Direction() const
return mDirection; return mDirection;
} }
CVector3f CCamera::UpVector() const
{
return mUpVector;
}
CVector3f CCamera::RightVector() const
{
return mRightVector;
}
float CCamera::Yaw() const float CCamera::Yaw() const
{ {
return mYaw; return mYaw;
@ -204,17 +207,6 @@ const CMatrix4f& CCamera::ViewMatrix()
return mCachedViewMatrix; return mCachedViewMatrix;
} }
CMatrix4f CCamera::RotationOnlyViewMatrix()
{
if (mViewOutdated)
CalculateView();
return CMatrix4f(mCachedViewMatrix[0][0], mCachedViewMatrix[0][1], mCachedViewMatrix[0][2], 0.f,
mCachedViewMatrix[1][0], mCachedViewMatrix[1][1], mCachedViewMatrix[1][2], 0.f,
mCachedViewMatrix[2][0], mCachedViewMatrix[2][1], mCachedViewMatrix[2][2], 0.f,
mCachedViewMatrix[3][0], mCachedViewMatrix[3][1], mCachedViewMatrix[3][2], 1.f);
}
const CMatrix4f& CCamera::ProjectionMatrix() const CMatrix4f& CCamera::ProjectionMatrix()
{ {
if (mProjectionOutdated) if (mProjectionOutdated)
@ -295,6 +287,14 @@ void CCamera::CalculateDirection()
cos(mPitch) * sin(mYaw), cos(mPitch) * sin(mYaw),
sin(mPitch) sin(mPitch)
); );
mRightVector = CVector3f(
cos(mYaw - Math::skHalfPi),
sin(mYaw - Math::skHalfPi),
0
);
mUpVector = mRightVector.Cross(mDirection);
} }
void CCamera::CalculateView() void CCamera::CalculateView()
@ -302,17 +302,9 @@ void CCamera::CalculateView()
// todo: don't use glm // todo: don't use glm
CalculateDirection(); CalculateDirection();
CVector3f Right(
cos(mYaw - Math::skHalfPi),
sin(mYaw - Math::skHalfPi),
0
);
CVector3f Up = Right.Cross(mDirection);
glm::vec3 glmpos(mPosition.x, mPosition.y, mPosition.z); glm::vec3 glmpos(mPosition.x, mPosition.y, mPosition.z);
glm::vec3 glmdir(mDirection.x, mDirection.y, mDirection.z); glm::vec3 glmdir(mDirection.x, mDirection.y, mDirection.z);
glm::vec3 glmup(Up.x, Up.y, Up.z); glm::vec3 glmup(mUpVector.x, mUpVector.y, mUpVector.z);
mCachedViewMatrix = CMatrix4f::FromGlmMat4(glm::lookAt(glmpos, glmpos + glmdir, glmup)).Transpose(); mCachedViewMatrix = CMatrix4f::FromGlmMat4(glm::lookAt(glmpos, glmpos + glmdir, glmup)).Transpose();
mViewOutdated = false; mViewOutdated = false;
} }

View File

@ -21,6 +21,8 @@ class CCamera
ECameraMoveMode mMode; ECameraMoveMode mMode;
CVector3f mPosition; CVector3f mPosition;
CVector3f mDirection; CVector3f mDirection;
CVector3f mRightVector;
CVector3f mUpVector;
float mAspectRatio; float mAspectRatio;
float mYaw; float mYaw;
@ -48,16 +50,16 @@ public:
void ProcessMouseInput(EKeyInputs KeyFlags, EMouseInputs MouseFlags, float XMovement, float YMovement); void ProcessMouseInput(EKeyInputs KeyFlags, EMouseInputs MouseFlags, float XMovement, float YMovement);
CRay CastRay(CVector2f DeviceCoords); CRay CastRay(CVector2f DeviceCoords);
void LoadMatrices(); void LoadMatrices();
void LoadRotationOnlyMatrices();
// Getters // Getters
CVector3f Position() const; CVector3f Position() const;
CVector3f Direction() const; CVector3f Direction() const;
CVector3f UpVector() const;
CVector3f RightVector() const;
float Yaw() const; float Yaw() const;
float Pitch() const; float Pitch() const;
float FieldOfView() const; float FieldOfView() const;
const CMatrix4f& ViewMatrix(); const CMatrix4f& ViewMatrix();
CMatrix4f RotationOnlyViewMatrix();
const CMatrix4f& ProjectionMatrix(); const CMatrix4f& ProjectionMatrix();
const CFrustumPlanes& FrustumPlanes(); const CFrustumPlanes& FrustumPlanes();

View File

@ -24,7 +24,7 @@ void CRenderBucket::Add(const SRenderablePtr& ptr)
mSize++; mSize++;
} }
void CRenderBucket::Sort(CCamera& Camera) void CRenderBucket::Sort(CCamera* pCamera)
{ {
struct { struct {
CCamera *pCamera; CCamera *pCamera;
@ -39,7 +39,7 @@ void CRenderBucket::Sort(CCamera& Camera)
return (dotL > dotR); return (dotL > dotR);
} }
} backToFront; } backToFront;
backToFront.pCamera = &Camera; backToFront.pCamera = pCamera;
if (mSortType == BackToFront) if (mSortType == BackToFront)
std::stable_sort(mRenderables.begin(), mRenderables.begin() + mSize, backToFront); std::stable_sort(mRenderables.begin(), mRenderables.begin() + mSize, backToFront);

View File

@ -25,7 +25,7 @@ public:
CRenderBucket(); CRenderBucket();
void SetSortType(ESortType Type); void SetSortType(ESortType Type);
void Add(const SRenderablePtr& ptr); void Add(const SRenderablePtr& ptr);
void Sort(CCamera& Camera); void Sort(CCamera* pCamera);
void Clear(); void Clear();
void Draw(ERenderOptions Options); void Draw(ERenderOptions Options);
}; };

View File

@ -54,7 +54,7 @@ void CRenderer::Init()
} }
// ************ GETTERS/SETTERS ************ // ************ GETTERS/SETTERS ************
ERenderOptions CRenderer::RenderOptions() ERenderOptions CRenderer::RenderOptions() const
{ {
return mOptions; return mOptions;
} }
@ -159,11 +159,10 @@ void CRenderer::SetViewportSize(u32 Width, u32 Height)
} }
// ************ RENDER ************ // ************ RENDER ************
void CRenderer::RenderBuckets(CCamera& Camera) void CRenderer::RenderBuckets(const SViewInfo& ViewInfo)
{ {
if (!mInitialized) Init(); if (!mInitialized) Init();
mSceneFramebuffer.Bind(); mSceneFramebuffer.Bind();
//Camera.LoadMatrices();
// Set backface culling // Set backface culling
if (mOptions & eEnableBackfaceCull) glEnable(GL_CULL_FACE); if (mOptions & eEnableBackfaceCull) glEnable(GL_CULL_FACE);
@ -175,7 +174,7 @@ void CRenderer::RenderBuckets(CCamera& Camera)
mOpaqueBucket.Draw(mOptions); mOpaqueBucket.Draw(mOptions);
mOpaqueBucket.Clear(); mOpaqueBucket.Clear();
mTransparentBucket.Sort(Camera); mTransparentBucket.Sort(ViewInfo.pCamera);
mTransparentBucket.Draw(mOptions); mTransparentBucket.Draw(mOptions);
mTransparentBucket.Clear(); mTransparentBucket.Clear();
} }
@ -288,7 +287,7 @@ void CRenderer::RenderBloom()
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
} }
void CRenderer::RenderSky(CModel *pSkyboxModel, CCamera& Camera) void CRenderer::RenderSky(CModel *pSkyboxModel, const SViewInfo& ViewInfo)
{ {
if (!mInitialized) Init(); if (!mInitialized) Init();
if (!pSkyboxModel) return; if (!pSkyboxModel) return;
@ -302,7 +301,10 @@ void CRenderer::RenderSky(CModel *pSkyboxModel, CCamera& Camera)
CGraphics::UpdateVertexBlock(); CGraphics::UpdateVertexBlock();
CGraphics::UpdatePixelBlock(); CGraphics::UpdatePixelBlock();
CGraphics::UpdateLightBlock(); CGraphics::UpdateLightBlock();
Camera.LoadRotationOnlyMatrices();
// Load rotation-only view matrix
CGraphics::sMVPBlock.ViewMatrix = ViewInfo.RotationOnlyViewMatrix;
CGraphics::UpdateMVPBlock();
glDepthRange(1.f, 1.f); glDepthRange(1.f, 1.f);
pSkyboxModel->Draw(mOptions, 0); pSkyboxModel->Draw(mOptions, 0);

View File

@ -9,6 +9,7 @@
#include "ERenderOptions.h" #include "ERenderOptions.h"
#include "ERenderCommand.h" #include "ERenderCommand.h"
#include "SRenderablePtr.h" #include "SRenderablePtr.h"
#include "SViewInfo.h"
#include <Common/CAABox.h> #include <Common/CAABox.h>
#include <Common/CColor.h> #include <Common/CColor.h>
#include <Common/CMatrix4f.h> #include <Common/CMatrix4f.h>
@ -53,7 +54,7 @@ public:
void Init(); void Init();
// Getters/Setters // Getters/Setters
ERenderOptions RenderOptions(); ERenderOptions RenderOptions() const;
void ToggleWorld(bool b); void ToggleWorld(bool b);
void ToggleWorldCollision(bool b); void ToggleWorldCollision(bool b);
void ToggleObjects(bool b); void ToggleObjects(bool b);
@ -71,9 +72,9 @@ public:
void SetViewportSize(u32 Width, u32 Height); void SetViewportSize(u32 Width, u32 Height);
// Render // Render
void RenderBuckets(CCamera& Camera); void RenderBuckets(const SViewInfo& ViewInfo);
void RenderBloom(); void RenderBloom();
void RenderSky(CModel *pSkyboxModel, CCamera& Camera); void RenderSky(CModel *pSkyboxModel, const SViewInfo& ViewInfo);
void AddOpaqueMesh(IRenderable *pRenderable, u32 AssetID, CAABox& AABox, ERenderCommand Command); void AddOpaqueMesh(IRenderable *pRenderable, u32 AssetID, CAABox& AABox, ERenderCommand Command);
void AddTransparentMesh(IRenderable *pRenderable, u32 AssetID, CAABox& AABox, ERenderCommand Command); void AddTransparentMesh(IRenderable *pRenderable, u32 AssetID, CAABox& AABox, ERenderCommand Command);
void BeginFrame(); void BeginFrame();

View File

@ -215,48 +215,50 @@ void CSceneManager::ClearScene()
mNodeCount = 0; mNodeCount = 0;
} }
void CSceneManager::AddSceneToRenderer(CRenderer *pRenderer, CCamera& camera) void CSceneManager::AddSceneToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
{ {
ERenderOptions options = pRenderer->RenderOptions(); ERenderOptions Options = pRenderer->RenderOptions();
if (options & eDrawWorld) if (Options & eDrawWorld)
{ {
for (u32 n = 0; n < mModelNodes.size(); n++) for (u32 n = 0; n < mModelNodes.size(); n++)
if (mModelNodes[n]->IsVisible()) if (mModelNodes[n]->IsVisible())
mModelNodes[n]->AddToRenderer(pRenderer, camera.FrustumPlanes()); mModelNodes[n]->AddToRenderer(pRenderer, ViewInfo);
for (u32 n = 0; n < mStaticNodes.size(); n++) for (u32 n = 0; n < mStaticNodes.size(); n++)
if (mStaticNodes[n]->IsVisible()) if (mStaticNodes[n]->IsVisible())
mStaticNodes[n]->AddToRenderer(pRenderer, camera.FrustumPlanes()); mStaticNodes[n]->AddToRenderer(pRenderer, ViewInfo);
} }
if (options & eDrawWorldCollision) if (Options & eDrawWorldCollision)
{ {
for (u32 n = 0; n < mCollisionNodes.size(); n++) for (u32 n = 0; n < mCollisionNodes.size(); n++)
if (mCollisionNodes[n]->IsVisible()) if (mCollisionNodes[n]->IsVisible())
mCollisionNodes[n]->AddToRenderer(pRenderer, camera.FrustumPlanes()); mCollisionNodes[n]->AddToRenderer(pRenderer, ViewInfo);
} }
if (options & eDrawLights) if (Options & eDrawLights)
{ {
for (u32 n = 0; n < mLightNodes.size(); n++) for (u32 n = 0; n < mLightNodes.size(); n++)
if (mLightNodes[n]->IsVisible()) if (mLightNodes[n]->IsVisible())
mLightNodes[n]->AddToRenderer(pRenderer, camera.FrustumPlanes()); mLightNodes[n]->AddToRenderer(pRenderer, ViewInfo);
} }
if ((options & eDrawObjects) || (options & eDrawObjectCollision)) if ((Options & eDrawObjects) || (Options & eDrawObjectCollision))
{ {
for (u32 n = 0; n < mScriptNodes.size(); n++) for (u32 n = 0; n < mScriptNodes.size(); n++)
if (mScriptNodes[n]->IsVisible()) if (mScriptNodes[n]->IsVisible())
mScriptNodes[n]->AddToRenderer(pRenderer, camera.FrustumPlanes()); mScriptNodes[n]->AddToRenderer(pRenderer, ViewInfo);
} }
} }
SRayIntersection CSceneManager::SceneRayCast(const CRay& Ray, ERenderOptions renderOptions) SRayIntersection CSceneManager::SceneRayCast(const CRay& Ray, const SViewInfo& ViewInfo)
{ {
// Terribly hacky stuff to avoid having tons of redundant code // Terribly hacky stuff to avoid having tons of redundant code
// because I'm too lazy to rewrite CSceneManager right now and fix it // because I'm too lazy to rewrite CSceneManager right now and fix it
// (I'm probably going to do it soon...) // (I'm probably going to do it soon...)
ERenderOptions renderOptions = ViewInfo.pRenderer->RenderOptions();
std::vector<CSceneNode*> *pNodeVectors[5] = { std::vector<CSceneNode*> *pNodeVectors[5] = {
reinterpret_cast<std::vector<CSceneNode*>*>(&mModelNodes), reinterpret_cast<std::vector<CSceneNode*>*>(&mModelNodes),
reinterpret_cast<std::vector<CSceneNode*>*>(&mStaticNodes), reinterpret_cast<std::vector<CSceneNode*>*>(&mStaticNodes),
@ -283,7 +285,7 @@ SRayIntersection CSceneManager::SceneRayCast(const CRay& Ray, ERenderOptions ren
vec[iNode]->RayAABoxIntersectTest(Tester); vec[iNode]->RayAABoxIntersectTest(Tester);
} }
return Tester.TestNodes(renderOptions); return Tester.TestNodes(ViewInfo);
} }
void CSceneManager::PickEnvironmentObjects() void CSceneManager::PickEnvironmentObjects()

View File

@ -7,6 +7,7 @@
#include "CAreaAttributes.h" #include "CAreaAttributes.h"
#include "CRenderer.h" #include "CRenderer.h"
#include "SViewInfo.h"
#include <Common/SRayIntersection.h> #include <Common/SRayIntersection.h>
#include <Common/types.h> #include <Common/types.h>
#include <Scene/CSceneNode.h> #include <Scene/CSceneNode.h>
@ -57,8 +58,8 @@ public:
void SetActiveArea(CGameArea *_area); void SetActiveArea(CGameArea *_area);
void SetActiveWorld(CWorld *_world); void SetActiveWorld(CWorld *_world);
void ClearScene(); void ClearScene();
void AddSceneToRenderer(CRenderer *pRenderer, CCamera& camera); void AddSceneToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo);
SRayIntersection SceneRayCast(const CRay& Ray, ERenderOptions renderOptions); SRayIntersection SceneRayCast(const CRay& Ray, const SViewInfo& ViewInfo);
void PickEnvironmentObjects(); void PickEnvironmentObjects();
CScriptNode* ScriptNodeByID(u32 InstanceID); CScriptNode* ScriptNodeByID(u32 InstanceID);
CScriptNode* NodeForObject(CScriptObject *pObj); CScriptNode* NodeForObject(CScriptObject *pObj);

View File

@ -2,9 +2,8 @@
#define IRENDERABLE_H #define IRENDERABLE_H
#include "ERenderOptions.h" #include "ERenderOptions.h"
#include <Common/CAABox.h> #include "SViewInfo.h"
#include <Common/types.h> #include <Common/types.h>
#include <Core/CFrustumPlanes.h>
class CRenderer; class CRenderer;
@ -13,7 +12,7 @@ class IRenderable
public: public:
IRenderable() {} IRenderable() {}
virtual ~IRenderable() {} virtual ~IRenderable() {}
virtual void AddToRenderer(CRenderer* pRenderer, const CFrustumPlanes& frustum) = 0; virtual void AddToRenderer(CRenderer* pRenderer, const SViewInfo& ViewInfo) = 0;
virtual void Draw(ERenderOptions /*options*/) {} virtual void Draw(ERenderOptions /*options*/) {}
virtual void DrawAsset(ERenderOptions /*options*/, u32 /*asset*/) {} virtual void DrawAsset(ERenderOptions /*options*/, u32 /*asset*/) {}
virtual void DrawSelection() {} virtual void DrawSelection() {}

19
Core/SViewInfo.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef SVIEWINFO
#define SVIEWINFO
#include "CFrustumPlanes.h"
#include <Common/CMatrix4f.h>
#include <Common/CRay.h>
struct SViewInfo
{
class CSceneManager *pScene;
class CRenderer *pRenderer;
class CCamera *pCamera;
CFrustumPlanes ViewFrustum;
CMatrix4f RotationOnlyViewMatrix;
};
#endif // SVIEWINFO

View File

@ -113,7 +113,6 @@ SOURCES += \
Resource/model/SSurface.cpp \ Resource/model/SSurface.cpp \
Common/CRayCollisionTester.cpp \ Common/CRayCollisionTester.cpp \
Common/Math.cpp \ Common/Math.cpp \
Scene/CBoundingBoxNode.cpp \
Core/Log.cpp \ Core/Log.cpp \
Common/CVector2i.cpp \ Common/CVector2i.cpp \
UI/CNodeSelection.cpp \ UI/CNodeSelection.cpp \
@ -273,7 +272,6 @@ HEADERS += \
Common/CRayCollisionTester.h \ Common/CRayCollisionTester.h \
Scene/ENodeType.h \ Scene/ENodeType.h \
Common/Math.h \ Common/Math.h \
Scene/CBoundingBoxNode.h \
Core/Log.h \ Core/Log.h \
Scene/CRootNode.h \ Scene/CRootNode.h \
Common/CVector2i.h \ Common/CVector2i.h \
@ -321,7 +319,8 @@ HEADERS += \
UI/WAnimParamsEditor.h \ UI/WAnimParamsEditor.h \
Resource/CCollisionMeshGroup.h \ Resource/CCollisionMeshGroup.h \
Core/CFrustumPlanes.h \ Core/CFrustumPlanes.h \
Core/CLightParameters.h Core/CLightParameters.h \
Core/SViewInfo.h
FORMS += \ FORMS += \
UI/CStartWindow.ui \ UI/CStartWindow.ui \

View File

@ -152,6 +152,49 @@ void CTexture::Resize(u32 Width, u32 Height)
} }
} }
float CTexture::ReadTexelAlpha(const CVector2f& TexCoord)
{
// todo: support texel formats other than DXT1
// DXT1 is definitely the most complicated one anyway; try reusing CTextureDecoder functions for other formats
u32 TexelX = (u32) ((mWidth - 1) * TexCoord.x);
u32 TexelY = (u32) ((mHeight - 1) * (1.f - fmodf(TexCoord.y, 1.f)));
if (mTexelFormat == eDXT1 && mBufferExists)
{
CMemoryInStream Buffer(mImgDataBuffer, mImgDataSize, IOUtil::SystemEndianness);
// 8 bytes per 4x4 16-pixel block, left-to-right top-to-bottom
u32 BlockIdxX = TexelX / 4;
u32 BlockIdxY = TexelY / 4;
u32 BlocksPerRow = mWidth / 4;
u32 BufferPos = (8 * BlockIdxX) + (8 * BlockIdxY * BlocksPerRow);
Buffer.Seek(BufferPos, SEEK_SET);
u16 PaletteA = Buffer.ReadShort();
u16 PaletteB = Buffer.ReadShort();
if (PaletteA > PaletteB)
{
// No palette colors have alpha
return 1.f;
}
// We only care about alpha, which is only present on palette index 3.
// We don't need to calculate/decode the actual palette colors.
u32 BlockCol = (TexelX & 0xF) / 4;
u32 BlockRow = (TexelY & 0xF) / 4;
Buffer.Seek(BlockRow, SEEK_CUR);
u8 Row = Buffer.ReadByte();
u8 Shift = (u8) (6 - (BlockCol * 2));
u8 PaletteIndex = (Row >> Shift) & 0x3;
return (PaletteIndex == 3 ? 0.f : 1.f);
}
return 1.f;
}
bool CTexture::WriteDDS(COutputStream& out) bool CTexture::WriteDDS(COutputStream& out)
{ {
if (!out.IsValid()) return false; if (!out.IsValid()) return false;

View File

@ -2,6 +2,7 @@
#define CTEXTURE_H #define CTEXTURE_H
#include <Common/types.h> #include <Common/types.h>
#include <Common/CVector2f.h>
#include <FileIO/FileIO.h> #include <FileIO/FileIO.h>
#include <gl/glew.h> #include <gl/glew.h>
#include "CResource.h" #include "CResource.h"
@ -35,6 +36,7 @@ public:
bool BufferGL(); bool BufferGL();
void Bind(u32 GLTextureUnit); void Bind(u32 GLTextureUnit);
void Resize(u32 Width, u32 Height); void Resize(u32 Width, u32 Height);
float ReadTexelAlpha(const CVector2f& TexCoord);
bool WriteDDS(COutputStream& out); bool WriteDDS(COutputStream& out);
// Getters // Getters

View File

@ -1,12 +0,0 @@
#include "CBoundingBoxNode.h"
CBoundingBoxNode::CBoundingBoxNode()
{
}
CBoundingBoxNode::~CBoundingBoxNode()
{
}

View File

@ -1,16 +0,0 @@
#ifndef CBOUNDINGBOXNODE_H
#define CBOUNDINGBOXNODE_H
#include "CSceneNode.h"
class CBoundingBoxNode
{
CColor mColor;
public:
CBoundingBoxNode();
~CBoundingBoxNode();
};
#endif // CBOUNDINGBOXNODE_H

View File

@ -15,10 +15,10 @@ ENodeType CCollisionNode::NodeType()
return eCollisionNode; return eCollisionNode;
} }
void CCollisionNode::AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum) void CCollisionNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
{ {
if (!mpCollision) return; if (!mpCollision) return;
if (!frustum.BoxInFrustum(AABox())) return; if (!ViewInfo.ViewFrustum.BoxInFrustum(AABox())) return;
pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawMesh); pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawMesh);
@ -47,7 +47,7 @@ void CCollisionNode::DrawAsset(ERenderOptions /*Options*/, u32 /*asset*/)
{ {
} }
SRayIntersection CCollisionNode::RayNodeIntersectTest(const CRay& /*Ray*/, u32 /*AssetID*/, ERenderOptions /*options*/) SRayIntersection CCollisionNode::RayNodeIntersectTest(const CRay& /*Ray*/, u32 /*AssetID*/, const SViewInfo& /*ViewInfo*/)
{ {
// todo // todo
SRayIntersection Result; SRayIntersection Result;

View File

@ -12,10 +12,10 @@ class CCollisionNode : public CSceneNode
public: public:
CCollisionNode(CSceneManager *pScene, CSceneNode *pParent = 0, CCollisionMeshGroup *pCollision = 0); CCollisionNode(CSceneManager *pScene, CSceneNode *pParent = 0, CCollisionMeshGroup *pCollision = 0);
ENodeType NodeType(); ENodeType NodeType();
void AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustumPlanes); void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo);
void Draw(ERenderOptions Options); void Draw(ERenderOptions Options);
void DrawAsset(ERenderOptions Options, u32 asset); void DrawAsset(ERenderOptions Options, u32 asset);
SRayIntersection RayNodeIntersectTest(const CRay &Ray, u32 AssetID, ERenderOptions options); SRayIntersection RayNodeIntersectTest(const CRay &Ray, u32 AssetID, const SViewInfo& ViewInfo);
void SetCollision(CCollisionMeshGroup *pCollision); void SetCollision(CCollisionMeshGroup *pCollision);
}; };

View File

@ -1,4 +1,5 @@
#include "CLightNode.h" #include "CLightNode.h"
#include <Common/Math.h>
#include <Core/CDrawUtil.h> #include <Core/CDrawUtil.h>
#include <Core/CGraphics.h> #include <Core/CGraphics.h>
#include <Core/CRenderer.h> #include <Core/CRenderer.h>
@ -24,16 +25,16 @@ ENodeType CLightNode::NodeType()
return eLightNode; return eLightNode;
} }
void CLightNode::AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum) void CLightNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
{ {
if (!frustum.BoxInFrustum(AABox())) return; if (!ViewInfo.ViewFrustum.BoxInFrustum(AABox())) return;
pRenderer->AddOpaqueMesh(this, 0, CAABox(mPosition + 0.5f, mPosition - 0.5f), eDrawMesh); pRenderer->AddOpaqueMesh(this, 0, CAABox(mPosition + 0.5f, mPosition - 0.5f), eDrawMesh);
} }
void CLightNode::Draw(ERenderOptions /*Options*/) void CLightNode::Draw(ERenderOptions /*Options*/)
{ {
CDrawUtil::DrawLightBillboard(mpLight->GetType(), mpLight->GetColor(), mPosition, mScale.xy() * CVector2f(0.75f), TintColor()); CDrawUtil::DrawLightBillboard(mpLight->GetType(), mpLight->GetColor(), mPosition, BillboardScale(), TintColor());
// Below commented-out code is for light radius visualization as a bounding box // Below commented-out code is for light radius visualization as a bounding box
/*float r = mLight->GetRadius(); /*float r = mLight->GetRadius();
@ -45,24 +46,86 @@ void CLightNode::DrawAsset(ERenderOptions /*Options*/, u32 /*asset*/)
{ {
} }
SRayIntersection CLightNode::RayNodeIntersectTest(const CRay& Ray, u32 /*AssetID*/, ERenderOptions options) void CLightNode::RayAABoxIntersectTest(CRayCollisionTester &Tester)
{ {
// Needs redo if I ever make these look like something other than boxes CVector2f BillScale = BillboardScale();
bool allowBackfaces = ((options & eEnableBackfaceCull) == 0); float ScaleXY = (BillScale.x > BillScale.y ? BillScale.x : BillScale.y);
if (!allowBackfaces && (AABox().IsPointInBox(Ray.Origin()))) CAABox BillBox = CAABox(mPosition + CVector3f(-ScaleXY, -ScaleXY, -BillScale.y),
return SRayIntersection(false, 0.f, nullptr, 0); mPosition + CVector3f( ScaleXY, ScaleXY, BillScale.y));
std::pair<bool,float> BoxResult = AABox().IntersectsRay(Ray); std::pair<bool,float> BoxResult = BillBox.IntersectsRay(Tester.Ray());
if (BoxResult.first) Tester.AddNode(this, 0, BoxResult.second);
}
if (BoxResult.first) SRayIntersection CLightNode::RayNodeIntersectTest(const CRay& Ray, u32 AssetID, const SViewInfo& ViewInfo)
return SRayIntersection(true, BoxResult.second, this, 0); {
// todo: come up with a better way to share this code between CScriptNode and CLightNode
SRayIntersection out;
out.pNode = this;
out.AssetIndex = 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 else
return SRayIntersection(false, 0.f, nullptr, 0); {
// 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() CLight* CLightNode::Light()
{ {
return mpLight; return mpLight;
} }
CVector2f CLightNode::BillboardScale()
{
return mScale.xz() * 0.75f;
}

View File

@ -10,11 +10,13 @@ class CLightNode : public CSceneNode
public: public:
CLightNode(CSceneManager *pScene, CSceneNode *pParent = 0, CLight *Light = 0); CLightNode(CSceneManager *pScene, CSceneNode *pParent = 0, CLight *Light = 0);
ENodeType NodeType(); ENodeType NodeType();
void AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum); void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo);
void Draw(ERenderOptions Options); void Draw(ERenderOptions Options);
void DrawAsset(ERenderOptions Options, u32 asset); void DrawAsset(ERenderOptions Options, u32 asset);
SRayIntersection RayNodeIntersectTest(const CRay &Ray, u32 AssetID, ERenderOptions options); void RayAABoxIntersectTest(CRayCollisionTester& Tester);
SRayIntersection RayNodeIntersectTest(const CRay &Ray, u32 AssetID, const SViewInfo& ViewInfo);
CLight* Light(); CLight* Light();
CVector2f BillboardScale();
}; };
#endif // CLIGHTNODE_H #endif // CLIGHTNODE_H

View File

@ -16,10 +16,10 @@ ENodeType CModelNode::NodeType()
return eModelNode; return eModelNode;
} }
void CModelNode::AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum) void CModelNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
{ {
if (!mpModel) return; if (!mpModel) return;
if (!frustum.BoxInFrustum(AABox())) return; if (!ViewInfo.ViewFrustum.BoxInFrustum(AABox())) return;
if (!mpModel->HasTransparency(mActiveMatSet)) if (!mpModel->HasTransparency(mActiveMatSet))
pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawMesh); pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawMesh);
@ -30,7 +30,7 @@ void CModelNode::AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frust
for (u32 iSurf = 0; iSurf < SurfaceCount; iSurf++) for (u32 iSurf = 0; iSurf < SurfaceCount; iSurf++)
{ {
if (frustum.BoxInFrustum(mpModel->GetSurfaceAABox(iSurf).Transformed(Transform()))) if (ViewInfo.ViewFrustum.BoxInFrustum(mpModel->GetSurfaceAABox(iSurf).Transformed(Transform())))
{ {
if (!mpModel->IsSurfaceTransparent(iSurf, mActiveMatSet)) if (!mpModel->IsSurfaceTransparent(iSurf, mActiveMatSet))
pRenderer->AddOpaqueMesh(this, iSurf, mpModel->GetSurfaceAABox(iSurf).Transformed(Transform()), eDrawAsset); pRenderer->AddOpaqueMesh(this, iSurf, mpModel->GetSurfaceAABox(iSurf).Transformed(Transform()), eDrawAsset);
@ -109,13 +109,14 @@ void CModelNode::RayAABoxIntersectTest(CRayCollisionTester &Tester)
} }
} }
SRayIntersection CModelNode::RayNodeIntersectTest(const CRay &Ray, u32 AssetID, ERenderOptions options) SRayIntersection CModelNode::RayNodeIntersectTest(const CRay &Ray, u32 AssetID, const SViewInfo& ViewInfo)
{ {
SRayIntersection out; SRayIntersection out;
out.pNode = this; out.pNode = this;
out.AssetIndex = AssetID; out.AssetIndex = AssetID;
CRay TransformedRay = Ray.Transformed(Transform().Inverse()); CRay TransformedRay = Ray.Transformed(Transform().Inverse());
ERenderOptions options = ViewInfo.pRenderer->RenderOptions();
std::pair<bool,float> Result = mpModel->GetSurface(AssetID)->IntersectsRay(TransformedRay, ((options & eEnableBackfaceCull) == 0)); std::pair<bool,float> Result = mpModel->GetSurface(AssetID)->IntersectsRay(TransformedRay, ((options & eEnableBackfaceCull) == 0));
if (Result.first) if (Result.first)

View File

@ -16,11 +16,11 @@ public:
explicit CModelNode(CSceneManager *pScene, CSceneNode *pParent = 0, CModel *pModel = 0); explicit CModelNode(CSceneManager *pScene, CSceneNode *pParent = 0, CModel *pModel = 0);
virtual ENodeType NodeType(); virtual ENodeType NodeType();
virtual void AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum); virtual void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo);
virtual void Draw(ERenderOptions Options); virtual void Draw(ERenderOptions Options);
virtual void DrawAsset(ERenderOptions Options, u32 asset); virtual void DrawAsset(ERenderOptions Options, u32 asset);
virtual void RayAABoxIntersectTest(CRayCollisionTester &Tester); virtual void RayAABoxIntersectTest(CRayCollisionTester &Tester);
virtual SRayIntersection RayNodeIntersectTest(const CRay &Ray, u32 AssetID, ERenderOptions options); virtual SRayIntersection RayNodeIntersectTest(const CRay &Ray, u32 AssetID, const SViewInfo& ViewInfo);
void SetModel(CModel *pModel); void SetModel(CModel *pModel);
void SetMatSet(u32 MatSet); void SetMatSet(u32 MatSet);

View File

@ -15,12 +15,12 @@ public:
return eRootNode; return eRootNode;
} }
inline void AddToRenderer(CRenderer *, const CFrustumPlanes&) {} inline void AddToRenderer(CRenderer *, const SViewInfo&) {}
inline void Draw(ERenderOptions) {} inline void Draw(ERenderOptions) {}
inline void DrawAsset(ERenderOptions, u32) {} inline void DrawAsset(ERenderOptions, u32) {}
inline void RayAABoxIntersectTest(CRayCollisionTester &) {} inline void RayAABoxIntersectTest(CRayCollisionTester &) {}
inline SRayIntersection RayNodeIntersectTest(const CRay &, u32, ERenderOptions) { inline SRayIntersection RayNodeIntersectTest(const CRay &, u32, const SViewInfo&) {
return SRayIntersection(false, 0.f, nullptr, 0); return SRayIntersection(false, 0.f, nullptr, 0);
} }

View File

@ -58,7 +58,7 @@ public:
virtual void DrawAsset(ERenderOptions options, u32 asset) = 0; virtual void DrawAsset(ERenderOptions options, u32 asset) = 0;
virtual void DrawSelection(); virtual void DrawSelection();
virtual void RayAABoxIntersectTest(CRayCollisionTester& Tester); virtual void RayAABoxIntersectTest(CRayCollisionTester& Tester);
virtual SRayIntersection RayNodeIntersectTest(const CRay& Ray, u32 AssetID, ERenderOptions options) = 0; virtual SRayIntersection RayNodeIntersectTest(const CRay& Ray, u32 AssetID, const SViewInfo& ViewInfo) = 0;
virtual bool IsVisible() const; virtual bool IsVisible() const;
void Unparent(); void Unparent();

View File

@ -101,18 +101,18 @@ TString CScriptNode::PrefixedName() const
return "[" + mpInstance->Template()->TemplateName() + "] " + mpInstance->InstanceName(); return "[" + mpInstance->Template()->TemplateName() + "] " + mpInstance->InstanceName();
} }
void CScriptNode::AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum) void CScriptNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
{ {
if (!mpInstance) return; if (!mpInstance) return;
ERenderOptions options = pRenderer->RenderOptions(); ERenderOptions options = pRenderer->RenderOptions();
if (options & eDrawObjectCollision) if (options & eDrawObjectCollision)
mpCollisionNode->AddToRenderer(pRenderer, frustum); mpCollisionNode->AddToRenderer(pRenderer, ViewInfo);
if (options & eDrawObjects) if (options & eDrawObjects)
{ {
if (frustum.BoxInFrustum(AABox())) if (ViewInfo.ViewFrustum.BoxInFrustum(AABox()))
{ {
if (!mpActiveModel) if (!mpActiveModel)
pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawMesh); pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawMesh);
@ -131,7 +131,7 @@ void CScriptNode::AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frus
for (u32 s = 0; s < SubmeshCount; s++) for (u32 s = 0; s < SubmeshCount; s++)
{ {
if (frustum.BoxInFrustum(mpActiveModel->GetSurfaceAABox(s).Transformed(Transform()))) if (ViewInfo.ViewFrustum.BoxInFrustum(mpActiveModel->GetSurfaceAABox(s).Transformed(Transform())))
{ {
if (!mpActiveModel->IsSurfaceTransparent(s, 0)) if (!mpActiveModel->IsSurfaceTransparent(s, 0))
pRenderer->AddOpaqueMesh(this, s, mpActiveModel->GetSurfaceAABox(s).Transformed(Transform()), eDrawAsset); pRenderer->AddOpaqueMesh(this, s, mpActiveModel->GetSurfaceAABox(s).Transformed(Transform()), eDrawAsset);
@ -152,7 +152,7 @@ void CScriptNode::AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frus
pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawSelection); pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawSelection);
if (mHasVolumePreview) if (mHasVolumePreview)
mpVolumePreviewNode->AddToRenderer(pRenderer, frustum); mpVolumePreviewNode->AddToRenderer(pRenderer, ViewInfo);
} }
} }
@ -171,7 +171,7 @@ void CScriptNode::Draw(ERenderOptions Options)
// Draw billboard // Draw billboard
else if (mpBillboard) else if (mpBillboard)
{ {
CDrawUtil::DrawBillboard(mpBillboard, mPosition, mScale.xy() * CVector2f(0.75f), TintColor()); CDrawUtil::DrawBillboard(mpBillboard, mPosition, BillboardScale(), TintColor());
} }
// If no model or billboard, default to drawing a purple box // If no model or billboard, default to drawing a purple box
@ -243,6 +243,9 @@ void CScriptNode::RayAABoxIntersectTest(CRayCollisionTester &Tester)
return; return;
const CRay& Ray = Tester.Ray(); const CRay& Ray = Tester.Ray();
if (mpActiveModel || !mpBillboard)
{
std::pair<bool,float> BoxResult = AABox().IntersectsRay(Ray); std::pair<bool,float> BoxResult = AABox().IntersectsRay(Ray);
if (BoxResult.first) if (BoxResult.first)
@ -259,19 +262,38 @@ void CScriptNode::RayAABoxIntersectTest(CRayCollisionTester &Tester)
} }
else Tester.AddNode(this, 0, BoxResult.second); 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, ERenderOptions options) SRayIntersection CScriptNode::RayNodeIntersectTest(const CRay& Ray, u32 AssetID, const SViewInfo& ViewInfo)
{ {
CRay TransformedRay = Ray.Transformed(Transform().Inverse()); ERenderOptions options = ViewInfo.pRenderer->RenderOptions();
SRayIntersection out; SRayIntersection out;
out.pNode = this; out.pNode = this;
out.AssetIndex = AssetID; out.AssetIndex = AssetID;
if (options & eDrawObjects) if (options & eDrawObjects)
{
// Model test
if (mpActiveModel || !mpBillboard)
{ {
CModel *pModel = (mpActiveModel ? mpActiveModel : CDrawUtil::GetCubeModel()); 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)); std::pair<bool,float> Result = pModel->GetSurface(AssetID)->IntersectsRay(TransformedRay, ((options & eEnableBackfaceCull) == 0));
if (Result.first) if (Result.first)
@ -286,6 +308,56 @@ SRayIntersection CScriptNode::RayNodeIntersectTest(const CRay &Ray, u32 AssetID,
else else
out.Hit = false; 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; else out.Hit = false;
return out; return out;
@ -380,3 +452,9 @@ CAABox CScriptNode::PreviewVolumeAABox()
else else
return mpVolumePreviewNode->AABox(); return mpVolumePreviewNode->AABox();
} }
CVector2f CScriptNode::BillboardScale()
{
CVector2f out = (mpInstance->Template()->ScaleType() == CScriptTemplate::eScaleEnabled ? mScale.xz() : CVector2f(1.f));
return out * 0.5f;
}

View File

@ -26,18 +26,19 @@ public:
CScriptNode(CSceneManager *pScene, CSceneNode *pParent = 0, CScriptObject *pObject = 0); CScriptNode(CSceneManager *pScene, CSceneNode *pParent = 0, CScriptObject *pObject = 0);
ENodeType NodeType(); ENodeType NodeType();
TString PrefixedName() const; TString PrefixedName() const;
void AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum); void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo);
void Draw(ERenderOptions Options); void Draw(ERenderOptions Options);
void DrawAsset(ERenderOptions Options, u32 Asset); void DrawAsset(ERenderOptions Options, u32 Asset);
void DrawSelection(); void DrawSelection();
void RayAABoxIntersectTest(CRayCollisionTester &Tester); void RayAABoxIntersectTest(CRayCollisionTester &Tester);
SRayIntersection RayNodeIntersectTest(const CRay &Ray, u32 AssetID, ERenderOptions options); SRayIntersection RayNodeIntersectTest(const CRay &Ray, u32 AssetID, const SViewInfo& ViewInfo);
bool IsVisible() const; bool IsVisible() const;
CScriptObject* Object(); CScriptObject* Object();
CModel* ActiveModel(); CModel* ActiveModel();
void GeneratePosition(); void GeneratePosition();
bool HasPreviewVolume(); bool HasPreviewVolume();
CAABox PreviewVolumeAABox(); CAABox PreviewVolumeAABox();
CVector2f BillboardScale();
}; };
#endif // CSCRIPTNODE_H #endif // CSCRIPTNODE_H

View File

@ -19,11 +19,11 @@ ENodeType CStaticNode::NodeType()
return eStaticNode; return eStaticNode;
} }
void CStaticNode::AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum) void CStaticNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
{ {
if (!mpModel) return; if (!mpModel) return;
if (mpModel->IsOccluder()) return; if (mpModel->IsOccluder()) return;
if (!frustum.BoxInFrustum(AABox())) return; if (!ViewInfo.ViewFrustum.BoxInFrustum(AABox())) return;
if (!mpModel->IsTransparent()) if (!mpModel->IsTransparent())
pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawMesh); pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawMesh);
@ -33,7 +33,7 @@ void CStaticNode::AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frus
u32 sm_count = mpModel->GetSurfaceCount(); u32 sm_count = mpModel->GetSurfaceCount();
for (u32 s = 0; s < sm_count; s++) for (u32 s = 0; s < sm_count; s++)
{ {
if (frustum.BoxInFrustum(mpModel->GetSurfaceAABox(s).Transformed(Transform()))) if (ViewInfo.ViewFrustum.BoxInFrustum(mpModel->GetSurfaceAABox(s).Transformed(Transform())))
pRenderer->AddTransparentMesh(this, s, mpModel->GetSurfaceAABox(s).Transformed(Transform()), eDrawAsset); pRenderer->AddTransparentMesh(this, s, mpModel->GetSurfaceAABox(s).Transformed(Transform()), eDrawAsset);
} }
} }
@ -90,13 +90,14 @@ void CStaticNode::RayAABoxIntersectTest(CRayCollisionTester &Tester)
} }
} }
SRayIntersection CStaticNode::RayNodeIntersectTest(const CRay &Ray, u32 AssetID, ERenderOptions options) SRayIntersection CStaticNode::RayNodeIntersectTest(const CRay &Ray, u32 AssetID, const SViewInfo& ViewInfo)
{ {
SRayIntersection out; SRayIntersection out;
out.pNode = this; out.pNode = this;
out.AssetIndex = AssetID; out.AssetIndex = AssetID;
CRay TransformedRay = Ray.Transformed(Transform().Inverse()); CRay TransformedRay = Ray.Transformed(Transform().Inverse());
ERenderOptions options = ViewInfo.pRenderer->RenderOptions();
std::pair<bool,float> Result = mpModel->GetSurface(AssetID)->IntersectsRay(TransformedRay, ((options & eEnableBackfaceCull) == 0)); std::pair<bool,float> Result = mpModel->GetSurface(AssetID)->IntersectsRay(TransformedRay, ((options & eEnableBackfaceCull) == 0));
if (Result.first) if (Result.first)

View File

@ -11,11 +11,11 @@ class CStaticNode : public CSceneNode
public: public:
CStaticNode(CSceneManager *pScene, CSceneNode *pParent = 0, CStaticModel *pModel = 0); CStaticNode(CSceneManager *pScene, CSceneNode *pParent = 0, CStaticModel *pModel = 0);
ENodeType NodeType(); ENodeType NodeType();
void AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum); void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo);
void Draw(ERenderOptions Options); void Draw(ERenderOptions Options);
void DrawAsset(ERenderOptions Options, u32 asset); void DrawAsset(ERenderOptions Options, u32 asset);
void RayAABoxIntersectTest(CRayCollisionTester &Tester); void RayAABoxIntersectTest(CRayCollisionTester &Tester);
SRayIntersection RayNodeIntersectTest(const CRay &Ray, u32 AssetID, ERenderOptions options); SRayIntersection RayNodeIntersectTest(const CRay &Ray, u32 AssetID, const SViewInfo& ViewInfo);
}; };
#endif // CSTATICNODE_H #endif // CSTATICNODE_H

View File

@ -14,6 +14,7 @@ CBasicViewport::CBasicViewport(QWidget *pParent) :
{ {
setMouseTracking(true); setMouseTracking(true);
mCamera.SetAspectRatio((float) width() / height()); mCamera.SetAspectRatio((float) width() / height());
mViewInfo.pCamera = &mCamera;
} }
CBasicViewport::~CBasicViewport() CBasicViewport::~CBasicViewport()
@ -47,9 +48,11 @@ void CBasicViewport::paintGL()
glViewport(0, 0, width(), height()); glViewport(0, 0, width(), height());
glLineWidth(1.f); glLineWidth(1.f);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
mViewInfo.ViewFrustum = mCamera.FrustumPlanes();
CGraphics::sMVPBlock.ProjectionMatrix = mCamera.ProjectionMatrix();
// Actual rendering is intended to be handled by subclassing CBasicViewport and // Actual rendering is intended to be handled by subclassing CBasicViewport and
// reimplementing Render(). // reimplementing Paint().
Paint(); Paint();
// Finally, draw XYZ axes in the corner // Finally, draw XYZ axes in the corner
@ -247,6 +250,15 @@ void CBasicViewport::ProcessInput()
if (IsKeyboardInputActive()) if (IsKeyboardInputActive())
mCamera.ProcessKeyInput((EKeyInputs) mKeysPressed, DeltaTime); mCamera.ProcessKeyInput((EKeyInputs) mKeysPressed, DeltaTime);
// Update view info
const CMatrix4f& View = mCamera.ViewMatrix();
mViewInfo.RotationOnlyViewMatrix = CMatrix4f(View[0][0], View[0][1], View[0][2], 0.f,
View[1][0], View[1][1], View[1][2], 0.f,
View[2][0], View[2][1], View[2][2], 0.f,
View[3][0], View[3][1], View[3][2], 1.f);
mViewInfo.ViewFrustum = mCamera.FrustumPlanes();
// Check user input // Check user input
CheckUserInput(); CheckUserInput();
} }
@ -259,10 +271,6 @@ void CBasicViewport::Render()
} }
// ************ PRIVATE ************ // ************ PRIVATE ************
void CBasicViewport::ProcessInput(double /*DeltaTime*/)
{
}
void CBasicViewport::DrawAxes() void CBasicViewport::DrawAxes()
{ {
// Draw 64x64 axes in lower-left corner with 8px margins // Draw 64x64 axes in lower-left corner with 8px margins
@ -272,7 +280,7 @@ void CBasicViewport::DrawAxes()
glDepthRange(0.f, 1.f); glDepthRange(0.f, 1.f);
CGraphics::sMVPBlock.ModelMatrix = CTransform4f::TranslationMatrix(mCamera.Direction() * 5).ToMatrix4f(); CGraphics::sMVPBlock.ModelMatrix = CTransform4f::TranslationMatrix(mCamera.Direction() * 5).ToMatrix4f();
CGraphics::sMVPBlock.ViewMatrix = mCamera.RotationOnlyViewMatrix(); CGraphics::sMVPBlock.ViewMatrix = mViewInfo.RotationOnlyViewMatrix;
CGraphics::sMVPBlock.ProjectionMatrix = Math::OrthographicMatrix(-1.f, 1.f, -1.f, 1.f, 0.1f, 100.f); CGraphics::sMVPBlock.ProjectionMatrix = Math::OrthographicMatrix(-1.f, 1.f, -1.f, 1.f, 0.1f, 100.f);
CGraphics::UpdateMVPBlock(); CGraphics::UpdateMVPBlock();

View File

@ -5,6 +5,7 @@
#include <QOpenGLWidget> #include <QOpenGLWidget>
#include <Core/CRenderer.h> #include <Core/CRenderer.h>
#include <Core/SViewInfo.h>
#include <Common/CRay.h> #include <Common/CRay.h>
#include <Common/CTimer.h> #include <Common/CTimer.h>
#include <Common/CVector2i.h> #include <Common/CVector2i.h>
@ -23,6 +24,7 @@ protected:
CCamera mCamera; CCamera mCamera;
CTimer mFrameTimer; CTimer mFrameTimer;
double mLastDrawTime; double mLastDrawTime;
SViewInfo mViewInfo;
// Cursor settings // Cursor settings
QCursor mCursorState; QCursor mCursorState;
@ -55,7 +57,6 @@ public:
bool IsCursorVisible(); bool IsCursorVisible();
bool IsMouseInputActive(); bool IsMouseInputActive();
bool IsKeyboardInputActive(); bool IsKeyboardInputActive();
CRenderer* Renderer();
CCamera& Camera(); CCamera& Camera();
CRay CastRay(); CRay CastRay();
CVector2f MouseDeviceCoordinates(); CVector2f MouseDeviceCoordinates();
@ -74,7 +75,6 @@ protected slots:
virtual void OnMouseRelease(QMouseEvent* /*pEvent*/) {} virtual void OnMouseRelease(QMouseEvent* /*pEvent*/) {}
private: private:
void ProcessInput(double DeltaTime);
void DrawAxes(); void DrawAxes();
}; };

View File

@ -42,7 +42,7 @@ CGizmo::~CGizmo()
{ {
} }
void CGizmo::AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes&) void CGizmo::AddToRenderer(CRenderer *pRenderer, const SViewInfo&)
{ {
// Transform is updated every frame even if the user doesn't modify the gizmo // Transform is updated every frame even if the user doesn't modify the gizmo
// in order to account for scale changes based on camera distance // in order to account for scale changes based on camera distance

View File

@ -124,7 +124,7 @@ public:
CGizmo(); CGizmo();
~CGizmo(); ~CGizmo();
void AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum); void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo);
void DrawAsset(ERenderOptions options, u32 asset); void DrawAsset(ERenderOptions options, u32 asset);
void IncrementSize(); void IncrementSize();

View File

@ -49,8 +49,8 @@ void CModelEditorViewport::Paint()
else if (mMode == eDrawMesh) else if (mMode == eDrawMesh)
{ {
CDrawUtil::DrawGrid(); CDrawUtil::DrawGrid();
mpModelNode->AddToRenderer(mpRenderer, mCamera.FrustumPlanes()); mpModelNode->AddToRenderer(mpRenderer, mViewInfo);
mpRenderer->RenderBuckets(mCamera); mpRenderer->RenderBuckets(mViewInfo);
} }
else if (mMode == eDrawSphere) else if (mMode == eDrawSphere)

View File

@ -1,5 +1,6 @@
#include "CSceneViewport.h" #include "CSceneViewport.h"
#include "undo/UndoCommands.h" #include "undo/UndoCommands.h"
#include <Core/SViewInfo.h>
CSceneViewport::CSceneViewport(QWidget *pParent) CSceneViewport::CSceneViewport(QWidget *pParent)
: CBasicViewport(pParent), : CBasicViewport(pParent),
@ -13,6 +14,9 @@ CSceneViewport::CSceneViewport(QWidget *pParent)
mpRenderer = new CRenderer(); mpRenderer = new CRenderer();
mpRenderer->SetClearColor(CColor::skBlack); mpRenderer->SetClearColor(CColor::skBlack);
mpRenderer->SetViewportSize(width(), height()); mpRenderer->SetViewportSize(width(), height());
mViewInfo.pScene = mpScene;
mViewInfo.pRenderer = mpRenderer;
} }
CSceneViewport::~CSceneViewport() CSceneViewport::~CSceneViewport()
@ -77,7 +81,7 @@ void CSceneViewport::SceneRayCast(const CRay& ray)
return; return;
} }
SRayIntersection result = mpScene->SceneRayCast(ray, mpRenderer->RenderOptions()); SRayIntersection result = mpScene->SceneRayCast(ray, mViewInfo);
if (result.Hit) if (result.Hit)
{ {
@ -131,12 +135,12 @@ void CSceneViewport::Paint()
if (mDrawSky) if (mDrawSky)
{ {
CModel *pSky = mpScene->GetActiveSkybox(); CModel *pSky = mpScene->GetActiveSkybox();
if (pSky) mpRenderer->RenderSky(pSky, mCamera); if (pSky) mpRenderer->RenderSky(pSky, mViewInfo);
} }
mCamera.LoadMatrices(); mCamera.LoadMatrices();
mpScene->AddSceneToRenderer(mpRenderer, mCamera); mpScene->AddSceneToRenderer(mpRenderer, mViewInfo);
mpRenderer->RenderBuckets(mCamera); mpRenderer->RenderBuckets(mViewInfo);
mpRenderer->RenderBloom(); mpRenderer->RenderBloom();
if (mpEditor->IsGizmoVisible()) if (mpEditor->IsGizmoVisible())
@ -146,8 +150,8 @@ void CSceneViewport::Paint()
mpRenderer->ClearDepthBuffer(); mpRenderer->ClearDepthBuffer();
pGizmo->UpdateForCamera(mCamera); pGizmo->UpdateForCamera(mCamera);
pGizmo->AddToRenderer(mpRenderer, mCamera.FrustumPlanes()); pGizmo->AddToRenderer(mpRenderer, mViewInfo);
mpRenderer->RenderBuckets(mCamera); mpRenderer->RenderBuckets(mViewInfo);
} }
mpRenderer->EndFrame(); mpRenderer->EndFrame();