Skip to content

Commit

Permalink
[merge] Merge pull request #321 from inexorgame/hanni/octree_collision
Browse files Browse the repository at this point in the history
[collision] First generation of octree collision detection
  • Loading branch information
IAmNotHanni authored Jun 9, 2021
2 parents 7cd5261 + a8ce3f7 commit 99d1a5d
Show file tree
Hide file tree
Showing 30 changed files with 792 additions and 24 deletions.
8 changes: 7 additions & 1 deletion benchmarks/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
add_executable(inexor-vulkan-renderer-benchmarks engine_benchmark_main.cpp)
set(INEXOR_BENCHMARKING_SOURCE_FILES
engine_benchmark_main.cpp

world/cube_collision.cpp
)

add_executable(inexor-vulkan-renderer-benchmarks ${INEXOR_BENCHMARKING_SOURCE_FILES})

set_target_properties(
inexor-vulkan-renderer-benchmarks PROPERTIES
Expand Down
23 changes: 23 additions & 0 deletions benchmarks/world/cube_collision.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <benchmark/benchmark.h>

#include <inexor/vulkan-renderer/world/collision_query.hpp>
#include <inexor/vulkan-renderer/world/cube.hpp>

namespace inexor::vulkan_renderer {

void CubeCollision(benchmark::State &state) {
for (auto _ : state) {
const glm::vec3 world_pos{0, 0, 0};
world::Cube world(1.0f, world_pos);
world.set_type(world::Cube::Type::SOLID);

glm::vec3 cam_pos{0.0f, 0.0f, 10.0f};
glm::vec3 cam_direction{0.0f, 0.0f, -1.0f};

benchmark::DoNotOptimize(ray_cube_collision_check(world, cam_pos, cam_direction));
}
}

BENCHMARK(CubeCollision);

}; // namespace inexor::vulkan_renderer
2 changes: 1 addition & 1 deletion conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def configure(self):

def requirements(self):
if self.options.build_benchmarks:
self.requires("benchmark/1.5.0")
self.requires("benchmark/1.5.2")
if self.options.build_tests:
self.requires("gtest/1.10.0")

Expand Down
15 changes: 13 additions & 2 deletions documentation/source/development/building.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,19 @@ The following CMake targets and options are available:
Windows
^^^^^^^

.. note::
When building a VS solution add ``--config Debug/Release`` to define the build type.
Example: Create Visual Studio 2019 project map for Debug mode including docs, tests, and benchmarks:

.. code-block:: shell
cmake -G "Visual Studio 16 2019" -A x64 -B./cmake-build-debug-vs/ -DCMAKE_BUILD_TYPE=Debug -DINEXOR_BUILD_DOC=ON -DINEXOR_BUILD_TESTS=ON -DINEXOR_BUILD_BENCHMARKS=ON ./
Example: Create Visual Studio 2019 project map for Release mode but without docs, tests, and benchmarks:

.. code-block:: shell
cmake -G "Visual Studio 16 2019" -A x64 -B./cmake-build-release-vs/ -DCMAKE_BUILD_TYPE=Release ./
If you have `Ninja build system <https://ninja-build.org/>`__ installed, you can use it like this:

.. code-block:: doscon
Expand Down
1 change: 1 addition & 0 deletions documentation/source/development/reference/main.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ Reference
binary-format-specification
keyboard-mouse-input
octree-file-format
octree-collision
187 changes: 187 additions & 0 deletions documentation/source/development/reference/octree-collision.rst

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions documentation/source/development/reference/octree-file-format.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,34 @@ If blocks or corners are ordered, they use this order.
6, "(1, 1, 0)"
7, "(1, 1, 1)"

**Order of Faces**

The following order of faces is used:

.. csv-table:: Order of Face
:header: ID, Name, Normal vector

0, "left", "(-1, 0, 0)"
1, "right", "(1, 0, 0)"
2, "front", "(0, 1, 0)"
3, "back", "(0, -1, 0)"
4, "top", "(0, 0, 1)"
5, "bottom", "(0, 0, -1)"

**Order of Indices on Face**

The following corner indices are associated to the faces:

.. csv-table:: Order of Indices on Face
:header: ID, Indices

0, "0, 1, 2, 3"
1, "4, 5, 6, 7"
2, "0, 1, 4, 5"
3, "2, 3, 6, 7"
4, "1, 3, 5, 7"
5, "0, 2, 4, 6"

**Edge Order**

- All edges are going into the positive direction of the axis.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions include/inexor/vulkan-renderer/application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "inexor/vulkan-renderer/input/keyboard_mouse_data.hpp"
#include "inexor/vulkan-renderer/renderer.hpp"
#include "inexor/vulkan-renderer/world/collision_query.hpp"
#include "inexor/vulkan-renderer/world/cube.hpp"

#include <GLFW/glfw3.h>
#include <vulkan/vulkan_core.h>
Expand Down Expand Up @@ -33,6 +35,8 @@ class Application : public VulkanRenderer {
std::unique_ptr<input::KeyboardMouseInputData> m_input_data;

bool m_enable_validation_layers = true;
/// Inexor engine supports a variable number of octrees.
std::vector<std::shared_ptr<world::Cube>> m_worlds;

// If the user specified command line argument "--stop-on-validation-message", the program will call std::abort();
// after reporting a validation layer (error) message.
Expand All @@ -50,6 +54,8 @@ class Application : public VulkanRenderer {
void update_imgui_overlay();
void check_application_specific_features();
void update_uniform_buffers();
/// Use the camera's position and view direction vector to check for ray-octree collisions with all octrees.
void check_octree_collisions();
void process_mouse_input();

public:
Expand Down
60 changes: 60 additions & 0 deletions include/inexor/vulkan-renderer/world/collision.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#pragma once

#include <glm/vec3.hpp>

#include <string>
#include <tuple>

namespace inexor::vulkan_renderer::world {

/// @brief A wrapper for collisions between a ray and octree geometry.
/// This class is used for octree collision, but it can be used for every cube-like data structure
/// @tparam T A templatable type which offers a size() and center() method.
template <typename T>
class RayCubeCollision {
const T &m_cube;

glm::vec3 m_intersection;
glm::vec3 m_selected_face;
glm::vec3 m_nearest_corner;
glm::vec3 m_nearest_edge;

public:
/// @brief Calculate point of intersection, selected face,
/// nearest corner on that face, and nearest edge on that face.
/// @param cube The cube to check for collision.
/// @param ray_pos The start point of the ray.
/// @param ray_dir The direction of the ray.
RayCubeCollision(const T &cube, glm::vec3 ray_pos, glm::vec3 ray_dir);

RayCubeCollision(const RayCubeCollision &) = delete;

RayCubeCollision(RayCubeCollision &&other) noexcept;

~RayCubeCollision() = default;

RayCubeCollision &operator=(const RayCubeCollision &) = delete;
RayCubeCollision &operator=(RayCubeCollision &&) = delete;

[[nodiscard]] const T &cube() const noexcept {
return m_cube;
}

[[nodiscard]] const glm::vec3 &intersection() const noexcept {
return m_intersection;
}

[[nodiscard]] const glm::vec3 &face() const noexcept {
return m_selected_face;
}

[[nodiscard]] const glm::vec3 &corner() const noexcept {
return m_nearest_corner;
}

[[nodiscard]] const glm::vec3 &edge() const noexcept {
return m_nearest_edge;
}
};

} // namespace inexor::vulkan_renderer::world
39 changes: 39 additions & 0 deletions include/inexor/vulkan-renderer/world/collision_query.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

#include "inexor/vulkan-renderer/world/collision.hpp"

#include <glm/vec3.hpp>

#include <optional>

// Forward declaration
namespace inexor::vulkan_renderer::world {
class Cube;
} // namespace inexor::vulkan_renderer::world

namespace inexor::vulkan_renderer::world {

// TODO: Implement PointCubeCollision

/// @brief ``True`` of the ray build from the two vectors collides with the cube's bounding box.
/// @note There is no such function as glm::intersectRayBox.
/// @param box_bounds An array of two vectors which represent the edges of the bounding box.
/// @param pos The start position of the ray.
/// @param dir The direction of the ray.
/// @return ``True`` if the ray collides with the octree cube's bounding box.
[[nodiscard]] bool ray_box_collision(std::array<glm::vec3, 2> &box_bounds, glm::vec3 &pos, glm::vec3 &dir);

/// @brief Check for a collision between a camera ray and octree geometry.
/// @param cube The cube to check collisions with.
/// @param pos The camera position.
/// @param dir The camera view direction.
/// @param max_depth The maximum subcube iteration depth. If this depth is reached and the cube is an octant, it
/// will be treated as if it was a solid cube. This is the foundation for the implementation of grid size in octree
/// editor.
/// @note This does not account yet for octree indentation!
/// @return A std::optional which contains the collision data (if any found).
[[nodiscard]] std::optional<RayCubeCollision<Cube>>
ray_cube_collision_check(const Cube &cube, glm::vec3 pos, glm::vec3 dir,
std::optional<std::uint32_t> max_depth = std::nullopt);

} // namespace inexor::vulkan_renderer::world
18 changes: 18 additions & 0 deletions include/inexor/vulkan-renderer/world/cube.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "inexor/vulkan-renderer/world/indentation.hpp"

#include <glm/geometric.hpp>
#include <glm/gtx/vector_angle.hpp>
#include <glm/vec3.hpp>

#include <array>
Expand Down Expand Up @@ -114,6 +116,22 @@ class Cube : public std::enable_shared_from_this<Cube> {
/// Count the number of Type::SOLID and Type::NORMAL cubes.
[[nodiscard]] std::size_t count_geometry_cubes() const noexcept;

[[nodiscard]] glm::vec3 center() const noexcept {
return m_position + 0.5f * m_size;
}

[[nodiscard]] glm::vec3 position() const noexcept {
return m_position;
}

[[nodiscard]] std::array<glm::vec3, 2> bounding_box() const {
return {m_position, {m_position.x + m_size, m_position.y + m_size, m_position.z + m_size}};
}

[[nodiscard]] float size() const noexcept {
return m_size;
}

/// Set a new type.
void set_type(Type new_type);
/// Get type.
Expand Down
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ set(INEXOR_SOURCE_FILES
vulkan-renderer/wrapper/window.cpp
vulkan-renderer/wrapper/window_surface.cpp

vulkan-renderer/world/collision.cpp
vulkan-renderer/world/collision_query.cpp
vulkan-renderer/world/cube.cpp
vulkan-renderer/world/indentation.cpp)

Expand Down
66 changes: 49 additions & 17 deletions src/vulkan-renderer/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "inexor/vulkan-renderer/exception.hpp"
#include "inexor/vulkan-renderer/standard_ubo.hpp"
#include "inexor/vulkan-renderer/tools/cla_parser.hpp"
#include "inexor/vulkan-renderer/world/collision.hpp"
#include "inexor/vulkan-renderer/world/cube.hpp"
#include "inexor/vulkan-renderer/wrapper/cpu_texture.hpp"
#include "inexor/vulkan-renderer/wrapper/descriptor_builder.hpp"
Expand Down Expand Up @@ -188,23 +189,32 @@ void Application::load_shaders() {
void Application::load_octree_geometry() {
spdlog::debug("Creating octree geometry.");

std::shared_ptr<world::Cube> cube = std::make_shared<world::Cube>(2.0f, glm::vec3{0, -1, -1});
cube->set_type(world::Cube::Type::OCTANT);

cube->children()[3]->set_type(world::Cube::Type::EMPTY);
cube->children()[5]->set_type(world::Cube::Type::EMPTY);
cube->children()[6]->set_type(world::Cube::Type::EMPTY);
cube->children()[7]->set_type(world::Cube::Type::EMPTY);

for (const auto &polygons : cube->polygons(true)) {
for (const auto &triangle : *polygons) {
for (const auto &vertex : triangle) {
glm::vec3 color = {
static_cast<float>(rand()) / static_cast<float>(RAND_MAX),
static_cast<float>(rand()) / static_cast<float>(RAND_MAX),
static_cast<float>(rand()) / static_cast<float>(RAND_MAX),
};
m_octree_vertices.emplace_back(vertex, color);
m_worlds.reserve(3);

m_worlds.emplace_back(std::make_shared<world::Cube>(5.0f, glm::vec3{10, 0, 0}));
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_worlds[0]->set_type(world::Cube::Type::OCTANT);
m_worlds[1]->set_type(world::Cube::Type::SOLID);
m_worlds[2]->set_type(world::Cube::Type::OCTANT);

m_worlds[0]->children()[3]->set_type(world::Cube::Type::EMPTY);
m_worlds[0]->children()[5]->set_type(world::Cube::Type::EMPTY);
m_worlds[0]->children()[6]->set_type(world::Cube::Type::EMPTY);
m_worlds[0]->children()[7]->set_type(world::Cube::Type::EMPTY);

for (const auto &world : m_worlds) {
for (const auto &polygons : world->polygons(true)) {
for (const auto &triangle : *polygons) {
for (const auto &vertex : triangle) {
glm::vec3 color = {
static_cast<float>(rand()) / static_cast<float>(RAND_MAX),
static_cast<float>(rand()) / static_cast<float>(RAND_MAX),
static_cast<float>(rand()) / static_cast<float>(RAND_MAX),
};
m_octree_vertices.emplace_back(vertex, color);
}
}
}
}
Expand Down Expand Up @@ -566,6 +576,27 @@ void Application::process_mouse_input() {
m_camera->set_movement_state(CameraMovement::RIGHT, m_input_data->is_key_pressed(GLFW_KEY_D));
}

void Application::check_octree_collisions() {
// Check for collision between camera ray and every octree
for (const auto &world : m_worlds) {
const auto collision = ray_cube_collision_check(*world, m_camera->position(), m_camera->front());

if (collision) {
const auto intersection = collision.value().intersection();
const auto face_normal = collision.value().face();
const auto corner = collision.value().corner();
const auto edge = collision.value().edge();

spdlog::trace("pos {} {} {} | face {} {} {} | corner {} {} {} | edge {} {} {}", intersection.x,
intersection.y, intersection.z, face_normal.x, face_normal.y, face_normal.z, corner.x,
corner.y, corner.z, edge.x, edge.y, edge.z);

// Break after one collision.
break;
}
}
}

void Application::run() {
spdlog::debug("Running Application.");

Expand All @@ -577,6 +608,7 @@ void Application::run() {
process_mouse_input();
m_camera->update(m_time_passed);
m_time_passed = m_stopwatch.time_step();
check_octree_collisions();
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/vulkan-renderer/renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,12 @@ void VulkanRenderer::recreate_swapchain() {
m_image_available_semaphore = std::make_unique<wrapper::Semaphore>(*m_device, "Image available semaphore");
m_rendering_finished_semaphore = std::make_unique<wrapper::Semaphore>(*m_device, "Rendering finished semaphore");

m_camera = std::make_unique<Camera>(glm::vec3(3.0f, 2.0f, 1.0f), 230.0f, -20.0f,
m_camera = std::make_unique<Camera>(glm::vec3(5.0f, 5.0f, 10.0f), 250.0f, 0.0f,
static_cast<float>(m_window->width()), static_cast<float>(m_window->height()));

m_camera->set_movement_speed(5.0f);
m_camera->set_rotation_speed(0.5f);

m_imgui_overlay.reset();
m_imgui_overlay = std::make_unique<ImGUIOverlay>(*m_device, *m_swapchain);
}
Expand Down
Loading

0 comments on commit 99d1a5d

Please sign in to comment.