#pragma once #define _USE_MATH_DEFINES #include #include #include #include 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::max()); vec3 vmax(std::numeric_limits::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 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& boxes) { std::vector 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()); }