From b63520841b9376096d61756561b94176974f9d3d Mon Sep 17 00:00:00 2001 From: kevyuu Date: Mon, 25 Aug 2025 14:25:42 +0700 Subject: [PATCH 1/9] Dummy --- .../asset/utils/CPolygonGeometryManipulator.h | 22 + include/nbl/builtin/hlsl/shapes/aabb.hlsl | 24 + .../utils/CPolygonGeometryManipulator.cpp | 490 ++++++++++++++++++ 3 files changed, 536 insertions(+) diff --git a/include/nbl/asset/utils/CPolygonGeometryManipulator.h b/include/nbl/asset/utils/CPolygonGeometryManipulator.h index 3aa3c25304..99ac6f440a 100644 --- a/include/nbl/asset/utils/CPolygonGeometryManipulator.h +++ b/include/nbl/asset/utils/CPolygonGeometryManipulator.h @@ -99,6 +99,28 @@ class NBL_API2 CPolygonGeometryManipulator EEM_QUATERNION, EEM_COUNT }; + + struct VertexCollection + { + using FetchFn = std::function; + FetchFn fetch; + size_t size; + + static auto fromSpan(std::span vertices) -> VertexCollection + { + return VertexCollection{ + .fetch = [data = vertices.data()](size_t vertexIndex)-> hlsl::float32_t3 + { + return data[vertexIndex]; + }, + .size = vertices.size() + }; + } + + hlsl::float32_t3 operator[](size_t index) const { return fetch(index); } + }; + static hlsl::shapes::OBB<3, hlsl::float32_t> calculateOBB(const VertexCollection& vertexCollection); + #if 0 // TODO: REDO //! Struct used to pass chosen comparison method and epsilon to functions performing error metrics. /** diff --git a/include/nbl/builtin/hlsl/shapes/aabb.hlsl b/include/nbl/builtin/hlsl/shapes/aabb.hlsl index d07b38df37..5c6435e3e7 100644 --- a/include/nbl/builtin/hlsl/shapes/aabb.hlsl +++ b/include/nbl/builtin/hlsl/shapes/aabb.hlsl @@ -60,6 +60,30 @@ struct AABB point_t maxVx; }; +template +struct OBB +{ + using scalar_t = Scalar; + using point_t = vector; + + static OBB createAxisAligned(point_t mid, point_t len) + { + OBB ret; + ret.mid = mid; + ret.ext = len; + for (auto dim_i = 0; dim_i < D; dim_i++) + { + ret.axes[dim_i] = point_t(); + ret.axes[dim_i][D] = 1; + } + return ret; + } + + point_t mid; + std::array axes; + point_t ext; +}; + namespace util { namespace impl diff --git a/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp b/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp index 1e08c172ba..be9d6dec3c 100644 --- a/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp +++ b/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp @@ -20,6 +20,496 @@ namespace nbl::asset { + +template +struct Extremals +{ + std::array values; + + T* minPtr() + { + return values.data(); + } + + const T* minPtr() const + { + return values.data(); + } + + T* maxPtr() + { + return values.data() + CountV; + } + + const T* maxPtr() const + { + return values.data() + CountV; + } +}; + + +hlsl::shapes::OBB<> CPolygonGeometryManipulator::calculateOBB(const VertexCollection& vertices) +{ + constexpr size_t SAMPLE_DIR_COUNT = 7; // Number of sample directions + constexpr size_t SAMPLE_COUNT = SAMPLE_DIR_COUNT * 2; + + if (vertices.size <= 0) + { + return hlsl::shapes::OBB<>::createAxisAligned({}, {}); + } + + static auto getQualityValue = [](hlsl::float32_t3 len) -> hlsl::float32_t + { + return len.x * len.y + len.x * len.z + len.y * len.z; //half box area + }; + + using ExtremalVertices = Extremals; + using ExtremalProjections = Extremals; + using Axes = std::array; + using Edges = std::array; + + struct ExtremalSamples + { + ExtremalVertices vertices; + ExtremalProjections projections; + }; + + struct LargeBaseTriangle + { + hlsl::float32_t3 normal = {}; + Axes vertices = {}; + Edges edges = {}; + enum Flag + { + NORMAL, + SECOND_POINT_CLOSE, + THIRD_POINT_CLOSE + } flag; + }; + + static auto findExtremals_7FixedDirs = [](const VertexCollection& vertices)-> ExtremalSamples + { + ExtremalSamples result; + hlsl::float32_t proj; + + const auto firstVertex = vertices.fetch(0); + + auto* minProjections = result.projections.minPtr(); + auto* maxProjections = result.projections.maxPtr(); + + auto* minVertices = result.vertices.minPtr(); + auto* maxVertices = result.vertices.maxPtr(); + + // Slab 0: dir {1, 0, 0} + proj = firstVertex.x; + minProjections[0] = minProjections[0] = proj; + minVertices[0] = firstVertex; maxVertices[0] = firstVertex; + // Slab 1: dir {0, 1, 0} + proj = firstVertex.y; + minProjections[1] = maxProjections[1] = proj; + minVertices[1] = firstVertex; maxVertices[1] = firstVertex; + // Slab 2: dir {0, 0, 1} + proj = firstVertex.z; + minProjections[2] = maxProjections[2] = proj; + minVertices[2] = firstVertex; maxVertices[2] = firstVertex; + // Slab 3: dir {1, 1, 1} + proj = firstVertex.x + firstVertex.y + firstVertex.z; + minProjections[3] = maxProjections[3] = proj; + minVertices[3] = firstVertex; maxVertices[3] = firstVertex; + // Slab 4: dir {1, 1, -1} + proj = firstVertex.x + firstVertex.y - firstVertex.z; + minProjections[4] = maxProjections[4] = proj; + minVertices[4] = firstVertex; maxVertices[4] = firstVertex; + // Slab 5: dir {1, -1, 1} + proj = firstVertex.x - firstVertex.y + firstVertex.z; + minProjections[5] = maxProjections[5] = proj; + minVertices[5] = firstVertex; maxVertices[5] = firstVertex; + // Slab 6: dir {1, -1, -1} + proj = firstVertex.x - firstVertex.y - firstVertex.z; + minProjections[6] = maxProjections[6] = proj; + minVertices[6] = firstVertex; maxVertices[6] = firstVertex; + + for (size_t vertex_i = 1; vertex_i < vertices.size; vertex_i++) + { + const auto vertex = vertices.fetch(vertex_i); + // Slab 0: dir {1, 0, 0} + proj = vertices.fetch(vertex_i).x; + if (proj < minProjections[0]) { minProjections[0] = proj; minVertices[0] = vertices.fetch(vertex_i); } + if (proj > maxProjections[0]) { maxProjections[0] = proj; maxVertices[0] = vertices.fetch(vertex_i); } + // Slab 1: dir {0, 1, 0} + proj = vertices.fetch(vertex_i).y; + if (proj < minProjections[1]) { minProjections[1] = proj; minVertices[1] = vertices.fetch(vertex_i); } + if (proj > maxProjections[1]) { maxProjections[1] = proj; maxVertices[1] = vertices.fetch(vertex_i); } + // Slab 2: dir {0, 0, 1} + proj = vertices.fetch(vertex_i).z; + if (proj < minProjections[2]) { minProjections[2] = proj; minVertices[2] = vertices.fetch(vertex_i); } + if (proj > maxProjections[2]) { maxProjections[2] = proj; maxVertices[2] = vertices.fetch(vertex_i); } + // Slab 3: dir {1, 1, 1} + proj = vertices.fetch(vertex_i).x + vertices.fetch(vertex_i).y + vertices.fetch(vertex_i).z; + if (proj < minProjections[3]) { minProjections[3] = proj; minVertices[3] = vertices.fetch(vertex_i); } + if (proj > maxProjections[3]) { maxProjections[3] = proj; maxVertices[3] = vertices.fetch(vertex_i); } + // Slab 4: dir {1, 1, -1} + proj = vertices.fetch(vertex_i).x + vertices.fetch(vertex_i).y - vertices.fetch(vertex_i).z; + if (proj < minProjections[4]) { minProjections[4] = proj; minVertices[4] = vertices.fetch(vertex_i); } + if (proj > maxProjections[4]) { maxProjections[4] = proj; maxVertices[4] = vertices.fetch(vertex_i); } + // Slab 5: dir {1, -1, 1} + proj = vertices.fetch(vertex_i).x - vertices.fetch(vertex_i).y + vertices.fetch(vertex_i).z; + if (proj < minProjections[5]) { minProjections[5] = proj; minVertices[5] = vertices.fetch(vertex_i); } + if (proj > maxProjections[5]) { maxProjections[5] = proj; maxVertices[5] = vertices.fetch(vertex_i); } + // Slab 6: dir {1, -1, -1} + proj = vertices.fetch(vertex_i).x - vertices.fetch(vertex_i).y - vertices.fetch(vertex_i).z; + if (proj < minProjections[6]) { minProjections[6] = proj; minVertices[6] = vertices.fetch(vertex_i); } + if (proj > maxProjections[6]) { maxProjections[6] = proj; maxVertices[6] = vertices.fetch(vertex_i); } + } + + return result; + }; + + + static auto findFurthestPointPair = [](const ExtremalVertices& extremalVertices) -> std::pair + { + int indexFurthestPair = 0; + auto maxSqDist = hlsl::dot(extremalVertices.maxPtr()[0], extremalVertices.minPtr()[0]); + for (int k = 1; k < SAMPLE_DIR_COUNT; k++) + { + const auto sqDist = hlsl::dot(extremalVertices.maxPtr()[k], extremalVertices.minPtr()[k]); + if (sqDist > maxSqDist) { maxSqDist = sqDist; indexFurthestPair = k; } + } + return { + extremalVertices.minPtr()[indexFurthestPair], + extremalVertices.maxPtr()[indexFurthestPair] + }; + }; + + static auto sqDistPointInfiniteEdge = [](const hlsl::float32_t3& q, const hlsl::float32_t3& p0, const hlsl::float32_t3& v) -> hlsl::float32_t + { + const auto u0 = q - p0; + const auto t = dot(v, u0); + const auto sqLen_v = hlsl::dot(v, v); + return hlsl::dot(u0, u0) - (t * t) / sqLen_v; + }; + + static auto findFurthestPointFromInfiniteEdge = [](const hlsl::float32_t3& p0, const hlsl::float32_t3& e0, const VertexCollection& vertices) + { + auto maxSqDist = sqDistPointInfiniteEdge(vertices[0], p0, e0); + int maxIndex = 0; + for (size_t i = 1; i < vertices.size; i++) + { + const auto sqDist = sqDistPointInfiniteEdge(vertices[i], p0, e0); + if (sqDist > maxSqDist) + { maxSqDist = sqDist; + maxIndex = i; + } + } + + struct Result + { + hlsl::float32_t3 point; + hlsl::float32_t sqDist; + }; + return Result{ + vertices[maxIndex], + maxSqDist + }; + }; + + static auto findExtremalProjs_OneDir = [](const hlsl::float32_t3& normal, const VertexCollection& vertices) + { + const auto firstProj = hlsl::dot(vertices[0], normal); + auto tMinProj = firstProj, tMaxProj = firstProj; + + for (int i = 1; i < vertices.size; i++) + { + const auto proj = hlsl::dot(vertices[i], normal); + if (proj < tMinProj) { tMinProj = proj; } + if (proj > tMaxProj) { tMaxProj = proj; } + } + + struct Result + { + hlsl::float32_t minProj; + hlsl::float32_t maxProj; + }; + return Result{ tMinProj, tMaxProj }; + }; + + static auto findExtremalPoints_OneDir = [](const hlsl::float32_t3& normal, const VertexCollection& vertices) + { + const auto firstProj = dot(vertices[0], normal); + + auto tMinProj = firstProj, tMaxProj = firstProj; + auto tMinVert = vertices[0], tMaxVert = vertices[0]; + + for (int i = 1; i < vertices.size; i++) + { + const auto proj = hlsl::dot(vertices[i], normal); + if (proj < tMinProj) { tMinProj = proj; tMinVert = vertices[i]; } + if (proj > tMaxProj) { tMaxProj = proj; tMaxVert = vertices[i]; } + } + + struct Result + { + hlsl::float32_t minProj; + hlsl::float32_t maxProj; + hlsl::float32_t3 minVert; + hlsl::float32_t3 maxVert; + }; + return Result{ tMinProj, tMaxProj, tMinVert, tMaxVert }; + }; + + static auto findUpperLowerTetraPoints = []( + const hlsl::float32_t3& n, + const VertexCollection& vertices, + const hlsl::float32_t3& p0) + { + const auto eps = 0.000001f; + const auto extremalPoints = findExtremalPoints_OneDir(n, vertices); + const auto triProj = hlsl::dot(p0, n); + + const auto maxVert = extremalPoints.maxProj - eps > triProj ? std::optional(extremalPoints.maxVert) : std::nullopt; + const auto minVert = extremalPoints.minProj + eps < triProj ? std::optional(extremalPoints.minVert) : std::nullopt; + + struct Result + { + std::optional minVert; + std::optional maxVert; + }; + return Result{ + minVert, + maxVert + }; + }; + + static auto findBestObbAxesFromTriangleNormalAndEdgeVectors = []( + const VertexCollection& vertices, + const hlsl::float32_t3 normal, + const std::array edges, + Axes& bestAxes, + hlsl::float32_t& bestVal) + { + hlsl::float32_t3 dmax, dmin, dlen; + + // The operands are assumed to be orthogonal and unit normals + const auto yExtremeProjs = findExtremalProjs_OneDir(normal, vertices); + dmin.y = yExtremeProjs.minProj; + dmax.y = yExtremeProjs.maxProj; + dlen.y = dmax.y - dmin.y; + + for (const auto& edge : edges) + { + const auto binormal = hlsl::cross(edge, normal); + + const auto xExtremeProjs = findExtremalProjs_OneDir(edge, vertices); + dmin.x = xExtremeProjs.minProj; + dmax.x = xExtremeProjs.maxProj; + dlen.x = dmax.x - dmin.x; + + const auto zExtremeProjs = findExtremalProjs_OneDir(binormal, vertices); + dmin.z = zExtremeProjs.minProj; + dmax.z = zExtremeProjs.maxProj; + dlen.z = dmax.z - dmin.z; + + const auto quality = getQualityValue(dlen); + if (quality < bestVal) + { + bestVal = quality; + bestAxes = { + edge, + normal, + binormal + }; + } + } + + }; + + + static auto findBaseTriangle = [](const ExtremalVertices& extremalVertices, const VertexCollection& vertices)-> LargeBaseTriangle + { + hlsl::float32_t eps = 0.000001f; + + std::array baseTriangleVertices; + Edges edges; + + // Find the furthest point pair among the selected min and max point pairs + std::tie(baseTriangleVertices[0], baseTriangleVertices[1]) = findFurthestPointPair(extremalVertices); + + // Degenerate case 1: + // If the found furthest points are located very close, return OBB aligned with the initial AABB + if (hlsl::dot(baseTriangleVertices[0], baseTriangleVertices[1]) < eps) + { + return { + .vertices = baseTriangleVertices, + .flag = LargeBaseTriangle::SECOND_POINT_CLOSE + }; + } + + // Compute edge vector of the line segment p0, p1 + edges[0] = hlsl::normalize(baseTriangleVertices[0] - baseTriangleVertices[1]); + + // Find a third point furthest away from line given by p0, e0 to define the large base triangle + const auto furthestPointRes = findFurthestPointFromInfiniteEdge(vertices[0], edges[0], vertices); + + // Degenerate case 2: + // If the third point is located very close to the line, return an OBB aligned with the line + if (furthestPointRes.sqDist < eps) + { + return { + .vertices = baseTriangleVertices, + .edges = edges, + .flag = LargeBaseTriangle::THIRD_POINT_CLOSE + }; + } + + // Compute the two remaining edge vectors and the normal vector of the base triangle + edges[1] = hlsl::normalize(baseTriangleVertices[1] - baseTriangleVertices[2]); + edges[2] = hlsl::normalize(baseTriangleVertices[2] - baseTriangleVertices[0]); + const auto normal = hlsl::normalize(hlsl::cross(edges[1], edges[0])); + + return { + .normal = normal, + .vertices = baseTriangleVertices, + .edges = edges, + .flag = LargeBaseTriangle::NORMAL + }; + }; + + auto findImprovedObbAxesFromUpperAndLowerTetrasOfBaseTriangle = [](const VertexCollection& vertices, + const LargeBaseTriangle& baseTriangle, + Axes& bestAxes, hlsl::float32_t& bestVal) + { + hlsl::float32_t3 q0, q1; // Top and bottom vertices for lower and upper tetra constructions + hlsl::float32_t3 f0, f1, f2; // Edge vectors towards q0; + hlsl::float32_t3 g0, g1, g2; // Edge vectors towards q1; + hlsl::float32_t3 n0, n1, n2; // Unit normals of top tetra tris + hlsl::float32_t3 m0, m1, m2; // Unit normals of bottom tetra tris + + // Find furthest points above and below the plane of the base triangle for tetra constructions + // For each found valid point, search for the best OBB axes based on the 3 arising triangles + const auto upperLowerTetraVertices = findUpperLowerTetraPoints(baseTriangle.normal, vertices, baseTriangle.vertices[0]); + if (upperLowerTetraVertices.minVert) + { + f0 = normalize(q0 - baseTriangle.vertices[0]); + f1 = normalize(q0 - baseTriangle.vertices[1]); + f2 = normalize(q0 - baseTriangle.vertices[2]); + n0 = normalize(cross(f1, baseTriangle.edges[0])); + n1 = normalize(cross(f2, baseTriangle.edges[1])); + n2 = normalize(cross(f0, baseTriangle.edges[2])); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n0, { baseTriangle.edges[0], f1, f0 }, bestAxes, bestVal); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n1, { baseTriangle.edges[1], f2, f1 }, bestAxes, bestVal); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n2, { baseTriangle.edges[2], f0, f2 }, bestAxes, bestVal); + } + if (upperLowerTetraVertices.maxVert) + { + g0 = normalize(q1 - baseTriangle.vertices[0]); + g1 = normalize(q1 - baseTriangle.vertices[1]); + g2 = normalize(q1 - baseTriangle.vertices[2]); + m0 = normalize(cross(g1, baseTriangle.edges[0])); + m1 = normalize(cross(g2, baseTriangle.edges[1])); + m2 = normalize(cross(g0, baseTriangle.edges[2])); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, m0, { baseTriangle.edges[0], g1, g0 }, bestAxes, bestVal); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, m1, { baseTriangle.edges[1], g2, g1 }, bestAxes, bestVal); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, m2, { baseTriangle.edges[2], g0, g2 }, bestAxes, bestVal); + } + }; + + static auto buildObbFromAxesAndLocalMinMax = []( + const Axes& axes, + const hlsl::float32_t3& localMin, + const hlsl::float32_t3& localMax) -> hlsl::shapes::OBB<3, hlsl::float32_t> + { + const auto localMid = 0.5f * (localMin + localMax); + auto globalMid = axes[0] * localMid.x; + globalMid += axes[1] * localMid.y; + globalMid += axes[2] * localMid.z; + return { + .mid = globalMid, + .axes = axes, + .ext = 0.5f * (localMax - localMin) + }; + }; + + static auto computeObb = [](const Axes& axes, const VertexCollection& vertices) + { + const auto extremalX = findExtremalProjs_OneDir(axes[0], vertices); + const auto extremalY = findExtremalProjs_OneDir(axes[1], vertices); + const auto extremalZ = findExtremalProjs_OneDir(axes[2], vertices); + const auto localMin = hlsl::float32_t3{ extremalX.minProj, extremalY.minProj, extremalZ.minProj }; + const auto localMax = hlsl::float32_t3{ extremalX.maxProj, extremalY.maxProj, extremalZ.maxProj }; + return buildObbFromAxesAndLocalMinMax(axes, localMin, localMax); + }; + + static auto computeLineAlignedObb = [](const hlsl::float32_t3& u, const VertexCollection& vertices) + { + // Given u, build any orthonormal base u, v, w + + // Make sure r is not equal to u + auto r = u; + if (fabs(u.x) > fabs(u.y) && fabs(u.x) > fabs(u.z)) { r.x = 0; } + else if (fabs(u.y) > fabs(u.z)) { r.y = 0; } + else { r.z = 0; } + + const auto sqLen = hlsl::dot(r, r); + if (sqLen < FLT_EPSILON) { r.x = r.y = r.z = 1; } + + const auto v = normalize(cross(u, r)); + const auto w = normalize(cross(u, v)); + return computeObb({ u, v, w }, vertices); + }; + + const auto extremals = findExtremals_7FixedDirs(vertices); + + const auto* minProj = extremals.projections.minPtr(); + const auto* maxProj = extremals.projections.maxPtr(); + const auto* minVert = extremals.vertices.minPtr(); + const auto* maxVert = extremals.vertices.maxPtr(); + + + // Determine which points to use in the iterations below + const auto selectedVertices = [&] + { + if (vertices.size < SAMPLE_COUNT) { return vertices; } + return VertexCollection::fromSpan(extremals.vertices.values); + }(); + + // Compute size of AABB (max and min projections of vertices are already computed as slabs 0-2) + auto alMid = hlsl::float32_t3((minProj[0] + maxProj[0]) * 0.5f, (minProj[1] + maxProj[1]) * 0.5f, (minProj[2] + maxProj[2]) * 0.5f); + auto alLen = hlsl::float32_t3(maxProj[0] - minProj[0], maxProj[1] - minProj[1], maxProj[2] - minProj[2]); + auto alVal = getQualityValue(alLen); + + const auto baseTriangle = findBaseTriangle(extremals.vertices, vertices); + if (baseTriangle.flag == LargeBaseTriangle::SECOND_POINT_CLOSE) + return hlsl::shapes::OBB<>::createAxisAligned(alMid, alLen); + if (baseTriangle.flag == LargeBaseTriangle::THIRD_POINT_CLOSE) + return computeLineAlignedObb(baseTriangle.edges[0], vertices); + + + Axes bestAxes = { + hlsl::float32_t3{1.f, 0.f, 0.f}, + {0.f, 1.f, 0.f}, + {0.f, 0.f, 1.f}, + }; + auto bestVal = alVal; + // Find best OBB axes based on the base triangle + findBestObbAxesFromTriangleNormalAndEdgeVectors(selectedVertices, baseTriangle.normal, baseTriangle.edges, bestAxes, bestVal); + + // Find improved OBB axes based on constructed di-tetrahedral shape raised from base triangle + findImprovedObbAxesFromUpperAndLowerTetrasOfBaseTriangle(selectedVertices, baseTriangle, bestAxes, bestVal); + + const auto obb = computeObb(bestAxes, vertices); + + // Check if the OBB extent is still smaller than the intial AABB + if (getQualityValue(2.f * obb.ext) < alVal) + { + return hlsl::shapes::OBB<>{ + .mid = alMid, + .axes = bestAxes, + .ext = alLen / 2.f, + }; + } + return hlsl::shapes::OBB<>::createAxisAligned(alMid, alLen); +} + #if 0 //! Flips the direction of surfaces. Changes backfacing triangles to frontfacing //! triangles and vice versa. From 12164ac7a66bb2033ec2ed64cf74abb9328eed83 Mon Sep 17 00:00:00 2001 From: kevyuu Date: Sat, 6 Sep 2025 10:55:52 +0700 Subject: [PATCH 2/9] Implement getTransformOBB for DrawAABB extension --- include/nbl/ext/DebugDraw/CDrawAABB.h | 2 ++ src/nbl/ext/DebugDraw/CDrawAABB.cpp | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/include/nbl/ext/DebugDraw/CDrawAABB.h b/include/nbl/ext/DebugDraw/CDrawAABB.h index a8e3205f22..5129e62419 100644 --- a/include/nbl/ext/DebugDraw/CDrawAABB.h +++ b/include/nbl/ext/DebugDraw/CDrawAABB.h @@ -71,6 +71,8 @@ class DrawAABB final : public core::IReferenceCounted static hlsl::float32_t4x4 getTransformFromAABB(const hlsl::shapes::AABB<3, float>& aabb); + static hlsl::float32_t4x4 getTransformFromOBB(const hlsl::shapes::OBB<3, float>& aabb); + protected: DrawAABB(SCreationParameters&& _params, core::smart_refctd_ptr singlePipeline, core::smart_refctd_ptr batchPipeline, core::smart_refctd_ptr indicesBuffer); diff --git a/src/nbl/ext/DebugDraw/CDrawAABB.cpp b/src/nbl/ext/DebugDraw/CDrawAABB.cpp index f3f33d2733..4f72b5a5a2 100644 --- a/src/nbl/ext/DebugDraw/CDrawAABB.cpp +++ b/src/nbl/ext/DebugDraw/CDrawAABB.cpp @@ -422,4 +422,25 @@ hlsl::float32_t4x4 DrawAABB::getTransformFromAABB(const hlsl::shapes::AABB<3, fl return transform; } +hlsl::float32_t4x4 DrawAABB::getTransformFromOBB(const hlsl::shapes::OBB<3, float>& obb) +{ + const auto obbScale = obb.ext * 2.0f; + const auto obbMat = hlsl::transpose(float32_t4x4{ + hlsl::float32_t4(obb.axes[0] * obbScale.x, 0), + hlsl::float32_t4(obb.axes[1] * obbScale.y, 0), + hlsl::float32_t4(obb.axes[2] * obbScale.z, 0), + hlsl::float32_t4(obb.mid, 1) + }); + + const auto translateUnitCube = float32_t4x4{ + hlsl::float32_t4(1, 0, 0, -0.5f), + hlsl::float32_t4(0, 1, 0, -0.5f), + hlsl::float32_t4(0, 0, 1, -0.5f), + hlsl::float32_t4(0, 0, 0, 1), + }; + + const auto transform = mul(obbMat, translateUnitCube); + return transform; +} + } From 5a30e8078d4fb6bbd8b53a6aad4e5b4a5fe1f8f1 Mon Sep 17 00:00:00 2001 From: kevyuu Date: Sat, 6 Sep 2025 17:20:04 +0700 Subject: [PATCH 3/9] Move OBB Calculation into its own file --- .../asset/utils/CPolygonGeometryManipulator.h | 2 +- src/nbl/CMakeLists.txt | 1 + .../utils/CPolygonGeometryManipulator.cpp | 488 +----------------- 3 files changed, 5 insertions(+), 486 deletions(-) diff --git a/include/nbl/asset/utils/CPolygonGeometryManipulator.h b/include/nbl/asset/utils/CPolygonGeometryManipulator.h index 067ac2b0b2..cb62cacb76 100644 --- a/include/nbl/asset/utils/CPolygonGeometryManipulator.h +++ b/include/nbl/asset/utils/CPolygonGeometryManipulator.h @@ -16,7 +16,7 @@ namespace nbl::asset { //! An interface for easy manipulation of polygon geometries. -class CPolygonGeometryManipulator +class NBL_API2 CPolygonGeometryManipulator { public: static inline void recomputeContentHashes(ICPUPolygonGeometry* geo) diff --git a/src/nbl/CMakeLists.txt b/src/nbl/CMakeLists.txt index 935beffe2c..b0c27fafb0 100755 --- a/src/nbl/CMakeLists.txt +++ b/src/nbl/CMakeLists.txt @@ -180,6 +180,7 @@ set(NBL_ASSET_SOURCES # Meshes asset/utils/CForsythVertexCacheOptimizer.cpp asset/utils/CSmoothNormalGenerator.cpp + asset/utils/COBBGenerator.cpp asset/utils/CGeometryCreator.cpp asset/utils/CPolygonGeometryManipulator.cpp asset/utils/COverdrawPolygonGeometryOptimizer.cpp diff --git a/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp b/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp index be9d6dec3c..7b2bdb3963 100644 --- a/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp +++ b/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp @@ -16,500 +16,18 @@ #include "nbl/asset/utils/CSmoothNormalGenerator.h" #include "nbl/asset/utils/CForsythVertexCacheOptimizer.h" #include "nbl/asset/utils/COverdrawPolygonGeometryOptimizer.h" +#include "nbl/asset/utils/COBBGenerator.h" namespace nbl::asset { -template -struct Extremals -{ - std::array values; - - T* minPtr() - { - return values.data(); - } - - const T* minPtr() const - { - return values.data(); - } - - T* maxPtr() - { - return values.data() + CountV; - } - - const T* maxPtr() const - { - return values.data() + CountV; - } -}; - - hlsl::shapes::OBB<> CPolygonGeometryManipulator::calculateOBB(const VertexCollection& vertices) { - constexpr size_t SAMPLE_DIR_COUNT = 7; // Number of sample directions - constexpr size_t SAMPLE_COUNT = SAMPLE_DIR_COUNT * 2; - - if (vertices.size <= 0) - { - return hlsl::shapes::OBB<>::createAxisAligned({}, {}); - } - - static auto getQualityValue = [](hlsl::float32_t3 len) -> hlsl::float32_t - { - return len.x * len.y + len.x * len.z + len.y * len.z; //half box area - }; - - using ExtremalVertices = Extremals; - using ExtremalProjections = Extremals; - using Axes = std::array; - using Edges = std::array; - - struct ExtremalSamples - { - ExtremalVertices vertices; - ExtremalProjections projections; - }; - - struct LargeBaseTriangle - { - hlsl::float32_t3 normal = {}; - Axes vertices = {}; - Edges edges = {}; - enum Flag - { - NORMAL, - SECOND_POINT_CLOSE, - THIRD_POINT_CLOSE - } flag; - }; - - static auto findExtremals_7FixedDirs = [](const VertexCollection& vertices)-> ExtremalSamples - { - ExtremalSamples result; - hlsl::float32_t proj; - - const auto firstVertex = vertices.fetch(0); - - auto* minProjections = result.projections.minPtr(); - auto* maxProjections = result.projections.maxPtr(); - - auto* minVertices = result.vertices.minPtr(); - auto* maxVertices = result.vertices.maxPtr(); - - // Slab 0: dir {1, 0, 0} - proj = firstVertex.x; - minProjections[0] = minProjections[0] = proj; - minVertices[0] = firstVertex; maxVertices[0] = firstVertex; - // Slab 1: dir {0, 1, 0} - proj = firstVertex.y; - minProjections[1] = maxProjections[1] = proj; - minVertices[1] = firstVertex; maxVertices[1] = firstVertex; - // Slab 2: dir {0, 0, 1} - proj = firstVertex.z; - minProjections[2] = maxProjections[2] = proj; - minVertices[2] = firstVertex; maxVertices[2] = firstVertex; - // Slab 3: dir {1, 1, 1} - proj = firstVertex.x + firstVertex.y + firstVertex.z; - minProjections[3] = maxProjections[3] = proj; - minVertices[3] = firstVertex; maxVertices[3] = firstVertex; - // Slab 4: dir {1, 1, -1} - proj = firstVertex.x + firstVertex.y - firstVertex.z; - minProjections[4] = maxProjections[4] = proj; - minVertices[4] = firstVertex; maxVertices[4] = firstVertex; - // Slab 5: dir {1, -1, 1} - proj = firstVertex.x - firstVertex.y + firstVertex.z; - minProjections[5] = maxProjections[5] = proj; - minVertices[5] = firstVertex; maxVertices[5] = firstVertex; - // Slab 6: dir {1, -1, -1} - proj = firstVertex.x - firstVertex.y - firstVertex.z; - minProjections[6] = maxProjections[6] = proj; - minVertices[6] = firstVertex; maxVertices[6] = firstVertex; - - for (size_t vertex_i = 1; vertex_i < vertices.size; vertex_i++) - { - const auto vertex = vertices.fetch(vertex_i); - // Slab 0: dir {1, 0, 0} - proj = vertices.fetch(vertex_i).x; - if (proj < minProjections[0]) { minProjections[0] = proj; minVertices[0] = vertices.fetch(vertex_i); } - if (proj > maxProjections[0]) { maxProjections[0] = proj; maxVertices[0] = vertices.fetch(vertex_i); } - // Slab 1: dir {0, 1, 0} - proj = vertices.fetch(vertex_i).y; - if (proj < minProjections[1]) { minProjections[1] = proj; minVertices[1] = vertices.fetch(vertex_i); } - if (proj > maxProjections[1]) { maxProjections[1] = proj; maxVertices[1] = vertices.fetch(vertex_i); } - // Slab 2: dir {0, 0, 1} - proj = vertices.fetch(vertex_i).z; - if (proj < minProjections[2]) { minProjections[2] = proj; minVertices[2] = vertices.fetch(vertex_i); } - if (proj > maxProjections[2]) { maxProjections[2] = proj; maxVertices[2] = vertices.fetch(vertex_i); } - // Slab 3: dir {1, 1, 1} - proj = vertices.fetch(vertex_i).x + vertices.fetch(vertex_i).y + vertices.fetch(vertex_i).z; - if (proj < minProjections[3]) { minProjections[3] = proj; minVertices[3] = vertices.fetch(vertex_i); } - if (proj > maxProjections[3]) { maxProjections[3] = proj; maxVertices[3] = vertices.fetch(vertex_i); } - // Slab 4: dir {1, 1, -1} - proj = vertices.fetch(vertex_i).x + vertices.fetch(vertex_i).y - vertices.fetch(vertex_i).z; - if (proj < minProjections[4]) { minProjections[4] = proj; minVertices[4] = vertices.fetch(vertex_i); } - if (proj > maxProjections[4]) { maxProjections[4] = proj; maxVertices[4] = vertices.fetch(vertex_i); } - // Slab 5: dir {1, -1, 1} - proj = vertices.fetch(vertex_i).x - vertices.fetch(vertex_i).y + vertices.fetch(vertex_i).z; - if (proj < minProjections[5]) { minProjections[5] = proj; minVertices[5] = vertices.fetch(vertex_i); } - if (proj > maxProjections[5]) { maxProjections[5] = proj; maxVertices[5] = vertices.fetch(vertex_i); } - // Slab 6: dir {1, -1, -1} - proj = vertices.fetch(vertex_i).x - vertices.fetch(vertex_i).y - vertices.fetch(vertex_i).z; - if (proj < minProjections[6]) { minProjections[6] = proj; minVertices[6] = vertices.fetch(vertex_i); } - if (proj > maxProjections[6]) { maxProjections[6] = proj; maxVertices[6] = vertices.fetch(vertex_i); } - } - - return result; - }; - - - static auto findFurthestPointPair = [](const ExtremalVertices& extremalVertices) -> std::pair - { - int indexFurthestPair = 0; - auto maxSqDist = hlsl::dot(extremalVertices.maxPtr()[0], extremalVertices.minPtr()[0]); - for (int k = 1; k < SAMPLE_DIR_COUNT; k++) - { - const auto sqDist = hlsl::dot(extremalVertices.maxPtr()[k], extremalVertices.minPtr()[k]); - if (sqDist > maxSqDist) { maxSqDist = sqDist; indexFurthestPair = k; } - } - return { - extremalVertices.minPtr()[indexFurthestPair], - extremalVertices.maxPtr()[indexFurthestPair] - }; - }; - - static auto sqDistPointInfiniteEdge = [](const hlsl::float32_t3& q, const hlsl::float32_t3& p0, const hlsl::float32_t3& v) -> hlsl::float32_t - { - const auto u0 = q - p0; - const auto t = dot(v, u0); - const auto sqLen_v = hlsl::dot(v, v); - return hlsl::dot(u0, u0) - (t * t) / sqLen_v; - }; - - static auto findFurthestPointFromInfiniteEdge = [](const hlsl::float32_t3& p0, const hlsl::float32_t3& e0, const VertexCollection& vertices) - { - auto maxSqDist = sqDistPointInfiniteEdge(vertices[0], p0, e0); - int maxIndex = 0; - for (size_t i = 1; i < vertices.size; i++) - { - const auto sqDist = sqDistPointInfiniteEdge(vertices[i], p0, e0); - if (sqDist > maxSqDist) - { maxSqDist = sqDist; - maxIndex = i; - } - } - - struct Result - { - hlsl::float32_t3 point; - hlsl::float32_t sqDist; - }; - return Result{ - vertices[maxIndex], - maxSqDist - }; - }; - - static auto findExtremalProjs_OneDir = [](const hlsl::float32_t3& normal, const VertexCollection& vertices) - { - const auto firstProj = hlsl::dot(vertices[0], normal); - auto tMinProj = firstProj, tMaxProj = firstProj; - - for (int i = 1; i < vertices.size; i++) - { - const auto proj = hlsl::dot(vertices[i], normal); - if (proj < tMinProj) { tMinProj = proj; } - if (proj > tMaxProj) { tMaxProj = proj; } - } - - struct Result - { - hlsl::float32_t minProj; - hlsl::float32_t maxProj; - }; - return Result{ tMinProj, tMaxProj }; - }; - - static auto findExtremalPoints_OneDir = [](const hlsl::float32_t3& normal, const VertexCollection& vertices) - { - const auto firstProj = dot(vertices[0], normal); - - auto tMinProj = firstProj, tMaxProj = firstProj; - auto tMinVert = vertices[0], tMaxVert = vertices[0]; - - for (int i = 1; i < vertices.size; i++) - { - const auto proj = hlsl::dot(vertices[i], normal); - if (proj < tMinProj) { tMinProj = proj; tMinVert = vertices[i]; } - if (proj > tMaxProj) { tMaxProj = proj; tMaxVert = vertices[i]; } - } - - struct Result - { - hlsl::float32_t minProj; - hlsl::float32_t maxProj; - hlsl::float32_t3 minVert; - hlsl::float32_t3 maxVert; - }; - return Result{ tMinProj, tMaxProj, tMinVert, tMaxVert }; - }; - - static auto findUpperLowerTetraPoints = []( - const hlsl::float32_t3& n, - const VertexCollection& vertices, - const hlsl::float32_t3& p0) - { - const auto eps = 0.000001f; - const auto extremalPoints = findExtremalPoints_OneDir(n, vertices); - const auto triProj = hlsl::dot(p0, n); - - const auto maxVert = extremalPoints.maxProj - eps > triProj ? std::optional(extremalPoints.maxVert) : std::nullopt; - const auto minVert = extremalPoints.minProj + eps < triProj ? std::optional(extremalPoints.minVert) : std::nullopt; - - struct Result - { - std::optional minVert; - std::optional maxVert; - }; - return Result{ - minVert, - maxVert - }; - }; - - static auto findBestObbAxesFromTriangleNormalAndEdgeVectors = []( - const VertexCollection& vertices, - const hlsl::float32_t3 normal, - const std::array edges, - Axes& bestAxes, - hlsl::float32_t& bestVal) - { - hlsl::float32_t3 dmax, dmin, dlen; - - // The operands are assumed to be orthogonal and unit normals - const auto yExtremeProjs = findExtremalProjs_OneDir(normal, vertices); - dmin.y = yExtremeProjs.minProj; - dmax.y = yExtremeProjs.maxProj; - dlen.y = dmax.y - dmin.y; - - for (const auto& edge : edges) - { - const auto binormal = hlsl::cross(edge, normal); - - const auto xExtremeProjs = findExtremalProjs_OneDir(edge, vertices); - dmin.x = xExtremeProjs.minProj; - dmax.x = xExtremeProjs.maxProj; - dlen.x = dmax.x - dmin.x; - - const auto zExtremeProjs = findExtremalProjs_OneDir(binormal, vertices); - dmin.z = zExtremeProjs.minProj; - dmax.z = zExtremeProjs.maxProj; - dlen.z = dmax.z - dmin.z; - - const auto quality = getQualityValue(dlen); - if (quality < bestVal) - { - bestVal = quality; - bestAxes = { - edge, - normal, - binormal - }; - } - } - - }; - - - static auto findBaseTriangle = [](const ExtremalVertices& extremalVertices, const VertexCollection& vertices)-> LargeBaseTriangle - { - hlsl::float32_t eps = 0.000001f; - - std::array baseTriangleVertices; - Edges edges; - - // Find the furthest point pair among the selected min and max point pairs - std::tie(baseTriangleVertices[0], baseTriangleVertices[1]) = findFurthestPointPair(extremalVertices); - - // Degenerate case 1: - // If the found furthest points are located very close, return OBB aligned with the initial AABB - if (hlsl::dot(baseTriangleVertices[0], baseTriangleVertices[1]) < eps) - { - return { - .vertices = baseTriangleVertices, - .flag = LargeBaseTriangle::SECOND_POINT_CLOSE - }; - } - - // Compute edge vector of the line segment p0, p1 - edges[0] = hlsl::normalize(baseTriangleVertices[0] - baseTriangleVertices[1]); - - // Find a third point furthest away from line given by p0, e0 to define the large base triangle - const auto furthestPointRes = findFurthestPointFromInfiniteEdge(vertices[0], edges[0], vertices); - - // Degenerate case 2: - // If the third point is located very close to the line, return an OBB aligned with the line - if (furthestPointRes.sqDist < eps) - { - return { - .vertices = baseTriangleVertices, - .edges = edges, - .flag = LargeBaseTriangle::THIRD_POINT_CLOSE - }; - } - - // Compute the two remaining edge vectors and the normal vector of the base triangle - edges[1] = hlsl::normalize(baseTriangleVertices[1] - baseTriangleVertices[2]); - edges[2] = hlsl::normalize(baseTriangleVertices[2] - baseTriangleVertices[0]); - const auto normal = hlsl::normalize(hlsl::cross(edges[1], edges[0])); - - return { - .normal = normal, - .vertices = baseTriangleVertices, - .edges = edges, - .flag = LargeBaseTriangle::NORMAL - }; - }; - - auto findImprovedObbAxesFromUpperAndLowerTetrasOfBaseTriangle = [](const VertexCollection& vertices, - const LargeBaseTriangle& baseTriangle, - Axes& bestAxes, hlsl::float32_t& bestVal) - { - hlsl::float32_t3 q0, q1; // Top and bottom vertices for lower and upper tetra constructions - hlsl::float32_t3 f0, f1, f2; // Edge vectors towards q0; - hlsl::float32_t3 g0, g1, g2; // Edge vectors towards q1; - hlsl::float32_t3 n0, n1, n2; // Unit normals of top tetra tris - hlsl::float32_t3 m0, m1, m2; // Unit normals of bottom tetra tris - - // Find furthest points above and below the plane of the base triangle for tetra constructions - // For each found valid point, search for the best OBB axes based on the 3 arising triangles - const auto upperLowerTetraVertices = findUpperLowerTetraPoints(baseTriangle.normal, vertices, baseTriangle.vertices[0]); - if (upperLowerTetraVertices.minVert) - { - f0 = normalize(q0 - baseTriangle.vertices[0]); - f1 = normalize(q0 - baseTriangle.vertices[1]); - f2 = normalize(q0 - baseTriangle.vertices[2]); - n0 = normalize(cross(f1, baseTriangle.edges[0])); - n1 = normalize(cross(f2, baseTriangle.edges[1])); - n2 = normalize(cross(f0, baseTriangle.edges[2])); - findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n0, { baseTriangle.edges[0], f1, f0 }, bestAxes, bestVal); - findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n1, { baseTriangle.edges[1], f2, f1 }, bestAxes, bestVal); - findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n2, { baseTriangle.edges[2], f0, f2 }, bestAxes, bestVal); - } - if (upperLowerTetraVertices.maxVert) - { - g0 = normalize(q1 - baseTriangle.vertices[0]); - g1 = normalize(q1 - baseTriangle.vertices[1]); - g2 = normalize(q1 - baseTriangle.vertices[2]); - m0 = normalize(cross(g1, baseTriangle.edges[0])); - m1 = normalize(cross(g2, baseTriangle.edges[1])); - m2 = normalize(cross(g0, baseTriangle.edges[2])); - findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, m0, { baseTriangle.edges[0], g1, g0 }, bestAxes, bestVal); - findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, m1, { baseTriangle.edges[1], g2, g1 }, bestAxes, bestVal); - findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, m2, { baseTriangle.edges[2], g0, g2 }, bestAxes, bestVal); - } - }; - - static auto buildObbFromAxesAndLocalMinMax = []( - const Axes& axes, - const hlsl::float32_t3& localMin, - const hlsl::float32_t3& localMax) -> hlsl::shapes::OBB<3, hlsl::float32_t> - { - const auto localMid = 0.5f * (localMin + localMax); - auto globalMid = axes[0] * localMid.x; - globalMid += axes[1] * localMid.y; - globalMid += axes[2] * localMid.z; - return { - .mid = globalMid, - .axes = axes, - .ext = 0.5f * (localMax - localMin) - }; - }; - - static auto computeObb = [](const Axes& axes, const VertexCollection& vertices) - { - const auto extremalX = findExtremalProjs_OneDir(axes[0], vertices); - const auto extremalY = findExtremalProjs_OneDir(axes[1], vertices); - const auto extremalZ = findExtremalProjs_OneDir(axes[2], vertices); - const auto localMin = hlsl::float32_t3{ extremalX.minProj, extremalY.minProj, extremalZ.minProj }; - const auto localMax = hlsl::float32_t3{ extremalX.maxProj, extremalY.maxProj, extremalZ.maxProj }; - return buildObbFromAxesAndLocalMinMax(axes, localMin, localMax); - }; - - static auto computeLineAlignedObb = [](const hlsl::float32_t3& u, const VertexCollection& vertices) - { - // Given u, build any orthonormal base u, v, w - - // Make sure r is not equal to u - auto r = u; - if (fabs(u.x) > fabs(u.y) && fabs(u.x) > fabs(u.z)) { r.x = 0; } - else if (fabs(u.y) > fabs(u.z)) { r.y = 0; } - else { r.z = 0; } - - const auto sqLen = hlsl::dot(r, r); - if (sqLen < FLT_EPSILON) { r.x = r.y = r.z = 1; } - - const auto v = normalize(cross(u, r)); - const auto w = normalize(cross(u, v)); - return computeObb({ u, v, w }, vertices); - }; - - const auto extremals = findExtremals_7FixedDirs(vertices); - - const auto* minProj = extremals.projections.minPtr(); - const auto* maxProj = extremals.projections.maxPtr(); - const auto* minVert = extremals.vertices.minPtr(); - const auto* maxVert = extremals.vertices.maxPtr(); - - - // Determine which points to use in the iterations below - const auto selectedVertices = [&] - { - if (vertices.size < SAMPLE_COUNT) { return vertices; } - return VertexCollection::fromSpan(extremals.vertices.values); - }(); - - // Compute size of AABB (max and min projections of vertices are already computed as slabs 0-2) - auto alMid = hlsl::float32_t3((minProj[0] + maxProj[0]) * 0.5f, (minProj[1] + maxProj[1]) * 0.5f, (minProj[2] + maxProj[2]) * 0.5f); - auto alLen = hlsl::float32_t3(maxProj[0] - minProj[0], maxProj[1] - minProj[1], maxProj[2] - minProj[2]); - auto alVal = getQualityValue(alLen); - - const auto baseTriangle = findBaseTriangle(extremals.vertices, vertices); - if (baseTriangle.flag == LargeBaseTriangle::SECOND_POINT_CLOSE) - return hlsl::shapes::OBB<>::createAxisAligned(alMid, alLen); - if (baseTriangle.flag == LargeBaseTriangle::THIRD_POINT_CLOSE) - return computeLineAlignedObb(baseTriangle.edges[0], vertices); - - - Axes bestAxes = { - hlsl::float32_t3{1.f, 0.f, 0.f}, - {0.f, 1.f, 0.f}, - {0.f, 0.f, 1.f}, - }; - auto bestVal = alVal; - // Find best OBB axes based on the base triangle - findBestObbAxesFromTriangleNormalAndEdgeVectors(selectedVertices, baseTriangle.normal, baseTriangle.edges, bestAxes, bestVal); - - // Find improved OBB axes based on constructed di-tetrahedral shape raised from base triangle - findImprovedObbAxesFromUpperAndLowerTetrasOfBaseTriangle(selectedVertices, baseTriangle, bestAxes, bestVal); - - const auto obb = computeObb(bestAxes, vertices); - - // Check if the OBB extent is still smaller than the intial AABB - if (getQualityValue(2.f * obb.ext) < alVal) - { - return hlsl::shapes::OBB<>{ - .mid = alMid, - .axes = bestAxes, - .ext = alLen / 2.f, - }; - } - return hlsl::shapes::OBB<>::createAxisAligned(alMid, alLen); + return CObbGenerator::calculateOBB(vertices); } + #if 0 //! Flips the direction of surfaces. Changes backfacing triangles to frontfacing //! triangles and vice versa. From b6e9c035c2785ffa1352495167798bd7d67bff85 Mon Sep 17 00:00:00 2001 From: kevyuu Date: Sat, 6 Sep 2025 17:20:16 +0700 Subject: [PATCH 4/9] Fix create axis aligned bounding box --- include/nbl/builtin/hlsl/shapes/aabb.hlsl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/nbl/builtin/hlsl/shapes/aabb.hlsl b/include/nbl/builtin/hlsl/shapes/aabb.hlsl index 6c3a2d278b..1d5b772f63 100644 --- a/include/nbl/builtin/hlsl/shapes/aabb.hlsl +++ b/include/nbl/builtin/hlsl/shapes/aabb.hlsl @@ -70,11 +70,11 @@ struct OBB { OBB ret; ret.mid = mid; - ret.ext = len; + ret.ext = len * 0.5f; for (auto dim_i = 0; dim_i < D; dim_i++) { - ret.axes[dim_i] = point_t(); - ret.axes[dim_i][D] = 1; + ret.axes[dim_i] = point_t(0); + ret.axes[dim_i][dim_i] = 1; } return ret; } From 229457cc6a5c5d5bc2547262dcab12eb589af56a Mon Sep 17 00:00:00 2001 From: kevyuu Date: Tue, 16 Sep 2025 11:25:24 +0700 Subject: [PATCH 5/9] Fix Imgui to always render --- src/nbl/ext/ImGui/ImGui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nbl/ext/ImGui/ImGui.cpp b/src/nbl/ext/ImGui/ImGui.cpp index f477e96cdf..4c7c96953e 100644 --- a/src/nbl/ext/ImGui/ImGui.cpp +++ b/src/nbl/ext/ImGui/ImGui.cpp @@ -332,6 +332,7 @@ core::smart_refctd_ptr UI::createPipeline(SCreation rasterizationParams.faceCullingMode = EFCM_NONE; rasterizationParams.depthWriteEnable = false; rasterizationParams.depthBoundsTestEnable = false; + rasterizationParams.depthCompareOp = ECO_ALWAYS; rasterizationParams.viewportCount = creationParams.viewportCount; } From b2e73a47c73e3c6e9c0e07085e6e6fc5cac9af0c Mon Sep 17 00:00:00 2001 From: kevyuu Date: Mon, 29 Dec 2025 17:52:42 +0700 Subject: [PATCH 6/9] Move obb into its own file --- .../asset/utils/CPolygonGeometryManipulator.h | 1 + include/nbl/builtin/hlsl/shapes/aabb.hlsl | 23 ---------- include/nbl/builtin/hlsl/shapes/obb.hlsl | 42 +++++++++++++++++++ src/nbl/builtin/CMakeLists.txt | 1 + 4 files changed, 44 insertions(+), 23 deletions(-) create mode 100644 include/nbl/builtin/hlsl/shapes/obb.hlsl diff --git a/include/nbl/asset/utils/CPolygonGeometryManipulator.h b/include/nbl/asset/utils/CPolygonGeometryManipulator.h index 4f00c9fea1..7b953b4fbd 100644 --- a/include/nbl/asset/utils/CPolygonGeometryManipulator.h +++ b/include/nbl/asset/utils/CPolygonGeometryManipulator.h @@ -10,6 +10,7 @@ #include "nbl/asset/ICPUPolygonGeometry.h" #include "nbl/asset/utils/CGeometryManipulator.h" #include "nbl/asset/utils/CSmoothNormalGenerator.h" +#include "nbl/builtin/hlsl/shapes/obb.hlsl" namespace nbl::asset { diff --git a/include/nbl/builtin/hlsl/shapes/aabb.hlsl b/include/nbl/builtin/hlsl/shapes/aabb.hlsl index 1d5b772f63..07219c6687 100644 --- a/include/nbl/builtin/hlsl/shapes/aabb.hlsl +++ b/include/nbl/builtin/hlsl/shapes/aabb.hlsl @@ -60,29 +60,6 @@ struct AABB point_t maxVx; }; -template -struct OBB -{ - using scalar_t = Scalar; - using point_t = vector; - - static OBB createAxisAligned(point_t mid, point_t len) - { - OBB ret; - ret.mid = mid; - ret.ext = len * 0.5f; - for (auto dim_i = 0; dim_i < D; dim_i++) - { - ret.axes[dim_i] = point_t(0); - ret.axes[dim_i][dim_i] = 1; - } - return ret; - } - - point_t mid; - std::array axes; - point_t ext; -}; namespace util { diff --git a/include/nbl/builtin/hlsl/shapes/obb.hlsl b/include/nbl/builtin/hlsl/shapes/obb.hlsl new file mode 100644 index 0000000000..45873cbc7b --- /dev/null +++ b/include/nbl/builtin/hlsl/shapes/obb.hlsl @@ -0,0 +1,42 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_SHAPES_OBB_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SHAPES_OBB_INCLUDED_ + +namespace nbl +{ +namespace hlsl +{ +namespace shapes +{ + +template +struct OBB +{ + using scalar_t = Scalar; + using point_t = vector; + + static OBB createAxisAligned(point_t mid, point_t len) + { + OBB ret; + ret.mid = mid; + ret.ext = len * 0.5f; + for (auto dim_i = 0; dim_i < D; dim_i++) + { + ret.axes[dim_i] = point_t(0); + ret.axes[dim_i][dim_i] = 1; + } + return ret; + } + + point_t mid; + std::array axes; + point_t ext; +}; + +} +} +} + +#endif diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 085ed3c923..d55362ef39 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -257,6 +257,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/triangle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/spherical_triangle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/spherical_rectangle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/aabb.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/obb.hlsl") #sampling LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/basic.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/linear.hlsl") From afb45589d183639a37b121c45f4bda541ec2a8e7 Mon Sep 17 00:00:00 2001 From: kevyuu Date: Mon, 29 Dec 2025 17:54:40 +0700 Subject: [PATCH 7/9] Recover missing COBBGenerator.h file --- include/nbl/asset/utils/COBBGenerator.h | 25 + src/nbl/asset/utils/COBBGenerator.cpp | 493 ++++++++++++++++++ .../utils/CPolygonGeometryManipulator.cpp | 2 +- 3 files changed, 519 insertions(+), 1 deletion(-) create mode 100644 include/nbl/asset/utils/COBBGenerator.h create mode 100644 src/nbl/asset/utils/COBBGenerator.cpp diff --git a/include/nbl/asset/utils/COBBGenerator.h b/include/nbl/asset/utils/COBBGenerator.h new file mode 100644 index 0000000000..2b0d408342 --- /dev/null +++ b/include/nbl/asset/utils/COBBGenerator.h @@ -0,0 +1,25 @@ + +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_ASSET_C_OBB_GENERATOR_H_INCLUDED_ +#define _NBL_ASSET_C_OBB_GENERATOR_H_INCLUDED_ + +#include "nbl/asset/utils/CPolygonGeometryManipulator.h" +#include "nbl/builtin/hlsl/shapes/obb.hlsl" + +namespace nbl::asset +{ + class COBBGenerator + { + public: + + using VertexCollection = CPolygonGeometryManipulator::VertexCollection; + + static hlsl::shapes::OBB<> compute(const VertexCollection& vertices); + + }; +} + +#endif diff --git a/src/nbl/asset/utils/COBBGenerator.cpp b/src/nbl/asset/utils/COBBGenerator.cpp new file mode 100644 index 0000000000..98b3707ab8 --- /dev/null +++ b/src/nbl/asset/utils/COBBGenerator.cpp @@ -0,0 +1,493 @@ +#include "nbl/asset/utils/COBBGenerator.h" + +namespace nbl::asset +{ + +namespace +{ + +template +struct Extremals +{ + std::array values; + + T* minPtr() + { + return values.data(); + } + + const T* minPtr() const + { + return values.data(); + } + + T* maxPtr() + { + return values.data() + CountV; + } + + const T* maxPtr() const + { + return values.data() + CountV; + } + +}; +} + +hlsl::shapes::OBB<> COBBGenerator::compute(const VertexCollection& vertices) +{ + constexpr size_t SAMPLE_DIR_COUNT = 7; // Number of sample directions + constexpr size_t SAMPLE_COUNT = SAMPLE_DIR_COUNT * 2; + + if (vertices.size <= 0) + { + return hlsl::shapes::OBB<>::createAxisAligned({}, {}); + } + + static auto getQualityValue = [](hlsl::float32_t3 len) -> hlsl::float32_t + { + return len.x * len.y + len.x * len.z + len.y * len.z; //half box area + }; + + using ExtremalVertices = Extremals; + using ExtremalProjections = Extremals; + using Axes = std::array; + using Edges = std::array; + + struct ExtremalSamples + { + ExtremalVertices vertices; + ExtremalProjections projections; + }; + + struct LargeBaseTriangle + { + hlsl::float32_t3 normal = {}; + Axes vertices = {}; + Edges edges = {}; + enum Flag + { + NORMAL, + SECOND_POINT_CLOSE, + THIRD_POINT_CLOSE + } flag; + }; + + static auto findExtremals_7FixedDirs = [](const VertexCollection& vertices)-> ExtremalSamples + { + ExtremalSamples result; + hlsl::float32_t proj; + + const auto firstVertex = vertices.fetch(0); + + auto* minProjections = result.projections.minPtr(); + auto* maxProjections = result.projections.maxPtr(); + + auto* minVertices = result.vertices.minPtr(); + auto* maxVertices = result.vertices.maxPtr(); + + // Slab 0: dir {1, 0, 0} + proj = firstVertex.x; + minProjections[0] = minProjections[0] = proj; + minVertices[0] = firstVertex; maxVertices[0] = firstVertex; + // Slab 1: dir {0, 1, 0} + proj = firstVertex.y; + minProjections[1] = maxProjections[1] = proj; + minVertices[1] = firstVertex; maxVertices[1] = firstVertex; + // Slab 2: dir {0, 0, 1} + proj = firstVertex.z; + minProjections[2] = maxProjections[2] = proj; + minVertices[2] = firstVertex; maxVertices[2] = firstVertex; + // Slab 3: dir {1, 1, 1} + proj = firstVertex.x + firstVertex.y + firstVertex.z; + minProjections[3] = maxProjections[3] = proj; + minVertices[3] = firstVertex; maxVertices[3] = firstVertex; + // Slab 4: dir {1, 1, -1} + proj = firstVertex.x + firstVertex.y - firstVertex.z; + minProjections[4] = maxProjections[4] = proj; + minVertices[4] = firstVertex; maxVertices[4] = firstVertex; + // Slab 5: dir {1, -1, 1} + proj = firstVertex.x - firstVertex.y + firstVertex.z; + minProjections[5] = maxProjections[5] = proj; + minVertices[5] = firstVertex; maxVertices[5] = firstVertex; + // Slab 6: dir {1, -1, -1} + proj = firstVertex.x - firstVertex.y - firstVertex.z; + minProjections[6] = maxProjections[6] = proj; + minVertices[6] = firstVertex; maxVertices[6] = firstVertex; + + for (size_t vertex_i = 1; vertex_i < vertices.size; vertex_i++) + { + const auto vertex = vertices.fetch(vertex_i); + // Slab 0: dir {1, 0, 0} + proj = vertices.fetch(vertex_i).x; + if (proj < minProjections[0]) { minProjections[0] = proj; minVertices[0] = vertices.fetch(vertex_i); } + if (proj > maxProjections[0]) { maxProjections[0] = proj; maxVertices[0] = vertices.fetch(vertex_i); } + // Slab 1: dir {0, 1, 0} + proj = vertices.fetch(vertex_i).y; + if (proj < minProjections[1]) { minProjections[1] = proj; minVertices[1] = vertices.fetch(vertex_i); } + if (proj > maxProjections[1]) { maxProjections[1] = proj; maxVertices[1] = vertices.fetch(vertex_i); } + // Slab 2: dir {0, 0, 1} + proj = vertices.fetch(vertex_i).z; + if (proj < minProjections[2]) { minProjections[2] = proj; minVertices[2] = vertices.fetch(vertex_i); } + if (proj > maxProjections[2]) { maxProjections[2] = proj; maxVertices[2] = vertices.fetch(vertex_i); } + // Slab 3: dir {1, 1, 1} + proj = vertices.fetch(vertex_i).x + vertices.fetch(vertex_i).y + vertices.fetch(vertex_i).z; + if (proj < minProjections[3]) { minProjections[3] = proj; minVertices[3] = vertices.fetch(vertex_i); } + if (proj > maxProjections[3]) { maxProjections[3] = proj; maxVertices[3] = vertices.fetch(vertex_i); } + // Slab 4: dir {1, 1, -1} + proj = vertices.fetch(vertex_i).x + vertices.fetch(vertex_i).y - vertices.fetch(vertex_i).z; + if (proj < minProjections[4]) { minProjections[4] = proj; minVertices[4] = vertices.fetch(vertex_i); } + if (proj > maxProjections[4]) { maxProjections[4] = proj; maxVertices[4] = vertices.fetch(vertex_i); } + // Slab 5: dir {1, -1, 1} + proj = vertices.fetch(vertex_i).x - vertices.fetch(vertex_i).y + vertices.fetch(vertex_i).z; + if (proj < minProjections[5]) { minProjections[5] = proj; minVertices[5] = vertices.fetch(vertex_i); } + if (proj > maxProjections[5]) { maxProjections[5] = proj; maxVertices[5] = vertices.fetch(vertex_i); } + // Slab 6: dir {1, -1, -1} + proj = vertices.fetch(vertex_i).x - vertices.fetch(vertex_i).y - vertices.fetch(vertex_i).z; + if (proj < minProjections[6]) { minProjections[6] = proj; minVertices[6] = vertices.fetch(vertex_i); } + if (proj > maxProjections[6]) { maxProjections[6] = proj; maxVertices[6] = vertices.fetch(vertex_i); } + } + + return result; + }; + + + static auto findFurthestPointPair = [](const ExtremalVertices& extremalVertices) -> std::pair + { + int indexFurthestPair = 0; + auto maxSqDist = hlsl::dot(extremalVertices.maxPtr()[0], extremalVertices.minPtr()[0]); + for (int k = 1; k < SAMPLE_DIR_COUNT; k++) + { + const auto sqDist = hlsl::dot(extremalVertices.maxPtr()[k], extremalVertices.minPtr()[k]); + if (sqDist > maxSqDist) { maxSqDist = sqDist; indexFurthestPair = k; } + } + return { + extremalVertices.minPtr()[indexFurthestPair], + extremalVertices.maxPtr()[indexFurthestPair] + }; + }; + + static auto sqDistPointInfiniteEdge = [](const hlsl::float32_t3& q, const hlsl::float32_t3& p0, const hlsl::float32_t3& v) -> hlsl::float32_t + { + const auto u0 = q - p0; + const auto t = dot(v, u0); + const auto sqLen_v = hlsl::dot(v, v); + return hlsl::dot(u0, u0) - (t * t) / sqLen_v; + }; + + static auto findFurthestPointFromInfiniteEdge = [](const hlsl::float32_t3& p0, const hlsl::float32_t3& e0, const VertexCollection& vertices) + { + auto maxSqDist = sqDistPointInfiniteEdge(vertices[0], p0, e0); + int maxIndex = 0; + for (size_t i = 1; i < vertices.size; i++) + { + const auto sqDist = sqDistPointInfiniteEdge(vertices[i], p0, e0); + if (sqDist > maxSqDist) + { maxSqDist = sqDist; + maxIndex = i; + } + } + + struct Result + { + hlsl::float32_t3 point; + hlsl::float32_t sqDist; + }; + return Result{ + vertices[maxIndex], + maxSqDist + }; + }; + + static auto findExtremalProjs_OneDir = [](const hlsl::float32_t3& normal, const VertexCollection& vertices) + { + const auto firstProj = hlsl::dot(vertices[0], normal); + auto tMinProj = firstProj, tMaxProj = firstProj; + + for (int i = 1; i < vertices.size; i++) + { + const auto proj = hlsl::dot(vertices[i], normal); + if (proj < tMinProj) { tMinProj = proj; } + if (proj > tMaxProj) { tMaxProj = proj; } + } + + struct Result + { + hlsl::float32_t minProj; + hlsl::float32_t maxProj; + }; + return Result{ tMinProj, tMaxProj }; + }; + + static auto findExtremalPoints_OneDir = [](const hlsl::float32_t3& normal, const VertexCollection& vertices) + { + const auto firstProj = dot(vertices[0], normal); + + auto tMinProj = firstProj, tMaxProj = firstProj; + auto tMinVert = vertices[0], tMaxVert = vertices[0]; + + for (int i = 1; i < vertices.size; i++) + { + const auto proj = hlsl::dot(vertices[i], normal); + if (proj < tMinProj) { tMinProj = proj; tMinVert = vertices[i]; } + if (proj > tMaxProj) { tMaxProj = proj; tMaxVert = vertices[i]; } + } + + struct Result + { + hlsl::float32_t minProj; + hlsl::float32_t maxProj; + hlsl::float32_t3 minVert; + hlsl::float32_t3 maxVert; + }; + return Result{ tMinProj, tMaxProj, tMinVert, tMaxVert }; + }; + + static auto findUpperLowerTetraPoints = []( + const hlsl::float32_t3& n, + const VertexCollection& vertices, + const hlsl::float32_t3& p0) + { + const auto eps = 0.000001f; + const auto extremalPoints = findExtremalPoints_OneDir(n, vertices); + const auto triProj = hlsl::dot(p0, n); + + const auto maxVert = extremalPoints.maxProj - eps > triProj ? std::optional(extremalPoints.maxVert) : std::nullopt; + const auto minVert = extremalPoints.minProj + eps < triProj ? std::optional(extremalPoints.minVert) : std::nullopt; + + struct Result + { + std::optional minVert; + std::optional maxVert; + }; + return Result{ + minVert, + maxVert + }; + }; + + static auto findBestObbAxesFromTriangleNormalAndEdgeVectors = []( + const VertexCollection& vertices, + const hlsl::float32_t3 normal, + const std::array edges, + Axes& bestAxes, + hlsl::float32_t& bestVal) + { + hlsl::float32_t3 dmax, dmin, dlen; + + // The operands are assumed to be orthogonal and unit normals + const auto yExtremeProjs = findExtremalProjs_OneDir(normal, vertices); + dmin.y = yExtremeProjs.minProj; + dmax.y = yExtremeProjs.maxProj; + dlen.y = dmax.y - dmin.y; + + for (const auto& edge : edges) + { + const auto binormal = hlsl::cross(edge, normal); + + const auto xExtremeProjs = findExtremalProjs_OneDir(edge, vertices); + dmin.x = xExtremeProjs.minProj; + dmax.x = xExtremeProjs.maxProj; + dlen.x = dmax.x - dmin.x; + + const auto zExtremeProjs = findExtremalProjs_OneDir(binormal, vertices); + dmin.z = zExtremeProjs.minProj; + dmax.z = zExtremeProjs.maxProj; + dlen.z = dmax.z - dmin.z; + + const auto quality = getQualityValue(dlen); + if (quality < bestVal) + { + bestVal = quality; + bestAxes = { + edge, + normal, + binormal + }; + } + } + + }; + + + static auto findBaseTriangle = [](const ExtremalVertices& extremalVertices, const VertexCollection& vertices)-> LargeBaseTriangle + { + hlsl::float32_t eps = 0.000001f; + + std::array baseTriangleVertices; + Edges edges; + + // Find the furthest point pair among the selected min and max point pairs + std::tie(baseTriangleVertices[0], baseTriangleVertices[1]) = findFurthestPointPair(extremalVertices); + + // Degenerate case 1: + // If the found furthest points are located very close, return OBB aligned with the initial AABB + if (hlsl::dot(baseTriangleVertices[0], baseTriangleVertices[1]) < eps) + { + return { + .vertices = baseTriangleVertices, + .flag = LargeBaseTriangle::SECOND_POINT_CLOSE + }; + } + + // Compute edge vector of the line segment p0, p1 + edges[0] = hlsl::normalize(baseTriangleVertices[0] - baseTriangleVertices[1]); + + // Find a third point furthest away from line given by p0, e0 to define the large base triangle + const auto furthestPointRes = findFurthestPointFromInfiniteEdge(vertices[0], edges[0], vertices); + + // Degenerate case 2: + // If the third point is located very close to the line, return an OBB aligned with the line + if (furthestPointRes.sqDist < eps) + { + return { + .vertices = baseTriangleVertices, + .edges = edges, + .flag = LargeBaseTriangle::THIRD_POINT_CLOSE + }; + } + + // Compute the two remaining edge vectors and the normal vector of the base triangle + edges[1] = hlsl::normalize(baseTriangleVertices[1] - baseTriangleVertices[2]); + edges[2] = hlsl::normalize(baseTriangleVertices[2] - baseTriangleVertices[0]); + const auto normal = hlsl::normalize(hlsl::cross(edges[1], edges[0])); + + return { + .normal = normal, + .vertices = baseTriangleVertices, + .edges = edges, + .flag = LargeBaseTriangle::NORMAL + }; + }; + + auto findImprovedObbAxesFromUpperAndLowerTetrasOfBaseTriangle = [](const VertexCollection& vertices, + const LargeBaseTriangle& baseTriangle, + Axes& bestAxes, hlsl::float32_t& bestVal) + { + hlsl::float32_t3 f0, f1, f2; // Edge vectors towards minVert; + hlsl::float32_t3 g0, g1, g2; // Edge vectors towards maxVert; + hlsl::float32_t3 n0, n1, n2; // Unit normals of top tetra tris + hlsl::float32_t3 m0, m1, m2; // Unit normals of bottom tetra tris + + // Find furthest points above and below the plane of the base triangle for tetra constructions + // For each found valid point, search for the best OBB axes based on the 3 arising triangles + const auto upperLowerTetraVertices = findUpperLowerTetraPoints(baseTriangle.normal, vertices, baseTriangle.vertices[0]); + if (upperLowerTetraVertices.minVert) + { + const auto minVert = *upperLowerTetraVertices.minVert; + f0 = normalize(minVert - baseTriangle.vertices[0]); + f1 = normalize(minVert - baseTriangle.vertices[1]); + f2 = normalize(minVert - baseTriangle.vertices[2]); + n0 = normalize(cross(f1, baseTriangle.edges[0])); + n1 = normalize(cross(f2, baseTriangle.edges[1])); + n2 = normalize(cross(f0, baseTriangle.edges[2])); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n0, { baseTriangle.edges[0], f1, f0 }, bestAxes, bestVal); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n1, { baseTriangle.edges[1], f2, f1 }, bestAxes, bestVal); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n2, { baseTriangle.edges[2], f0, f2 }, bestAxes, bestVal); + } + if (upperLowerTetraVertices.maxVert) + { + const auto maxVert = *upperLowerTetraVertices.minVert; + g0 = normalize(maxVert - baseTriangle.vertices[0]); + g1 = normalize(maxVert - baseTriangle.vertices[1]); + g2 = normalize(maxVert - baseTriangle.vertices[2]); + m0 = normalize(cross(g1, baseTriangle.edges[0])); + m1 = normalize(cross(g2, baseTriangle.edges[1])); + m2 = normalize(cross(g0, baseTriangle.edges[2])); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, m0, { baseTriangle.edges[0], g1, g0 }, bestAxes, bestVal); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, m1, { baseTriangle.edges[1], g2, g1 }, bestAxes, bestVal); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, m2, { baseTriangle.edges[2], g0, g2 }, bestAxes, bestVal); + } + }; + + static auto buildObbFromAxesAndLocalMinMax = []( + const Axes& axes, + const hlsl::float32_t3& localMin, + const hlsl::float32_t3& localMax) -> hlsl::shapes::OBB<3, hlsl::float32_t> + { + const auto localMid = 0.5f * (localMin + localMax); + auto globalMid = axes[0] * localMid.x; + globalMid += axes[1] * localMid.y; + globalMid += axes[2] * localMid.z; + return { + .mid = globalMid, + .axes = axes, + .ext = 0.5f * (localMax - localMin) + }; + }; + + static auto computeObb = [](const Axes& axes, const VertexCollection& vertices) + { + const auto extremalX = findExtremalProjs_OneDir(axes[0], vertices); + const auto extremalY = findExtremalProjs_OneDir(axes[1], vertices); + const auto extremalZ = findExtremalProjs_OneDir(axes[2], vertices); + const auto localMin = hlsl::float32_t3{ extremalX.minProj, extremalY.minProj, extremalZ.minProj }; + const auto localMax = hlsl::float32_t3{ extremalX.maxProj, extremalY.maxProj, extremalZ.maxProj }; + return buildObbFromAxesAndLocalMinMax(axes, localMin, localMax); + }; + + static auto computeLineAlignedObb = [](const hlsl::float32_t3& u, const VertexCollection& vertices) + { + // Given u, build any orthonormal base u, v, w + + // Make sure r is not equal to u + auto r = u; + if (fabs(u.x) > fabs(u.y) && fabs(u.x) > fabs(u.z)) { r.x = 0; } + else if (fabs(u.y) > fabs(u.z)) { r.y = 0; } + else { r.z = 0; } + + const auto sqLen = hlsl::dot(r, r); + if (sqLen < FLT_EPSILON) { r.x = r.y = r.z = 1; } + + const auto v = normalize(cross(u, r)); + const auto w = normalize(cross(u, v)); + return computeObb({ u, v, w }, vertices); + }; + + const auto extremals = findExtremals_7FixedDirs(vertices); + + const auto* minProj = extremals.projections.minPtr(); + const auto* maxProj = extremals.projections.maxPtr(); + + // Determine which points to use in the iterations below + const auto selectedVertices = [&] + { + if (vertices.size < SAMPLE_COUNT) { return vertices; } + return VertexCollection::fromSpan(extremals.vertices.values); + }(); + + // Compute size of AABB (max and min projections of vertices are already computed as slabs 0-2) + auto alMid = hlsl::float32_t3((minProj[0] + maxProj[0]) * 0.5f, (minProj[1] + maxProj[1]) * 0.5f, (minProj[2] + maxProj[2]) * 0.5f); + auto alLen = hlsl::float32_t3(maxProj[0] - minProj[0], maxProj[1] - minProj[1], maxProj[2] - minProj[2]); + auto alVal = getQualityValue(alLen); + + + const auto baseTriangle = findBaseTriangle(extremals.vertices, vertices); + + if (baseTriangle.flag == LargeBaseTriangle::SECOND_POINT_CLOSE) + return hlsl::shapes::OBB<>::createAxisAligned(alMid, alLen); + if (baseTriangle.flag == LargeBaseTriangle::THIRD_POINT_CLOSE) + return computeLineAlignedObb(baseTriangle.edges[0], vertices); + + + Axes bestAxes = { + hlsl::float32_t3{1.f, 0.f, 0.f}, + {0.f, 1.f, 0.f}, + {0.f, 0.f, 1.f}, + }; + auto bestVal = alVal; + // Find best OBB axes based on the base triangle + findBestObbAxesFromTriangleNormalAndEdgeVectors(selectedVertices, baseTriangle.normal, baseTriangle.edges, bestAxes, bestVal); + + // Find improved OBB axes based on constructed di-tetrahedral shape raised from base triangle + findImprovedObbAxesFromUpperAndLowerTetrasOfBaseTriangle(selectedVertices, baseTriangle, bestAxes, bestVal); + + const auto obb = computeObb(bestAxes, vertices); + + // Check if the OBB extent is still smaller than the intial AABB + if (getQualityValue(2.f * obb.ext) < alVal) + return obb; + return hlsl::shapes::OBB<>::createAxisAligned(alMid, alLen); +} + +} diff --git a/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp b/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp index 52e23461b2..74169eb0d8 100644 --- a/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp +++ b/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp @@ -21,7 +21,7 @@ namespace nbl::asset hlsl::shapes::OBB<> CPolygonGeometryManipulator::calculateOBB(const VertexCollection& vertices) { - return CObbGenerator::calculateOBB(vertices); + return COBBGenerator::compute(vertices); } core::smart_refctd_ptr CPolygonGeometryManipulator::createUnweldedList(const ICPUPolygonGeometry* inGeo) From d5f70116c1a244410542f81a7222e8d51f539c91 Mon Sep 17 00:00:00 2001 From: kevyuu Date: Mon, 29 Dec 2025 18:55:38 +0700 Subject: [PATCH 8/9] Refactor obb calculation and some fixes --- src/nbl/asset/utils/COBBGenerator.cpp | 61 ++++++++++----------------- 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/src/nbl/asset/utils/COBBGenerator.cpp b/src/nbl/asset/utils/COBBGenerator.cpp index 98b3707ab8..d869a89cc6 100644 --- a/src/nbl/asset/utils/COBBGenerator.cpp +++ b/src/nbl/asset/utils/COBBGenerator.cpp @@ -269,33 +269,25 @@ hlsl::shapes::OBB<> COBBGenerator::compute(const VertexCollection& vertices) static auto findBestObbAxesFromTriangleNormalAndEdgeVectors = []( const VertexCollection& vertices, const hlsl::float32_t3 normal, - const std::array edges, + const std::array& edges, Axes& bestAxes, hlsl::float32_t& bestVal) { - hlsl::float32_t3 dmax, dmin, dlen; - // The operands are assumed to be orthogonal and unit normals const auto yExtremeProjs = findExtremalProjs_OneDir(normal, vertices); - dmin.y = yExtremeProjs.minProj; - dmax.y = yExtremeProjs.maxProj; - dlen.y = dmax.y - dmin.y; + const auto yLen = yExtremeProjs.maxProj - yExtremeProjs.minProj; for (const auto& edge : edges) { const auto binormal = hlsl::cross(edge, normal); const auto xExtremeProjs = findExtremalProjs_OneDir(edge, vertices); - dmin.x = xExtremeProjs.minProj; - dmax.x = xExtremeProjs.maxProj; - dlen.x = dmax.x - dmin.x; + const auto xLen = xExtremeProjs.maxProj - xExtremeProjs.minProj; const auto zExtremeProjs = findExtremalProjs_OneDir(binormal, vertices); - dmin.z = zExtremeProjs.minProj; - dmax.z = zExtremeProjs.maxProj; - dlen.z = dmax.z - dmin.z; + const auto zLen = zExtremeProjs.maxProj - zExtremeProjs.minProj; - const auto quality = getQualityValue(dlen); + const auto quality = getQualityValue({xLen, yLen, zLen}); if (quality < bestVal) { bestVal = quality; @@ -312,7 +304,7 @@ hlsl::shapes::OBB<> COBBGenerator::compute(const VertexCollection& vertices) static auto findBaseTriangle = [](const ExtremalVertices& extremalVertices, const VertexCollection& vertices)-> LargeBaseTriangle { - hlsl::float32_t eps = 0.000001f; + constexpr hlsl::float32_t eps = 0.000001f; std::array baseTriangleVertices; Edges edges; @@ -364,10 +356,6 @@ hlsl::shapes::OBB<> COBBGenerator::compute(const VertexCollection& vertices) const LargeBaseTriangle& baseTriangle, Axes& bestAxes, hlsl::float32_t& bestVal) { - hlsl::float32_t3 f0, f1, f2; // Edge vectors towards minVert; - hlsl::float32_t3 g0, g1, g2; // Edge vectors towards maxVert; - hlsl::float32_t3 n0, n1, n2; // Unit normals of top tetra tris - hlsl::float32_t3 m0, m1, m2; // Unit normals of bottom tetra tris // Find furthest points above and below the plane of the base triangle for tetra constructions // For each found valid point, search for the best OBB axes based on the 3 arising triangles @@ -375,28 +363,28 @@ hlsl::shapes::OBB<> COBBGenerator::compute(const VertexCollection& vertices) if (upperLowerTetraVertices.minVert) { const auto minVert = *upperLowerTetraVertices.minVert; - f0 = normalize(minVert - baseTriangle.vertices[0]); - f1 = normalize(minVert - baseTriangle.vertices[1]); - f2 = normalize(minVert - baseTriangle.vertices[2]); - n0 = normalize(cross(f1, baseTriangle.edges[0])); - n1 = normalize(cross(f2, baseTriangle.edges[1])); - n2 = normalize(cross(f0, baseTriangle.edges[2])); + const auto f0 = normalize(minVert - baseTriangle.vertices[0]); + const auto f1 = normalize(minVert - baseTriangle.vertices[1]); + const auto f2 = normalize(minVert - baseTriangle.vertices[2]); + const auto n0 = normalize(cross(f1, baseTriangle.edges[0])); + const auto n1 = normalize(cross(f2, baseTriangle.edges[1])); + const auto n2 = normalize(cross(f0, baseTriangle.edges[2])); findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n0, { baseTriangle.edges[0], f1, f0 }, bestAxes, bestVal); findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n1, { baseTriangle.edges[1], f2, f1 }, bestAxes, bestVal); findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n2, { baseTriangle.edges[2], f0, f2 }, bestAxes, bestVal); } if (upperLowerTetraVertices.maxVert) { - const auto maxVert = *upperLowerTetraVertices.minVert; - g0 = normalize(maxVert - baseTriangle.vertices[0]); - g1 = normalize(maxVert - baseTriangle.vertices[1]); - g2 = normalize(maxVert - baseTriangle.vertices[2]); - m0 = normalize(cross(g1, baseTriangle.edges[0])); - m1 = normalize(cross(g2, baseTriangle.edges[1])); - m2 = normalize(cross(g0, baseTriangle.edges[2])); - findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, m0, { baseTriangle.edges[0], g1, g0 }, bestAxes, bestVal); - findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, m1, { baseTriangle.edges[1], g2, g1 }, bestAxes, bestVal); - findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, m2, { baseTriangle.edges[2], g0, g2 }, bestAxes, bestVal); + const auto maxVert = *upperLowerTetraVertices.maxVert; + const auto f0 = normalize(maxVert - baseTriangle.vertices[0]); + const auto f1 = normalize(maxVert - baseTriangle.vertices[1]); + const auto f2 = normalize(maxVert - baseTriangle.vertices[2]); + const auto n0 = normalize(cross(f1, baseTriangle.edges[0])); + const auto n1 = normalize(cross(f2, baseTriangle.edges[1])); + const auto n2 = normalize(cross(f0, baseTriangle.edges[2])); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n0, { baseTriangle.edges[0], f1, f0 }, bestAxes, bestVal); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n1, { baseTriangle.edges[1], f2, f1 }, bestAxes, bestVal); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n2, { baseTriangle.edges[2], f0, f2 }, bestAxes, bestVal); } }; @@ -406,11 +394,8 @@ hlsl::shapes::OBB<> COBBGenerator::compute(const VertexCollection& vertices) const hlsl::float32_t3& localMax) -> hlsl::shapes::OBB<3, hlsl::float32_t> { const auto localMid = 0.5f * (localMin + localMax); - auto globalMid = axes[0] * localMid.x; - globalMid += axes[1] * localMid.y; - globalMid += axes[2] * localMid.z; return { - .mid = globalMid, + .mid = axes[0] * localMid.x + axes[1] * localMid.y + axes[2] * localMid.z, .axes = axes, .ext = 0.5f * (localMax - localMin) }; From 32cbb366ddcc7cb227b18d5bc268fe0c54076f22 Mon Sep 17 00:00:00 2001 From: kevyuu Date: Mon, 29 Dec 2025 19:25:56 +0700 Subject: [PATCH 9/9] Update CDrawAABB::computeOBBTransform to return float32_t3x4 --- include/nbl/ext/DebugDraw/CDrawAABB.h | 2 +- src/nbl/ext/DebugDraw/CDrawAABB.cpp | 24 ++++++++---------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/include/nbl/ext/DebugDraw/CDrawAABB.h b/include/nbl/ext/DebugDraw/CDrawAABB.h index 509608c2f8..6263378024 100644 --- a/include/nbl/ext/DebugDraw/CDrawAABB.h +++ b/include/nbl/ext/DebugDraw/CDrawAABB.h @@ -208,7 +208,7 @@ namespace nbl::ext::debug_draw return transform; } - static hlsl::float32_t4x4 getTransformFromOBB(const hlsl::shapes::OBB<3, float>& aabb); + static hlsl::float32_t3x4 getTransformFromOBB(const hlsl::shapes::OBB<3, float>& aabb); protected: struct ConstructorParams diff --git a/src/nbl/ext/DebugDraw/CDrawAABB.cpp b/src/nbl/ext/DebugDraw/CDrawAABB.cpp index ad04483ba2..a6a1cb1bce 100644 --- a/src/nbl/ext/DebugDraw/CDrawAABB.cpp +++ b/src/nbl/ext/DebugDraw/CDrawAABB.cpp @@ -367,25 +367,17 @@ bool DrawAABB::renderSingle(const DrawParameters& params, const hlsl::shapes::AA return true; } -hlsl::float32_t4x4 DrawAABB::getTransformFromOBB(const hlsl::shapes::OBB<3, float>& obb) +hlsl::float32_t3x4 DrawAABB::getTransformFromOBB(const hlsl::shapes::OBB<3, float>& obb) { const auto obbScale = obb.ext * 2.0f; - const auto obbMat = hlsl::transpose(float32_t4x4{ - hlsl::float32_t4(obb.axes[0] * obbScale.x, 0), - hlsl::float32_t4(obb.axes[1] * obbScale.y, 0), - hlsl::float32_t4(obb.axes[2] * obbScale.z, 0), - hlsl::float32_t4(obb.mid, 1) - }); - - const auto translateUnitCube = float32_t4x4{ - hlsl::float32_t4(1, 0, 0, -0.5f), - hlsl::float32_t4(0, 1, 0, -0.5f), - hlsl::float32_t4(0, 0, 1, -0.5f), - hlsl::float32_t4(0, 0, 0, 1), + const auto axesScaleX = obb.axes[0] * obbScale.x; + const auto axesScaleY = obb.axes[1] * obbScale.y; + const auto axesScaleZ = obb.axes[2] * obbScale.z; + return float32_t3x4{ + axesScaleX.x, axesScaleY.x, axesScaleZ.x, obb.mid.x - (0.5 * (axesScaleX.x + axesScaleY.x + axesScaleZ.x)), + axesScaleX.y, axesScaleY.y, axesScaleZ.y, obb.mid.y - (0.5 * (axesScaleX.y + axesScaleY.y + axesScaleZ.y)), + axesScaleX.z, axesScaleY.z, axesScaleZ.z, obb.mid.z - (0.5 * (axesScaleX.z + axesScaleY.z + axesScaleZ.z)), }; - - const auto transform = mul(obbMat, translateUnitCube); - return transform; } }