Skip to content

Commit

Permalink
[collision] Simplify code
Browse files Browse the repository at this point in the history
  • Loading branch information
IAmNotHanni committed Apr 4, 2021
1 parent 046755c commit 31b0273
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 72 deletions.
34 changes: 16 additions & 18 deletions include/inexor/vulkan-renderer/world/collision.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <vector>

namespace inexor::vulkan_renderer::world {

using cube_side = std::tuple<std::string, glm::vec3>;

// TODO: Implement PointCubeCollision
class RayCubeCollision {
const Cube &m_cube;
Expand All @@ -25,6 +28,14 @@ class RayCubeCollision {
std::size_t m_selected_face_index{0};
std::string m_face_name;

std::array<glm::vec3, 6> m_face_normals;
std::array<glm::vec3, 6> m_face_centers;

const std::array<cube_side, 6> m_cube_sides{
cube_side{"left", glm::vec3(-1.0f, 0.0f, 0.0f)}, cube_side{"right", glm::vec3(1.0f, 0.0f, 0.0f)},
cube_side{"front", glm::vec3(0.0f, -1.0f, 0.0f)}, cube_side{"back", glm::vec3(0.0f, 1.0f, 0.0f)},
cube_side{"top", glm::vec3(0.0f, 0.0f, 1.0f)}, cube_side{"bottom", glm::vec3(0.0f, 0.0f, -1.0f)}};

/// @brief Calculate the intersection point between a ray and a plane.
/// @note Surprisingly glm does not implement such a function.
/// @param plane_pos The position of the plane.
Expand All @@ -35,25 +46,12 @@ class RayCubeCollision {
[[nodiscard]] glm::vec3 ray_plane_intersection(glm::vec3 plane_pos, glm::vec3 plane_normal, glm::vec3 ray_pos,
glm::vec3 ray_dir);

/// @brief Calculate the angle between a plane and a ray.
/// @param plane_pos The position of the plane.
/// @param plane_normal The normal vector of the plane.
/// @param ray_pos A point on the ray.
/// @param ray_dir The direction of the ray.
/// @return The angle between the ray and the plane.
[[nodiscard]] float plane_ray_sin_of_angle(glm::vec3 plane_pos, glm::vec3 plane_normal, glm::vec3 ray_pos,
glm::vec3 ray_dir);

/// @brief Determine which one of the 6 faces is in selection.
/// @param ray_pos The start position of the ray.
/// @param ray_dir The direction of the ray.
void get_selected_face(glm::vec3 ray_pos, glm::vec3 ray_dir);

/// @brief Determine which one of the 4 corners is in selection.
/// @param ray_pos The start position of the ray.
/// @brief Check of the specified face index is visible.
/// @param face_index The index of the face.
/// @param ray_pos The position of the ray.
/// @param ray_dir The direction of the ray.
/// @return The selected corner index.
[[nodiscard]] std::uint32_t get_selected_corner(glm::vec3 ray_pos, glm::vec3 ray_dir) const;
/// @return ``True`` if the face is visible.
[[nodiscard]] bool is_face_visible(std::size_t face_index, glm::vec3 ray_pos, glm::vec3 ray_dir);

public:
/// @brief Constructor which calculates selected face and selected corner on that face.
Expand Down
80 changes: 26 additions & 54 deletions src/vulkan-renderer/world/collision.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,56 +6,20 @@
#include <algorithm>
#include <cassert>
#include <cmath>
#include <tuple>

namespace inexor::vulkan_renderer::world {

RayCubeCollision::RayCubeCollision(const Cube &cube, const glm::vec3 ray_pos, const glm::vec3 ray_dir) : m_cube(cube) {
get_selected_face(ray_pos, ray_dir);
}

float RayCubeCollision::plane_ray_sin_of_angle(const glm::vec3 plane_pos, const glm::vec3 plane_normal,
const glm::vec3 ray_pos, const glm::vec3 ray_dir) {
return glm::dot(plane_normal, ray_dir) / ((glm::length(plane_normal) * glm::length(ray_dir)));
}

// TODO: Move to a math helper file!
glm::vec3 RayCubeCollision::ray_plane_intersection(const glm::vec3 plane_pos, const glm::vec3 plane_normal,
const glm::vec3 ray_pos, const glm::vec3 ray_dir) {
return ray_pos - ray_dir * (glm::dot((ray_pos - plane_pos), plane_normal) / glm::dot(ray_dir, plane_normal));
}

void RayCubeCollision::get_selected_face(const glm::vec3 ray_pos, const glm::vec3 ray_dir) {
// TODO: Take rotations of the cube into account when calculating face center positions!
// Then the vectors need to be normalized as well!
using cube_side = std::tuple<std::string, glm::vec3>;

const std::array cube_sides{
cube_side{"left", glm::vec3(-1.0f, 0.0f, 0.0f)}, cube_side{"right", glm::vec3(1.0f, 0.0f, 0.0f)},
cube_side{"front", glm::vec3(0.0f, -1.0f, 0.0f)}, cube_side{"back", glm::vec3(0.0f, 1.0f, 0.0f)},
cube_side{"top", glm::vec3(0.0f, 0.0f, 1.0f)}, cube_side{"bottom", glm::vec3(0.0f, 0.0f, -1.0f)}};
m_face_centers = {((m_cube.size() / 2) * std::get<1>(m_cube_sides[0])) + m_cube.center(),
((m_cube.size() / 2) * std::get<1>(m_cube_sides[1])) + m_cube.center(),
((m_cube.size() / 2) * std::get<1>(m_cube_sides[2])) + m_cube.center(),
((m_cube.size() / 2) * std::get<1>(m_cube_sides[3])) + m_cube.center(),
((m_cube.size() / 2) * std::get<1>(m_cube_sides[4])) + m_cube.center(),
((m_cube.size() / 2) * std::get<1>(m_cube_sides[5])) + m_cube.center()};

const float half_size = m_cube.size() / 2;

const std::array<glm::vec3, 6> face_centers{(half_size * std::get<1>(cube_sides[0])) + m_cube.center(),
(half_size * std::get<1>(cube_sides[1])) + m_cube.center(),
(half_size * std::get<1>(cube_sides[2])) + m_cube.center(),
(half_size * std::get<1>(cube_sides[3])) + m_cube.center(),
(half_size * std::get<1>(cube_sides[4])) + m_cube.center(),
(half_size * std::get<1>(cube_sides[5])) + m_cube.center()};

const std::array<glm::vec3, 6> face_center_normals{std::get<1>(cube_sides[0]), std::get<1>(cube_sides[1]),
std::get<1>(cube_sides[2]), std::get<1>(cube_sides[3]),
std::get<1>(cube_sides[4]), std::get<1>(cube_sides[5])};

const std::array<float, 8> cube_face_to_camera_angles{
plane_ray_sin_of_angle(face_centers[0], face_center_normals[0], ray_pos, ray_dir),
plane_ray_sin_of_angle(face_centers[1], face_center_normals[1], ray_pos, ray_dir),
plane_ray_sin_of_angle(face_centers[2], face_center_normals[2], ray_pos, ray_dir),
plane_ray_sin_of_angle(face_centers[3], face_center_normals[3], ray_pos, ray_dir),
plane_ray_sin_of_angle(face_centers[4], face_center_normals[4], ray_pos, ray_dir),
plane_ray_sin_of_angle(face_centers[5], face_center_normals[5], ray_pos, ray_dir),
};
m_face_normals = {std::get<1>(m_cube_sides[0]), std::get<1>(m_cube_sides[1]), std::get<1>(m_cube_sides[2]),
std::get<1>(m_cube_sides[3]), std::get<1>(m_cube_sides[4]), std::get<1>(m_cube_sides[5])};

// TOOD: Don't calculate all this data for every construction of an object. Buffer somehow!
// TODO: Automatic tests for math functions.
Expand All @@ -65,32 +29,40 @@ void RayCubeCollision::get_selected_face(const glm::vec3 ray_pos, const glm::vec
for (std::size_t i = 0; i < 6; i++) {
// Check the angle between the face's normal vector and the camera's direction vector.
// Only check for a collision of the side of the cube we are checking currently is facing the camera.
if (cube_face_to_camera_angles[i] < 0.0f) {
if (is_face_visible(i, ray_pos, ray_dir)) {
const auto intersection =
ray_plane_intersection(face_centers[i], std::get<1>(cube_sides[i]), ray_pos, ray_dir);
ray_plane_intersection(m_face_centers[i], std::get<1>(m_cube_sides[i]), ray_pos, ray_dir);

// TODO: Explain face collision detection mechanism in docs.
const auto distance = glm::distance(m_cube.center(), intersection);

if (distance < m_shortest_distance) {
m_shortest_distance = distance;
m_selected_face_normal = std::get<1>(cube_sides[i]);
m_selected_face_normal = std::get<1>(m_cube_sides[i]);
m_selected_face_index = i;
m_intersection = intersection;
m_face_name = std::get<0>(cube_sides[i]);
m_face_name = std::get<0>(m_cube_sides[i]);
}
}
}

spdlog::trace("intersection {} {} {} at {} (normal: {} {} {}).", m_face_name.c_str(), m_intersection.x,
m_intersection.y, m_intersection.z, m_selected_face_normal.x, m_selected_face_normal.y,
spdlog::trace("intersection {} {} {} at {} (normal: {} {} {}).", m_intersection.x, m_intersection.y,
m_intersection.z, m_face_name.c_str(), m_selected_face_normal.x, m_selected_face_normal.y,
m_selected_face_normal.z);

// TODO: Determine selected corner on the face.
}

bool RayCubeCollision::is_face_visible(const std::size_t face_index, const glm::vec3 ray_pos, const glm::vec3 ray_dir) {
const auto sin_of_angle = glm::dot(m_face_normals[face_index], ray_dir) /
((glm::length(m_face_normals[face_index]) * glm::length(ray_dir)));
return sin_of_angle < 0.0f;
}

std::uint32_t RayCubeCollision::get_selected_corner(const glm::vec3 ray_pos, const glm::vec3 ray_dir) const {
// TODO: Implement!
// TODO: This must be the same edge order as defined in the cube class.
return 0;
// TODO: Move to a math helper file!
glm::vec3 RayCubeCollision::ray_plane_intersection(const glm::vec3 plane_pos, const glm::vec3 plane_normal,
const glm::vec3 ray_pos, const glm::vec3 ray_dir) {
return ray_pos - ray_dir * (glm::dot((ray_pos - plane_pos), plane_normal) / glm::dot(ray_dir, plane_normal));
}

} // namespace inexor::vulkan_renderer::world

0 comments on commit 31b0273

Please sign in to comment.