Skip to content

Commit

Permalink
[collision-solver] Avoid unnecessary memory allocations
Browse files Browse the repository at this point in the history
  • Loading branch information
IAmNotHanni committed Jul 24, 2021
1 parent eaa20a2 commit da34d60
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 33 deletions.
22 changes: 10 additions & 12 deletions include/inexor/vulkan-renderer/world/collision_solver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,44 +16,42 @@ namespace inexor::vulkan_renderer::world {

class OctreeCollisionSolver {
private:
std::vector<std::pair<std::shared_ptr<world::Cube>, float>> m_collision_candidates{};
const std::vector<std::shared_ptr<world::Cube>> m_worlds;
std::vector<std::pair<std::shared_ptr<world::Cube>, float>> m_collision_candidates;
std::shared_mutex m_collision_solver_mutex;

/// @brief Find all collisions between the given ray and the given octrees, sorted by increasing distance between
/// the octree and the camera.
/// @param worlds The octrees to check for collision
/// @param position The start position of the ray
/// @param direction The direction vector of the ray
/// @return A vector of the found collisions. If no collisions were found, the vector is empty.
[[nodiscard]] std::vector<RayCubeCollision<Cube>>
find_all_ray_octree_collisions(const std::vector<std::shared_ptr<world::Cube>> &worlds, glm::vec3 position,
glm::vec3 direction, bool find_only_one_collision = true);
find_all_ray_octree_collisions(glm::vec3 position, glm::vec3 direction, bool find_only_one_collision = true);

public:
/// @brief Default constructor
/// @param worlds The octree worlds to test collision with
explicit OctreeCollisionSolver(const std::vector<std::shared_ptr<world::Cube>> &worlds);

// TODO: Implement min/max collision distance if required.
// TODO: Start sorting octrees by distance to camera only after a certain number of worlds.
// TODO: Implement a bounding volume hierarchy (BVH) tree, for example an octree for all the octrees.

/// @brief Find a collision between a ray and an octree which is closest to the camera.
/// @param worlds The octrees to check for collision
/// @param position The start position of the ray
/// @param direction The direction vector of the ray
/// @return The collision data
[[nodiscard]] std::optional<RayCubeCollision<Cube>>
find_ray_octree_collision(const std::vector<std::shared_ptr<world::Cube>> &worlds, glm::vec3 position,
glm::vec3 direction);
[[nodiscard]] std::optional<RayCubeCollision<Cube>> find_ray_octree_collision(glm::vec3 position,
glm::vec3 direction);

/// @brief Find all collisions between the given ray and the given octrees, sorted by increasing distance between
/// the octree and the camera.
/// @note Expect this method to be more costly than find_ray_octree_collision!
/// @param worlds The octrees to check for collision
/// @param position The start position of the ray
/// @param direction The direction vector of the ray
/// @return A vector of the found collisions. If no collisions were found, the vector is empty.
[[nodiscard]] std::vector<RayCubeCollision<Cube>>
find_all_ray_octree_collisions(const std::vector<std::shared_ptr<world::Cube>> &worlds, glm::vec3 position,
glm::vec3 direction);
[[nodiscard]] std::vector<RayCubeCollision<Cube>> find_all_ray_octree_collisions(glm::vec3 position,
glm::vec3 direction);
};

} // namespace inexor::vulkan_renderer::world
5 changes: 2 additions & 3 deletions src/vulkan-renderer/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ void Application::load_octree_geometry() {
m_worlds.emplace_back(std::make_shared<world::Cube>(1.0f, glm::vec3{0, 0, 0}));
m_worlds.emplace_back(std::make_shared<world::Cube>(1.6f, glm::vec3{0, 10, 0}));

m_collision_solver = std::make_unique<world::OctreeCollisionSolver>();
m_collision_solver = std::make_unique<world::OctreeCollisionSolver>(m_worlds);

m_worlds[0]->set_type(world::Cube::Type::OCTANT);
m_worlds[1]->set_type(world::Cube::Type::SOLID);
Expand Down Expand Up @@ -576,8 +576,7 @@ void Application::check_octree_collisions() {
// TODO: Apply one big global octree for bounding volume hierarchy.
// TODO: Implement a world manager which resolves multiple octree collision.

const auto collision =
m_collision_solver->find_ray_octree_collision(m_worlds, m_camera->position(), m_camera->front());
const auto collision = m_collision_solver->find_ray_octree_collision(m_camera->position(), m_camera->front());

if (collision) {
const auto cube_hit = collision.value().cube_intersection();
Expand Down
36 changes: 18 additions & 18 deletions src/vulkan-renderer/world/collision_solver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,29 @@

namespace inexor::vulkan_renderer::world {

OctreeCollisionSolver::OctreeCollisionSolver(const std::vector<std::shared_ptr<world::Cube>> &worlds)
: m_worlds(worlds) {
assert(!worlds.empty());
std::scoped_lock lock(m_collision_solver_mutex);
m_collision_candidates.reserve(worlds.size());
}

std::vector<RayCubeCollision<Cube>>
OctreeCollisionSolver::find_all_ray_octree_collisions(const std::vector<std::shared_ptr<world::Cube>> &worlds,
const glm::vec3 position, const glm::vec3 direction,
OctreeCollisionSolver::find_all_ray_octree_collisions(const glm::vec3 position, const glm::vec3 direction,
bool find_only_one_collision) {
if (worlds.empty()) {
return {};
}

std::vector<RayCubeCollision<Cube>> found_collisions;

found_collisions.reserve(worlds.size());
found_collisions.reserve(m_worlds.size());

// We need a critical section because we are modifying m_collision_candidates.
{
std::scoped_lock lock(m_collision_solver_mutex);

// TODO: Optimize this! Avoid memory re-allocation if possible and benchmark it.
// We must delete the entry from previous calls of this method so we can call emplace_back.
// This will not change the capacity of the vector which was set in the constructor.
m_collision_candidates.clear();
m_collision_candidates.reserve(worlds.size());

for (const auto &world : worlds) {
for (const auto &world : m_worlds) {
if (is_bounding_box_and_bounding_sphere_hit(world, position, direction)) {
m_collision_candidates.emplace_back(std::make_pair(world, glm::distance2(position, direction)));
}
Expand Down Expand Up @@ -60,10 +62,9 @@ OctreeCollisionSolver::find_all_ray_octree_collisions(const std::vector<std::sha
return std::move(found_collisions);
}

std::optional<RayCubeCollision<Cube>>
OctreeCollisionSolver::find_ray_octree_collision(const std::vector<std::shared_ptr<world::Cube>> &worlds,
const glm::vec3 position, const glm::vec3 direction) {
const auto result = find_all_ray_octree_collisions(worlds, position, direction, true);
std::optional<RayCubeCollision<Cube>> OctreeCollisionSolver::find_ray_octree_collision(const glm::vec3 position,
const glm::vec3 direction) {
const auto result = find_all_ray_octree_collisions(position, direction, true);

if (!result.empty()) {
return result[0];
Expand All @@ -72,10 +73,9 @@ OctreeCollisionSolver::find_ray_octree_collision(const std::vector<std::shared_p
return std::nullopt;
}

std::vector<RayCubeCollision<Cube>>
OctreeCollisionSolver::find_all_ray_octree_collisions(const std::vector<std::shared_ptr<world::Cube>> &worlds,
const glm::vec3 position, const glm::vec3 direction) {
return find_all_ray_octree_collisions(worlds, position, direction, false);
std::vector<RayCubeCollision<Cube>> OctreeCollisionSolver::find_all_ray_octree_collisions(const glm::vec3 position,
const glm::vec3 direction) {
return find_all_ray_octree_collisions(position, direction, false);
}

} // namespace inexor::vulkan_renderer::world

0 comments on commit da34d60

Please sign in to comment.