Fixes a bounding box projection to an incorrect camera causes the red water bug.
Tested By: Langbart Differential Revision: https://code.wildfiregames.com/D3905 This was SVN commit r25440.
This commit is contained in:
parent
6b0802836a
commit
44f8d2c6f5
@ -377,38 +377,42 @@ CVector3D CCamera::GetFocus() const
|
||||
|
||||
CBoundingBoxAligned CCamera::GetBoundsInViewPort(const CBoundingBoxAligned& boundigBox) const
|
||||
{
|
||||
CVector4D v1 = GetViewProjection().Transform(CVector4D(boundigBox[0].X, boundigBox[1].Y, boundigBox[0].Z, 1.0f));
|
||||
CVector4D v2 = GetViewProjection().Transform(CVector4D(boundigBox[1].X, boundigBox[1].Y, boundigBox[0].Z, 1.0f));
|
||||
CVector4D v3 = GetViewProjection().Transform(CVector4D(boundigBox[0].X, boundigBox[1].Y, boundigBox[1].Z, 1.0f));
|
||||
CVector4D v4 = GetViewProjection().Transform(CVector4D(boundigBox[1].X, boundigBox[1].Y, boundigBox[1].Z, 1.0f));
|
||||
const CVector3D cameraPosition = GetOrientation().GetTranslation();
|
||||
if (boundigBox.IsPointInside(cameraPosition))
|
||||
return CBoundingBoxAligned(CVector3D(-1.0f, -1.0f, 0.0f), CVector3D(1.0f, 1.0f, 0.0f));
|
||||
|
||||
const CMatrix3D viewProjection = GetViewProjection();
|
||||
CBoundingBoxAligned viewPortBounds;
|
||||
#define ADDBOUND(v1, v2, v3, v4) \
|
||||
if (v1.Z >= -v1.W) \
|
||||
viewPortBounds += CVector3D(v1.X, v1.Y, v1.Z) * (1.0f / v1.W); \
|
||||
else \
|
||||
{ \
|
||||
float t = v1.Z + v1.W; \
|
||||
if (v2.Z > -v2.W) \
|
||||
{ \
|
||||
CVector4D c2 = v1 + (v2 - v1) * (t / (t - (v2.Z + v2.W))); \
|
||||
viewPortBounds += CVector3D(c2.X, c2.Y, c2.Z) * (1.0f / c2.W); \
|
||||
} \
|
||||
if (v3.Z > -v3.W) \
|
||||
{ \
|
||||
CVector4D c3 = v1 + (v3 - v1) * (t / (t - (v3.Z + v3.W))); \
|
||||
viewPortBounds += CVector3D(c3.X, c3.Y, c3.Z) * (1.0f / c3.W); \
|
||||
} \
|
||||
if (v4.Z > -v4.W) \
|
||||
{ \
|
||||
CVector4D c4 = v1 + (v4 - v1) * (t / (t - (v4.Z + v4.W))); \
|
||||
viewPortBounds += CVector3D(c4.X, c4.Y, c4.Z) * (1.0f / c4.W); \
|
||||
} \
|
||||
#define ADD_VISIBLE_POINT_TO_VIEWBOUNDS(POSITION) STMT( \
|
||||
CVector4D v = viewProjection.Transform(CVector4D((POSITION).X, (POSITION).Y, (POSITION).Z, 1.0f)); \
|
||||
if (v.W != 0.0f) \
|
||||
viewPortBounds += CVector3D(v.X, v.Y, v.Z) * (1.0f / v.W); )
|
||||
|
||||
std::array<CVector3D, 8> worldPositions;
|
||||
std::array<bool, 8> isBehindNearPlane;
|
||||
const CVector3D lookDirection = GetOrientation().GetIn();
|
||||
// Check corners.
|
||||
for (size_t idx = 0; idx < 8; ++idx)
|
||||
{
|
||||
worldPositions[idx] = CVector3D(boundigBox[(idx >> 0) & 0x1].X, boundigBox[(idx >> 1) & 0x1].Y, boundigBox[(idx >> 2) & 0x1].Z);
|
||||
isBehindNearPlane[idx] = lookDirection.Dot(worldPositions[idx]) < lookDirection.Dot(cameraPosition) + GetNearPlane();
|
||||
if (!isBehindNearPlane[idx])
|
||||
ADD_VISIBLE_POINT_TO_VIEWBOUNDS(worldPositions[idx]);
|
||||
}
|
||||
ADDBOUND(v1, v2, v3, v4);
|
||||
ADDBOUND(v2, v1, v3, v4);
|
||||
ADDBOUND(v3, v1, v2, v4);
|
||||
ADDBOUND(v4, v1, v2, v3);
|
||||
#undef ADDBOUND
|
||||
// Check edges for intersections with the near plane.
|
||||
for (size_t idxBegin = 0; idxBegin < 8; ++idxBegin)
|
||||
for (size_t nextComponent = 0; nextComponent < 3; ++nextComponent)
|
||||
{
|
||||
const size_t idxEnd = idxBegin | (1u << nextComponent);
|
||||
if (idxBegin == idxEnd || isBehindNearPlane[idxBegin] == isBehindNearPlane[idxEnd])
|
||||
continue;
|
||||
CVector3D intersection;
|
||||
// Intersect the segment with the near plane.
|
||||
if (!m_ViewFrustum[5].FindLineSegIntersection(worldPositions[idxBegin], worldPositions[idxEnd], &intersection))
|
||||
continue;
|
||||
ADD_VISIBLE_POINT_TO_VIEWBOUNDS(intersection);
|
||||
}
|
||||
#undef ADD_VISIBLE_POINT_TO_VIEWBOUNDS
|
||||
if (viewPortBounds[0].X >= 1.0f || viewPortBounds[1].X <= -1.0f || viewPortBounds[0].Y >= 1.0f || viewPortBounds[1].Y <= -1.0f)
|
||||
return CBoundingBoxAligned{};
|
||||
return viewPortBounds;
|
||||
|
@ -19,7 +19,9 @@
|
||||
|
||||
#include "graphics/Camera.h"
|
||||
#include "maths/MathUtil.h"
|
||||
#include "maths/Vector2D.h"
|
||||
#include "maths/Vector3D.h"
|
||||
#include "maths/Vector4D.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
@ -341,4 +343,82 @@ public:
|
||||
CompareVectors(dir, expectedDir, EPS);
|
||||
}
|
||||
}
|
||||
|
||||
void CompareBoundingBoxes(const CBoundingBoxAligned& bb1, const CBoundingBoxAligned& bb2)
|
||||
{
|
||||
constexpr float EPS = 1e-3f;
|
||||
CompareVectors(bb1[0], bb2[0], EPS);
|
||||
CompareVectors(bb1[1], bb2[1], EPS);
|
||||
}
|
||||
|
||||
void test_viewport_bounds_perspective()
|
||||
{
|
||||
SViewPort viewPort;
|
||||
viewPort.m_X = 0;
|
||||
viewPort.m_Y = 0;
|
||||
viewPort.m_Width = 512;
|
||||
viewPort.m_Height = 512;
|
||||
|
||||
CCamera camera;
|
||||
camera.SetViewPort(viewPort);
|
||||
camera.LookAlong(
|
||||
CVector3D(0.0f, 0.0f, 0.0f),
|
||||
CVector3D(0.0f, 0.0f, 1.0f),
|
||||
CVector3D(0.0f, 1.0f, 0.0f)
|
||||
);
|
||||
camera.SetPerspectiveProjection(1.0f, 101.0f, DEGTORAD(90.0f));
|
||||
camera.UpdateFrustum();
|
||||
|
||||
struct TestCase
|
||||
{
|
||||
CBoundingBoxAligned worldSpaceBoundingBox;
|
||||
CBoundingBoxAligned expectedViewPortBoundingBox;
|
||||
};
|
||||
const TestCase testCases[] = {
|
||||
// Box is in front of the camera.
|
||||
{
|
||||
{{-1.0f, 0.0f, 5.0f}, {1.0f, 0.0f, 7.0f}},
|
||||
{{-0.2f, 0.0f, 0.616f}, {0.2f, 0.0f, 0.731429f}}
|
||||
},
|
||||
// Box is out of the camera view.
|
||||
{
|
||||
{{-10.0f, -1.0f, 5.0f}, {-8.0f, 1.0f, 7.0f}},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{{-1.0f, -10.0f, 5.0f}, {1.0f, -8.0f, 7.0f}},
|
||||
{}
|
||||
},
|
||||
// Box is in the bottom part of the camera view.
|
||||
{
|
||||
{{-1.0f, -3.0f, 5.0f}, {1.0f, -3.0f, 7.0f}},
|
||||
{{-0.2f, -0.6f, 0.616f}, {0.2f, -0.428571f, 0.731429f}}
|
||||
},
|
||||
{
|
||||
{{-1.0f, -3.0f, 0.0f}, {1.0f, -3.0f, 7.0f}},
|
||||
{{-1.0f, -3.0f, -1.0f}, {1.0f, -0.428571f, 0.731429f}}
|
||||
},
|
||||
{
|
||||
{{-1.0f, -3.0f, -7.0f}, {1.0f, -3.0f, 7.0f}},
|
||||
{{-1.0f, -3.0f, -1.0f}, {1.0f, -0.428571f, 0.731429f}}
|
||||
},
|
||||
};
|
||||
|
||||
for (const TestCase& testCase : testCases)
|
||||
{
|
||||
TS_ASSERT(testCase.worldSpaceBoundingBox[0].X <= testCase.worldSpaceBoundingBox[1].X);
|
||||
TS_ASSERT(testCase.worldSpaceBoundingBox[0].Y <= testCase.worldSpaceBoundingBox[1].Y);
|
||||
TS_ASSERT(testCase.worldSpaceBoundingBox[0].Z <= testCase.worldSpaceBoundingBox[1].Z);
|
||||
|
||||
const CBoundingBoxAligned result =
|
||||
camera.GetBoundsInViewPort(testCase.worldSpaceBoundingBox);
|
||||
if (testCase.expectedViewPortBoundingBox.IsEmpty())
|
||||
{
|
||||
TS_ASSERT(result.IsEmpty());
|
||||
}
|
||||
else
|
||||
CompareBoundingBoxes(result, testCase.expectedViewPortBoundingBox);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -263,3 +263,11 @@ void CBoundingBoxAligned::Expand(float amount)
|
||||
m_Data[0] -= CVector3D(amount, amount, amount);
|
||||
m_Data[1] += CVector3D(amount, amount, amount);
|
||||
}
|
||||
|
||||
bool CBoundingBoxAligned::IsPointInside(const CVector3D& point) const
|
||||
{
|
||||
return
|
||||
m_Data[0].X <= point.X && point.X <= m_Data[1].X &&
|
||||
m_Data[0].Y <= point.Y && point.Y <= m_Data[1].Y &&
|
||||
m_Data[0].Z <= point.Z && point.Z <= m_Data[1].Z;
|
||||
}
|
||||
|
@ -107,6 +107,8 @@ public:
|
||||
*/
|
||||
bool RayIntersect(const CVector3D& origin, const CVector3D& dir, float& tmin, float& tmax) const;
|
||||
|
||||
bool IsPointInside(const CVector3D& point) const;
|
||||
|
||||
// return the volume of this bounding box
|
||||
float GetVolume() const
|
||||
{
|
||||
|
@ -208,4 +208,34 @@ public:
|
||||
TS_ASSERT(!isnan(result.m_Basis[2].X) && !isnan(result.m_Basis[2].Y) && !isnan(result.m_Basis[2].Z));
|
||||
}
|
||||
|
||||
void test_point_visibility()
|
||||
{
|
||||
const CBoundingBoxAligned bb(CVector3D(1.0f, -10.0f, 3.0f), CVector3D(3.0f, -8.0f, 5.0f));
|
||||
|
||||
TS_ASSERT(!bb.IsPointInside(CVector3D(0.0f, 0.0f, 0.0f)));
|
||||
TS_ASSERT(bb.IsPointInside(bb[0]));
|
||||
TS_ASSERT(bb.IsPointInside(bb[1]));
|
||||
|
||||
CVector3D center;
|
||||
bb.GetCenter(center);
|
||||
TS_ASSERT(bb.IsPointInside(center));
|
||||
|
||||
for (int offsetX = -1; offsetX <= 1; ++offsetX)
|
||||
for (int offsetY = -1; offsetY <= 1; ++offsetY)
|
||||
for (int offsetZ = -1; offsetZ <= 1; ++offsetZ)
|
||||
{
|
||||
TS_ASSERT(bb.IsPointInside(
|
||||
center + CVector3D(offsetX, offsetY, offsetZ) * 0.9f));
|
||||
const bool isInside = bb.IsPointInside(
|
||||
center + CVector3D(offsetX, offsetY, offsetZ) * 1.1f);
|
||||
if (offsetX == 0 && offsetY == 0 && offsetZ == 0)
|
||||
{
|
||||
TS_ASSERT(isInside);
|
||||
}
|
||||
else
|
||||
{
|
||||
TS_ASSERT(!isInside);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -989,6 +989,8 @@ void CRenderer::RenderReflections(const CShaderDefines& context, const CBounding
|
||||
CCamera normalCamera = m_ViewCamera;
|
||||
|
||||
ComputeReflectionCamera(m_ViewCamera, scissor);
|
||||
const CBoundingBoxAligned reflectionScissor =
|
||||
m->terrainRenderer.ScissorWater(CULL_DEFAULT, m_ViewCamera);
|
||||
|
||||
SetViewport(m_ViewCamera.GetViewPort());
|
||||
|
||||
@ -999,10 +1001,10 @@ void CRenderer::RenderReflections(const CShaderDefines& context, const CBounding
|
||||
float vpWidth = wm.m_RefTextureSize;
|
||||
|
||||
SScreenRect screenScissor;
|
||||
screenScissor.x1 = (GLint)floor((scissor[0].X*0.5f+0.5f)*vpWidth);
|
||||
screenScissor.y1 = (GLint)floor((scissor[0].Y*0.5f+0.5f)*vpHeight);
|
||||
screenScissor.x2 = (GLint)ceil((scissor[1].X*0.5f+0.5f)*vpWidth);
|
||||
screenScissor.y2 = (GLint)ceil((scissor[1].Y*0.5f+0.5f)*vpHeight);
|
||||
screenScissor.x1 = (GLint)floor((reflectionScissor[0].X*0.5f+0.5f)*vpWidth);
|
||||
screenScissor.y1 = (GLint)floor((reflectionScissor[0].Y*0.5f+0.5f)*vpHeight);
|
||||
screenScissor.x2 = (GLint)ceil((reflectionScissor[1].X*0.5f+0.5f)*vpWidth);
|
||||
screenScissor.y2 = (GLint)ceil((reflectionScissor[1].Y*0.5f+0.5f)*vpHeight);
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(screenScissor.x1, screenScissor.y1, screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
|
||||
@ -1062,6 +1064,8 @@ void CRenderer::RenderRefractions(const CShaderDefines& context, const CBounding
|
||||
CCamera normalCamera = m_ViewCamera;
|
||||
|
||||
ComputeRefractionCamera(m_ViewCamera, scissor);
|
||||
const CBoundingBoxAligned refractionScissor =
|
||||
m->terrainRenderer.ScissorWater(CULL_DEFAULT, m_ViewCamera);
|
||||
|
||||
CVector4D camPlane(0, -1, 0, wm.m_WaterHeight + 2.0f);
|
||||
SetObliqueFrustumClipping(m_ViewCamera, camPlane);
|
||||
@ -1077,13 +1081,13 @@ void CRenderer::RenderRefractions(const CShaderDefines& context, const CBounding
|
||||
float vpWidth = wm.m_RefTextureSize;
|
||||
|
||||
SScreenRect screenScissor;
|
||||
screenScissor.x1 = (GLint)floor((scissor[0].X*0.5f+0.5f)*vpWidth);
|
||||
screenScissor.y1 = (GLint)floor((scissor[0].Y*0.5f+0.5f)*vpHeight);
|
||||
screenScissor.x2 = (GLint)ceil((scissor[1].X*0.5f+0.5f)*vpWidth);
|
||||
screenScissor.y2 = (GLint)ceil((scissor[1].Y*0.5f+0.5f)*vpHeight);
|
||||
screenScissor.x1 = (GLint)floor((refractionScissor[0].X*0.5f+0.5f)*vpWidth);
|
||||
screenScissor.y1 = (GLint)floor((refractionScissor[0].Y*0.5f+0.5f)*vpHeight);
|
||||
screenScissor.x2 = (GLint)ceil((refractionScissor[1].X*0.5f+0.5f)*vpWidth);
|
||||
screenScissor.y2 = (GLint)ceil((refractionScissor[1].Y*0.5f+0.5f)*vpHeight);
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(screenScissor.x1, screenScissor.y1, screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
|
||||
//glEnable(GL_SCISSOR_TEST);
|
||||
//glScissor(screenScissor.x1, screenScissor.y1, screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
|
||||
|
||||
// try binding the framebuffer
|
||||
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, wm.m_RefractionFbo);
|
||||
@ -1091,6 +1095,9 @@ void CRenderer::RenderRefractions(const CShaderDefines& context, const CBounding
|
||||
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(screenScissor.x1, screenScissor.y1, screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
|
||||
|
||||
// Render terrain and models
|
||||
RenderPatches(context, CULL_REFRACTIONS);
|
||||
ogl_WarnIfError();
|
||||
|
Loading…
Reference in New Issue
Block a user