our_dick/include/egn/collision.hpp
2022-02-10 16:56:01 -08:00

194 lines
7.8 KiB
C++

/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OUR_DICK_ENGINE_COLLISION_HPP
#define OUR_DICK_ENGINE_COLLISION_HPP
#include "base_types.hpp"
namespace egn{
//How far away before a collision is considered to have occurred
static constexpr float default_collision_epsilon = 1.0e-10f;
class collidable_visitor;
//Anything which derives from this is considered collidable and must be comparible to any other collidable
class collidable_iface
{
public:
//Check if this and the other collidable have collided
virtual bool check_collision(const collidable_iface& c, float epsilon)const = 0;
virtual void accept_visitor(collidable_visitor& v)const = 0;
};
//CRTP collidable to allow the collision checks to determine the concrete type of the collidable
template<typename Derived>
class collidable : public collidable_iface
{
public:
bool check_collision(const collidable_iface& c, float epsilon = default_collision_epsilon)const override final;
private:
void accept_visitor(collidable_visitor& v)const override final;
};
namespace collision{
//Basic collidable objects
//A 1D point
class point : public egn::point, public collidable<point>
{
public:
using egn::point::point;
using egn::point::operator=;
};
//A 2D line
class line_segment : public egn::line_segment, public collidable<line_segment>{};
//A 2D rectangle
class rectangle : public egn::rectangle, public collidable<rectangle>{};
//A 3D bounding box
class aabb : public egn::aabb, public collidable<aabb>
{
public:
using egn::aabb::aabb;
using egn::aabb::operator=;
aabb(const egn::aabb& b);
aabb& operator=(const egn::aabb& b);
};
//A 3D sphere
class sphere : public egn::sphere, public collidable<sphere>
{
public:
using egn::sphere::sphere;
using egn::sphere::operator=;
sphere(const egn::sphere& b);
sphere& operator=(const egn::sphere& b);
};
}
//base class for collision visitor to provide polymorphic access to visitation functions
class collidable_visitor
{
public:
virtual void visit(const collision::aabb& a) = 0;
virtual void visit(const collision::sphere& a) = 0;
virtual void visit(const collision::line_segment& a) = 0;
virtual void visit(const collision::rectangle& a) = 0;
virtual void visit(const collision::point& p) = 0;
};
//Templated visitor to allow checking collision from polymorphic types
template<typename T>
class collision_visitor : public collidable_visitor
{
private:
const T& m_l;
float m_epsilon;
bool m_result = false;
public:
constexpr explicit collision_visitor(const T& l, float epsilon);
void visit(const collision::aabb& a)override;
void visit(const collision::sphere& a)override;
void visit(const collision::line_segment& a)override;
void visit(const collision::rectangle& a)override;
void visit(const collision::point& p)override;
constexpr bool result()const;
};
//Concrete collision detection that is eventually called
//bool check_collision(const collidable_iface& l, const collidable_iface& r, float epsilon = default_collision_epsilon);
bool check_collision(const aabb& l, const aabb& r, float epsilon = default_collision_epsilon);
bool check_collision(const aabb& l, const sphere& r, float epsilon = default_collision_epsilon);
bool check_collision(const aabb& l, const line_segment& r, float epsilon = default_collision_epsilon);
bool check_collision(const aabb& l, const rectangle& r, float epsilon = default_collision_epsilon);
bool check_collision(const aabb& l, const point& r, float epsilon = default_collision_epsilon);
bool check_collision(const sphere& l, const sphere& r, float epsilon = default_collision_epsilon);
bool check_collision(const sphere& l, const aabb& r, float epsilon = default_collision_epsilon);
bool check_collision(const sphere& l, const line_segment& r, float epsilon = default_collision_epsilon);
bool check_collision(const sphere& l, const rectangle& r, float epsilon = default_collision_epsilon);
bool check_collision(const sphere& l, const point& r, float epsilon = default_collision_epsilon);
bool check_collision(const line_segment& l, const line_segment& r, float epsilon = default_collision_epsilon);
bool check_collision(const line_segment& l, const aabb& r, float epsilon = default_collision_epsilon);
bool check_collision(const line_segment& l, const sphere& r, float epsilon = default_collision_epsilon);
bool check_collision(const line_segment& l, const rectangle& r, float epsilon = default_collision_epsilon);
bool check_collision(const line_segment& l, const point& r, float epsilon = default_collision_epsilon);
bool check_collision(const rectangle& l, const rectangle& r, float epsilon = default_collision_epsilon);
bool check_collision(const rectangle& l, const aabb& r, float epsilon = default_collision_epsilon);
bool check_collision(const rectangle& l, const sphere& r, float epsilon = default_collision_epsilon);
bool check_collision(const rectangle& l, const line_segment& r, float epsilon = default_collision_epsilon);
bool check_collision(const rectangle& l, const point& r, float epsilon = default_collision_epsilon);
bool check_collision(const point& l, const point& r, float epsilon = default_collision_epsilon);
bool check_collision(const point& l, const aabb& r, float epsilon = default_collision_epsilon);
bool check_collision(const point& l, const sphere& r, float epsilon = default_collision_epsilon);
bool check_collision(const point& l, const line_segment& r, float epsilon = default_collision_epsilon);
bool check_collision(const point& l, const rectangle& r, float epsilon = default_collision_epsilon);
//Check collision between this and c via a visitor
template<typename Derived>
bool collidable<Derived>::check_collision(const collidable_iface& c, float epsilon)const{
collision_visitor<Derived> vis(static_cast<const Derived&>(*this), epsilon); //Determine our true type for visitor
c.accept_visitor(vis); //have c give its true type to our visitor and check for collision
return vis.result();
}
template<typename Derived>
void collidable<Derived>::accept_visitor(collidable_visitor& v)const{
v.visit(static_cast<const Derived&>(*this)); //Give the visitor our true type to allow calling correct collision check function
}
template<typename T>
constexpr collision_visitor<T>::collision_visitor(const T& l, float epsilon):
m_l(l),
m_epsilon(epsilon){}
//Call correct concrete function for given collision
template<typename T>
void collision_visitor<T>::visit(const collision::aabb& a){
m_result = check_collision(m_l, a, m_epsilon);
}
template<typename T>
void collision_visitor<T>::visit(const collision::sphere& a){
m_result = check_collision(m_l, a, m_epsilon);
}
template<typename T>
void collision_visitor<T>::visit(const collision::line_segment& a){
m_result = check_collision(m_l, a, m_epsilon);
}
template<typename T>
void collision_visitor<T>::visit(const collision::rectangle& a){
m_result = check_collision(m_l, a, m_epsilon);
}
template<typename T>
void collision_visitor<T>::visit(const collision::point& a){
m_result = check_collision(m_l, a, m_epsilon);
}
template<typename T>
constexpr bool collision_visitor<T>::result()const{
return m_result;
}
}
#endif