Skip to content

Commit

Permalink
BVH - missing changes
Browse files Browse the repository at this point in the history
  • Loading branch information
feiy committed Dec 18, 2023
1 parent d404a16 commit 8d0c534
Show file tree
Hide file tree
Showing 5 changed files with 364 additions and 2 deletions.
2 changes: 0 additions & 2 deletions ThreeEngine/core/BoundingVolumeHierarchy.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,6 @@ class BoundingVolumeHierarchy
void serialize(FILE* fp);

private:
bool m_cull_front = false;

struct PrimitiveInfo
{
Object3D* obj;
Expand Down
160 changes: 160 additions & 0 deletions thirdparty/bvh/include/bvh/collison_traverser.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#pragma once

#include "bvh/bvh.hpp"
#include "bvh/sphere.hpp"
#include "bvh/node_colliders.hpp"
#include "bvh/utilities.hpp"

namespace bvh {

template <typename Bvh, size_t StackSize = 64, typename NodeCollider = NodeCollider<Bvh>>
class CollisionTraverser
{
public:
static constexpr size_t stack_size = StackSize;

private:
using Scalar = typename Bvh::ScalarType;

struct Stack {
using Element = typename Bvh::IndexType;

Element elements[stack_size];
size_t size = 0;

void push(const Element& t) {
assert(size < stack_size);
elements[size++] = t;
}

Element pop() {
assert(!empty());
return elements[--size];
}

bool empty() const { return size == 0; }
};

template <typename PrimitiveIntersector>
bvh_always_inline
std::optional<typename PrimitiveIntersector::Result>& collide_leaf(
const typename Bvh::Node& node,
Sphere<Scalar>& sphere,
std::optional<typename PrimitiveIntersector::Result>& best_hit,
PrimitiveIntersector& primitive_intersector) const
{
size_t begin = node.first_child_or_primitive;
size_t end = begin + node.primitive_count;

for (size_t i = begin; i < end; ++i)
{
if (auto hit = primitive_intersector.collide(i, sphere))
{
best_hit = hit;
sphere.radius = hit->distance();
}
}

return best_hit;
}

template <typename PrimitiveIntersector>
bvh_always_inline
std::optional<typename PrimitiveIntersector::Result>
collide(Sphere<Scalar> sphere, PrimitiveIntersector& primitive_intersector) const
{
auto best_hit = std::optional<typename PrimitiveIntersector::Result>(std::nullopt);

if (bvh_unlikely(bvh.nodes[0].is_leaf()))
{
return collide_leaf(bvh.nodes[0], sphere, best_hit, primitive_intersector);
}


NodeCollider node_collider;

Stack stack;
auto* left_child = &bvh.nodes[bvh.nodes[0].first_child_or_primitive];
while (true)
{
auto* right_child = left_child + 1;
auto distance_left = node_collider.collide(*left_child, sphere);
auto distance_right = node_collider.collide(*right_child, sphere);

if (distance_left.second < sphere.radius)
{
sphere.radius = distance_left.second;
}

if (distance_right.second < sphere.radius)
{
sphere.radius = distance_right.second;
}

if (distance_left.first <= sphere.radius)
{
if (bvh_unlikely(left_child->is_leaf()))
{
collide_leaf(*left_child, sphere, best_hit, primitive_intersector);
left_child = nullptr;
}
}
else
{
left_child = nullptr;
}

if (distance_right.first <= sphere.radius)
{
if (bvh_unlikely(right_child->is_leaf()))
{
collide_leaf(*right_child, sphere, best_hit, primitive_intersector);
right_child = nullptr;
}
}
else
{
right_child = nullptr;
}

if (left_child)
{
if (right_child)
{
if (distance_left.first > distance_right.first)
{
std::swap(left_child, right_child);
}
stack.push(right_child->first_child_or_primitive);
}
left_child = &bvh.nodes[left_child->first_child_or_primitive];
}
else if (right_child)
{
left_child = &bvh.nodes[right_child->first_child_or_primitive];
}
else
{
if (stack.empty()) break;
left_child = &bvh.nodes[stack.pop()];
}
}
return best_hit;
}

const Bvh& bvh;

public:
CollisionTraverser(const Bvh& bvh) : bvh(bvh) {}

template <typename PrimitiveIntersector>
bvh_always_inline
std::optional<typename PrimitiveIntersector::Result>
traverse(const Sphere<Scalar>& sphere, PrimitiveIntersector& primitive_intersector) const
{
return collide(sphere, primitive_intersector);
}
};

}

88 changes: 88 additions & 0 deletions thirdparty/bvh/include/bvh/node_colliders.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#pragma once

#include <cmath>

#include "bvh/vector.hpp"
#include "bvh/sphere.hpp"
#include "bvh/platform.hpp"
#include "bvh/utilities.hpp"


namespace bvh {

template <typename Bvh>
struct NodeCollider {
using Scalar = typename Bvh::ScalarType;

NodeCollider() {}
~NodeCollider() {}


std::pair<Scalar, Scalar> collide(const typename Bvh::Node& node, const Sphere<Scalar>& sphere) const
{
Scalar dis_far = 0.0f;

for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 2; j++)
{
for (int k = 0; k < 2; k++)
{
float x = node.bounds[k];
float y = node.bounds[2 + j];
float z = node.bounds[4 + i];

float dx = x - sphere.origin[0];
float dy = y - sphere.origin[1];
float dz = z - sphere.origin[2];

float d = sqrtf(dx * dx + dy * dy + dz * dz);
if (d > dis_far)
{
dis_far = d;
}
}
}
}

Scalar dx = 0.0f;
Scalar dy = 0.0f;
Scalar dz = 0.0f;
if (sphere.origin[0] < node.bounds[0])
{
dx = node.bounds[0] - sphere.origin[0];
}
else if (sphere.origin[0] > node.bounds[1])
{
dx = sphere.origin[0] - node.bounds[1];
}

if (sphere.origin[1] < node.bounds[2])
{
dy = node.bounds[2] - sphere.origin[1];
}
else if (sphere.origin[1] > node.bounds[3])
{
dy = sphere.origin[1] - node.bounds[3];
}

if (sphere.origin[2] < node.bounds[4])
{
dz = node.bounds[4] - sphere.origin[2];
}
else if (sphere.origin[2] > node.bounds[5])
{
dz = sphere.origin[2] - node.bounds[5];
}

Scalar dis_near = sqrtf(dx*dx + dy*dy + dz*dz);

return std::make_pair(dis_near, dis_far);
}

};


}


8 changes: 8 additions & 0 deletions thirdparty/bvh/include/bvh/primitive_intersectors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <optional>

#include "bvh/ray.hpp"
#include "bvh/sphere.hpp"

namespace bvh {

Expand Down Expand Up @@ -52,6 +53,13 @@ struct ClosestPrimitiveIntersector : public PrimitiveIntersector<Bvh, Primitive,
return std::make_optional(Result { i, *hit });
return std::nullopt;
}

std::optional<Result> collide(size_t index, const Sphere<Scalar>& sphere) const {
auto [p, i] = this->primitive_at(index);
if (auto hit = p.collide(sphere))
return std::make_optional(Result{ i, *hit });
return std::nullopt;
}
};

/// An intersector that exits after the first hit and only stores the distance to the primitive.
Expand Down
Loading

0 comments on commit 8d0c534

Please sign in to comment.