Index: ps/trunk/source/graphics/Camera.cpp
===================================================================
--- ps/trunk/source/graphics/Camera.cpp (revision 25157)
+++ ps/trunk/source/graphics/Camera.cpp (revision 25158)
@@ -1,460 +1,460 @@
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/*
* CCamera holds a view and a projection matrix. It also has a frustum
* which can be used to cull objects for rendering.
*/
#include "precompiled.h"
#include "Camera.h"
#include "graphics/HFTracer.h"
#include "graphics/Terrain.h"
#include "lib/ogl.h"
#include "maths/MathUtil.h"
#include "maths/Vector4D.h"
#include "ps/Game.h"
#include "ps/World.h"
#include "renderer/Renderer.h"
#include "renderer/WaterManager.h"
CCamera::CCamera()
{
// Set viewport to something anything should handle, but should be initialised
// to window size before use.
m_ViewPort.m_X = 0;
m_ViewPort.m_Y = 0;
m_ViewPort.m_Width = 800;
m_ViewPort.m_Height = 600;
}
CCamera::~CCamera() = default;
void CCamera::SetProjection(const CMatrix3D& matrix)
{
m_ProjType = ProjectionType::CUSTOM;
m_ProjMat = matrix;
}
void CCamera::SetProjectionFromCamera(const CCamera& camera)
{
m_ProjType = camera.m_ProjType;
m_NearPlane = camera.m_NearPlane;
m_FarPlane = camera.m_FarPlane;
if (m_ProjType == ProjectionType::PERSPECTIVE)
{
m_FOV = camera.m_FOV;
}
else if (m_ProjType == ProjectionType::ORTHO)
{
m_OrthoScale = camera.m_OrthoScale;
}
m_ProjMat = camera.m_ProjMat;
}
void CCamera::SetOrthoProjection(float nearp, float farp, float scale)
{
m_ProjType = ProjectionType::ORTHO;
m_NearPlane = nearp;
m_FarPlane = farp;
m_OrthoScale = scale;
const float halfHeight = 0.5f * m_OrthoScale;
const float halfWidth = halfHeight * GetAspectRatio();
m_ProjMat.SetOrtho(-halfWidth, halfWidth, -halfHeight, halfHeight, m_NearPlane, m_FarPlane);
}
void CCamera::SetPerspectiveProjection(float nearp, float farp, float fov)
{
m_ProjType = ProjectionType::PERSPECTIVE;
m_NearPlane = nearp;
m_FarPlane = farp;
m_FOV = fov;
m_ProjMat.SetPerspective(m_FOV, GetAspectRatio(), m_NearPlane, m_FarPlane);
}
// Updates the frustum planes. Should be called
// everytime the view or projection matrices are
// altered.
void CCamera::UpdateFrustum(const CBoundingBoxAligned& scissor)
{
CMatrix3D MatFinal;
CMatrix3D MatView;
m_Orientation.GetInverse(MatView);
MatFinal = m_ProjMat * MatView;
m_ViewFrustum.SetNumPlanes(6);
// get the RIGHT plane
- m_ViewFrustum.m_aPlanes[0].m_Norm.X = scissor[1].X*MatFinal._41 - MatFinal._11;
- m_ViewFrustum.m_aPlanes[0].m_Norm.Y = scissor[1].X*MatFinal._42 - MatFinal._12;
- m_ViewFrustum.m_aPlanes[0].m_Norm.Z = scissor[1].X*MatFinal._43 - MatFinal._13;
- m_ViewFrustum.m_aPlanes[0].m_Dist = scissor[1].X*MatFinal._44 - MatFinal._14;
+ m_ViewFrustum[0].m_Norm.X = scissor[1].X*MatFinal._41 - MatFinal._11;
+ m_ViewFrustum[0].m_Norm.Y = scissor[1].X*MatFinal._42 - MatFinal._12;
+ m_ViewFrustum[0].m_Norm.Z = scissor[1].X*MatFinal._43 - MatFinal._13;
+ m_ViewFrustum[0].m_Dist = scissor[1].X*MatFinal._44 - MatFinal._14;
// get the LEFT plane
- m_ViewFrustum.m_aPlanes[1].m_Norm.X = -scissor[0].X*MatFinal._41 + MatFinal._11;
- m_ViewFrustum.m_aPlanes[1].m_Norm.Y = -scissor[0].X*MatFinal._42 + MatFinal._12;
- m_ViewFrustum.m_aPlanes[1].m_Norm.Z = -scissor[0].X*MatFinal._43 + MatFinal._13;
- m_ViewFrustum.m_aPlanes[1].m_Dist = -scissor[0].X*MatFinal._44 + MatFinal._14;
+ m_ViewFrustum[1].m_Norm.X = -scissor[0].X*MatFinal._41 + MatFinal._11;
+ m_ViewFrustum[1].m_Norm.Y = -scissor[0].X*MatFinal._42 + MatFinal._12;
+ m_ViewFrustum[1].m_Norm.Z = -scissor[0].X*MatFinal._43 + MatFinal._13;
+ m_ViewFrustum[1].m_Dist = -scissor[0].X*MatFinal._44 + MatFinal._14;
// get the BOTTOM plane
- m_ViewFrustum.m_aPlanes[2].m_Norm.X = -scissor[0].Y*MatFinal._41 + MatFinal._21;
- m_ViewFrustum.m_aPlanes[2].m_Norm.Y = -scissor[0].Y*MatFinal._42 + MatFinal._22;
- m_ViewFrustum.m_aPlanes[2].m_Norm.Z = -scissor[0].Y*MatFinal._43 + MatFinal._23;
- m_ViewFrustum.m_aPlanes[2].m_Dist = -scissor[0].Y*MatFinal._44 + MatFinal._24;
+ m_ViewFrustum[2].m_Norm.X = -scissor[0].Y*MatFinal._41 + MatFinal._21;
+ m_ViewFrustum[2].m_Norm.Y = -scissor[0].Y*MatFinal._42 + MatFinal._22;
+ m_ViewFrustum[2].m_Norm.Z = -scissor[0].Y*MatFinal._43 + MatFinal._23;
+ m_ViewFrustum[2].m_Dist = -scissor[0].Y*MatFinal._44 + MatFinal._24;
// get the TOP plane
- m_ViewFrustum.m_aPlanes[3].m_Norm.X = scissor[1].Y*MatFinal._41 - MatFinal._21;
- m_ViewFrustum.m_aPlanes[3].m_Norm.Y = scissor[1].Y*MatFinal._42 - MatFinal._22;
- m_ViewFrustum.m_aPlanes[3].m_Norm.Z = scissor[1].Y*MatFinal._43 - MatFinal._23;
- m_ViewFrustum.m_aPlanes[3].m_Dist = scissor[1].Y*MatFinal._44 - MatFinal._24;
+ m_ViewFrustum[3].m_Norm.X = scissor[1].Y*MatFinal._41 - MatFinal._21;
+ m_ViewFrustum[3].m_Norm.Y = scissor[1].Y*MatFinal._42 - MatFinal._22;
+ m_ViewFrustum[3].m_Norm.Z = scissor[1].Y*MatFinal._43 - MatFinal._23;
+ m_ViewFrustum[3].m_Dist = scissor[1].Y*MatFinal._44 - MatFinal._24;
// get the FAR plane
- m_ViewFrustum.m_aPlanes[4].m_Norm.X = scissor[1].Z*MatFinal._41 - MatFinal._31;
- m_ViewFrustum.m_aPlanes[4].m_Norm.Y = scissor[1].Z*MatFinal._42 - MatFinal._32;
- m_ViewFrustum.m_aPlanes[4].m_Norm.Z = scissor[1].Z*MatFinal._43 - MatFinal._33;
- m_ViewFrustum.m_aPlanes[4].m_Dist = scissor[1].Z*MatFinal._44 - MatFinal._34;
+ m_ViewFrustum[4].m_Norm.X = scissor[1].Z*MatFinal._41 - MatFinal._31;
+ m_ViewFrustum[4].m_Norm.Y = scissor[1].Z*MatFinal._42 - MatFinal._32;
+ m_ViewFrustum[4].m_Norm.Z = scissor[1].Z*MatFinal._43 - MatFinal._33;
+ m_ViewFrustum[4].m_Dist = scissor[1].Z*MatFinal._44 - MatFinal._34;
// get the NEAR plane
- m_ViewFrustum.m_aPlanes[5].m_Norm.X = -scissor[0].Z*MatFinal._41 + MatFinal._31;
- m_ViewFrustum.m_aPlanes[5].m_Norm.Y = -scissor[0].Z*MatFinal._42 + MatFinal._32;
- m_ViewFrustum.m_aPlanes[5].m_Norm.Z = -scissor[0].Z*MatFinal._43 + MatFinal._33;
- m_ViewFrustum.m_aPlanes[5].m_Dist = -scissor[0].Z*MatFinal._44 + MatFinal._34;
+ m_ViewFrustum[5].m_Norm.X = -scissor[0].Z*MatFinal._41 + MatFinal._31;
+ m_ViewFrustum[5].m_Norm.Y = -scissor[0].Z*MatFinal._42 + MatFinal._32;
+ m_ViewFrustum[5].m_Norm.Z = -scissor[0].Z*MatFinal._43 + MatFinal._33;
+ m_ViewFrustum[5].m_Dist = -scissor[0].Z*MatFinal._44 + MatFinal._34;
for (size_t i = 0; i < 6; ++i)
- m_ViewFrustum.m_aPlanes[i].Normalize();
+ m_ViewFrustum[i].Normalize();
}
void CCamera::ClipFrustum(const CPlane& clipPlane)
{
CPlane normClipPlane = clipPlane;
normClipPlane.Normalize();
m_ViewFrustum.AddPlane(normClipPlane);
}
void CCamera::SetViewPort(const SViewPort& viewport)
{
m_ViewPort.m_X = viewport.m_X;
m_ViewPort.m_Y = viewport.m_Y;
m_ViewPort.m_Width = viewport.m_Width;
m_ViewPort.m_Height = viewport.m_Height;
}
float CCamera::GetAspectRatio() const
{
return static_cast(m_ViewPort.m_Width) / static_cast(m_ViewPort.m_Height);
}
void CCamera::GetViewQuad(float dist, Quad& quad) const
{
ENSURE(m_ProjType == ProjectionType::PERSPECTIVE || m_ProjType == ProjectionType::ORTHO);
const float y = m_ProjType == ProjectionType::PERSPECTIVE ? dist * tanf(m_FOV * 0.5f) : m_OrthoScale * 0.5f;
const float x = y * GetAspectRatio();
quad[0].X = -x;
quad[0].Y = -y;
quad[0].Z = dist;
quad[1].X = x;
quad[1].Y = -y;
quad[1].Z = dist;
quad[2].X = x;
quad[2].Y = y;
quad[2].Z = dist;
quad[3].X = -x;
quad[3].Y = y;
quad[3].Z = dist;
}
void CCamera::BuildCameraRay(int px, int py, CVector3D& origin, CVector3D& dir) const
{
ENSURE(m_ProjType == ProjectionType::PERSPECTIVE || m_ProjType == ProjectionType::ORTHO);
// Coordinates relative to the camera plane.
const float dx = static_cast(px) / m_ViewPort.m_Width;
const float dy = 1.0f - static_cast(py) / m_ViewPort.m_Height;
Quad points;
GetViewQuad(m_FarPlane, points);
// Transform from camera space to world space.
for (CVector3D& point : points)
point = m_Orientation.Transform(point);
// Get world space position of mouse point at the far clipping plane.
const CVector3D basisX = points[1] - points[0];
const CVector3D basisY = points[3] - points[0];
if (m_ProjType == ProjectionType::PERSPECTIVE)
{
// Build direction for the camera origin to the target point.
origin = m_Orientation.GetTranslation();
CVector3D targetPoint = points[0] + (basisX * dx) + (basisY * dy);
dir = targetPoint - origin;
}
else if (m_ProjType == ProjectionType::ORTHO)
{
origin = m_Orientation.GetTranslation() + (basisX * (dx - 0.5f)) + (basisY * (dy - 0.5f));
dir = m_Orientation.GetIn();
}
dir.Normalize();
}
void CCamera::GetScreenCoordinates(const CVector3D& world, float& x, float& y) const
{
CMatrix3D transform = m_ProjMat * m_Orientation.GetInverse();
CVector4D screenspace = transform.Transform(CVector4D(world.X, world.Y, world.Z, 1.0f));
x = screenspace.X / screenspace.W;
y = screenspace.Y / screenspace.W;
x = (x + 1) * 0.5f * m_ViewPort.m_Width;
y = (1 - y) * 0.5f * m_ViewPort.m_Height;
}
CVector3D CCamera::GetWorldCoordinates(int px, int py, bool aboveWater) const
{
CHFTracer tracer(g_Game->GetWorld()->GetTerrain());
int x, z;
CVector3D origin, dir, delta, terrainPoint, waterPoint;
BuildCameraRay(px, py, origin, dir);
bool gotTerrain = tracer.RayIntersect(origin, dir, x, z, terrainPoint);
if (!aboveWater)
{
if (gotTerrain)
return terrainPoint;
// Off the edge of the world?
// Work out where it /would/ hit, if the map were extended out to infinity with average height.
return GetWorldCoordinates(px, py, 50.0f);
}
CPlane plane;
plane.Set(CVector3D(0.f, 1.f, 0.f), // upwards normal
CVector3D(0.f, g_Renderer.GetWaterManager()->m_WaterHeight, 0.f)); // passes through water plane
bool gotWater = plane.FindRayIntersection( origin, dir, &waterPoint );
// Clamp the water intersection to within the map's bounds, so that
// we'll always return a valid position on the map
ssize_t mapSize = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();
if (gotWater)
{
waterPoint.X = Clamp(waterPoint.X, 0.f, static_cast((mapSize - 1) * TERRAIN_TILE_SIZE));
waterPoint.Z = Clamp(waterPoint.Z, 0.f, static_cast((mapSize - 1) * TERRAIN_TILE_SIZE));
}
if (gotTerrain)
{
if (gotWater)
{
// Intersecting both heightmap and water plane; choose the closest of those
if ((origin - terrainPoint).LengthSquared() < (origin - waterPoint).LengthSquared())
return terrainPoint;
else
return waterPoint;
}
else
{
// Intersecting heightmap but parallel to water plane
return terrainPoint;
}
}
else
{
if (gotWater)
{
// Only intersecting water plane
return waterPoint;
}
else
{
// Not intersecting terrain or water; just return 0,0,0.
return CVector3D(0.f, 0.f, 0.f);
}
}
}
CVector3D CCamera::GetWorldCoordinates(int px, int py, float h) const
{
CPlane plane;
plane.Set(CVector3D(0.f, 1.f, 0.f), CVector3D(0.f, h, 0.f)); // upwards normal, passes through h
CVector3D origin, dir, delta, currentTarget;
BuildCameraRay(px, py, origin, dir);
if (plane.FindRayIntersection(origin, dir, ¤tTarget))
return currentTarget;
// No intersection with the infinite plane - nothing sensible can be returned,
// so just choose an arbitrary point on the plane
return CVector3D(0.f, h, 0.f);
}
CVector3D CCamera::GetFocus() const
{
// Basically the same as GetWorldCoordinates
CHFTracer tracer(g_Game->GetWorld()->GetTerrain());
int x, z;
CVector3D origin, dir, delta, terrainPoint, waterPoint;
origin = m_Orientation.GetTranslation();
dir = m_Orientation.GetIn();
bool gotTerrain = tracer.RayIntersect(origin, dir, x, z, terrainPoint);
CPlane plane;
plane.Set(CVector3D(0.f, 1.f, 0.f), // upwards normal
CVector3D(0.f, g_Renderer.GetWaterManager()->m_WaterHeight, 0.f)); // passes through water plane
bool gotWater = plane.FindRayIntersection( origin, dir, &waterPoint );
// Clamp the water intersection to within the map's bounds, so that
// we'll always return a valid position on the map
ssize_t mapSize = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();
if (gotWater)
{
waterPoint.X = Clamp(waterPoint.X, 0.f, static_cast((mapSize - 1) * TERRAIN_TILE_SIZE));
waterPoint.Z = Clamp(waterPoint.Z, 0.f, static_cast((mapSize - 1) * TERRAIN_TILE_SIZE));
}
if (gotTerrain)
{
if (gotWater)
{
// Intersecting both heightmap and water plane; choose the closest of those
if ((origin - terrainPoint).LengthSquared() < (origin - waterPoint).LengthSquared())
return terrainPoint;
else
return waterPoint;
}
else
{
// Intersecting heightmap but parallel to water plane
return terrainPoint;
}
}
else
{
if (gotWater)
{
// Only intersecting water plane
return waterPoint;
}
else
{
// Not intersecting terrain or water; just return 0,0,0.
return CVector3D(0.f, 0.f, 0.f);
}
}
}
void CCamera::LookAt(const CVector3D& camera, const CVector3D& focus, const CVector3D& up)
{
CVector3D delta = focus - camera;
LookAlong(camera, delta, up);
}
void CCamera::LookAlong(const CVector3D& camera, CVector3D orientation, CVector3D up)
{
orientation.Normalize();
up.Normalize();
CVector3D s = orientation.Cross(up);
m_Orientation._11 = -s.X; m_Orientation._12 = up.X; m_Orientation._13 = orientation.X; m_Orientation._14 = camera.X;
m_Orientation._21 = -s.Y; m_Orientation._22 = up.Y; m_Orientation._23 = orientation.Y; m_Orientation._24 = camera.Y;
m_Orientation._31 = -s.Z; m_Orientation._32 = up.Z; m_Orientation._33 = orientation.Z; m_Orientation._34 = camera.Z;
m_Orientation._41 = 0.0f; m_Orientation._42 = 0.0f; m_Orientation._43 = 0.0f; m_Orientation._44 = 1.0f;
}
// Render the camera's frustum
void CCamera::Render(int intermediates) const
{
#if CONFIG2_GLES
#warning TODO: implement camera frustum for GLES
#else
Quad nearPoints;
Quad farPoints;
GetViewQuad(m_NearPlane, nearPoints);
GetViewQuad(m_FarPlane, farPoints);
for(int i = 0; i < 4; i++)
{
nearPoints[i] = m_Orientation.Transform(nearPoints[i]);
farPoints[i] = m_Orientation.Transform(farPoints[i]);
}
// near plane
glBegin(GL_POLYGON);
glVertex3fv(&nearPoints[0].X);
glVertex3fv(&nearPoints[1].X);
glVertex3fv(&nearPoints[2].X);
glVertex3fv(&nearPoints[3].X);
glEnd();
// far plane
glBegin(GL_POLYGON);
glVertex3fv(&farPoints[0].X);
glVertex3fv(&farPoints[1].X);
glVertex3fv(&farPoints[2].X);
glVertex3fv(&farPoints[3].X);
glEnd();
// connection lines
glBegin(GL_QUAD_STRIP);
glVertex3fv(&nearPoints[0].X);
glVertex3fv(&farPoints[0].X);
glVertex3fv(&nearPoints[1].X);
glVertex3fv(&farPoints[1].X);
glVertex3fv(&nearPoints[2].X);
glVertex3fv(&farPoints[2].X);
glVertex3fv(&nearPoints[3].X);
glVertex3fv(&farPoints[3].X);
glVertex3fv(&nearPoints[0].X);
glVertex3fv(&farPoints[0].X);
glEnd();
// intermediate planes
CVector3D intermediatePoints[4];
for(int i = 0; i < intermediates; ++i)
{
float t = (i+1.0)/(intermediates+1.0);
for(int j = 0; j < 4; ++j)
intermediatePoints[j] = nearPoints[j]*t + farPoints[j]*(1.0-t);
glBegin(GL_POLYGON);
glVertex3fv(&intermediatePoints[0].X);
glVertex3fv(&intermediatePoints[1].X);
glVertex3fv(&intermediatePoints[2].X);
glVertex3fv(&intermediatePoints[3].X);
glEnd();
}
#endif
}
Index: ps/trunk/source/graphics/Frustum.cpp
===================================================================
--- ps/trunk/source/graphics/Frustum.cpp (revision 25157)
+++ ps/trunk/source/graphics/Frustum.cpp (revision 25158)
@@ -1,160 +1,156 @@
-/* Copyright (C) 2009 Wildfire Games.
+/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/*
- * CFrustum is a collection of planes which define a viewing space.
- */
-
-/*
Usually associated with the camera, there are 6 planes which define the
view pyramid. But we allow more planes per frustum which may be used for
portal rendering, where a portal may have 3 or more edges.
*/
#include "precompiled.h"
#include "Frustum.h"
+
#include "maths/BoundingBoxAligned.h"
#include "maths/MathUtil.h"
#include "maths/Matrix3D.h"
-CFrustum::CFrustum ()
+CFrustum::CFrustum()
{
m_NumPlanes = 0;
}
-CFrustum::~CFrustum ()
+CFrustum::~CFrustum()
{
}
-void CFrustum::SetNumPlanes (size_t num)
+void CFrustum::SetNumPlanes(size_t num)
{
m_NumPlanes = num;
- //clip it
if (m_NumPlanes >= MAX_NUM_FRUSTUM_PLANES)
{
debug_warn(L"CFrustum::SetNumPlanes: Too many planes");
- m_NumPlanes = MAX_NUM_FRUSTUM_PLANES-1;
+ m_NumPlanes = MAX_NUM_FRUSTUM_PLANES - 1;
}
}
void CFrustum::AddPlane(const CPlane& plane)
{
if (m_NumPlanes >= MAX_NUM_FRUSTUM_PLANES)
{
debug_warn(L"CFrustum::AddPlane: Too many planes");
return;
}
- m_aPlanes[m_NumPlanes++] = plane;
+ m_Planes[m_NumPlanes++] = plane;
}
-void CFrustum::Transform(CMatrix3D& m)
+void CFrustum::Transform(const CMatrix3D& m)
{
for (size_t i = 0; i < m_NumPlanes; ++i)
{
- CVector3D n = m.Rotate(m_aPlanes[i].m_Norm);
- CVector3D p = m.Transform(m_aPlanes[i].m_Norm * -m_aPlanes[i].m_Dist);
- m_aPlanes[i].Set(n, p);
- m_aPlanes[i].Normalize();
+ CVector3D n = m.Rotate(m_Planes[i].m_Norm);
+ CVector3D p = m.Transform(m_Planes[i].m_Norm * -m_Planes[i].m_Dist);
+ m_Planes[i].Set(n, p);
+ m_Planes[i].Normalize();
}
}
bool CFrustum::IsPointVisible(const CVector3D& point) const
{
- for (size_t i=0; i radius)
+ // it is outside the frustum.
+ if (-m_Planes[i].DistanceToPlane(center) > radius)
return false;
}
return true;
}
bool CFrustum::IsBoxVisible(const CVector3D& position, const CBoundingBoxAligned& bounds) const
{
- //basically for every plane we calculate the furthest point
- //in the box to that plane. If that point is beyond the plane
- //then the box is not visible
- CVector3D FarPoint;
- CVector3D Min = position+bounds[0];
- CVector3D Max = position+bounds[1];
-
- for (size_t i=0; i 0.0f ? Max.X : Min.X;
- FarPoint.Y = m_aPlanes[i].m_Norm.Y > 0.0f ? Max.Y : Min.Y;
- FarPoint.Z = m_aPlanes[i].m_Norm.Z > 0.0f ? Max.Z : Min.Z;
+ // Basically for every plane we calculate the furthest point
+ // in the box to that plane. If that point is beyond the plane
+ // then the box is not visible.
+ CVector3D farPoint;
+ CVector3D minPoint = position + bounds[0];
+ CVector3D maxPoint = position + bounds[1];
- if (m_aPlanes[i].IsPointOnBackSide(FarPoint))
+ for (size_t i = 0; i < m_NumPlanes; ++i)
+ {
+ farPoint.X = m_Planes[i].m_Norm.X > 0.0f ? maxPoint.X : minPoint.X;
+ farPoint.Y = m_Planes[i].m_Norm.Y > 0.0f ? maxPoint.Y : minPoint.Y;
+ farPoint.Z = m_Planes[i].m_Norm.Z > 0.0f ? maxPoint.Z : minPoint.Z;
+
+ if (m_Planes[i].IsPointOnBackSide(farPoint))
return false;
}
return true;
}
bool CFrustum::IsBoxVisible(const CBoundingBoxAligned& bounds) const
{
- //Same as the previous one, but with the position = (0, 0, 0)
- CVector3D FarPoint;
+ // Same as the previous one, but with the position = (0, 0, 0).
+ CVector3D farPoint;
- for (size_t i=0; i 0.0f ? bounds[1].X : bounds[0].X;
- FarPoint.Y = m_aPlanes[i].m_Norm.Y > 0.0f ? bounds[1].Y : bounds[0].Y;
- FarPoint.Z = m_aPlanes[i].m_Norm.Z > 0.0f ? bounds[1].Z : bounds[0].Z;
+ farPoint.X = m_Planes[i].m_Norm.X > 0.0f ? bounds[1].X : bounds[0].X;
+ farPoint.Y = m_Planes[i].m_Norm.Y > 0.0f ? bounds[1].Y : bounds[0].Y;
+ farPoint.Z = m_Planes[i].m_Norm.Z > 0.0f ? bounds[1].Z : bounds[0].Z;
- if (m_aPlanes[i].IsPointOnBackSide(FarPoint))
+ if (m_Planes[i].IsPointOnBackSide(farPoint))
return false;
}
return true;
}
Index: ps/trunk/source/graphics/Frustum.h
===================================================================
--- ps/trunk/source/graphics/Frustum.h (revision 25157)
+++ ps/trunk/source/graphics/Frustum.h (revision 25158)
@@ -1,75 +1,70 @@
-/* Copyright (C) 2009 Wildfire Games.
+/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/*
* CFrustum is a collection of planes which define a viewing space.
*/
/*
Usually associated with the camera, there are 6 planes which define the
view pyramid. But we allow more planes per frustum which may be used for
portal rendering, where a portal may have 3 or more edges.
*/
#ifndef INCLUDED_FRUSTUM
#define INCLUDED_FRUSTUM
#include "maths/Plane.h"
-//10 planes should be enough
-#define MAX_NUM_FRUSTUM_PLANES (10)
-
class CBoundingBoxAligned;
class CMatrix3D;
class CFrustum
{
public:
- CFrustum ();
- ~CFrustum ();
+ CFrustum();
+ ~CFrustum();
- //Set the number of planes to use for
- //calculations. This is clipped to
- //[0,MAX_NUM_FRUSTUM_PLANES]
- void SetNumPlanes (size_t num);
+ // Set the number of planes to use for calculations. This is clamped to
+ // [0, MAX_NUM_FRUSTUM_PLANES].
+ void SetNumPlanes(size_t num);
size_t GetNumPlanes() const { return m_NumPlanes; }
- void AddPlane (const CPlane& plane);
+ void AddPlane(const CPlane& plane);
- void Transform(CMatrix3D& m);
+ void Transform(const CMatrix3D& m);
- //The following methods return true if the shape is
- //partially or completely in front of the frustum planes
+ // The following methods return true if the shape is
+ // partially or completely in front of the frustum planes.
bool IsPointVisible(const CVector3D& point) const;
bool DoesSegmentIntersect(const CVector3D& start, const CVector3D& end) const;
bool IsSphereVisible(const CVector3D& center, float radius) const;
bool IsBoxVisible(const CVector3D& position, const CBoundingBoxAligned& bounds) const;
bool IsBoxVisible(const CBoundingBoxAligned& bounds) const;
- CPlane& operator[](size_t idx) { return m_aPlanes[idx]; }
- const CPlane& operator[](size_t idx) const { return m_aPlanes[idx]; }
-
-public:
- //make the planes public for ease of use
- CPlane m_aPlanes[MAX_NUM_FRUSTUM_PLANES];
+ CPlane& operator[](size_t idx) { return m_Planes[idx]; }
+ const CPlane& operator[](size_t idx) const { return m_Planes[idx]; }
private:
+ static const size_t MAX_NUM_FRUSTUM_PLANES = 10;
+
+ CPlane m_Planes[MAX_NUM_FRUSTUM_PLANES];
size_t m_NumPlanes;
};
-#endif
+#endif // INCLUDED_FRUSTUM
Index: ps/trunk/source/maths/BoundingBoxAligned.cpp
===================================================================
--- ps/trunk/source/maths/BoundingBoxAligned.cpp (revision 25157)
+++ ps/trunk/source/maths/BoundingBoxAligned.cpp (revision 25158)
@@ -1,335 +1,335 @@
-/* Copyright (C) 2019 Wildfire Games.
+/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/*
* Axis-aligned bounding box
*/
#include "precompiled.h"
#include "BoundingBoxAligned.h"
#include "graphics/Frustum.h"
#include "graphics/ShaderProgram.h"
#include "lib/ogl.h"
#include "maths/BoundingBoxOriented.h"
#include "maths/Brush.h"
#include "maths/Matrix3D.h"
#include
const CBoundingBoxAligned CBoundingBoxAligned::EMPTY = CBoundingBoxAligned(); // initializes to an empty bound
///////////////////////////////////////////////////////////////////////////////
// RayIntersect: intersect ray with this bound; return true
// if ray hits (and store entry and exit times), or false
// otherwise
// note: incoming ray direction must be normalised
bool CBoundingBoxAligned::RayIntersect(
const CVector3D& origin, const CVector3D& dir, float& tmin, float& tmax) const
{
float t1, t2;
float tnear, tfar;
if (dir[0] == 0)
{
if (origin[0] < m_Data[0][0] || origin[0] > m_Data[1][0])
return false;
else
{
tnear = -std::numeric_limits::max();
tfar = std::numeric_limits::max();
}
}
else
{
t1 = (m_Data[0][0] - origin[0]) / dir[0];
t2 = (m_Data[1][0] - origin[0]) / dir[0];
if (dir[0] < 0)
{
tnear = t2;
tfar = t1;
}
else
{
tnear = t1;
tfar = t2;
}
if (tfar < 0)
return false;
}
if (dir[1] == 0 && (origin[1] < m_Data[0][1] || origin[1] > m_Data[1][1]))
return false;
else
{
t1 = (m_Data[0][1] - origin[1]) / dir[1];
t2 = (m_Data[1][1] - origin[1]) / dir[1];
if (dir[1] < 0)
{
if (t2 > tnear)
tnear = t2;
if (t1 < tfar)
tfar = t1;
}
else
{
if (t1 > tnear)
tnear = t1;
if (t2 < tfar)
tfar = t2;
}
if (tnear > tfar || tfar < 0)
return false;
}
if (dir[2] == 0 && (origin[2] < m_Data[0][2] || origin[2] > m_Data[1][2]))
return false;
else
{
t1 = (m_Data[0][2] - origin[2]) / dir[2];
t2 = (m_Data[1][2] - origin[2]) / dir[2];
if (dir[2] < 0)
{
if (t2 > tnear)
tnear = t2;
if (t1 < tfar)
tfar = t1;
}
else
{
if (t1 > tnear)
tnear = t1;
if (t2 < tfar)
tfar = t2;
}
if (tnear > tfar || tfar < 0)
return false;
}
tmin = tnear;
tmax = tfar;
return true;
}
///////////////////////////////////////////////////////////////////////////////
// SetEmpty: initialise this bound as empty
void CBoundingBoxAligned::SetEmpty()
{
m_Data[0] = CVector3D::Max();
m_Data[1] = CVector3D::Min();
}
///////////////////////////////////////////////////////////////////////////////
// IsEmpty: tests whether this bound is empty
bool CBoundingBoxAligned::IsEmpty() const
{
return m_Data[0] == CVector3D::Max() && m_Data[1] == CVector3D::Min();
}
///////////////////////////////////////////////////////////////////////////////
// Transform: transform this bound by given matrix; return transformed bound
// in 'result' parameter - slightly modified version of code in Graphic Gems
// (can't remember which one it was, though)
void CBoundingBoxAligned::Transform(const CMatrix3D& m, CBoundingBoxAligned& result) const
{
ENSURE(this != &result);
for (int i = 0; i < 3; ++i)
{
// handle translation
result[0][i] = result[1][i] = m(i, 3);
// Now find the extreme points by considering the product of the
// min and max with each component of matrix
for (int j = 0; j < 3; ++j)
{
float a = m(i, j) * m_Data[0][j];
float b = m(i, j) * m_Data[1][j];
if (a >= b)
std::swap(a, b);
result[0][i] += a;
result[1][i] += b;
}
}
}
void CBoundingBoxAligned::Transform(const CMatrix3D& transform, CBoundingBoxOriented& result) const
{
const CVector3D& pMin = m_Data[0];
const CVector3D& pMax = m_Data[1];
// the basis vectors of the OBB are the normalized versions of the transformed AABB basis vectors, which
// are the columns of the identity matrix, so the unnormalized OBB basis vectors are the transformation
// matrix columns:
CVector3D u(transform._11, transform._21, transform._31);
CVector3D v(transform._12, transform._22, transform._32);
CVector3D w(transform._13, transform._23, transform._33);
// the half-sizes are scaled by whatever factor the AABB unit vectors end up scaled by
result.m_HalfSizes = CVector3D(
(pMax.X - pMin.X) / 2.f * u.Length(),
(pMax.Y - pMin.Y) / 2.f * v.Length(),
(pMax.Z - pMin.Z) / 2.f * w.Length()
);
u.Normalize();
v.Normalize();
w.Normalize();
result.m_Basis[0] = u;
result.m_Basis[1] = v;
result.m_Basis[2] = w;
result.m_Center = transform.Transform((pMax + pMin) * 0.5f);
}
///////////////////////////////////////////////////////////////////////////////
// Intersect with the given frustum in a conservative manner
void CBoundingBoxAligned::IntersectFrustumConservative(const CFrustum& frustum)
{
// if this bound is empty, then the result must be empty (we should not attempt to intersect with
// a brush, may cause crashes due to the numeric representation of empty bounds -- see
// http://trac.wildfiregames.com/ticket/1027)
if (IsEmpty())
return;
CBrush brush(*this);
CBrush buf;
brush.Intersect(frustum, buf);
buf.Bounds(*this);
}
///////////////////////////////////////////////////////////////////////////////
CFrustum CBoundingBoxAligned::ToFrustum() const
{
CFrustum frustum;
frustum.SetNumPlanes(6);
// get the LEFT plane
- frustum.m_aPlanes[0].m_Norm = CVector3D(1, 0, 0);
- frustum.m_aPlanes[0].m_Dist = -m_Data[0].X;
+ frustum[0].m_Norm = CVector3D(1, 0, 0);
+ frustum[0].m_Dist = -m_Data[0].X;
// get the RIGHT plane
- frustum.m_aPlanes[1].m_Norm = CVector3D(-1, 0, 0);
- frustum.m_aPlanes[1].m_Dist = m_Data[1].X;
+ frustum[1].m_Norm = CVector3D(-1, 0, 0);
+ frustum[1].m_Dist = m_Data[1].X;
// get the BOTTOM plane
- frustum.m_aPlanes[2].m_Norm = CVector3D(0, 1, 0);
- frustum.m_aPlanes[2].m_Dist = -m_Data[0].Y;
+ frustum[2].m_Norm = CVector3D(0, 1, 0);
+ frustum[2].m_Dist = -m_Data[0].Y;
// get the TOP plane
- frustum.m_aPlanes[3].m_Norm = CVector3D(0, -1, 0);
- frustum.m_aPlanes[3].m_Dist = m_Data[1].Y;
+ frustum[3].m_Norm = CVector3D(0, -1, 0);
+ frustum[3].m_Dist = m_Data[1].Y;
// get the NEAR plane
- frustum.m_aPlanes[4].m_Norm = CVector3D(0, 0, 1);
- frustum.m_aPlanes[4].m_Dist = -m_Data[0].Z;
+ frustum[4].m_Norm = CVector3D(0, 0, 1);
+ frustum[4].m_Dist = -m_Data[0].Z;
// get the FAR plane
- frustum.m_aPlanes[5].m_Norm = CVector3D(0, 0, -1);
- frustum.m_aPlanes[5].m_Dist = m_Data[1].Z;
+ frustum[5].m_Norm = CVector3D(0, 0, -1);
+ frustum[5].m_Dist = m_Data[1].Z;
return frustum;
}
///////////////////////////////////////////////////////////////////////////////
void CBoundingBoxAligned::Expand(float amount)
{
m_Data[0] -= CVector3D(amount, amount, amount);
m_Data[1] += CVector3D(amount, amount, amount);
}
///////////////////////////////////////////////////////////////////////////////
// Render the bounding box
void CBoundingBoxAligned::Render(CShaderProgramPtr& shader) const
{
std::vector data;
#define ADD_FACE(x, y, z) \
ADD_PT(0, 0, x, y, z); ADD_PT(1, 0, x, y, z); ADD_PT(1, 1, x, y, z); \
ADD_PT(1, 1, x, y, z); ADD_PT(0, 1, x, y, z); ADD_PT(0, 0, x, y, z);
#define ADD_PT(u_, v_, x, y, z) \
STMT(int u = u_; int v = v_; \
data.push_back(u); \
data.push_back(v); \
data.push_back(m_Data[x].X); \
data.push_back(m_Data[y].Y); \
data.push_back(m_Data[z].Z); \
)
ADD_FACE(u, v, 0);
ADD_FACE(0, u, v);
ADD_FACE(u, 0, 1-v);
ADD_FACE(u, 1-v, 1);
ADD_FACE(1, u, 1-v);
ADD_FACE(u, 1, v);
#undef ADD_FACE
shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 5*sizeof(float), &data[0]);
shader->VertexPointer(3, GL_FLOAT, 5*sizeof(float), &data[2]);
shader->AssertPointersBound();
glDrawArrays(GL_TRIANGLES, 0, 6*6);
}
void CBoundingBoxAligned::RenderOutline(CShaderProgramPtr& shader) const
{
std::vector data;
#define ADD_FACE(x, y, z) \
ADD_PT(0, 0, x, y, z); ADD_PT(1, 0, x, y, z); \
ADD_PT(1, 0, x, y, z); ADD_PT(1, 1, x, y, z); \
ADD_PT(1, 1, x, y, z); ADD_PT(0, 1, x, y, z); \
ADD_PT(0, 1, x, y, z); ADD_PT(0, 0, x, y, z);
#define ADD_PT(u_, v_, x, y, z) \
STMT(int u = u_; int v = v_; \
data.push_back(u); \
data.push_back(v); \
data.push_back(m_Data[x].X); \
data.push_back(m_Data[y].Y); \
data.push_back(m_Data[z].Z); \
)
ADD_FACE(u, v, 0);
ADD_FACE(0, u, v);
ADD_FACE(u, 0, 1-v);
ADD_FACE(u, 1-v, 1);
ADD_FACE(1, u, 1-v);
ADD_FACE(u, 1, v);
#undef ADD_FACE
shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 5*sizeof(float), &data[0]);
shader->VertexPointer(3, GL_FLOAT, 5*sizeof(float), &data[2]);
shader->AssertPointersBound();
glDrawArrays(GL_LINES, 0, 6*8);
}