144 lines
4.7 KiB
C++
144 lines
4.7 KiB
C++
/*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "Math.hpp"
|
|
#include "Range.hpp"
|
|
|
|
#ifdef NEAR
|
|
#undef NEAR
|
|
#endif
|
|
#ifdef FAR
|
|
#undef FAR
|
|
#endif
|
|
|
|
namespace OpenVulkano::Math
|
|
{
|
|
class Frustum
|
|
{
|
|
enum Planes
|
|
{
|
|
LEFT = 0,
|
|
RIGHT,
|
|
BOTTOM,
|
|
TOP,
|
|
NEAR,
|
|
FAR,
|
|
COUNT,
|
|
COMBINATIONS = COUNT * (COUNT - 1) / 2
|
|
};
|
|
|
|
public:
|
|
Frustum()
|
|
{}
|
|
|
|
// m = ProjectionMatrix * ViewMatrix
|
|
Frustum(Matrix4f m)
|
|
{
|
|
m = Math::Utils::transpose(m);
|
|
m_planes[LEFT] = m[3] + m[0];
|
|
m_planes[RIGHT] = m[3] - m[0];
|
|
m_planes[BOTTOM] = m[3] + m[1];
|
|
m_planes[TOP] = m[3] - m[1];
|
|
m_planes[NEAR] = m[3] + m[2];
|
|
m_planes[FAR] = m[3] - m[2];
|
|
|
|
Math::Vector3f crosses[COMBINATIONS] = {
|
|
Math::Utils::cross(Vector3f(m_planes[LEFT]), Vector3f(m_planes[RIGHT])),
|
|
Math::Utils::cross(Vector3f(m_planes[LEFT]), Vector3f(m_planes[BOTTOM])),
|
|
Math::Utils::cross(Vector3f(m_planes[LEFT]), Vector3f(m_planes[TOP])),
|
|
Math::Utils::cross(Vector3f(m_planes[LEFT]), Vector3f(m_planes[NEAR])),
|
|
Math::Utils::cross(Vector3f(m_planes[LEFT]), Vector3f(m_planes[FAR])),
|
|
Math::Utils::cross(Vector3f(m_planes[RIGHT]), Vector3f(m_planes[BOTTOM])),
|
|
Math::Utils::cross(Vector3f(m_planes[RIGHT]), Vector3f(m_planes[TOP])),
|
|
Math::Utils::cross(Vector3f(m_planes[RIGHT]), Vector3f(m_planes[NEAR])),
|
|
Math::Utils::cross(Vector3f(m_planes[RIGHT]), Vector3f(m_planes[FAR])),
|
|
Math::Utils::cross(Vector3f(m_planes[BOTTOM]), Vector3f(m_planes[TOP])),
|
|
Math::Utils::cross(Vector3f(m_planes[BOTTOM]), Vector3f(m_planes[NEAR])),
|
|
Math::Utils::cross(Vector3f(m_planes[BOTTOM]), Vector3f(m_planes[FAR])),
|
|
Math::Utils::cross(Vector3f(m_planes[TOP]), Vector3f(m_planes[NEAR])),
|
|
Math::Utils::cross(Vector3f(m_planes[TOP]), Vector3f(m_planes[FAR])),
|
|
Math::Utils::cross(Vector3f(m_planes[NEAR]), Vector3f(m_planes[FAR]))
|
|
};
|
|
|
|
m_points[0] = Intersection<LEFT, BOTTOM, NEAR>(crosses);
|
|
m_points[1] = Intersection<LEFT, TOP, NEAR>(crosses);
|
|
m_points[2] = Intersection<RIGHT, BOTTOM, NEAR>(crosses);
|
|
m_points[3] = Intersection<RIGHT, TOP, NEAR>(crosses);
|
|
m_points[4] = Intersection<LEFT, BOTTOM, FAR>(crosses);
|
|
m_points[5] = Intersection<LEFT, TOP, FAR>(crosses);
|
|
m_points[6] = Intersection<RIGHT, BOTTOM, FAR>(crosses);
|
|
m_points[7] = Intersection<RIGHT, TOP, FAR>(crosses);
|
|
}
|
|
|
|
// http://iquilezles.org/www/articles/frustumcorrect/frustumcorrect.htm
|
|
[[nodiscard]] bool IsBoxVisible(const Math::Vector3f& minp, const Math::Vector3f& maxp) const
|
|
{ // http://iquilezles.org/www/articles/frustumcorrect/frustumcorrect.htm
|
|
// check box outside/inside of frustum
|
|
for (int i = 0; i < COUNT; i++)
|
|
{
|
|
if ((Math::Utils::dot(m_planes[i], Math::Vector4f(minp.x, minp.y, minp.z, 1.0f)) < 0.0) &&
|
|
(Math::Utils::dot(m_planes[i], Math::Vector4f(maxp.x, minp.y, minp.z, 1.0f)) < 0.0) &&
|
|
(Math::Utils::dot(m_planes[i], Math::Vector4f(minp.x, maxp.y, minp.z, 1.0f)) < 0.0) &&
|
|
(Math::Utils::dot(m_planes[i], Math::Vector4f(maxp.x, maxp.y, minp.z, 1.0f)) < 0.0) &&
|
|
(Math::Utils::dot(m_planes[i], Math::Vector4f(minp.x, minp.y, maxp.z, 1.0f)) < 0.0) &&
|
|
(Math::Utils::dot(m_planes[i], Math::Vector4f(maxp.x, minp.y, maxp.z, 1.0f)) < 0.0) &&
|
|
(Math::Utils::dot(m_planes[i], Math::Vector4f(minp.x, maxp.y, maxp.z, 1.0f)) < 0.0) &&
|
|
(Math::Utils::dot(m_planes[i], Math::Vector4f(maxp.x, maxp.y, maxp.z, 1.0f)) < 0.0))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// check frustum outside/inside box
|
|
for (int j = 0, out = 0; j < 3; j++)
|
|
{
|
|
for (int i = 0; i < 8; i++) out += m_points[i][j] > maxp[j];
|
|
if (out == 8) return false;
|
|
out = 0;
|
|
for (int i = 0; i < 8; i++) out += m_points[i][j] < minp[j];
|
|
if (out == 8) return false;
|
|
out = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
[[nodiscard]] bool IsBoxVisible(const Vector4f& minp, const Vector4f& maxp) const
|
|
{
|
|
return IsBoxVisible(Vector3f(minp), Vector3f(maxp));
|
|
}
|
|
|
|
[[nodiscard]] bool IsBoxVisible(const Range<Vector3f>& range) const
|
|
{
|
|
return IsBoxVisible(range.min, range.max);
|
|
}
|
|
|
|
private:
|
|
template<Planes i, Planes j>
|
|
struct ij2k
|
|
{
|
|
enum
|
|
{
|
|
k = i * (9 - i) / 2 + j - 1
|
|
};
|
|
};
|
|
|
|
template<Planes a, Planes b, Planes c>
|
|
Vector3f Intersection(const Vector3f* crosses) const
|
|
{
|
|
float D = Math::Utils::dot(Vector3f(m_planes[a]), crosses[ij2k<b, c>::k]);
|
|
Vector3f res = Matrix3f(crosses[ij2k<b, c>::k], -crosses[ij2k<a, c>::k], crosses[ij2k<a, b>::k]) *
|
|
Vector3f(m_planes[a].w, m_planes[b].w, m_planes[c].w);
|
|
return res * (-1.0f / D);
|
|
}
|
|
|
|
Vector4f m_planes[COUNT];
|
|
Vector3f m_points[8];
|
|
};
|
|
}
|