From 373426a98f601ac5b77143f24c0504873e0ef5b9 Mon Sep 17 00:00:00 2001 From: parax0 Date: Wed, 25 Nov 2015 14:37:34 -0700 Subject: [PATCH] Added the ability to do accurate raycasts against billboards --- Common/CQuaternion.cpp | 56 +++++++++++++- Common/CQuaternion.h | 2 + Common/CRayCollisionTester.cpp | 4 +- Common/CRayCollisionTester.h | 4 +- Common/Math.cpp | 5 ++ Common/Math.h | 2 + Core/CCamera.cpp | 46 +++++------- Core/CCamera.h | 6 +- Core/CRenderBucket.cpp | 4 +- Core/CRenderBucket.h | 2 +- Core/CRenderer.cpp | 14 ++-- Core/CRenderer.h | 7 +- Core/CSceneManager.cpp | 28 +++---- Core/CSceneManager.h | 5 +- Core/IRenderable.h | 5 +- Core/SViewInfo.h | 19 +++++ PrimeWorldEditor.pro | 5 +- Resource/CTexture.cpp | 43 +++++++++++ Resource/CTexture.h | 2 + Scene/CBoundingBoxNode.cpp | 12 --- Scene/CBoundingBoxNode.h | 16 ---- Scene/CCollisionNode.cpp | 6 +- Scene/CCollisionNode.h | 4 +- Scene/CLightNode.cpp | 87 +++++++++++++++++++--- Scene/CLightNode.h | 6 +- Scene/CModelNode.cpp | 9 ++- Scene/CModelNode.h | 4 +- Scene/CRootNode.h | 4 +- Scene/CSceneNode.h | 2 +- Scene/CScriptNode.cpp | 132 ++++++++++++++++++++++++++------- Scene/CScriptNode.h | 5 +- Scene/CStaticNode.cpp | 9 ++- Scene/CStaticNode.h | 4 +- UI/CBasicViewport.cpp | 20 +++-- UI/CBasicViewport.h | 4 +- UI/CGizmo.cpp | 2 +- UI/CGizmo.h | 2 +- UI/CModelEditorViewport.cpp | 4 +- UI/CSceneViewport.cpp | 16 ++-- 39 files changed, 431 insertions(+), 176 deletions(-) create mode 100644 Core/SViewInfo.h delete mode 100644 Scene/CBoundingBoxNode.cpp delete mode 100644 Scene/CBoundingBoxNode.h diff --git a/Common/CQuaternion.cpp b/Common/CQuaternion.cpp index da00e734..01d551f9 100644 --- a/Common/CQuaternion.cpp +++ b/Common/CQuaternion.cpp @@ -1,7 +1,8 @@ #include "CQuaternion.h" #include #include -#include +#include "Math.h" +#include "CMatrix4f.h" 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::skZero = CQuaternion(0.f, 0.f, 0.f, 0.f); diff --git a/Common/CQuaternion.h b/Common/CQuaternion.h index 11240338..ddcc0e5b 100644 --- a/Common/CQuaternion.h +++ b/Common/CQuaternion.h @@ -25,6 +25,8 @@ public: // Static static CQuaternion FromEuler(CVector3f euler); 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 skZero; diff --git a/Common/CRayCollisionTester.cpp b/Common/CRayCollisionTester.cpp index c1e731dc..81f77740 100644 --- a/Common/CRayCollisionTester.cpp +++ b/Common/CRayCollisionTester.cpp @@ -19,7 +19,7 @@ void CRayCollisionTester::AddNode(CSceneNode *pNode, u32 AssetIndex, float Dista Intersection.Distance = Distance; } -SRayIntersection CRayCollisionTester::TestNodes(ERenderOptions options) +SRayIntersection CRayCollisionTester::TestNodes(const SViewInfo& ViewInfo) { // Sort nodes by distance from ray mBoxIntersectList.sort( @@ -43,7 +43,7 @@ SRayIntersection CRayCollisionTester::TestNodes(ERenderOptions options) // Otherwise, more intersection tests... CSceneNode *pNode = Intersection.pNode; - SRayIntersection MidResult = pNode->RayNodeIntersectTest(mRay, Intersection.AssetIndex, options); + SRayIntersection MidResult = pNode->RayNodeIntersectTest(mRay, Intersection.AssetIndex, ViewInfo); if (MidResult.Hit) { diff --git a/Common/CRayCollisionTester.h b/Common/CRayCollisionTester.h index a936e2f6..dadb0da8 100644 --- a/Common/CRayCollisionTester.h +++ b/Common/CRayCollisionTester.h @@ -6,7 +6,7 @@ #include "CVector3f.h" #include "SRayIntersection.h" #include "types.h" -#include +#include #include @@ -22,7 +22,7 @@ public: ~CRayCollisionTester(); const CRay& Ray() const; void AddNode(CSceneNode *pNode, u32 AssetIndex, float Distance); - SRayIntersection TestNodes(ERenderOptions options); + SRayIntersection TestNodes(const SViewInfo& ViewInfo); }; inline const CRay& CRayCollisionTester::Ray() const diff --git a/Common/Math.cpp b/Common/Math.cpp index 4c14b354..aeabb7cf 100644 --- a/Common/Math.cpp +++ b/Common/Math.cpp @@ -15,6 +15,11 @@ float Pow(float Base, float Exponent) return pow(Base, Exponent); } +float Sqrt(float v) +{ + return sqrtf(v); +} + float Distance(const CVector3f& A, const CVector3f& B) { return sqrtf( Pow(B.x - A.x, 2.f) + diff --git a/Common/Math.h b/Common/Math.h index 6d8ff97e..f0a76eac 100644 --- a/Common/Math.h +++ b/Common/Math.h @@ -15,6 +15,8 @@ float Abs(float v); float Pow(float Base, float Exponent); +float Sqrt(float v); + float Distance(const CVector3f& A, const CVector3f& B); float DegreesToRadians(float deg); diff --git a/Core/CCamera.cpp b/Core/CCamera.cpp index 9723242d..b6d25268 100644 --- a/Core/CCamera.cpp +++ b/Core/CCamera.cpp @@ -163,13 +163,6 @@ void CCamera::LoadMatrices() CGraphics::UpdateMVPBlock(); } -void CCamera::LoadRotationOnlyMatrices() -{ - CGraphics::sMVPBlock.ViewMatrix = RotationOnlyViewMatrix(); - CGraphics::sMVPBlock.ProjectionMatrix = ProjectionMatrix(); - CGraphics::UpdateMVPBlock(); -} - // ************ GETTERS ************ CVector3f CCamera::Position() const { @@ -181,6 +174,16 @@ CVector3f CCamera::Direction() const return mDirection; } +CVector3f CCamera::UpVector() const +{ + return mUpVector; +} + +CVector3f CCamera::RightVector() const +{ + return mRightVector; +} + float CCamera::Yaw() const { return mYaw; @@ -204,17 +207,6 @@ const CMatrix4f& CCamera::ViewMatrix() 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() { if (mProjectionOutdated) @@ -295,6 +287,14 @@ void CCamera::CalculateDirection() cos(mPitch) * sin(mYaw), sin(mPitch) ); + + mRightVector = CVector3f( + cos(mYaw - Math::skHalfPi), + sin(mYaw - Math::skHalfPi), + 0 + ); + + mUpVector = mRightVector.Cross(mDirection); } void CCamera::CalculateView() @@ -302,17 +302,9 @@ void CCamera::CalculateView() // todo: don't use glm 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 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(); mViewOutdated = false; } diff --git a/Core/CCamera.h b/Core/CCamera.h index f1505b93..233520b6 100644 --- a/Core/CCamera.h +++ b/Core/CCamera.h @@ -21,6 +21,8 @@ class CCamera ECameraMoveMode mMode; CVector3f mPosition; CVector3f mDirection; + CVector3f mRightVector; + CVector3f mUpVector; float mAspectRatio; float mYaw; @@ -48,16 +50,16 @@ public: void ProcessMouseInput(EKeyInputs KeyFlags, EMouseInputs MouseFlags, float XMovement, float YMovement); CRay CastRay(CVector2f DeviceCoords); void LoadMatrices(); - void LoadRotationOnlyMatrices(); // Getters CVector3f Position() const; CVector3f Direction() const; + CVector3f UpVector() const; + CVector3f RightVector() const; float Yaw() const; float Pitch() const; float FieldOfView() const; const CMatrix4f& ViewMatrix(); - CMatrix4f RotationOnlyViewMatrix(); const CMatrix4f& ProjectionMatrix(); const CFrustumPlanes& FrustumPlanes(); diff --git a/Core/CRenderBucket.cpp b/Core/CRenderBucket.cpp index 65a3373c..7e9a8162 100644 --- a/Core/CRenderBucket.cpp +++ b/Core/CRenderBucket.cpp @@ -24,7 +24,7 @@ void CRenderBucket::Add(const SRenderablePtr& ptr) mSize++; } -void CRenderBucket::Sort(CCamera& Camera) +void CRenderBucket::Sort(CCamera* pCamera) { struct { CCamera *pCamera; @@ -39,7 +39,7 @@ void CRenderBucket::Sort(CCamera& Camera) return (dotL > dotR); } } backToFront; - backToFront.pCamera = &Camera; + backToFront.pCamera = pCamera; if (mSortType == BackToFront) std::stable_sort(mRenderables.begin(), mRenderables.begin() + mSize, backToFront); diff --git a/Core/CRenderBucket.h b/Core/CRenderBucket.h index e5aaee35..2f2ffd0a 100644 --- a/Core/CRenderBucket.h +++ b/Core/CRenderBucket.h @@ -25,7 +25,7 @@ public: CRenderBucket(); void SetSortType(ESortType Type); void Add(const SRenderablePtr& ptr); - void Sort(CCamera& Camera); + void Sort(CCamera* pCamera); void Clear(); void Draw(ERenderOptions Options); }; diff --git a/Core/CRenderer.cpp b/Core/CRenderer.cpp index a20a23c9..885e1337 100644 --- a/Core/CRenderer.cpp +++ b/Core/CRenderer.cpp @@ -54,7 +54,7 @@ void CRenderer::Init() } // ************ GETTERS/SETTERS ************ -ERenderOptions CRenderer::RenderOptions() +ERenderOptions CRenderer::RenderOptions() const { return mOptions; } @@ -159,11 +159,10 @@ void CRenderer::SetViewportSize(u32 Width, u32 Height) } // ************ RENDER ************ -void CRenderer::RenderBuckets(CCamera& Camera) +void CRenderer::RenderBuckets(const SViewInfo& ViewInfo) { if (!mInitialized) Init(); mSceneFramebuffer.Bind(); - //Camera.LoadMatrices(); // Set backface culling if (mOptions & eEnableBackfaceCull) glEnable(GL_CULL_FACE); @@ -175,7 +174,7 @@ void CRenderer::RenderBuckets(CCamera& Camera) mOpaqueBucket.Draw(mOptions); mOpaqueBucket.Clear(); - mTransparentBucket.Sort(Camera); + mTransparentBucket.Sort(ViewInfo.pCamera); mTransparentBucket.Draw(mOptions); mTransparentBucket.Clear(); } @@ -288,7 +287,7 @@ void CRenderer::RenderBloom() glEnable(GL_DEPTH_TEST); } -void CRenderer::RenderSky(CModel *pSkyboxModel, CCamera& Camera) +void CRenderer::RenderSky(CModel *pSkyboxModel, const SViewInfo& ViewInfo) { if (!mInitialized) Init(); if (!pSkyboxModel) return; @@ -302,7 +301,10 @@ void CRenderer::RenderSky(CModel *pSkyboxModel, CCamera& Camera) CGraphics::UpdateVertexBlock(); CGraphics::UpdatePixelBlock(); CGraphics::UpdateLightBlock(); - Camera.LoadRotationOnlyMatrices(); + + // Load rotation-only view matrix + CGraphics::sMVPBlock.ViewMatrix = ViewInfo.RotationOnlyViewMatrix; + CGraphics::UpdateMVPBlock(); glDepthRange(1.f, 1.f); pSkyboxModel->Draw(mOptions, 0); diff --git a/Core/CRenderer.h b/Core/CRenderer.h index 2dc56f99..940a57ea 100644 --- a/Core/CRenderer.h +++ b/Core/CRenderer.h @@ -9,6 +9,7 @@ #include "ERenderOptions.h" #include "ERenderCommand.h" #include "SRenderablePtr.h" +#include "SViewInfo.h" #include #include #include @@ -53,7 +54,7 @@ public: void Init(); // Getters/Setters - ERenderOptions RenderOptions(); + ERenderOptions RenderOptions() const; void ToggleWorld(bool b); void ToggleWorldCollision(bool b); void ToggleObjects(bool b); @@ -71,9 +72,9 @@ public: void SetViewportSize(u32 Width, u32 Height); // Render - void RenderBuckets(CCamera& Camera); + void RenderBuckets(const SViewInfo& ViewInfo); 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 AddTransparentMesh(IRenderable *pRenderable, u32 AssetID, CAABox& AABox, ERenderCommand Command); void BeginFrame(); diff --git a/Core/CSceneManager.cpp b/Core/CSceneManager.cpp index 3807b693..febef633 100644 --- a/Core/CSceneManager.cpp +++ b/Core/CSceneManager.cpp @@ -215,48 +215,50 @@ void CSceneManager::ClearScene() 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++) if (mModelNodes[n]->IsVisible()) - mModelNodes[n]->AddToRenderer(pRenderer, camera.FrustumPlanes()); + mModelNodes[n]->AddToRenderer(pRenderer, ViewInfo); for (u32 n = 0; n < mStaticNodes.size(); n++) 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++) 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++) 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++) 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 // 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 *pNodeVectors[5] = { reinterpret_cast*>(&mModelNodes), reinterpret_cast*>(&mStaticNodes), @@ -283,7 +285,7 @@ SRayIntersection CSceneManager::SceneRayCast(const CRay& Ray, ERenderOptions ren vec[iNode]->RayAABoxIntersectTest(Tester); } - return Tester.TestNodes(renderOptions); + return Tester.TestNodes(ViewInfo); } void CSceneManager::PickEnvironmentObjects() diff --git a/Core/CSceneManager.h b/Core/CSceneManager.h index f4dc45a1..6b458bf5 100644 --- a/Core/CSceneManager.h +++ b/Core/CSceneManager.h @@ -7,6 +7,7 @@ #include "CAreaAttributes.h" #include "CRenderer.h" +#include "SViewInfo.h" #include #include #include @@ -57,8 +58,8 @@ public: void SetActiveArea(CGameArea *_area); void SetActiveWorld(CWorld *_world); void ClearScene(); - void AddSceneToRenderer(CRenderer *pRenderer, CCamera& camera); - SRayIntersection SceneRayCast(const CRay& Ray, ERenderOptions renderOptions); + 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); diff --git a/Core/IRenderable.h b/Core/IRenderable.h index 5e2c69cf..13e6bdf3 100644 --- a/Core/IRenderable.h +++ b/Core/IRenderable.h @@ -2,9 +2,8 @@ #define IRENDERABLE_H #include "ERenderOptions.h" -#include +#include "SViewInfo.h" #include -#include class CRenderer; @@ -13,7 +12,7 @@ class IRenderable public: 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 DrawAsset(ERenderOptions /*options*/, u32 /*asset*/) {} virtual void DrawSelection() {} diff --git a/Core/SViewInfo.h b/Core/SViewInfo.h new file mode 100644 index 00000000..cf520617 --- /dev/null +++ b/Core/SViewInfo.h @@ -0,0 +1,19 @@ +#ifndef SVIEWINFO +#define SVIEWINFO + +#include "CFrustumPlanes.h" +#include +#include + +struct SViewInfo +{ + class CSceneManager *pScene; + class CRenderer *pRenderer; + + class CCamera *pCamera; + CFrustumPlanes ViewFrustum; + CMatrix4f RotationOnlyViewMatrix; +}; + +#endif // SVIEWINFO + diff --git a/PrimeWorldEditor.pro b/PrimeWorldEditor.pro index 9ae65b19..889a3c68 100644 --- a/PrimeWorldEditor.pro +++ b/PrimeWorldEditor.pro @@ -113,7 +113,6 @@ SOURCES += \ Resource/model/SSurface.cpp \ Common/CRayCollisionTester.cpp \ Common/Math.cpp \ - Scene/CBoundingBoxNode.cpp \ Core/Log.cpp \ Common/CVector2i.cpp \ UI/CNodeSelection.cpp \ @@ -273,7 +272,6 @@ HEADERS += \ Common/CRayCollisionTester.h \ Scene/ENodeType.h \ Common/Math.h \ - Scene/CBoundingBoxNode.h \ Core/Log.h \ Scene/CRootNode.h \ Common/CVector2i.h \ @@ -321,7 +319,8 @@ HEADERS += \ UI/WAnimParamsEditor.h \ Resource/CCollisionMeshGroup.h \ Core/CFrustumPlanes.h \ - Core/CLightParameters.h + Core/CLightParameters.h \ + Core/SViewInfo.h FORMS += \ UI/CStartWindow.ui \ diff --git a/Resource/CTexture.cpp b/Resource/CTexture.cpp index 8a42cd75..4585cd6e 100644 --- a/Resource/CTexture.cpp +++ b/Resource/CTexture.cpp @@ -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) { if (!out.IsValid()) return false; diff --git a/Resource/CTexture.h b/Resource/CTexture.h index 6330e5a0..9a197160 100644 --- a/Resource/CTexture.h +++ b/Resource/CTexture.h @@ -2,6 +2,7 @@ #define CTEXTURE_H #include +#include #include #include #include "CResource.h" @@ -35,6 +36,7 @@ public: bool BufferGL(); void Bind(u32 GLTextureUnit); void Resize(u32 Width, u32 Height); + float ReadTexelAlpha(const CVector2f& TexCoord); bool WriteDDS(COutputStream& out); // Getters diff --git a/Scene/CBoundingBoxNode.cpp b/Scene/CBoundingBoxNode.cpp deleted file mode 100644 index 47f467cf..00000000 --- a/Scene/CBoundingBoxNode.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "CBoundingBoxNode.h" - -CBoundingBoxNode::CBoundingBoxNode() -{ - -} - -CBoundingBoxNode::~CBoundingBoxNode() -{ - -} - diff --git a/Scene/CBoundingBoxNode.h b/Scene/CBoundingBoxNode.h deleted file mode 100644 index ab28107c..00000000 --- a/Scene/CBoundingBoxNode.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef CBOUNDINGBOXNODE_H -#define CBOUNDINGBOXNODE_H - -#include "CSceneNode.h" - -class CBoundingBoxNode -{ - CColor mColor; - -public: - CBoundingBoxNode(); - ~CBoundingBoxNode(); - -}; - -#endif // CBOUNDINGBOXNODE_H diff --git a/Scene/CCollisionNode.cpp b/Scene/CCollisionNode.cpp index aae1f292..004a2ddf 100644 --- a/Scene/CCollisionNode.cpp +++ b/Scene/CCollisionNode.cpp @@ -15,10 +15,10 @@ ENodeType CCollisionNode::NodeType() return eCollisionNode; } -void CCollisionNode::AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum) +void CCollisionNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo) { if (!mpCollision) return; - if (!frustum.BoxInFrustum(AABox())) return; + if (!ViewInfo.ViewFrustum.BoxInFrustum(AABox())) return; 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 SRayIntersection Result; diff --git a/Scene/CCollisionNode.h b/Scene/CCollisionNode.h index 36137c05..a6daef56 100644 --- a/Scene/CCollisionNode.h +++ b/Scene/CCollisionNode.h @@ -12,10 +12,10 @@ class CCollisionNode : public CSceneNode public: CCollisionNode(CSceneManager *pScene, CSceneNode *pParent = 0, CCollisionMeshGroup *pCollision = 0); ENodeType NodeType(); - void AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustumPlanes); + void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo); void Draw(ERenderOptions Options); 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); }; diff --git a/Scene/CLightNode.cpp b/Scene/CLightNode.cpp index a81432cc..bec63a1d 100644 --- a/Scene/CLightNode.cpp +++ b/Scene/CLightNode.cpp @@ -1,4 +1,5 @@ #include "CLightNode.h" +#include #include #include #include @@ -24,16 +25,16 @@ ENodeType CLightNode::NodeType() 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); } 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 /*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 - bool allowBackfaces = ((options & eEnableBackfaceCull) == 0); + CVector2f BillScale = BillboardScale(); + float ScaleXY = (BillScale.x > BillScale.y ? BillScale.x : BillScale.y); - if (!allowBackfaces && (AABox().IsPointInBox(Ray.Origin()))) - return SRayIntersection(false, 0.f, nullptr, 0); + CAABox BillBox = CAABox(mPosition + CVector3f(-ScaleXY, -ScaleXY, -BillScale.y), + mPosition + CVector3f( ScaleXY, ScaleXY, BillScale.y)); - std::pair BoxResult = AABox().IntersectsRay(Ray); + std::pair BoxResult = BillBox.IntersectsRay(Tester.Ray()); + if (BoxResult.first) Tester.AddNode(this, 0, BoxResult.second); +} - if (BoxResult.first) - return SRayIntersection(true, BoxResult.second, this, 0); +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.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 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 - return SRayIntersection(false, 0.f, nullptr, 0); + out.Hit = false; + + return out; } CLight* CLightNode::Light() { return mpLight; } + +CVector2f CLightNode::BillboardScale() +{ + return mScale.xz() * 0.75f; +} diff --git a/Scene/CLightNode.h b/Scene/CLightNode.h index 86cb310e..9a1f6f53 100644 --- a/Scene/CLightNode.h +++ b/Scene/CLightNode.h @@ -10,11 +10,13 @@ class CLightNode : public CSceneNode public: CLightNode(CSceneManager *pScene, CSceneNode *pParent = 0, CLight *Light = 0); ENodeType NodeType(); - void AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum); + void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo); void Draw(ERenderOptions Options); 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(); + CVector2f BillboardScale(); }; #endif // CLIGHTNODE_H diff --git a/Scene/CModelNode.cpp b/Scene/CModelNode.cpp index 401c6f3b..71a54b21 100644 --- a/Scene/CModelNode.cpp +++ b/Scene/CModelNode.cpp @@ -16,10 +16,10 @@ ENodeType CModelNode::NodeType() return eModelNode; } -void CModelNode::AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum) +void CModelNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo) { if (!mpModel) return; - if (!frustum.BoxInFrustum(AABox())) return; + if (!ViewInfo.ViewFrustum.BoxInFrustum(AABox())) return; if (!mpModel->HasTransparency(mActiveMatSet)) 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++) { - if (frustum.BoxInFrustum(mpModel->GetSurfaceAABox(iSurf).Transformed(Transform()))) + if (ViewInfo.ViewFrustum.BoxInFrustum(mpModel->GetSurfaceAABox(iSurf).Transformed(Transform()))) { if (!mpModel->IsSurfaceTransparent(iSurf, mActiveMatSet)) 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; out.pNode = this; out.AssetIndex = AssetID; CRay TransformedRay = Ray.Transformed(Transform().Inverse()); + ERenderOptions options = ViewInfo.pRenderer->RenderOptions(); std::pair Result = mpModel->GetSurface(AssetID)->IntersectsRay(TransformedRay, ((options & eEnableBackfaceCull) == 0)); if (Result.first) diff --git a/Scene/CModelNode.h b/Scene/CModelNode.h index ad973c73..c1d73fe3 100644 --- a/Scene/CModelNode.h +++ b/Scene/CModelNode.h @@ -16,11 +16,11 @@ public: explicit CModelNode(CSceneManager *pScene, CSceneNode *pParent = 0, CModel *pModel = 0); 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 DrawAsset(ERenderOptions Options, u32 asset); 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 SetMatSet(u32 MatSet); diff --git a/Scene/CRootNode.h b/Scene/CRootNode.h index 0c011e6f..7d297847 100644 --- a/Scene/CRootNode.h +++ b/Scene/CRootNode.h @@ -15,12 +15,12 @@ public: return eRootNode; } - inline void AddToRenderer(CRenderer *, const CFrustumPlanes&) {} + inline void AddToRenderer(CRenderer *, const SViewInfo&) {} inline void Draw(ERenderOptions) {} inline void DrawAsset(ERenderOptions, u32) {} 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); } diff --git a/Scene/CSceneNode.h b/Scene/CSceneNode.h index 61bcb500..212a2e37 100644 --- a/Scene/CSceneNode.h +++ b/Scene/CSceneNode.h @@ -58,7 +58,7 @@ public: virtual void DrawAsset(ERenderOptions options, u32 asset) = 0; virtual void DrawSelection(); 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; void Unparent(); diff --git a/Scene/CScriptNode.cpp b/Scene/CScriptNode.cpp index 9a1e40c2..32cd6dc2 100644 --- a/Scene/CScriptNode.cpp +++ b/Scene/CScriptNode.cpp @@ -101,18 +101,18 @@ TString CScriptNode::PrefixedName() const 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; ERenderOptions options = pRenderer->RenderOptions(); if (options & eDrawObjectCollision) - mpCollisionNode->AddToRenderer(pRenderer, frustum); + mpCollisionNode->AddToRenderer(pRenderer, ViewInfo); if (options & eDrawObjects) { - if (frustum.BoxInFrustum(AABox())) + if (ViewInfo.ViewFrustum.BoxInFrustum(AABox())) { if (!mpActiveModel) 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++) { - if (frustum.BoxInFrustum(mpActiveModel->GetSurfaceAABox(s).Transformed(Transform()))) + if (ViewInfo.ViewFrustum.BoxInFrustum(mpActiveModel->GetSurfaceAABox(s).Transformed(Transform()))) { if (!mpActiveModel->IsSurfaceTransparent(s, 0)) 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); if (mHasVolumePreview) - mpVolumePreviewNode->AddToRenderer(pRenderer, frustum); + mpVolumePreviewNode->AddToRenderer(pRenderer, ViewInfo); } } @@ -171,7 +171,7 @@ void CScriptNode::Draw(ERenderOptions Options) // Draw billboard 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 @@ -243,27 +243,44 @@ void CScriptNode::RayAABoxIntersectTest(CRayCollisionTester &Tester) return; const CRay& Ray = Tester.Ray(); - std::pair BoxResult = AABox().IntersectsRay(Ray); - if (BoxResult.first) + if (mpActiveModel || !mpBillboard) { - if (mpActiveModel) - { - for (u32 iSurf = 0; iSurf < mpActiveModel->GetSurfaceCount(); iSurf++) - { - std::pair SurfResult = mpActiveModel->GetSurfaceAABox(iSurf).Transformed(Transform()).IntersectsRay(Ray); + std::pair BoxResult = AABox().IntersectsRay(Ray); - if (SurfResult.first) - Tester.AddNode(this, iSurf, SurfResult.second); + if (BoxResult.first) + { + if (mpActiveModel) + { + for (u32 iSurf = 0; iSurf < mpActiveModel->GetSurfaceCount(); iSurf++) + { + std::pair SurfResult = mpActiveModel->GetSurfaceAABox(iSurf).Transformed(Transform()).IntersectsRay(Ray); + + if (SurfResult.first) + Tester.AddNode(this, iSurf, SurfResult.second); + } } + 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 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; out.pNode = this; @@ -271,20 +288,75 @@ SRayIntersection CScriptNode::RayNodeIntersectTest(const CRay &Ray, u32 AssetID, if (options & eDrawObjects) { - CModel *pModel = (mpActiveModel ? mpActiveModel : CDrawUtil::GetCubeModel()); - std::pair Result = pModel->GetSurface(AssetID)->IntersectsRay(TransformedRay, ((options & eEnableBackfaceCull) == 0)); - - if (Result.first) + // Model test + if (mpActiveModel || !mpBillboard) { - out.Hit = true; + CModel *pModel = (mpActiveModel ? mpActiveModel : CDrawUtil::GetCubeModel()); - CVector3f HitPoint = TransformedRay.PointOnRay(Result.second); - CVector3f WorldHitPoint = Transform() * HitPoint; - out.Distance = Math::Distance(Ray.Origin(), WorldHitPoint); + CRay TransformedRay = Ray.Transformed(Transform().Inverse()); + std::pair 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 - out.Hit = false; + { + // Step 1: check whether the ray intersects with the plane the billboard is on + CPlane BillboardPlane(-ViewInfo.pCamera->Direction(), mPosition); + std::pair 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; @@ -380,3 +452,9 @@ CAABox CScriptNode::PreviewVolumeAABox() else return mpVolumePreviewNode->AABox(); } + +CVector2f CScriptNode::BillboardScale() +{ + CVector2f out = (mpInstance->Template()->ScaleType() == CScriptTemplate::eScaleEnabled ? mScale.xz() : CVector2f(1.f)); + return out * 0.5f; +} diff --git a/Scene/CScriptNode.h b/Scene/CScriptNode.h index 3ea6efbb..3edae38d 100644 --- a/Scene/CScriptNode.h +++ b/Scene/CScriptNode.h @@ -26,18 +26,19 @@ public: CScriptNode(CSceneManager *pScene, CSceneNode *pParent = 0, CScriptObject *pObject = 0); ENodeType NodeType(); TString PrefixedName() const; - void AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum); + void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo); void Draw(ERenderOptions Options); void DrawAsset(ERenderOptions Options, u32 Asset); void DrawSelection(); 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; CScriptObject* Object(); CModel* ActiveModel(); void GeneratePosition(); bool HasPreviewVolume(); CAABox PreviewVolumeAABox(); + CVector2f BillboardScale(); }; #endif // CSCRIPTNODE_H diff --git a/Scene/CStaticNode.cpp b/Scene/CStaticNode.cpp index e9127756..ccf9722e 100644 --- a/Scene/CStaticNode.cpp +++ b/Scene/CStaticNode.cpp @@ -19,11 +19,11 @@ ENodeType CStaticNode::NodeType() return eStaticNode; } -void CStaticNode::AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum) +void CStaticNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo) { if (!mpModel) return; if (mpModel->IsOccluder()) return; - if (!frustum.BoxInFrustum(AABox())) return; + if (!ViewInfo.ViewFrustum.BoxInFrustum(AABox())) return; if (!mpModel->IsTransparent()) pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawMesh); @@ -33,7 +33,7 @@ void CStaticNode::AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frus u32 sm_count = mpModel->GetSurfaceCount(); 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); } } @@ -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; out.pNode = this; out.AssetIndex = AssetID; CRay TransformedRay = Ray.Transformed(Transform().Inverse()); + ERenderOptions options = ViewInfo.pRenderer->RenderOptions(); std::pair Result = mpModel->GetSurface(AssetID)->IntersectsRay(TransformedRay, ((options & eEnableBackfaceCull) == 0)); if (Result.first) diff --git a/Scene/CStaticNode.h b/Scene/CStaticNode.h index e7117c6b..f3059e11 100644 --- a/Scene/CStaticNode.h +++ b/Scene/CStaticNode.h @@ -11,11 +11,11 @@ class CStaticNode : public CSceneNode public: CStaticNode(CSceneManager *pScene, CSceneNode *pParent = 0, CStaticModel *pModel = 0); ENodeType NodeType(); - void AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum); + void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo); void Draw(ERenderOptions Options); void DrawAsset(ERenderOptions Options, u32 asset); 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 diff --git a/UI/CBasicViewport.cpp b/UI/CBasicViewport.cpp index 1779b019..d8ad81a6 100644 --- a/UI/CBasicViewport.cpp +++ b/UI/CBasicViewport.cpp @@ -14,6 +14,7 @@ CBasicViewport::CBasicViewport(QWidget *pParent) : { setMouseTracking(true); mCamera.SetAspectRatio((float) width() / height()); + mViewInfo.pCamera = &mCamera; } CBasicViewport::~CBasicViewport() @@ -47,9 +48,11 @@ void CBasicViewport::paintGL() glViewport(0, 0, width(), height()); glLineWidth(1.f); glEnable(GL_DEPTH_TEST); + mViewInfo.ViewFrustum = mCamera.FrustumPlanes(); + CGraphics::sMVPBlock.ProjectionMatrix = mCamera.ProjectionMatrix(); // Actual rendering is intended to be handled by subclassing CBasicViewport and - // reimplementing Render(). + // reimplementing Paint(). Paint(); // Finally, draw XYZ axes in the corner @@ -247,6 +250,15 @@ void CBasicViewport::ProcessInput() if (IsKeyboardInputActive()) 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 CheckUserInput(); } @@ -259,10 +271,6 @@ void CBasicViewport::Render() } // ************ PRIVATE ************ -void CBasicViewport::ProcessInput(double /*DeltaTime*/) -{ -} - void CBasicViewport::DrawAxes() { // Draw 64x64 axes in lower-left corner with 8px margins @@ -272,7 +280,7 @@ void CBasicViewport::DrawAxes() glDepthRange(0.f, 1.f); 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::UpdateMVPBlock(); diff --git a/UI/CBasicViewport.h b/UI/CBasicViewport.h index 8b23e1c9..c65114ac 100644 --- a/UI/CBasicViewport.h +++ b/UI/CBasicViewport.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -23,6 +24,7 @@ protected: CCamera mCamera; CTimer mFrameTimer; double mLastDrawTime; + SViewInfo mViewInfo; // Cursor settings QCursor mCursorState; @@ -55,7 +57,6 @@ public: bool IsCursorVisible(); bool IsMouseInputActive(); bool IsKeyboardInputActive(); - CRenderer* Renderer(); CCamera& Camera(); CRay CastRay(); CVector2f MouseDeviceCoordinates(); @@ -74,7 +75,6 @@ protected slots: virtual void OnMouseRelease(QMouseEvent* /*pEvent*/) {} private: - void ProcessInput(double DeltaTime); void DrawAxes(); }; diff --git a/UI/CGizmo.cpp b/UI/CGizmo.cpp index 263e0be3..0ab2c50d 100644 --- a/UI/CGizmo.cpp +++ b/UI/CGizmo.cpp @@ -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 // in order to account for scale changes based on camera distance diff --git a/UI/CGizmo.h b/UI/CGizmo.h index 0d670d6c..d09b4118 100644 --- a/UI/CGizmo.h +++ b/UI/CGizmo.h @@ -124,7 +124,7 @@ public: CGizmo(); ~CGizmo(); - void AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frustum); + void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo); void DrawAsset(ERenderOptions options, u32 asset); void IncrementSize(); diff --git a/UI/CModelEditorViewport.cpp b/UI/CModelEditorViewport.cpp index 7525b86c..7b1ff2e1 100644 --- a/UI/CModelEditorViewport.cpp +++ b/UI/CModelEditorViewport.cpp @@ -49,8 +49,8 @@ void CModelEditorViewport::Paint() else if (mMode == eDrawMesh) { CDrawUtil::DrawGrid(); - mpModelNode->AddToRenderer(mpRenderer, mCamera.FrustumPlanes()); - mpRenderer->RenderBuckets(mCamera); + mpModelNode->AddToRenderer(mpRenderer, mViewInfo); + mpRenderer->RenderBuckets(mViewInfo); } else if (mMode == eDrawSphere) diff --git a/UI/CSceneViewport.cpp b/UI/CSceneViewport.cpp index 3d041a8c..2844a41f 100644 --- a/UI/CSceneViewport.cpp +++ b/UI/CSceneViewport.cpp @@ -1,5 +1,6 @@ #include "CSceneViewport.h" #include "undo/UndoCommands.h" +#include CSceneViewport::CSceneViewport(QWidget *pParent) : CBasicViewport(pParent), @@ -13,6 +14,9 @@ CSceneViewport::CSceneViewport(QWidget *pParent) mpRenderer = new CRenderer(); mpRenderer->SetClearColor(CColor::skBlack); mpRenderer->SetViewportSize(width(), height()); + + mViewInfo.pScene = mpScene; + mViewInfo.pRenderer = mpRenderer; } CSceneViewport::~CSceneViewport() @@ -77,7 +81,7 @@ void CSceneViewport::SceneRayCast(const CRay& ray) return; } - SRayIntersection result = mpScene->SceneRayCast(ray, mpRenderer->RenderOptions()); + SRayIntersection result = mpScene->SceneRayCast(ray, mViewInfo); if (result.Hit) { @@ -131,12 +135,12 @@ void CSceneViewport::Paint() if (mDrawSky) { CModel *pSky = mpScene->GetActiveSkybox(); - if (pSky) mpRenderer->RenderSky(pSky, mCamera); + if (pSky) mpRenderer->RenderSky(pSky, mViewInfo); } mCamera.LoadMatrices(); - mpScene->AddSceneToRenderer(mpRenderer, mCamera); - mpRenderer->RenderBuckets(mCamera); + mpScene->AddSceneToRenderer(mpRenderer, mViewInfo); + mpRenderer->RenderBuckets(mViewInfo); mpRenderer->RenderBloom(); if (mpEditor->IsGizmoVisible()) @@ -146,8 +150,8 @@ void CSceneViewport::Paint() mpRenderer->ClearDepthBuffer(); pGizmo->UpdateForCamera(mCamera); - pGizmo->AddToRenderer(mpRenderer, mCamera.FrustumPlanes()); - mpRenderer->RenderBuckets(mCamera); + pGizmo->AddToRenderer(mpRenderer, mViewInfo); + mpRenderer->RenderBuckets(mViewInfo); } mpRenderer->EndFrame();