200 lines
5.6 KiB
C
200 lines
5.6 KiB
C
|
|
#pragma once
|
|||
|
|
|
|||
|
|
#define _USE_MATH_DEFINES
|
|||
|
|
#include <cmath>
|
|||
|
|
#include <glm/ext.hpp>
|
|||
|
|
#include <glm/glm.hpp>
|
|||
|
|
|
|||
|
|
#include <vector>
|
|||
|
|
|
|||
|
|
using glm::mat4;
|
|||
|
|
using glm::vec2;
|
|||
|
|
using glm::vec3;
|
|||
|
|
using glm::vec4;
|
|||
|
|
|
|||
|
|
namespace Math
|
|||
|
|
{
|
|||
|
|
static constexpr float PI = 3.14159265359f;
|
|||
|
|
static constexpr float TWOPI = 6.28318530718f;
|
|||
|
|
} // namespace Math
|
|||
|
|
|
|||
|
|
inline vec2 clampLength(const vec2& v, float maxLength)
|
|||
|
|
{
|
|||
|
|
const float l = length(v);
|
|||
|
|
|
|||
|
|
return (l > maxLength) ? normalize(v) * maxLength : v;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
struct BoundingBox {
|
|||
|
|
vec3 min_;
|
|||
|
|
vec3 max_;
|
|||
|
|
BoundingBox() = default;
|
|||
|
|
BoundingBox(const vec3& min, const vec3& max)
|
|||
|
|
: min_(glm::min(min, max))
|
|||
|
|
, max_(glm::max(min, max))
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
BoundingBox(const vec3* points, size_t numPoints)
|
|||
|
|
{
|
|||
|
|
vec3 vmin(std::numeric_limits<float>::max());
|
|||
|
|
vec3 vmax(std::numeric_limits<float>::lowest());
|
|||
|
|
|
|||
|
|
for (size_t i = 0; i != numPoints; i++) {
|
|||
|
|
vmin = glm::min(vmin, points[i]);
|
|||
|
|
vmax = glm::max(vmax, points[i]);
|
|||
|
|
}
|
|||
|
|
min_ = vmin;
|
|||
|
|
max_ = vmax;
|
|||
|
|
}
|
|||
|
|
vec3 getSize() const { return vec3(max_[0] - min_[0], max_[1] - min_[1], max_[2] - min_[2]); }
|
|||
|
|
vec3 getCenter() const { return 0.5f * vec3(max_[0] + min_[0], max_[1] + min_[1], max_[2] + min_[2]); }
|
|||
|
|
void transform(const glm::mat4& t)
|
|||
|
|
{
|
|||
|
|
vec3 corners[] = {
|
|||
|
|
vec3(min_.x, min_.y, min_.z), vec3(min_.x, max_.y, min_.z), vec3(min_.x, min_.y, max_.z), vec3(min_.x, max_.y, max_.z),
|
|||
|
|
vec3(max_.x, min_.y, min_.z), vec3(max_.x, max_.y, min_.z), vec3(max_.x, min_.y, max_.z), vec3(max_.x, max_.y, max_.z),
|
|||
|
|
};
|
|||
|
|
for (auto& v : corners)
|
|||
|
|
v = vec3(t * vec4(v, 1.0f));
|
|||
|
|
*this = BoundingBox(corners, 8);
|
|||
|
|
}
|
|||
|
|
BoundingBox getTransformed(const glm::mat4& t) const
|
|||
|
|
{
|
|||
|
|
BoundingBox b = *this;
|
|||
|
|
b.transform(t);
|
|||
|
|
return b;
|
|||
|
|
}
|
|||
|
|
void combinePoint(const vec3& p)
|
|||
|
|
{
|
|||
|
|
min_ = glm::min(min_, p);
|
|||
|
|
max_ = glm::max(max_, p);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
template <typename T> T clamp(T v, T a, T b)
|
|||
|
|
{
|
|||
|
|
if (v < a)
|
|||
|
|
return a;
|
|||
|
|
if (v > b)
|
|||
|
|
return b;
|
|||
|
|
return v;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
inline float random01()
|
|||
|
|
{
|
|||
|
|
return (float)rand() / (float)RAND_MAX;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
inline float randomFloat(float min, float max)
|
|||
|
|
{
|
|||
|
|
return min + (max - min) * random01();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
inline vec3 randomVec(const vec3& min, const vec3& max)
|
|||
|
|
{
|
|||
|
|
return vec3(randomFloat(min.x, max.x), randomFloat(min.y, max.y), randomFloat(min.z, max.z));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
inline vec3 randVec()
|
|||
|
|
{
|
|||
|
|
return randomVec(vec3(-5, -5, -5), vec3(5, 5, 5));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
inline void getFrustumPlanes(mat4 viewProj, vec4* planes)
|
|||
|
|
{
|
|||
|
|
viewProj = glm::transpose(viewProj);
|
|||
|
|
planes[0] = vec4(viewProj[3] + viewProj[0]); // left
|
|||
|
|
planes[1] = vec4(viewProj[3] - viewProj[0]); // right
|
|||
|
|
planes[2] = vec4(viewProj[3] + viewProj[1]); // bottom
|
|||
|
|
planes[3] = vec4(viewProj[3] - viewProj[1]); // top
|
|||
|
|
planes[4] = vec4(viewProj[3] + viewProj[2]); // near
|
|||
|
|
planes[5] = vec4(viewProj[3] - viewProj[2]); // far
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
inline void getFrustumCorners(mat4 viewProj, vec4* points)
|
|||
|
|
{
|
|||
|
|
const vec4 corners[] = { vec4(-1, -1, -1, 1), vec4(1, -1, -1, 1), vec4(1, 1, -1, 1), vec4(-1, 1, -1, 1),
|
|||
|
|
vec4(-1, -1, 1, 1), vec4(1, -1, 1, 1), vec4(1, 1, 1, 1), vec4(-1, 1, 1, 1) };
|
|||
|
|
|
|||
|
|
const glm::mat4 invViewProj = glm::inverse(viewProj);
|
|||
|
|
|
|||
|
|
for (int i = 0; i != 8; i++) {
|
|||
|
|
const vec4 q = invViewProj * corners[i];
|
|||
|
|
points[i] = q / q.w;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
inline bool isBoxInFrustum(glm::vec4* frustumPlanes, glm::vec4* frustumCorners, const BoundingBox& box)
|
|||
|
|
{
|
|||
|
|
using glm::dot;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < 6; i++) {
|
|||
|
|
int r = 0;
|
|||
|
|
r += (dot(frustumPlanes[i], vec4(box.min_.x, box.min_.y, box.min_.z, 1.0f)) < 0.0) ? 1 : 0;
|
|||
|
|
r += (dot(frustumPlanes[i], vec4(box.max_.x, box.min_.y, box.min_.z, 1.0f)) < 0.0) ? 1 : 0;
|
|||
|
|
r += (dot(frustumPlanes[i], vec4(box.min_.x, box.max_.y, box.min_.z, 1.0f)) < 0.0) ? 1 : 0;
|
|||
|
|
r += (dot(frustumPlanes[i], vec4(box.max_.x, box.max_.y, box.min_.z, 1.0f)) < 0.0) ? 1 : 0;
|
|||
|
|
r += (dot(frustumPlanes[i], vec4(box.min_.x, box.min_.y, box.max_.z, 1.0f)) < 0.0) ? 1 : 0;
|
|||
|
|
r += (dot(frustumPlanes[i], vec4(box.max_.x, box.min_.y, box.max_.z, 1.0f)) < 0.0) ? 1 : 0;
|
|||
|
|
r += (dot(frustumPlanes[i], vec4(box.min_.x, box.max_.y, box.max_.z, 1.0f)) < 0.0) ? 1 : 0;
|
|||
|
|
r += (dot(frustumPlanes[i], vec4(box.max_.x, box.max_.y, box.max_.z, 1.0f)) < 0.0) ? 1 : 0;
|
|||
|
|
if (r == 8)
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// check frustum outside/inside box
|
|||
|
|
int r = 0;
|
|||
|
|
r = 0;
|
|||
|
|
for (int i = 0; i < 8; i++)
|
|||
|
|
r += ((frustumCorners[i].x > box.max_.x) ? 1 : 0);
|
|||
|
|
if (r == 8)
|
|||
|
|
return false;
|
|||
|
|
r = 0;
|
|||
|
|
for (int i = 0; i < 8; i++)
|
|||
|
|
r += ((frustumCorners[i].x < box.min_.x) ? 1 : 0);
|
|||
|
|
if (r == 8)
|
|||
|
|
return false;
|
|||
|
|
r = 0;
|
|||
|
|
for (int i = 0; i < 8; i++)
|
|||
|
|
r += ((frustumCorners[i].y > box.max_.y) ? 1 : 0);
|
|||
|
|
if (r == 8)
|
|||
|
|
return false;
|
|||
|
|
r = 0;
|
|||
|
|
for (int i = 0; i < 8; i++)
|
|||
|
|
r += ((frustumCorners[i].y < box.min_.y) ? 1 : 0);
|
|||
|
|
if (r == 8)
|
|||
|
|
return false;
|
|||
|
|
r = 0;
|
|||
|
|
for (int i = 0; i < 8; i++)
|
|||
|
|
r += ((frustumCorners[i].z > box.max_.z) ? 1 : 0);
|
|||
|
|
if (r == 8)
|
|||
|
|
return false;
|
|||
|
|
r = 0;
|
|||
|
|
for (int i = 0; i < 8; i++)
|
|||
|
|
r += ((frustumCorners[i].z < box.min_.z) ? 1 : 0);
|
|||
|
|
if (r == 8)
|
|||
|
|
return false;
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
inline BoundingBox combineBoxes(const std::vector<BoundingBox>& boxes)
|
|||
|
|
{
|
|||
|
|
std::vector<vec3> allPoints;
|
|||
|
|
allPoints.reserve(boxes.size() * 8);
|
|||
|
|
|
|||
|
|
for (const auto& b : boxes) {
|
|||
|
|
allPoints.emplace_back(b.min_.x, b.min_.y, b.min_.z);
|
|||
|
|
allPoints.emplace_back(b.min_.x, b.min_.y, b.max_.z);
|
|||
|
|
allPoints.emplace_back(b.min_.x, b.max_.y, b.min_.z);
|
|||
|
|
allPoints.emplace_back(b.min_.x, b.max_.y, b.max_.z);
|
|||
|
|
|
|||
|
|
allPoints.emplace_back(b.max_.x, b.min_.y, b.min_.z);
|
|||
|
|
allPoints.emplace_back(b.max_.x, b.min_.y, b.max_.z);
|
|||
|
|
allPoints.emplace_back(b.max_.x, b.max_.y, b.min_.z);
|
|||
|
|
allPoints.emplace_back(b.max_.x, b.max_.y, b.max_.z);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return BoundingBox(allPoints.data(), allPoints.size());
|
|||
|
|
}
|