Wayverb
voxelised_scene_data.h
1 #pragma once
2 
3 #include "core/azimuth_elevation.h"
4 #include "core/geo/geometric.h"
5 #include "core/scene_data.h"
6 #include "core/spatial_division/voxel_collection.h"
7 
8 #include <random>
9 
10 namespace wayverb {
11 namespace core {
12 
13 template <typename Vertex, typename Surface>
14 class voxelised_scene_data final {
15  static auto compute_triangle_indices(size_t num) {
16  util::aligned::vector<size_t> ret(num);
17  std::iota(ret.begin(), ret.end(), 0);
18  return ret;
19  }
20 
21 public:
22  // invariant:
23  // The 'voxels' structure holds references/indexes to valid triangles in
24  // the 'scene_data' structure.
25 
27 
29  size_t octree_depth,
30  const geo::box& aabb)
31  : scene_{std::move(scene)}
32  , voxels_{ndim_tree<3>{
33  octree_depth,
34  [this](auto item, const auto& aabb) {
35  // This is a bit greedy - we're sacrificing some speed
36  // in the name of correctness.
37  return geo::overlaps(
38  padded(aabb, glm::vec3{0.001}),
39  geo::get_triangle_vec3(
40  scene_.get_triangles()[item],
41  scene_.get_vertices().data()));
42  },
43  compute_triangle_indices(scene_.get_triangles().size()),
44  aabb}} {}
45 
46  const scene_data& get_scene_data() const { return scene_; }
47  const voxel_collection<3>& get_voxels() const { return voxels_; }
48 
49  // We can allow modifying surfaces without violating the invariant.
50  template <typename It>
51  void set_surfaces(It begin, It end) {
52  scene_.set_surfaces(begin, end);
53  }
54  void set_surfaces(const Surface& surface) { scene_.set_surfaces(surface); }
55 
56 private:
57  scene_data scene_;
58  voxel_collection<3> voxels_;
59 };
60 
61 template <typename Vertex, typename Surface, typename T>
62 auto make_voxelised_scene_data(generic_scene_data<Vertex, Surface> scene,
63  size_t octree_depth,
64  const util::range<T>& aabb) {
66  std::move(scene), octree_depth, aabb};
67 }
68 
69 template <typename Vertex, typename Surface, typename Pad>
70 auto make_voxelised_scene_data(generic_scene_data<Vertex, Surface> scene,
71  size_t octree_depth,
72  Pad padding) {
73  const auto aabb =
74  padded(geo::compute_aabb(scene.get_vertices()), glm::vec3{padding});
75  return make_voxelised_scene_data(std::move(scene), octree_depth, aabb);
76 }
77 
79 
80 template <typename Vertex, typename Surface>
81 std::experimental::optional<intersection> intersects(
83  const geo::ray& ray,
84  size_t to_ignore = ~size_t{0}) {
85  std::experimental::optional<intersection> state;
86  traverse(voxelised.get_voxels(),
87  ray,
88  [&](const geo::ray& ray,
89  const voxel& to_test,
90  float /*min_dist_inside_voxel*/,
91  float max_dist_inside_voxel) {
92  const auto i = ray_triangle_intersection(
93  ray,
94  to_test.data(),
95  to_test.size(),
96  voxelised.get_scene_data().get_triangles().data(),
97  voxelised.get_scene_data().get_vertices().data(),
98  to_ignore);
99  if (i && i->inter.t <= max_dist_inside_voxel) {
100  state = i;
101  return true;
102  }
103  return false;
104  });
105  return state;
106 }
107 
108 template <typename Vertex, typename Surface>
109 std::experimental::optional<size_t> count_intersections(
110  const voxelised_scene_data<Vertex, Surface>& voxelised,
111  const geo::ray& ray) {
112  size_t count{0};
113  bool degenerate{false};
114  // for each voxel along the ray
115  traverse(voxelised.get_voxels(),
116  ray,
117  [&](const geo::ray& ray,
118  const voxel& to_test,
119  float min_dist_inside_voxel,
120  float max_dist_inside_voxel) {
121  // for each triangle in the voxel
122  for (const auto i : to_test) {
123  // find any intersection between the triangle and the
124  // ray
125  const auto intersection = triangle_intersection(
126  voxelised.get_scene_data().get_triangles()[i],
127  voxelised.get_scene_data().get_vertices().data(),
128  ray);
129  // if there is an intersection
130  if (intersection) {
131  if (is_degenerate(*intersection)) {
132  // if the intersection is degenerate, set the
133  // 'degenerate' flag to true, and quit
134  // traversal
135  degenerate = true;
136  return true;
137  }
138  // if the intersection is inside the current voxel
139  if (min_dist_inside_voxel < intersection->t &&
140  intersection->t <= max_dist_inside_voxel) {
141  // increment the intersection counter
142  count += 1;
143  }
144  }
145  }
146  return false;
147  });
148  if (degenerate) {
149  return std::experimental::nullopt;
150  }
151  return count;
152 }
153 
154 namespace {
155 template <typename Vertex, typename Surface>
156 std::experimental::optional<bool> is_inside(
157  const voxelised_scene_data<Vertex, Surface>& voxelised,
158  const geo::ray& ray) {
159  if (const auto i = count_intersections(voxelised, ray)) {
160  return *i % 2;
161  }
162  return std::experimental::nullopt;
163 }
164 } // namespace
165 
166 template <typename Vertex, typename Surface>
167 bool inside(const voxelised_scene_data<Vertex, Surface>& voxelised,
168  const glm::vec3& pt) {
169  std::default_random_engine engine{std::random_device{}()};
170  for (;;) {
171  const direction_rng rng{engine};
172  const geo::ray ray{pt, sphere_point(rng.get_z(), rng.get_theta())};
173  if (const auto i{is_inside(voxelised, ray)}) {
174  return *i;
175  }
176  }
177 }
178 
179 } // namespace core
180 } // namespace wayverb
Definition: scene_data.h:13
Definition: geometry_structs.h:11
I would do this with a struct, but rays have an invariant:
Definition: geometric.h:19
A generic interface for spatial division algorithms (octree, quadtree)
Definition: ndim_tree.h:38
Definition: voxel_structs.h:9
Definition: traits.cpp:2
void set_surfaces(It begin, It end)
Definition: scene_data.h:64
Definition: geometry_structs.h:71
Definition: azimuth_elevation.h:15
Definition: range.h:12
Definition: scene_structs.h:28
Definition: capsule_base.h:9
Definition: voxelised_scene_data.h:14