#pragma once #include #include #include #include #include #include #include #include #include "globals.hpp" //! Returns (f(ax, bx) && f(ay, by)) || (f(ax, by) && f(ay, bx)) //! Useful for instance when you want to compare two pairs if they contain the same elements, but don't care about the order //! (and we provide an overload for that of course) template bool order_agnostic_compare(First const& ax, Second const& ay, Third const& bx, Fourth const& by, CompareFunction const& f){ return (f(ax, bx) && f(ay, by)) || (f(ax, by) && f(ay, bx)); } //TODO: Figure out what the syntax of this was, then express equal, less{,or equal}, greater{, or equal}. //using order_agnostic_equalness = order_agnostic_compare; template bool order_agnostic_equal(First const& ax, Second const& ay, Third const& bx, Fourth const& by){ return ((ax == bx && ay == by) || (ax == by && ay == bx)); } template bool order_agnostic_equal(std::pair const& a, std::pair const& b){ return order_agnostic_equalness(a.first, a.second, b.first, b.second); } namespace games { using namespace motor; struct Base { static ResourceCache resource_cache; std::shared_ptr scene = std::make_shared(); int window_width; int window_height; Fbo fbo{window_width, window_height}; float time = 0.0; // So all this is needed to set up Bullet! // You pay for modularity with a lot of code, but perhaps we can wrap this in our own motor::world or something. std::unique_ptr collison_configuration{new btDefaultCollisionConfiguration()}; std::unique_ptr collision_dispatcher{new btCollisionDispatcher(collison_configuration.get())}; btVector3 const world_minimum = btVector3(-1000,-1000,-1000); btVector3 const world_maximum = btVector3(1000,1000,1000); std::unique_ptr overlapping_pair_cache{new btAxisSweep3(world_minimum, world_maximum)}; std::unique_ptr constraint_solver{new btSequentialImpulseConstraintSolver()}; std::unique_ptr dynamics_world{new btDiscreteDynamicsWorld(collision_dispatcher.get(), overlapping_pair_cache.get(), constraint_solver.get(), collison_configuration.get())}; Base(int window_width, int window_height); virtual ~Base(); virtual bool has_ended(); private: // Contains objects that were colliding in the previous update std::vector> bullet_objects_in_collision_previous_frame{}; public: virtual void update(float const dt, std::map keys_went_down, std::map is_pressed, std::map keys_went_up); void update_bullet(float const dt); void check_collisions(); //! A callback for when a collision between two objects occurs. //! TODO: Expand to N-objects? //! The pointers aren't const here so we can call f with the two arguments struct dual_collision_callback { //! The constructor. //! CallbackFunctor must be a valid ctor-parameter for @member f template dual_collision_callback(std::shared_ptr first_, std::shared_ptr second_, CallbackFunctor&& c) : first(first_) , second(second_) , f(c) {} std::shared_ptr first; std::shared_ptr second; std::function f; void operator()() { f(first, second); } }; struct single_collision_callback { template single_collision_callback(std::shared_ptr b_, CallbackFunctor&& c) : b(b_) , f(c) {} std::shared_ptr b; std::function f; void operator()() { f(b); } }; void register_on_enter_collision_callback(dual_collision_callback&& c){ on_enter_dual_collision_callback.emplace_back(std::move(c)); } void register_on_exit_collision_callback(dual_collision_callback&& c){ on_exit_dual_collision_callback.emplace_back(std::move(c)); } void register_on_enter_collision_callback(single_collision_callback&& c){ on_enter_single_collision_callback.emplace_back(std::move(c)); } void register_on_exit_collision_callback(single_collision_callback&& c){ on_exit_single_collision_callback.emplace_back(std::move(c)); } private: std::vector on_enter_dual_collision_callback{}; std::vector on_exit_dual_collision_callback{}; std::vector on_enter_single_collision_callback{}; std::vector on_exit_single_collision_callback{}; void bullet_objects_entered_collision(btCollisionObject const* x, btCollisionObject const* y){ for(auto& c : on_enter_dual_collision_callback){ if(order_agnostic_equal(c.first->get_physical_body(), c.second->get_physical_body(), x, y)){ c(); } } for(auto& c : on_enter_single_collision_callback){ if(c.b->get_physical_body() == x || c.b->get_physical_body() == y){ c(); } } } void bullet_objects_exited_collision(btCollisionObject const* x, btCollisionObject const* y){ for(auto& c : on_exit_dual_collision_callback){ if(order_agnostic_equal(c.first->get_physical_body(), c.second->get_physical_body(), x, y)){ c(); } } for(auto& c : on_exit_single_collision_callback){ if(c.b->get_physical_body() == x || c.b->get_physical_body() == y){ c(); } } } public: virtual void draw(); }; } // namespace games