Skip to content

Commit 94f8805

Browse files
authored
Merge pull request #189 from orange-cpp/feature/obb
added obb
2 parents 6b637f6 + fbc3539 commit 94f8805

4 files changed

Lines changed: 407 additions & 41 deletions

File tree

.idea/omath.iml

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// Created by Vladislav on 07.05.2026.
3+
//
4+
5+
#pragma once
6+
#include "omath/linear_algebra/vector3.hpp"
7+
#include <array>
8+
#include <type_traits>
9+
10+
namespace omath::primitives
11+
{
12+
// Oriented bounding box: a rectangular cuboid defined by a center, three
13+
// orthonormal local axes, and the half-size along each of those axes.
14+
template<class Type>
15+
requires std::is_floating_point_v<Type>
16+
struct Obb final
17+
{
18+
Vector3<Type> center;
19+
Vector3<Type> axis_x;
20+
Vector3<Type> axis_y;
21+
Vector3<Type> axis_z;
22+
Vector3<Type> half_extents;
23+
24+
[[nodiscard]]
25+
constexpr std::array<Vector3<Type>, 8> vertices() const noexcept
26+
{
27+
const auto ex = axis_x * half_extents.x;
28+
const auto ey = axis_y * half_extents.y;
29+
const auto ez = axis_z * half_extents.z;
30+
31+
return {
32+
center - ex - ey - ez, center + ex - ey - ez, center - ex + ey - ez, center + ex + ey - ez,
33+
center - ex - ey + ez, center + ex - ey + ez, center - ex + ey + ez, center + ex + ey + ez,
34+
};
35+
}
36+
};
37+
} // namespace omath::primitives

include/omath/projection/camera.hpp

Lines changed: 67 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#pragma once
66

77
#include "omath/3d_primitives/aabb.hpp"
8+
#include "omath/3d_primitives/obb.hpp"
89
#include "omath/linear_algebra/mat.hpp"
910
#include "omath/linear_algebra/triangle.hpp"
1011
#include "omath/linear_algebra/vector3.hpp"
@@ -380,49 +381,9 @@ namespace omath::projection
380381

381382
[[nodiscard]] bool is_aabb_culled_by_frustum(const primitives::Aabb<NumericType>& aabb) const noexcept
382383
{
383-
const auto& m = get_view_projection_matrix();
384-
385-
// Gribb-Hartmann: extract 6 frustum planes from the view-projection matrix.
386-
// Each plane is (a, b, c, d) such that ax + by + cz + d >= 0 means inside.
387-
// For a 4x4 matrix with rows r0..r3:
388-
// Left = r3 + r0
389-
// Right = r3 - r0
390-
// Bottom = r3 + r1
391-
// Top = r3 - r1
392-
// Near = r3 + r2 ([-1,1]) or r2 ([0,1])
393-
// Far = r3 - r2
394-
struct Plane final
395-
{
396-
NumericType a, b, c, d;
397-
};
398-
399-
const auto extract_plane = [&m](const int sign, const int row) -> Plane
400-
{
401-
return {
402-
m.at(3, 0) + static_cast<NumericType>(sign) * m.at(row, 0),
403-
m.at(3, 1) + static_cast<NumericType>(sign) * m.at(row, 1),
404-
m.at(3, 2) + static_cast<NumericType>(sign) * m.at(row, 2),
405-
m.at(3, 3) + static_cast<NumericType>(sign) * m.at(row, 3),
406-
};
407-
};
408-
409-
std::array<Plane, 6> planes = {
410-
extract_plane(1, 0), // left
411-
extract_plane(-1, 0), // right
412-
extract_plane(1, 1), // bottom
413-
extract_plane(-1, 1), // top
414-
extract_plane(-1, 2), // far
415-
};
416-
417-
// Near plane depends on NDC depth range
418-
if constexpr (depth_range == NDCDepthRange::ZERO_TO_ONE)
419-
planes[5] = {m.at(2, 0), m.at(2, 1), m.at(2, 2), m.at(2, 3)};
420-
else
421-
planes[5] = extract_plane(1, 2);
422-
423384
// For each plane, find the AABB corner most in the direction of the plane normal
424385
// (the "positive vertex"). If it's outside, the entire AABB is outside.
425-
for (const auto& [a, b, c, d] : planes)
386+
for (const auto& [a, b, c, d] : extract_frustum_planes())
426387
{
427388
const auto px = a >= NumericType{0} ? aabb.max.x : aabb.min.x;
428389
const auto py = b >= NumericType{0} ? aabb.max.y : aabb.min.y;
@@ -435,6 +396,26 @@ namespace omath::projection
435396
return false;
436397
}
437398

399+
[[nodiscard]] bool is_obb_culled_by_frustum(const primitives::Obb<NumericType>& obb) const noexcept
400+
{
401+
// For each plane, project the OBB extents onto the plane normal to get the
402+
// effective radius, then test the center's signed distance against it.
403+
for (const auto& [a, b, c, d] : extract_frustum_planes())
404+
{
405+
const Vector3<NumericType> normal{a, b, c};
406+
407+
const auto center_distance = normal.dot(obb.center) + d;
408+
const auto radius = obb.half_extents.x * std::abs(normal.dot(obb.axis_x))
409+
+ obb.half_extents.y * std::abs(normal.dot(obb.axis_y))
410+
+ obb.half_extents.z * std::abs(normal.dot(obb.axis_z));
411+
412+
if (center_distance + radius < NumericType{0})
413+
return true;
414+
}
415+
416+
return false;
417+
}
418+
438419
[[nodiscard]] std::expected<Vector3<NumericType>, Error>
439420
world_to_view_port(const Vector3<NumericType>& world_position,
440421
const ViewPortClipping& clipping = ViewPortClipping::AUTO) const noexcept
@@ -517,6 +498,51 @@ namespace omath::projection
517498
Vector3<NumericType> m_origin;
518499

519500
private:
501+
struct FrustumPlane final
502+
{
503+
NumericType a, b, c, d;
504+
};
505+
506+
// Gribb-Hartmann: extract 6 frustum planes from the view-projection matrix.
507+
// Each plane is (a, b, c, d) such that ax + by + cz + d >= 0 means inside.
508+
// For a 4x4 matrix with rows r0..r3:
509+
// Left = r3 + r0
510+
// Right = r3 - r0
511+
// Bottom = r3 + r1
512+
// Top = r3 - r1
513+
// Near = r3 + r2 ([-1,1]) or r2 ([0,1])
514+
// Far = r3 - r2
515+
[[nodiscard]] std::array<FrustumPlane, 6> extract_frustum_planes() const noexcept
516+
{
517+
const auto& m = get_view_projection_matrix();
518+
519+
const auto extract_plane = [&m](const int sign, const int row) -> FrustumPlane
520+
{
521+
return {
522+
m.at(3, 0) + static_cast<NumericType>(sign) * m.at(row, 0),
523+
m.at(3, 1) + static_cast<NumericType>(sign) * m.at(row, 1),
524+
m.at(3, 2) + static_cast<NumericType>(sign) * m.at(row, 2),
525+
m.at(3, 3) + static_cast<NumericType>(sign) * m.at(row, 3),
526+
};
527+
};
528+
529+
std::array<FrustumPlane, 6> planes = {
530+
extract_plane(1, 0), // left
531+
extract_plane(-1, 0), // right
532+
extract_plane(1, 1), // bottom
533+
extract_plane(-1, 1), // top
534+
extract_plane(-1, 2), // far
535+
};
536+
537+
// Near plane depends on NDC depth range
538+
if constexpr (depth_range == NDCDepthRange::ZERO_TO_ONE)
539+
planes[5] = {m.at(2, 0), m.at(2, 1), m.at(2, 2), m.at(2, 3)};
540+
else
541+
planes[5] = extract_plane(1, 2);
542+
543+
return planes;
544+
}
545+
520546
template<class Type>
521547
[[nodiscard]] constexpr static bool is_ndc_out_of_bounds(const Type& ndc) noexcept
522548
{

0 commit comments

Comments
 (0)