💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

136 lines
3.5 KiB

/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include <engine/physics/kinematics/colliders/mesh-collider.hpp>
#include <engine/debug/log.hpp>
#include <engine/geom/intersection.hpp>
#include <engine/geom/brep/brep-operations.hpp>
#include <limits>
namespace physics {
mesh_collider::mesh_collider(std::shared_ptr<mesh_type> mesh)
{
set_mesh(mesh);
}
void mesh_collider::set_mesh(std::shared_ptr<mesh_type> mesh)
{
m_mesh = mesh;
if (m_mesh)
{
// Store pointer to mesh vertex positions
m_vertex_positions = &m_mesh->vertices().attributes().at<math::fvec3>("position");
// If mesh has no face normals
if (!m_mesh->faces().attributes().contains("normal"))
{
// Generate normals
// generate_face_normals(*m_mesh);
generate_vertex_normals(*m_mesh);
}
// Store pointer to mesh face normals
m_face_normals = &m_mesh->faces().attributes().at<math::fvec3>("normal");
}
else
{
m_vertex_positions = nullptr;
m_face_normals = nullptr;
}
rebuild_bvh();
}
void mesh_collider::rebuild_bvh()
{
if (m_mesh)
{
m_bvh.build(*m_mesh);
}
else
{
m_bvh.clear();
}
}
std::optional<std::tuple<float, std::uint32_t, math::fvec3>> mesh_collider::intersection(const geom::ray<float, 3>& ray) const
{
if (!m_mesh)
{
return std::nullopt;
}
std::size_t box_hit_count = 0;
std::size_t triangle_hit_count = 0;
float nearest_face_distance = std::numeric_limits<float>::infinity();
std::uint32_t nearest_face_index;
// For each BVH leaf node that intersects ray
m_bvh.visit
(
ray,
[&](std::uint32_t index)
{
++box_hit_count;
// If ray is facing backside of face
if (math::dot((*m_face_normals)[index], ray.direction) > 0.0f)
{
// Ignore face
return;
}
// Get pointer to mesh face from BVH primitive index
geom::brep_face* face = m_mesh->faces()[index];
// Get face vertex positions
auto loop = face->loops().begin();
const auto& a = (*m_vertex_positions)[loop->vertex()->index()];
const auto& b = (*m_vertex_positions)[(++loop)->vertex()->index()];
const auto& c = (*m_vertex_positions)[(++loop)->vertex()->index()];
// If ray intersects face
if (const auto intersection = geom::intersection(ray, a, b, c))
{
++triangle_hit_count;
// If distance to point of intersection is nearer than nearest intersection
float t = std::get<0>(*intersection);
if (t < nearest_face_distance)
{
// Update nearest intersection
nearest_face_distance = t;
nearest_face_index = index;
}
}
}
);
// debug::log::debug("mesh collider intersection test:\n\tboxes hit: {}\n\ttriangles hit: {}", box_hit_count, triangle_hit_count);
if (!triangle_hit_count)
{
return std::nullopt;
}
return std::make_tuple(nearest_face_distance, nearest_face_index, (*m_face_normals)[nearest_face_index]);
}
} // namespace physics