Files
2026-02-02 04:50:13 +01:00

413 lines
13 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Copyright (C) 2006-2010 - Frictional Games
*
* This file is part of HPL1 Engine.
*/
#include "hpl1/engine/math/Frustum.h"
#include "hpl1/engine/graphics/LowLevelGraphics.h"
#include "hpl1/engine/math/Math.h"
#include "hpl1/engine/system/low_level_system.h"
namespace hpl {
//////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cFrustum::cFrustum() {
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
void cFrustum::SetViewProjMatrix(const cMatrixf &a_mtxProj, const cMatrixf &a_mtxView,
float afFarPlane, float afNearPlane, float afFOV, float afAspect,
const cVector3f &avOrigin, bool abInfFarPlane) {
m_mtxViewProj = cMath::MatrixMul(a_mtxProj, a_mtxView);
m_mtxModelView = a_mtxView;
mfFarPlane = afFarPlane;
mfNearPlane = afNearPlane;
mfFOV = afFOV;
mfAspect = afAspect;
mvOrigin = avOrigin;
mbInfFarPlane = abInfFarPlane;
// This could be made more accurate.
mOriginBV.SetSize(afNearPlane * 2);
mOriginBV.SetPosition(mvOrigin);
UpdatePlanes();
UpdateSphere();
UpdateEndPoints();
UpdateBV();
}
//-----------------------------------------------------------------------
cPlanef cFrustum::GetPlane(eFrustumPlane aType) {
return mPlane[aType];
}
//-----------------------------------------------------------------------
eFrustumCollision cFrustum::CollideBoundingVolume(cBoundingVolume *aBV) {
// Check if the BV is in the Frustum sphere.
if (CollideFustrumSphere(aBV) == eFrustumCollision_Outside) {
return eFrustumCollision_Outside;
}
// Do a simple sphere collide test
eFrustumCollision ret = CollideBVSphere(aBV);
// If there was an intersection, collide with the AABB
if (ret == eFrustumCollision_Intersect) {
return CollideBVAABB(aBV);
}
return ret;
}
//-----------------------------------------------------------------------
bool cFrustum::CheckLineIntersection(const cVector3f &avPoint1, const cVector3f &avPoint2) {
return cMath::CheckFrustumLineIntersection(&mPlane[0], avPoint1, avPoint2, 3);
}
//-----------------------------------------------------------------------
bool cFrustum::CheckQuadMeshIntersection(tVector3fVec *apPoints) {
return cMath::CheckFrustumQuadMeshIntersection(&mPlane[0], apPoints, 3);
}
//-----------------------------------------------------------------------
bool cFrustum::CheckVolumeIntersection(cShadowVolumeBV *apVolume) {
if (CheckQuadMeshIntersection(&apVolume->mvPoints)) {
return true;
}
// The first shadow plane is the near plane which we can skip.
int lPairNum = (apVolume->mlPlaneCount - 1) / 2;
// Check with volume planes.
for (int i = 0; i < 4; ++i) {
if (cMath::CheckFrustumLineIntersection(&apVolume->mvPlanes[1], mvOrigin, mvEndPoints[i],
lPairNum)) {
return true;
}
}
return false;
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
eFrustumCollision cFrustum::CollideFustrumSphere(cBoundingVolume *aBV) {
float fRadiusSum = mBoundingSphere.r + aBV->GetRadius();
cVector3f vSepAxis = mBoundingSphere.center - aBV->GetWorldCenter();
if (vSepAxis.SqrLength() > (fRadiusSum * fRadiusSum)) {
return eFrustumCollision_Outside;
}
return eFrustumCollision_Intersect;
}
//-----------------------------------------------------------------------
eFrustumCollision cFrustum::CollideBVSphere(cBoundingVolume *aBV) {
int lPlanes = 6;
if (mbInfFarPlane)
lPlanes = 5;
for (int i = 0; i < lPlanes; i++) {
float fDist = cMath::PlaneToPointDist(mPlane[i], aBV->GetWorldCenter());
if (fDist < -aBV->GetRadius()) {
return eFrustumCollision_Outside;
}
if (ABS(fDist) < aBV->GetRadius()) {
return eFrustumCollision_Intersect;
}
}
return eFrustumCollision_Inside;
}
//-----------------------------------------------------------------------
eFrustumCollision cFrustum::CollideBVAABB(cBoundingVolume *aBV) {
cVector3f vMax = aBV->GetMax();
cVector3f vMin = aBV->GetMin();
// Get the corners from the AAB
cVector3f vCorners[9] = {
cVector3f(vMax.x, vMax.y, vMax.z),
cVector3f(vMax.x, vMax.y, vMin.z),
cVector3f(vMax.x, vMin.y, vMax.z),
cVector3f(vMax.x, vMin.y, vMin.z),
cVector3f(vMin.x, vMax.y, vMax.z),
cVector3f(vMin.x, vMax.y, vMin.z),
cVector3f(vMin.x, vMin.y, vMax.z),
cVector3f(vMin.x, vMin.y, vMin.z),
// The "fuling", add center as well...
aBV->GetPosition()};
int lTotalIn = 0;
int lPlanes = 6;
if (mbInfFarPlane)
lPlanes = 5;
// Go through all the planes
for (int i = 0; i < lPlanes; i++) {
int lInCount = 9;
bool bIsIn = true;
for (int j = 0; j < 9; j++) {
float fDist = cMath::PlaneToPointDist(mPlane[i], vCorners[j]);
if (fDist < 0) {
lInCount--;
bIsIn = false;
}
}
if (lInCount == 0)
return eFrustumCollision_Outside;
if (bIsIn)
lTotalIn++;
}
if (lTotalIn == lPlanes)
return eFrustumCollision_Inside;
return eFrustumCollision_Intersect;
}
//-----------------------------------------------------------------------
void cFrustum::UpdateSphere() {
// calculate the radius of the frustum sphere
float fViewLen = mfFarPlane - mfNearPlane;
float fHeight = fViewLen * tan(mfFOV * 0.5f);
float fWidth = fHeight * mfAspect;
// halfway point between near/far planes starting at the origin and extending along the z axis
cVector3f P(0.0f, 0.0f, mfNearPlane + fViewLen * 0.5f);
// the calculate far corner of the frustum
cVector3f Q(fWidth, fHeight, fViewLen);
// the vector between P and Q
cVector3f vDiff = P - Q;
// the radius becomes the length of this vector
float fRadius = vDiff.Length();
// get the look vector of the camera from the view matrix
cVector3f vLookVector = m_mtxModelView.GetForward() * -1;
// calculate the center of the sphere
cVector3f vCenter = (mvOrigin) + (vLookVector * (fViewLen * 0.5f + mfNearPlane));
mBoundingSphere = cSpheref(vCenter, fRadius);
}
//-----------------------------------------------------------------------
void cFrustum::UpdatePlanes() {
// Left
mPlane[eFrustumPlane_Left] = cPlanef(m_mtxViewProj.m[3][0] + m_mtxViewProj.m[0][0],
m_mtxViewProj.m[3][1] + m_mtxViewProj.m[0][1],
m_mtxViewProj.m[3][2] + m_mtxViewProj.m[0][2],
m_mtxViewProj.m[3][3] + m_mtxViewProj.m[0][3]);
// Right
mPlane[eFrustumPlane_Right] = cPlanef(m_mtxViewProj.m[3][0] - m_mtxViewProj.m[0][0],
m_mtxViewProj.m[3][1] - m_mtxViewProj.m[0][1],
m_mtxViewProj.m[3][2] - m_mtxViewProj.m[0][2],
m_mtxViewProj.m[3][3] - m_mtxViewProj.m[0][3]);
// Bottom
mPlane[eFrustumPlane_Bottom] = cPlanef(m_mtxViewProj.m[3][0] + m_mtxViewProj.m[1][0],
m_mtxViewProj.m[3][1] + m_mtxViewProj.m[1][1],
m_mtxViewProj.m[3][2] + m_mtxViewProj.m[1][2],
m_mtxViewProj.m[3][3] + m_mtxViewProj.m[1][3]);
// Top
mPlane[eFrustumPlane_Top] = cPlanef(m_mtxViewProj.m[3][0] - m_mtxViewProj.m[1][0],
m_mtxViewProj.m[3][1] - m_mtxViewProj.m[1][1],
m_mtxViewProj.m[3][2] - m_mtxViewProj.m[1][2],
m_mtxViewProj.m[3][3] - m_mtxViewProj.m[1][3]);
// Near
mPlane[eFrustumPlane_Near] = cPlanef(m_mtxViewProj.m[3][0] + m_mtxViewProj.m[2][0],
m_mtxViewProj.m[3][1] + m_mtxViewProj.m[2][1],
m_mtxViewProj.m[3][2] + m_mtxViewProj.m[2][2],
m_mtxViewProj.m[3][3] + m_mtxViewProj.m[2][3]);
// Far
mPlane[eFrustumPlane_Far] = cPlanef(m_mtxViewProj.m[3][0] - m_mtxViewProj.m[2][0],
m_mtxViewProj.m[3][1] - m_mtxViewProj.m[2][1],
m_mtxViewProj.m[3][2] - m_mtxViewProj.m[2][2],
m_mtxViewProj.m[3][3] - m_mtxViewProj.m[2][3]);
for (int i = 0; i < 6; i++) {
mPlane[i].Normalise();
mPlane[i].CalcNormal();
}
}
//-----------------------------------------------------------------------
void cFrustum::UpdateEndPoints() {
float fXAngle = mfFOV * 0.5f;
float fYAngle = mfFOV * 0.5f * mfAspect;
cVector3f vForward = m_mtxModelView.GetForward();
// cVector3f vUp = m_mtxModelView.GetUp();
cVector3f vRight = m_mtxModelView.GetRight();
// Point the forward vec in different dirs.
cVector3f vUpDir = cMath::MatrixMul(cMath::MatrixQuaternion(cQuaternion(fXAngle, vRight)), vForward);
cVector3f vDownDir = cMath::MatrixMul(cMath::MatrixQuaternion(cQuaternion(-fXAngle, vRight)), vForward);
// cVector3f vRightDir = cMath::MatrixMul(cMath::MatrixQuaternion(cQuaternion(fYAngle,vUp)), vForward);
// cVector3f vLeftDir = cMath::MatrixMul(cMath::MatrixQuaternion(cQuaternion(-fYAngle,vUp)), vForward);
vForward = vForward * -1;
float fRightAdd = sin(fYAngle);
// float fUpAdd = sin(fXAngle);
cVector3f vVec0 = vUpDir + vRight * fRightAdd;
cVector3f vVec2 = vDownDir + vRight * fRightAdd;
cVector3f vVec3 = vDownDir + vRight * -fRightAdd;
cVector3f vVec1 = vUpDir + vRight * -fRightAdd;
/*cVector3f vVec0 = vUpDir;//cMath::Vector3Normalize(vUpDir+vRightDir);
cVector3f vVec1 = vRightDir;//cMath::Vector3Normalize(vUpDir+vLeftDir);
cVector3f vVec2 = vDownDir;//cMath::Vector3Normalize(vDownDir+vLeftDir);
cVector3f vVec3 = vLeftDir;//cMath::Vector3Normalize(vDownDir+vRightDir);*/
// angles between forward and the vectors
float fAngle0 = cMath::Vector3Angle(vVec0, vForward);
float fAngle1 = cMath::Vector3Angle(vVec1, vForward);
float fAngle2 = cMath::Vector3Angle(vVec2, vForward);
float fAngle3 = cMath::Vector3Angle(vVec3, vForward);
// create end points.
mvEndPoints[0] = mvOrigin + vVec0 * (mfFarPlane / cos(fAngle0));
mvEndPoints[1] = mvOrigin + vVec1 * (mfFarPlane / cos(fAngle1));
mvEndPoints[2] = mvOrigin + vVec2 * (mfFarPlane / cos(fAngle2));
mvEndPoints[3] = mvOrigin + vVec3 * (mfFarPlane / cos(fAngle3));
/*mvEndPoints[0] = mvOrigin + (vUpDir+vRightDir) * mfFarPlane *-1;
mvEndPoints[1] = mvOrigin + (vUpDir+vLeftDir) * mfFarPlane*-1;
mvEndPoints[2] = mvOrigin + (vDownDir+vRightDir)* mfFarPlane*-1;
mvEndPoints[3] = mvOrigin + (vDownDir+vLeftDir) * mfFarPlane*-1;*/
}
//-----------------------------------------------------------------------
void cFrustum::UpdateBV() {
cVector3f vMin = mvOrigin;
cVector3f vMax = mvOrigin;
for (int i = 0; i < 4; i++) {
if (vMax.x < mvEndPoints[i].x)
vMax.x = mvEndPoints[i].x;
else if (vMin.x > mvEndPoints[i].x)
vMin.x = mvEndPoints[i].x;
if (vMax.y < mvEndPoints[i].y)
vMax.y = mvEndPoints[i].y;
else if (vMin.y > mvEndPoints[i].y)
vMin.y = mvEndPoints[i].y;
if (vMax.z < mvEndPoints[i].z)
vMax.z = mvEndPoints[i].z;
else if (vMin.z > mvEndPoints[i].z)
vMin.z = mvEndPoints[i].z;
}
mBoundingVolume.SetLocalMinMax(vMin, vMax);
}
//-----------------------------------------------------------------------
const cVector3f &cFrustum::GetOrigin() {
return mvOrigin;
}
//-----------------------------------------------------------------------
cBoundingVolume *cFrustum::GetOriginBV() {
return &mOriginBV;
}
//-----------------------------------------------------------------------
cVector3f cFrustum::GetForward() {
return m_mtxModelView.GetForward();
}
//-----------------------------------------------------------------------
void cFrustum::Draw(iLowLevelGraphics *apLowLevelGraphics) {
apLowLevelGraphics->DrawLine(mvOrigin, mvEndPoints[0], cColor(1, 1, 1, 1));
apLowLevelGraphics->DrawLine(mvOrigin, mvEndPoints[1], cColor(1, 1, 1, 1));
apLowLevelGraphics->DrawLine(mvOrigin, mvEndPoints[2], cColor(1, 1, 1, 1));
apLowLevelGraphics->DrawLine(mvOrigin, mvEndPoints[3], cColor(1, 1, 1, 1));
apLowLevelGraphics->DrawLine(mvEndPoints[0], mvEndPoints[1], cColor(1, 1, 1, 1));
apLowLevelGraphics->DrawLine(mvEndPoints[1], mvEndPoints[2], cColor(1, 1, 1, 1));
apLowLevelGraphics->DrawLine(mvEndPoints[2], mvEndPoints[3], cColor(1, 1, 1, 1));
apLowLevelGraphics->DrawLine(mvEndPoints[3], mvEndPoints[0], cColor(1, 1, 1, 1));
apLowLevelGraphics->DrawLine(mvEndPoints[0], mvEndPoints[2], cColor(1, 1, 1, 1));
apLowLevelGraphics->DrawLine(mvEndPoints[1], mvEndPoints[3], cColor(1, 1, 1, 1));
}
//-----------------------------------------------------------------------
} // namespace hpl