From b949a9d5a85c2cc56011edb56687434cf8784855 Mon Sep 17 00:00:00 2001 From: Joshua Moerman Date: Tue, 16 Apr 2013 15:29:55 +0200 Subject: [PATCH] Updated to new motor things. Made a empty client. --- .gitmodules | 3 + CMakeLists.txt | 6 + contrib/libwebsockets | 1 + sdlgame.sublime-project | 3 +- src/Base.cpp | 219 +++++++++++----- src/Base.hpp | 177 +++---------- src/CMakeLists.txt | 10 +- src/ai_world.cpp | 25 ++ src/ai_world.hpp | 21 ++ src/beamer/beamer.cpp | 303 ----------------------- src/beamer/beamer.hpp | 64 ----- src/beamer/coordinates.h | 45 ---- src/beamer/procedural_textures.h | 39 --- src/beamer/simplex.h | 131 ---------- src/client/client.cpp | 109 ++++++++ src/client/client.hpp | 29 +++ src/{beamer_main.cpp => client_main.cpp} | 4 +- src/generic_main.hpp | 80 ++++-- src/globals.hpp | 4 + src/input.hpp | 44 ++++ src/physics.cpp | 185 ++++++++++++++ src/physics.hpp | 247 ++++++++++++++++++ src/physics_test_triangle_mesh.hpp | 39 +++ src/world.hpp | 12 + 24 files changed, 997 insertions(+), 803 deletions(-) create mode 100644 .gitmodules create mode 160000 contrib/libwebsockets create mode 100644 src/ai_world.cpp create mode 100644 src/ai_world.hpp delete mode 100644 src/beamer/beamer.cpp delete mode 100644 src/beamer/beamer.hpp delete mode 100644 src/beamer/coordinates.h delete mode 100644 src/beamer/procedural_textures.h delete mode 100644 src/beamer/simplex.h create mode 100644 src/client/client.cpp create mode 100644 src/client/client.hpp rename src/{beamer_main.cpp => client_main.cpp} (86%) create mode 100644 src/input.hpp create mode 100644 src/physics.cpp create mode 100644 src/physics.hpp create mode 100644 src/physics_test_triangle_mesh.hpp create mode 100644 src/world.hpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a4e2a7c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "contrib/libwebsockets"] + path = contrib/libwebsockets + url = git://github.com/warmcat/libwebsockets.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b253d4..0cb837a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,4 +43,10 @@ include(motor/cmake/join.cmake) join("${warnings}" " " warnings) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${warnings}") + +# websockets lib +set(WITHOUT_TESTAPPS ON CACHE INTERNAL "" FORCE) +add_subdirectory(${PROJECT_SOURCE_DIR}/contrib/libwebsockets/) +include_directories(SYSTEM "${PROJECT_SOURCE_DIR}/contrib/libwebsockets/") + add_subdirectory("src") diff --git a/contrib/libwebsockets b/contrib/libwebsockets new file mode 160000 index 0000000..2577c83 --- /dev/null +++ b/contrib/libwebsockets @@ -0,0 +1 @@ +Subproject commit 2577c831f41700d8162b2509198482e4a438b332 diff --git a/sdlgame.sublime-project b/sdlgame.sublime-project index f1d84bb..c7fe87f 100644 --- a/sdlgame.sublime-project +++ b/sdlgame.sublime-project @@ -38,6 +38,7 @@ "-Wno-unused-parameter", "-I${folder:${project_path:sdlgame.sublime-project}}", "-I${folder:${project_path:sdlgame.sublime-project}}/motor", + "-isystem${folder:${project_path:sdlgame.sublime-project}}/contrib/libwebsockets/", "-isystem${folder:${project_path:sdlgame.sublime-project}}/motor/contrib/assimp-3.0.1270/include", "-isystem${folder:${project_path:sdlgame.sublime-project}}/motor/contrib/soil", "-isystem${folder:${project_path:sdlgame.sublime-project}}/motor/contrib/bullet/src", @@ -50,8 +51,8 @@ "-stdlib=libc++", "-DDEBUG=1", "-nostdinc", + "-isystem/Users/joshua/Documents/Code/libcxx/include/", "-isystem/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk", - "-isystem/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/c++/v1", "-isystem/System/Library/Frameworks/OpenGL.framework/Versions/Current/Header", "-isystem/usr/local/include", "-isystem/sw/include", diff --git a/src/Base.cpp b/src/Base.cpp index 4bff3fb..553482d 100644 --- a/src/Base.cpp +++ b/src/Base.cpp @@ -1,82 +1,179 @@ - #include "Base.hpp" +#include "input.hpp" + +#include + +#include namespace games { using namespace motor; -Base::Base(int window_width_, int window_height_) -: window_width(window_width_), window_height(window_height_) -{} +std::ostream& operator<<(std::ostream& out, Input::Mouse::Button const& rh){ + using Button = Input::Mouse::Button; + switch(rh){ + case Button::left: return out << "left"; + case Button::middle: return out << "middle"; + case Button::right: return out << "right"; + case Button::wheeldown: return out << "downscroll"; + case Button::wheelup: return out << "upscroll"; + default: { + return out << rh; + } + } +} -Base::~Base(){} +Base::Base(int window_width_, int window_height_, std::shared_ptr& active_base_) +: window_width(window_width_) +, window_height(window_height_) +, active_base(active_base_) +{ + physics_scene->used_shader = physics_scene->flat_shader; +} + +Base::~Base() +{ + resource_cache.free_all_resources(); + motor::resource_cache.free_all_resources(); +} bool Base::has_ended(){ return false; } -void Base::update(float const dt, std::map __attribute__((unused)) keys_went_down, std::map __attribute__((unused)) is_pressed, std::map __attribute__((unused)) keyw_went_up){ +void Base::update(float const dt, Input input){ time += dt; - - update_bullet(dt); - check_collisions(); -} - -void Base::update_bullet(float const dt){ - dynamics_world->stepSimulation(dt, 20); - - for (int j=dynamics_world->getNumCollisionObjects()-1; j>=0 ;j--) - { - btCollisionObject* obj = dynamics_world->getCollisionObjectArray()[j]; - btRigidBody* body = btRigidBody::upcast(obj); - if (body && body->getMotionState()) - { - if(body->getUserPointer() != nullptr){ - btTransform trans = body->getWorldTransform(); - Body* b = static_cast(body->getUserPointer()); - b->sync_from_bullet(trans); - } - } + + if(physics_update){ + world.physics.update(dt, 10); } -} - -void Base::check_collisions(){ - std::vector> bullet_objects_colliding; - int const manifolds = dynamics_world->getDispatcher()->getNumManifolds(); - for(int manifold_index = 0; manifold_index < manifolds; ++manifold_index){ - auto const manifold = dynamics_world->getDispatcher()->getManifoldByIndexInternal(manifold_index); - - btCollisionObject const* first_object = manifold->getBody0(); - btCollisionObject const* second_object = manifold->getBody1(); - - bullet_objects_colliding.emplace_back(first_object, second_object); + + if(free_camera_mode){ + handle_camera(dt, input); } - for(auto const& c : bullet_objects_colliding){ - if(!astrant::contains(bullet_objects_in_collision_previous_frame, c)){ - bullet_objects_entered_collision(c.first, c.second); - } - } - - for(auto const& c : bullet_objects_in_collision_previous_frame){ - if(!astrant::contains(bullet_objects_colliding, c)){ - bullet_objects_exited_collision(c.first, c.second); - } - } - - bullet_objects_in_collision_previous_frame = bullet_objects_colliding; + world.ai.update(dt, input, world); } void Base::draw(){ - //First, clear the screen - fbo.unbind(); - moggle::gl::set_viewport(0, 0, window_width, window_height); - moggle::gl::set_clear_color(0.1f, 0.1f, 0.1f, 1.0f); - moggle::gl::clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - moggle::gl::enable(GL_DEPTH_TEST); - moggle::gl::enable(GL_BLEND); - moggle::gl::blend_equation(GL_FUNC_ADD); - moggle::gl::blend_function(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - scene->draw(); + try { + fbo.unbind(); + moggle::gl::set_viewport(0, 0, window_width, window_height); + moggle::gl::set_clear_color(0.1f, 0.1f, 0.1f, 1.0f); + moggle::gl::clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + moggle::gl::enable(GL_DEPTH_TEST); + moggle::gl::enable(GL_BLEND); + moggle::gl::blend_function(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if(draw_physics_world){ + physics_scene->camera = world.scene->camera; + + for(auto mesh : world.physics.get_debug_draw_result(1 | 2 | 8)){ + assert(mesh != nullptr); + + auto const b = std::make_shared(mesh); + physics_scene->add(b); + } + + physics_scene->draw(); + physics_scene->remove_all_bodies(); + } + + if(!(draw_physics_world && draw_only_physics_world)){ + world.scene->draw(); + } + + static constexpr bool print_screen = false; + if(print_screen){ + std::vector> pixels(window_width * window_height); + moggle::gl::clear_error(); + glReadPixels(0, 0, window_width, window_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); + moggle::gl::print_error(); + for(int y = 0; y < window_height; ++y){ + for(int x = 0; x < window_width; ++x){ + for(int i = 0; i < 4; ++i){ + std::cout << std::setw(3) << std::setfill('0') << size_t(pixels[y * window_height + x][i]) << ((i != 3) ? " " : ""); + } + std::cout << "|"; + } + std::cout << std::endl; + } + } + } catch (std::runtime_error& e){ + CERR << "Something went wrong during drawing: " << e.what() << std::endl; + } + + try { + moggle::gl::clear(GL_DEPTH_BUFFER_BIT); + world.hud->used_shader = world.hud->flat_shader; + world.hud->draw(); + } catch (std::runtime_error& e){ + CERR << "Something went wrong during drawing: " << e.what() << std::endl; + } +} + +motor::Body* Base::raycast(moggle::vector2 const screen_position) const{ + motor::Position const view = {0, 0, window_width, window_height}; + + motor::Position in = {screen_position[0], screen_position[1], 0.0, 1.f}; + + auto const near_point = world.scene->camera.unproject(in, view); + in[2] = 1.0f; + auto const far_point = world.scene->camera.unproject(in, view); + return world.physics.raycast(near_point, far_point); +} + +void Base::handle_camera(const float dt, Input input) +{ + SDL_ShowCursor(SDL_DISABLE); + + float const distance = camera_speed * dt; + float const rotation = camera_rotation_speed * dt; + + auto const move = [](Camera& p, Position const displacement){ + p.set_position(p.get_position() + displacement); + }; + + Camera& b = world.scene->camera; + + auto const m = inverse(b.get_rotation_matrix()); + Position const down = m*Position{0, -distance, 0}; + Position const up = m*Position{0, distance, 0}; + Position const left = m*Position{-distance, 0, 0}; + Position const right = m*Position{distance, 0, 0}; + Position const backward = m*Position{0, 0, distance}; + Position const forward = m*Position{0, 0, -distance}; + + if(input.keys_pressed[SDLK_w]){ + move(b, forward); + } + + if(input.keys_pressed[SDLK_s]){ + move(b, backward); + } + + if(input.keys_pressed[SDLK_d]){ + move(b, right); + } + + if(input.keys_pressed[SDLK_a]){ + move(b, left); + } + + if(input.keys_pressed[SDLK_t]){ + move(b, up); + } + + if(input.keys_pressed[SDLK_g]){ + move(b, down); + } + + b.add_pitch(-input.mouse.difference[1] * rotation); + b.add_yaw(-input.mouse.difference[0] * rotation); + + input.mouse.position[0] = window_width/2; + input.mouse.position[1] = window_height/2; + SDL_WarpMouse(input.mouse.position[0], input.mouse.position[1]); + } ResourceCache Base::resource_cache; diff --git a/src/Base.hpp b/src/Base.hpp index d098c1a..d24aa71 100644 --- a/src/Base.hpp +++ b/src/Base.hpp @@ -7,165 +7,70 @@ #include #include #include +#include #include -#include - #include "globals.hpp" +#include "physics.hpp" +#include "ai_world.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); -} - +#include "input.hpp" +#include "world.hpp" namespace games { -using namespace motor; + +std::ostream& operator<<(std::ostream& out, Input::Mouse::Button const& rh); struct Base { - static ResourceCache resource_cache; - std::shared_ptr scene = std::make_shared(); + static motor::ResourceCache resource_cache; + std::shared_ptr physics_scene = std::make_shared(); + + World world; + std::shared_ptr& scene{world.scene}; + std::shared_ptr& hud{world.hud}; + motor::AIWorld& ai{world.ai}; + motor::Physics& physics{world.physics}; int window_width; int window_height; - Fbo fbo{window_width, window_height}; + motor::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())}; + //! If true, will draw the physics world to the seen. Also see draw_only_physics_world + bool draw_physics_world = false; - Base(int window_width, int window_height); + //! If false, will not automatically call motor::Physics::update, which can be useful e.g. when want to step the physics world to catch some bug. + bool physics_update = true; + + //! Whether to still draw the real world or not when draw_physics_world is true + bool draw_only_physics_world = false; + + //! You can use this to change the Base that is draw/updated in the main loop + std::shared_ptr& active_base; + + Base(int window_width, int window_height, std::shared_ptr& active_base); virtual ~Base(); virtual bool has_ended(); -private: - // Contains objects that were colliding in the previous update - std::vector> bullet_objects_in_collision_previous_frame{}; + virtual void update(float const dt, Input input); -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)); + //! Creates a body and adds it to the world + template + std::shared_ptr create_body(Args&&... args){ + auto b = std::make_shared(std::forward(args)...); + add_to_world(b); + return b; } - 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(); + motor::Body* raycast(moggle::vector2 const screen_position) const; + + bool free_camera_mode = false; + float camera_speed = 35.0f; + float camera_rotation_speed = M_PI * .2f; + void handle_camera(float const dt, Input input); }; } // namespace games diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eca4929..a935f59 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8) project(onegame) -find_package(Boost 1.48 REQUIRED) +find_package(Boost 1.48 COMPONENTS chrono REQUIRED) include_directories(${Boost_INCLUDE_DIRS}) find_package(OpenGL REQUIRED) @@ -55,9 +55,9 @@ include_directories(SYSTEM "${PROJECT_SOURCE_DIR}/../motor/contrib/freetype-gl") include_directories(SYSTEM "${PROJECT_SOURCE_DIR}/../motor/contrib/") include_directories(SYSTEM "${PROJECT_SOURCE_DIR}/../motor/moggle/include") -add_library(onegame_common Base.cpp) +add_library(onegame_common Base.cpp physics.cpp ai_world.cpp) set(all_friggin_libraries onegame_common ${boost} ${motor} ${moggle} ${assimp} ${zlib} ${opengl} ${sdl} ${openal} ${alure} ${graphics_libs} ${os_libs} ${glew}) -file(GLOB beamer_sources "beamer/*.cpp") -add_executable(beamer beamer_main.cpp ${beamer_sources}) -target_link_libraries(beamer ${all_friggin_libraries}) +file(GLOB client_sources "client/*.cpp") +add_executable(client client_main.cpp ${client_sources}) +target_link_libraries(client ${all_friggin_libraries} websockets) diff --git a/src/ai_world.cpp b/src/ai_world.cpp new file mode 100644 index 0000000..b6c9442 --- /dev/null +++ b/src/ai_world.cpp @@ -0,0 +1,25 @@ +#include "ai_world.hpp" + +#include +#include "physics.hpp" +#include + + +namespace motor { + void AIWorld::update(float dt, Input& input, World& world){ + auto bodies_copy = bodies; + for(auto& b : bodies_copy){ + if(b->ai){ + b->ai->update(dt, {*b, input, world}); + } + } + } + + void AIWorld::add(std::shared_ptr b){ + astrant::append(bodies, {b}); + } + + void AIWorld::remove(std::shared_ptr b){ + astrant::remove_element(bodies, b); + } +} diff --git a/src/ai_world.hpp b/src/ai_world.hpp new file mode 100644 index 0000000..bef2aea --- /dev/null +++ b/src/ai_world.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +struct Input; +struct World; + +namespace motor { + struct Body; + + struct AIWorld { + void update(float dt, Input& input, World& world); + + void add(std::shared_ptr b); + void remove(std::shared_ptr b); + + private: + std::vector> bodies; + }; +} diff --git a/src/beamer/beamer.cpp b/src/beamer/beamer.cpp deleted file mode 100644 index cdb1790..0000000 --- a/src/beamer/beamer.cpp +++ /dev/null @@ -1,303 +0,0 @@ -#include "beamer.hpp" -#include "simplex.h" -#include "coordinates.h" -#include "procedural_textures.h" - -namespace games { -using namespace motor; -using namespace std; - -Beamer::House::House(){ - model = std::make_shared(resource_cache.get_mesh(::bundle.get_model_path() + "house.obj")); - model->set_scale({0.2, 0.2, 0.2}); -} - -Beamer::Planet::Planet(float const waterlevel, float const displacement, Color const watercolor){ - srand(::time(0)); - - { - auto watermesh = std::make_shared(::bundle.get_model_path() + "planet.obj"); - - auto const & positions = watermesh->geometry->vertices()->attribute("position").buffer>(); - auto const & colors = watermesh->geometry->vertices()->attribute("color").buffer>(); - for(unsigned int i = 0; i < positions->size(); ++i){ - auto& p = positions->at(i); - auto& c = colors->at(i); - float r = watercolor[0] * (0.5 + 0.5*simplex_noise(1, p[0], p[1], p[2])); - float g = watercolor[1] * (0.5 + 0.5*simplex_noise(1, p[0], p[1], p[2])); - float b = watercolor[2]; - c = {r, g, b, 1.0}; - } - colors->mark_dirty(); - - water = std::make_shared(watermesh); - water->set_scale({radius*waterlevel, radius*waterlevel, radius*waterlevel}); - } - - { - auto planetmesh = std::make_shared(::bundle.get_model_path() + "planet.obj"); - - auto vertices = planetmesh->geometry->vertices(); - auto const & positions = vertices->attribute("position").buffer>(); - auto const & normals = vertices->attribute("normal").buffer>(); - auto const & colors = vertices->attribute("color").buffer>(); - - moggle::buffer> texcoordsbuf(positions->size()); - vertices->attribute("texture_coordinate", std::move(texcoordsbuf)); - - auto const & texcoords = planetmesh->geometry->vertices()->attribute("texture_coordinate").buffer>(); - for(unsigned int i = 0; i < positions->size(); ++i){ - auto& p = positions->at(i); - auto& n = normals->at(i); - auto& c = colors->at(i); - c = {0,0,0,0}; - auto& tc= texcoords->at(i); - - auto sp = to_spherical(moggle::vector3{p[0], p[1], p[2]}); - tc = {0.5 * sp[1] * M_1_PI, sp[2] * M_1_PI + 0.5}; - if(tc[0] < 0) tc[0] += 1.0; - - float const speed = 0.9; - float r = simplex_noise(1, p[0]*speed, p[1]*speed, p[2]*speed); - p += displacement * r * n; - } - auto texture = std::make_shared(create_noise_for_sphere(4, 4, [](moggle::vector3 position) -> moggle::hvector4{ - float r = simplex_noise(1, position[0], position[1], position[2]); - r += 1.0; - r *= 0.5; - if(r > 1) r = 1; - if(r < 0) r = 0; - if(r > 0.75) - return {r, r, r}; - else - return {0.2, 0.6, 0.5*r}; - })); - planetmesh->material->diffuse_map = texture; - positions->mark_dirty(); - normals->mark_dirty(); - - model = std::make_shared(planetmesh); - model->set_scale({radius, radius, radius}); - } - - int number_of_houses = astrant::random_value(5, 10); - for(int i = 0; i < number_of_houses; ++i){ - House house; - - float phi = astrant::random_value(0.0, 2 * M_PI); - float psi = astrant::random_value(-M_PI_2, M_PI_2); - - Position house_pos = from_spherical(moggle::vector3{radius, phi, psi}); - // COUT << "house at: " << house_pos << std::endl; - house.model->set_position(house_pos); - - houses.push_back(house); - } -} - -std::vector> Beamer::Planet::get_all_bodies() const { - std::vector> ret{model, water}; - for(auto& h : houses){ - ret.push_back(h.model); - } - return ret; -} - -struct Beamer::Player{ - // 3D speed, or 2D? - typedef moggle::vector3 Speed; - - Position position{0.0, 0.0, 5.0}; - Speed speed{0.0, 1.0, 0.0}; - float time{0}; - float flight_height{5.0}; - - void update(float dt){ - time += dt; - - // move - position += dt*speed; - - // clamp to sphere (we will later set it to its radius) - normalize(position); - - // accelerator with noise - float noisespeed = 0.03; - float x = simplex_noise(3, noisespeed*time, 0, 0); - float y = simplex_noise(3, 0, noisespeed*time, 0); - float z = simplex_noise(3, 0, 0, noisespeed*time); - moggle::vector3 change{x, y, z}; - speed += 100.0 * dt * change; - - // make speed follow the sphere - float p = dot(speed, position); - speed -= p * moggle::vector3(position); - normalize(speed); - - // put the ufo in the air :) - position *= flight_height; - } -}; - -bool Beamer::has_ended(){ - return false; -} - -void Beamer::reset_camera(){ - scene->camera.set_perspective(80.0f, 4.0f/3.0f, 1.0f, 1000.0f); - scene->camera.set_position({0, 0, 5}); -} - -void Beamer::reset_players(){ - player.reset(new Player()); -} - -void Beamer::reset(){ - reset_camera(); - reset_players(); -} - -Beamer::Beamer(int window_width_, int window_height_) -: Base(window_width_, window_height_) -, player(new Player()) -{ - { - PointLight p{Position{0.f, 0.0f, 8.0f, 1.0f}, 200.0f, Color{1, 0, 0, 1}}; - scene->add(std::make_shared(p)); - } - - { - PointLight p{Position{-4.f, -4.0f, -6.0f, 1.0f}, 200.0f, Color{0, 0, 1, 1}}; - scene->add(std::make_shared(p)); - } - - { - PointLight p{Position{4.f, -4.0f, -6.0f, 1.0f}, 200.0f, Color{0, 1, 0, 1}}; - scene->add(std::make_shared(p)); - } - - { - PointLight p{Position{0.f, 6.0f, -6.0f, 1.0f}, 200.0f, Color{1, 1, 1, 1}}; - scene->add(std::make_shared(p)); - } - - reset(); - - for(auto& body : planet.get_all_bodies()){ - scene->add(body); - } -} - -Beamer::~Beamer() = default; - -void Beamer::reset_effects(){ - game_speed = 1.0; -} - -void Beamer::handle_players(float const dt, map is_pressed){ - constexpr float paddle_rotation_speed = M_PI*2.0f; - float const paddle_rotation = paddle_rotation_speed * dt; - - if(is_pressed[SDLK_UP]){ - for(auto& model : planet.get_all_bodies()){ - model->add_pitch(paddle_rotation); - } - } - - if(is_pressed[SDLK_DOWN]){ - for(auto& model : planet.get_all_bodies()){ - model->add_pitch(-paddle_rotation); - } - } - - if(is_pressed[SDLK_LEFT]){ - for(auto& model : planet.get_all_bodies()){ - model->add_roll(-paddle_rotation); - } - } - - if(is_pressed[SDLK_RIGHT]){ - for(auto& model : planet.get_all_bodies()){ - model->add_roll(paddle_rotation); - } - } - - if(is_pressed[SDLK_k]){ - scene->update_shader_pipeline(); - } - - bool planet_dirty = false; - if(is_pressed[SDLK_z] && !is_pressed[SDLK_LSHIFT]){ - waterlevel += 3.0*dt; - planet_dirty = true; - } - - if(is_pressed[SDLK_z] && is_pressed[SDLK_LSHIFT]){ - waterlevel -= 3.0*dt; - planet_dirty = true; - } - - if(is_pressed[SDLK_x] && !is_pressed[SDLK_LSHIFT]){ - displacement += 3.0*dt; - planet_dirty = true; - } - - if(is_pressed[SDLK_x] && is_pressed[SDLK_LSHIFT]){ - displacement -= 3.0*dt; - planet_dirty = true; - } - - if(is_pressed[SDLK_r] && !is_pressed[SDLK_LSHIFT]){ - watercolor[0] += 0.2; - planet_dirty = true; - } - - if(is_pressed[SDLK_r] && is_pressed[SDLK_LSHIFT]){ - watercolor[0] -= 0.2; - planet_dirty = true; - } - - if(is_pressed[SDLK_g] && !is_pressed[SDLK_LSHIFT]){ - watercolor[1] += 0.2; - planet_dirty = true; - } - - if(is_pressed[SDLK_g] && is_pressed[SDLK_LSHIFT]){ - watercolor[1] -= 0.2; - planet_dirty = true; - } - - if(is_pressed[SDLK_b] && !is_pressed[SDLK_LSHIFT]){ - watercolor[2] += 0.2; - planet_dirty = true; - } - - if(is_pressed[SDLK_b] && is_pressed[SDLK_LSHIFT]){ - watercolor[2] -= 0.2; - planet_dirty = true; - } - - if(planet_dirty){ - for(auto const& body : planet.get_all_bodies()){ - scene->remove(body); - } - planet = Planet(waterlevel, displacement, watercolor); - for(auto const& body : planet.get_all_bodies()){ - scene->add(body); - } - } - - - -} - -void Beamer::update(float const dt, map keys_went_down, map is_pressed, map keys_went_up){ - Base::update(game_speed*dt, keys_went_down, is_pressed, keys_went_up); - handle_players(game_speed*dt, is_pressed); - player->update(game_speed*dt); - - scene->camera.set_position(player->position); - scene->camera.look_at({0, 0, 0}, player->speed); -} - -} diff --git a/src/beamer/beamer.hpp b/src/beamer/beamer.hpp deleted file mode 100644 index fb0ec99..0000000 --- a/src/beamer/beamer.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include -#include - -#include "../Base.hpp" - -namespace games { -using namespace motor; -using namespace std; - -template -struct check_type { - template - bool operator()(shared_ptr const& p){ - return (dynamic_pointer_cast(p) != nullptr); - } -}; - -struct Beamer : public Base { - static constexpr char bundle_name[] = "beamer"; - - struct House{ - std::shared_ptr model; - House(); - }; - - struct Planet { - float radius{2.5}; - std::shared_ptr model; - std::shared_ptr water; - - std::vector houses; - - std::vector> get_all_bodies() const; - Planet(float const waterlevel, float const displacement, Color const watercolor); - }; - - float waterlevel = 0.7; - float displacement = 0.2; - Color watercolor = {0.0, 1.0, 1.0, 1.0}; - Planet planet{waterlevel, displacement, watercolor}; - - struct Player; - std::unique_ptr player; - - float game_speed{1.0}; - - Beamer(int window_width, int window_height); - ~Beamer(); - - virtual bool has_ended(); - virtual void update(float const dt, map keys_went_down, map is_pressed, map keys_went_up); - - void reset_camera(); - void reset_players(); - void reset_effects(); - void reset(); - - void handle_players(float const dt, map is_pressed); -}; - - -} // namespace games diff --git a/src/beamer/coordinates.h b/src/beamer/coordinates.h deleted file mode 100644 index 8a36bba..0000000 --- a/src/beamer/coordinates.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include -#include - -/* - Spherical coordinates: (r, phi1, phi2) - where r is the radius, phi1 rotation through y-axis, phi2 rotation determining height - This way we can see the sphere as something 2-dimensional (r=1). -*/ - -template -moggle::vector3 from_spherical(moggle::vector3 const & p){ - return { - p[0] * cos(p[2]) * cos(p[1]), - p[0] * sin(p[2]), - p[0] * cos(p[2]) * sin(p[1]) - }; -} - -template -moggle::vector3 to_spherical(moggle::vector3 const & p){ - float r = sqrt(p[0]*p[0] + p[1]*p[1] + p[2]*p[2]); - if(r == 0) return {0.0, 0.0, 0.0}; - return { - r, - atan2(p[2], p[0]), - asin(p[1] / r) - }; -} - -inline void test(){ - for(unsigned int i = 0; i < 20; ++i){ - auto f = [](moggle::vector3 p){return from_spherical(to_spherical(p));}; - auto g = [](moggle::vector3 p){return to_spherical(from_spherical(p));}; - - moggle::vector3 p{ - rand() / float(RAND_MAX) - 0.5, - rand() / float(RAND_MAX) - 0.5, - rand() / float(RAND_MAX) - 0.5 - }; - COUT << p << "\n" << f(p) << "\n" << g(p) << std::endl; - } -} \ No newline at end of file diff --git a/src/beamer/procedural_textures.h b/src/beamer/procedural_textures.h deleted file mode 100644 index cea4952..0000000 --- a/src/beamer/procedural_textures.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "coordinates.h" -#include "simplex.h" - -// takes size of texture (width x height), and a generator function, which is given positions on the sphere, and should return some kind of pixel -template -inline moggle::texture create_noise_for_sphere(int width, int height, F const & f){ - std::unique_ptr data(new GLubyte[4 * width * height]); - - auto const & generator = f; - - for(int y = 0; y < height; ++y){ - for(int x = 0; x < width; ++x){ - GLubyte* pixel = &data[4 * (y*width + x)]; - - moggle::vector3 sp{1.0, 2 * M_PI * x/double(width), M_PI * (y/double(height) - 0.5)}; - auto position = from_spherical(sp); - auto result = generator(position); - pixel[0] = 255*result[0]; - pixel[1] = 255*result[1]; - pixel[2] = 255*result[2]; - pixel[3] = 255*result[3]; - } - } - - moggle::texture t(width, height); - t.bind(); - moggle::gl::texture_image2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.get()); - t.set_parameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - t.set_parameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - return t; -} \ No newline at end of file diff --git a/src/beamer/simplex.h b/src/beamer/simplex.h deleted file mode 100644 index de21428..0000000 --- a/src/beamer/simplex.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -#include - -// I couldn't find the one we had for the old sdlgame, but i found this one - -static float grad[12][3] = { - {1.0,1.0,0.0},{-1.0,1.0,0.0},{1.0,-1.0,0.0},{-1.0,-1.0,0.0}, - {1.0,0.0,1.0},{-1.0,0.0,1.0},{1.0,0.0,-1.0},{-1.0,0.0,-1.0}, - {0.0,1.0,1.0},{0.0,-1.0,1.0},{0.0,1.0,-1.0},{0.0,-1.0,-1.0} -}; - -static int perm[512] = {151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180, 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180}; - -inline float dot(float x, float y, float z, float* g){ - return x*g[0] + y*g[1] + z*g[2]; -} - -inline float noise(float xin, float yin, float zin){ - float F3, G3, t, X0, Y0, Z0, x0, y0, z0, s, x1, y1, z1, x2, y2, z2, x3, y3, z3, t0, t1, t2, t3, n0, n1, n2, n3; - int i, j, k, ii, jj, kk, i1, j1, k1, i2, j2, k2, gi0, gi1, gi2, gi3; - - F3 = 1.0f/3.0f; - s = (xin+yin+zin)*F3; - i = std::floor(xin+s); - j = std::floor(yin+s); - k = std::floor(zin+s); - G3 = 1.0/6.0; - t = (i+j+k)*G3; - X0 = i-t; - Y0 = j-t; - Z0 = k-t; - x0 = xin-X0; - y0 = yin-Y0; - z0 = zin-Z0; - - if(x0 >= y0){ - if(y0 >= z0){ - i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; - } - else if(x0 >= z0){ - i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; - } - else{ - i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; - } - } - else{ - if(y0 < z0){ - i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; - } - else if(x0 < z0){ - i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; - } - else{ - i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; - } - } - - x1 = x0 - i1 + G3; - y1 = y0 - j1 + G3; - z1 = z0 - k1 + G3; - x2 = x0 - i2 + 2.0*G3; - y2 = y0 - j2 + 2.0*G3; - z2 = z0 - k2 + 2.0*G3; - x3 = x0 - 1.0 + 3.0*G3; - y3 = y0 - 1.0 + 3.0*G3; - z3 = z0 - 1.0 + 3.0*G3; - - ii = i & 255; - jj = j & 255; - kk = k & 255; - - gi0 = perm[ii+perm[jj+perm[kk]]] % 12; - gi1 = perm[ii+i1+perm[jj+j1+perm[kk+k1]]] % 12; - gi2 = perm[ii+i2+perm[jj+j2+perm[kk+k2]]] % 12; - gi3 = perm[ii+1+perm[jj+1+perm[kk+1]]] % 12; - - t0 = 0.6 - x0*x0 - y0*y0 - z0*z0; - if(t0<0){ - n0 = 0.0; - } - else{ - t0 *= t0; - n0 = t0 * t0 * dot(x0, y0, z0, grad[gi0]); - } - - t1 = 0.6 - x1*x1 - y1*y1 - z1*z1; - if(t1<0){ - n1 = 0.0; - } - else{ - t1 *= t1; - n1 = t1 * t1 * dot(x1, y1, z1, grad[gi1]); - } - - t2 = 0.6 - x2*x2 - y2*y2 - z2*z2; - if(t2<0){ - n2 = 0.0; - } - else{ - t2 *= t2; - n2 = t2 * t2 * dot(x2, y2, z2, grad[gi2]); - } - - t3 = 0.6 - x3*x3 - y3*y3 - z3*z3; - if(t3<0){ - n3 = 0.0; - } - else{ - t3 *= t3; - n3 = t3 * t3 * dot(x3, y3, z3, grad[gi3]); - } - - // The result is scaled to stay just inside [-1,1] - // source: http://webstaff.itn.liu.se/~stegu/simplexnoise/ - return 32.0*(n0 + n1 + n2 + n3); -} - -inline float simplex_noise(int octaves, float x, float y, float z){ - float value = 0.0; - int i; - for(i=0; i(in), len) << std::endl; + break; + case LWS_CALLBACK_CLIENT_WRITEABLE: + libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], 1, LWS_WRITE_TEXT); + default: break; + } + return 0; + } + + static libwebsocket_protocols protocols[] = { + { "uberclient", callback_func, 0 }, + { NULL, NULL, 0 } + }; + + bool Client::has_ended(){ + return false; + } + + Client::Client(int window_width_, int window_height_, std::shared_ptr& active_base_) + : Base(window_width_, window_height_, active_base_) + { + // *** websockets code *** + lws_context_creation_info info; + memset(&info, 0, sizeof info); + + info.port = CONTEXT_PORT_NO_LISTEN; + info.protocols = protocols; + info.gid = -1; + info.uid = -1; + + context = libwebsocket_create_context(&info); + assert(context); + + std::string address = "127.0.0.1"; + wsi = libwebsocket_client_connect(context, address.c_str(), 7681, false, "/", address.c_str(), "origin", "uberclient", -1); + assert(wsi); + // *** *** + + { + motor::DirectionalLight p{motor::Position{0, 0, 20}, 1.0f, motor::Color{1, 1, 1, 1}}; + scene->add(std::make_shared(p)); + } + + world.physics.set_gravity({0.0, 0.0, 0.0}); + + scene->camera.set_perspective(80.0f, 4.0f/3.0f, .5f, 50.0f); + } + + void Client::handle_input(float dt, Input input){ + using motor::Position; + + if(input.keys_went_down[SDLK_u]){ + //Iterates over drawing the physics and normal world, drawing only the physics world, and drawing only the normal world. + if(!draw_physics_world){ + draw_physics_world = true; + } else { + if(draw_only_physics_world){ + draw_physics_world = false; + draw_only_physics_world = false; + } else { + draw_only_physics_world = true; + } + } + } + + if(input.keys_went_down[SDLK_k]){ + scene->update_shader_pipeline(); + } + } + + void Client::draw(){ + Base::draw(); + } + + void Client::update(float const dt, Input input){ + Base::update(dt, input); + handle_input(dt, input); + + scene->camera.set_position({0, 0, 20}); + + const float poll_interval = 5.0; + poll_time += dt; + + int ret = libwebsocket_service(context, 10); + if(ret < 0) { + CERR << "ERROR: libwebsocket returns < 0\n"; + } + if(poll_time >= poll_interval){ + libwebsocket_callback_on_writable(context, wsi); + poll_time -= poll_interval; + } + } + + Client::~Client(){ + libwebsocket_context_destroy(context); + } +} diff --git a/src/client/client.hpp b/src/client/client.hpp new file mode 100644 index 0000000..e83e842 --- /dev/null +++ b/src/client/client.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#include + +#include "../Base.hpp" +#include "../input.hpp" + + +namespace games { + struct Client : public Base { + static constexpr char bundle_name[] = "client"; + libwebsocket_context * context{nullptr}; + libwebsocket * wsi{nullptr}; + float poll_time{0.0}; + + Client(int window_width, int window_height, std::shared_ptr& active_base); + + virtual bool has_ended(); + virtual void update(float const dt, Input input); + virtual void draw(); + + void handle_input(float const dt, Input input); + + ~Client(); + }; +} diff --git a/src/beamer_main.cpp b/src/client_main.cpp similarity index 86% rename from src/beamer_main.cpp rename to src/client_main.cpp index 24f5193..ae93804 100644 --- a/src/beamer_main.cpp +++ b/src/client_main.cpp @@ -1,9 +1,9 @@ #include "generic_main.hpp" -#include "beamer/beamer.hpp" +#include "client/client.hpp" #include -typedef games::Beamer Game; +typedef games::Client Game; constexpr char Game::bundle_name[]; motor::Bundle const bundle(Game::bundle_name); diff --git a/src/generic_main.hpp b/src/generic_main.hpp index 5fc3e00..48c3277 100644 --- a/src/generic_main.hpp +++ b/src/generic_main.hpp @@ -6,14 +6,22 @@ #include #include +#include "Base.hpp" + #ifndef __unused #define __unused __attribute__((unused)) #endif +std::map mysterious_pointers; + +#include +unsigned int motor::al::Buffer::nr_buffers = 0; +unsigned int motor::al::Source::nr_sources = 0; + template struct GenericMain { private: - motor::AL al_context; + motor::al::AL al_context; int screen_width = 0; int screen_height = 0; @@ -31,9 +39,9 @@ private: SDL_Surface * screen = nullptr; if(fullscreen){ - screen = SDL_SetVideoMode(0, 0, 24, SDL_OPENGL | SDL_FULLSCREEN); + screen = SDL_SetVideoMode(0, 0, 32, SDL_OPENGL | SDL_FULLSCREEN); } else { - screen = SDL_SetVideoMode(wanted_resolution_width, wanted_resolution_height, 24, SDL_OPENGL); + screen = SDL_SetVideoMode(wanted_resolution_width, wanted_resolution_height, 32, SDL_OPENGL); } if(!screen){ @@ -63,10 +71,12 @@ public: template void main(T... args){ - std::unique_ptr game{new Game {screen_width, screen_height, args...}}; + std::shared_ptr game{new Game {screen_width, screen_height, game, args...}}; + std::weak_ptr previous_game = game; bool done = false; - std::map keys_pressed; + + Input input; // for calculation fps auto fps_time = SDL_GetTicks(); @@ -75,8 +85,10 @@ public: auto now = SDL_GetTicks(); while (!done) { SDL_Event event; - std::map keys_went_down; - std::map keys_went_up; + input.keys_went_down.clear(); + input.keys_went_up.clear(); + input.mouse.went_down.clear(); + input.mouse.went_up.clear(); bool any_key_pressed = false; while (SDL_PollEvent(&event)) { @@ -87,8 +99,8 @@ public: case SDL_KEYDOWN: { auto const& key = event.key.keysym.sym; - keys_pressed[key] = true; - keys_went_down[key] = true; + input.keys_pressed[key] = true; + input.keys_went_down[key] = true; if (key == SDLK_ESCAPE){ done = true; } @@ -99,8 +111,37 @@ public: { any_key_pressed = true; auto const& key = event.key.keysym.sym; - keys_pressed[key] = false; - keys_went_up[key] = true; + input.keys_pressed[key] = false; + input.keys_went_up[key] = true; + break; + } + + case SDL_MOUSEMOTION: + { + input.mouse.difference[0] = event.motion.x - input.mouse.position[0]; + input.mouse.difference[1] = event.motion.y - input.mouse.position[1]; + input.mouse.position[0] = event.motion.x; + input.mouse.position[1] = event.motion.y; + break; + } + + case SDL_MOUSEBUTTONDOWN: + { + auto const b = Input::Mouse::button_from_sdl_button(event.button.button); + input.mouse.is_pressed[b] = true; + input.mouse.went_down[b] = true; + input.mouse.position[0] = event.button.x; + input.mouse.position[1] = event.button.y; + break; + } + + case SDL_MOUSEBUTTONUP: + { + auto const b = Input::Mouse::button_from_sdl_button(event.button.button); + input.mouse.is_pressed[b] = false; + input.mouse.went_up[b] = true; + input.mouse.position[0] = event.button.x; + input.mouse.position[1] = event.button.y; break; } @@ -108,24 +149,31 @@ public: break; } } - + if(any_key_pressed && game->has_ended()){ - game.reset(new Game {screen_width, screen_height, args...}); + game.reset(new Game {screen_width, screen_height, game, args...}); + } + + if(previous_game.expired()){ + previous_game = game; + now = SDL_GetTicks(); } float const dt = (SDL_GetTicks() - now)/1000.0f; now = SDL_GetTicks(); - game->update(dt, keys_went_down, keys_pressed, keys_went_up); + game->update(dt, input); game->draw(); SDL_GL_SwapBuffers(); ++frame_count_in_second; // one second has past - if(SDL_GetTicks() - fps_time > 1000.0f){ + if(SDL_GetTicks() - fps_time > 30.0f * 1000.0f){ fps_time = SDL_GetTicks(); - COUT << "Frames in last second: " << frame_count_in_second << std::endl; + COUT << "Average FPS: " << frame_count_in_second/30.0f << std::endl; frame_count_in_second = 0; } + + input.mouse.difference = moggle::vector2{0,0}; } } }; diff --git a/src/globals.hpp b/src/globals.hpp index aa99ec4..dd18f9f 100644 --- a/src/globals.hpp +++ b/src/globals.hpp @@ -1,3 +1,7 @@ #include +#include extern motor::Bundle const bundle; +extern Configuration const configuration; + +extern std::map mysterious_pointers; diff --git a/src/input.hpp b/src/input.hpp new file mode 100644 index 0000000..dcb3b82 --- /dev/null +++ b/src/input.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +#include + +struct Input { + std::map keys_pressed{}; + std::map keys_went_down{}; + std::map keys_went_up{}; + + struct Mouse { + enum Button { + left, + middle, + right, + wheelup, + wheeldown, + }; + + inline static Button button_from_sdl_button(size_t const sdl_button){ + switch(sdl_button){ + case SDL_BUTTON_LEFT: return Button::left; + case SDL_BUTTON_MIDDLE: return Button::middle; + case SDL_BUTTON_RIGHT: return Button::right; + case SDL_BUTTON_WHEELUP: return Button::wheelup; + case SDL_BUTTON_WHEELDOWN: return Button::wheeldown; + default: { + return Mouse::Button(sdl_button); + } + } + } + + moggle::vector2 position{0.0f, 0.0f}; + moggle::vector2 difference{0.0f, 0.0f}; + + std::map went_down{}; + std::map is_pressed{}; + std::map went_up{}; + }; + + Mouse mouse{}; +}; diff --git a/src/physics.cpp b/src/physics.cpp new file mode 100644 index 0000000..ce8a686 --- /dev/null +++ b/src/physics.cpp @@ -0,0 +1,185 @@ +#include "physics.hpp" + +#include "globals.hpp" + +namespace motor { + +void Physics::update(float const dt, size_t const maximum_number_of_steps){ + in_update = true; + + for(auto & a : attractors){ + for(auto body : *this){ + if(!body) continue; + auto position = body->getWorldTransform().getOrigin(); + auto difference = convert>(position) - a->position; + auto force = dt * a->strength * difference; + body->applyCentralImpulse(convert(force)); + } + } + + update_bullet(dt, maximum_number_of_steps); + check_collisions(); + + in_update = false; +} + +void Physics::update_bullet(float const dt, size_t const maximum_number_of_steps){ + dynamics_world->stepSimulation(dt, maximum_number_of_steps); + + debug_draw_up_to_date = false; + + for(auto body : *this){ + if(!body) continue; + if(body->getMotionState()) { + if(body->getUserPointer() != nullptr){ + btTransform trans = body->getWorldTransform(); + Body* b = static_cast(body->getUserPointer()); + b->sync_from_bullet(trans); + } + } + } +} + +void Physics::check_collisions(){ + std::vector> bullet_objects_colliding; + int const manifolds = dynamics_world->getDispatcher()->getNumManifolds(); + for(int manifold_index = 0; manifold_index < manifolds; ++manifold_index){ + auto const manifold = dynamics_world->getDispatcher()->getManifoldByIndexInternal(manifold_index); + + btCollisionObject const* first_object = manifold->getBody0(); + btCollisionObject const* second_object = manifold->getBody1(); + + if(manifold->getNumContacts() > 0){ + bullet_objects_colliding.emplace_back(first_object, second_object); + } + } + + for(auto const& c : bullet_objects_colliding){ + if(!astrant::contains(bullet_objects_in_collision_previous_frame, c)){ + bullet_objects_entered_collision(c.first, c.second); + } + } + + for(auto const& c : bullet_objects_in_collision_previous_frame){ + if(!astrant::contains(bullet_objects_colliding, c)){ + bullet_objects_exited_collision(c.first, c.second); + } + } + + bullet_objects_in_collision_previous_frame = bullet_objects_colliding; +} + +void Physics::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(); + } + } + + for(auto& c : on_enter_single_dual_collision_callback){ + if(c.b->get_physical_body() == x){ + c(y); + } else if (c.b->get_physical_body() == y){ + c(x); + } + } +} + +void Physics::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(); + } + } + + for(auto& c : on_exit_single_dual_collision_callback){ + if(c.b->get_physical_body() == x){ + c(y); + } else if (c.b->get_physical_body() == y){ + c(x); + } + } +} + +void Physics::set_gravity(moggle::vector3 gravity){ + dynamics_world->setGravity(convert(gravity)); +} + +void Physics::add(std::shared_ptr b){ + assert(!in_update); + assert(b->physics); + + dynamics_world->addRigidBody(b->get_physical_body()); + debug_draw_up_to_date = false; +} + +void Physics::add(std::shared_ptr a){ + assert(a); + attractors.push_back(a); +} + +void Physics::remove(std::shared_ptr b){ + assert(!in_update); + dynamics_world->removeRigidBody(b->get_physical_body()); + + auto const remove_callback_dual = [](std::shared_ptr x, std::vector& y){ + y.erase(std::remove_if(y.begin(), y.end(), [&](dual_collision_callback c){ + return c.first == x || c.second == x; + }), y.end()); + }; + + auto const remove_callback_single = [](std::shared_ptr x, std::vector& y){ + y.erase(std::remove_if(y.begin(), y.end(), [&](single_collision_callback c){ + return c.b == x; + }), y.end()); + }; + + auto const remove_callback_single_dual = [](std::shared_ptr x, std::vector& y){ + y.erase(std::remove_if(y.begin(), y.end(), [&](single_dual_collision_callback c){ + return c.b == x; + }), y.end()); + }; + + remove_callback_dual(b, on_enter_dual_collision_callback); + remove_callback_dual(b, on_exit_dual_collision_callback); + remove_callback_single(b, on_enter_single_collision_callback); + remove_callback_single(b, on_exit_single_collision_callback); + remove_callback_single_dual(b, on_enter_single_dual_collision_callback); + remove_callback_single_dual(b, on_exit_single_dual_collision_callback); + + debug_draw_up_to_date = false; +} + +void Physics::remove(std::shared_ptr a){ + astrant::remove_element(attractors, a); +} + +Body* Physics::raycast(Position const& from, Position const& to) const{ + btCollisionWorld::ClosestRayResultCallback ray_call(convert(from),convert(to)); + dynamics_world->rayTest(convert(from), convert(to), ray_call); + if (ray_call.hasHit()) { + Body* b = static_cast(ray_call.m_collisionObject->getUserPointer()); + btRigidBody const * body = btRigidBody::upcast(ray_call.m_collisionObject); + if (body && b) { + return b; + } else { + return nullptr; + } + } else { + return nullptr; + } +} + +} diff --git a/src/physics.hpp b/src/physics.hpp new file mode 100644 index 0000000..40e2639 --- /dev/null +++ b/src/physics.hpp @@ -0,0 +1,247 @@ +#pragma once + +#include +#include + +#include + + + +namespace motor { +//! 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); +} + +struct Attractor { + Position position{0,0,0}; + float strength{100}; +}; + +//! Interface to bullet, +struct Physics { + //! iterates over the bodies in the simulation. Assumes all bodies are btRigidBody objects! + struct iterator{ + iterator(std::shared_ptr dynamics_world_, int index_) + : index(index_) + , dynamics_world(dynamics_world_) + {} + + iterator& operator++(){ + ++index; + return *this; + } + + iterator operator++(int){ + iterator ret = *this; + ++*this; + return ret; + } + + bool operator==(iterator const & it2){ + return dynamics_world == it2.dynamics_world && index == it2.index; + } + + bool operator!=(iterator const & it2){ + return !(*this == it2); + } + + btRigidBody* operator*(){ + btCollisionObject* obj = dynamics_world->getCollisionObjectArray()[index]; + btRigidBody* body = btRigidBody::upcast(obj); + return body; + } + private: + int index{0}; + std::shared_ptr dynamics_world; + }; + + //! returns body-iterator to the first body + iterator begin(){ + return {dynamics_world, 0}; + } + + //! returns body-iterator after the last body + iterator end(){ + return {dynamics_world, dynamics_world->getNumCollisionObjects()}; + } + + Physics(){ + dynamics_world->setDebugDrawer(debug_drawer.get()); + } + + std::vector> get_debug_draw_result(int const mode) { + if(!debug_draw_up_to_date){ + debug_drawer->clear(); + debug_drawer->setDebugMode(mode); + dynamics_world->debugDrawWorld(); + debug_drawer->finish(); + debug_draw_up_to_date = true; + } + + return debug_drawer->get_meshes(); + + } + + + void update(float const dt, size_t const maximum_number_of_steps); + + //! 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); + } + }; + + struct single_dual_collision_callback { + template + single_dual_collision_callback(std::shared_ptr b_, CallbackFunctor&& c) + : b(b_) + , f(c) + {} + + std::shared_ptr b; + std::function f; + + void operator()(btCollisionObject const* them) { + assert(them); + if(them->getUserPointer() != nullptr){ + Body* them_body = static_cast(them->getUserPointer()); + f(b, them_body); + } + } + }; + + 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)); + } + + void register_on_enter_collision_callback(single_dual_collision_callback&& c){ + on_enter_single_dual_collision_callback.emplace_back(std::move(c)); + } + + void register_on_exit_collision_callback(single_dual_collision_callback&& c){ + on_exit_single_dual_collision_callback.emplace_back(std::move(c)); + } + + //! Adds an object to the simulation + void add(std::shared_ptr b); + + //! Adds an attractor to the simulation + void add(std::shared_ptr a); + + //! Removes object from the simulation, and also removes all the registered callbacks to that object + void remove(std::shared_ptr b); + + //! Adds an attractor to the simulation + void remove(std::shared_ptr a); + + //! Sets the force that is applied to all bodies each tick + void set_gravity(moggle::vector3 gravity); + + btDynamicsWorld const& get_world() const { + return *dynamics_world; + } + +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{}; + std::vector on_enter_single_dual_collision_callback{}; + std::vector on_exit_single_dual_collision_callback{}; + + void bullet_objects_entered_collision(btCollisionObject const* x, btCollisionObject const* y); + void bullet_objects_exited_collision(btCollisionObject const* x, btCollisionObject const* y); + +public: + motor::Body* raycast(motor::Position const& from, motor::Position const& to) const; + +private: + 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::shared_ptr dynamics_world{new btDiscreteDynamicsWorld(collision_dispatcher.get(), overlapping_pair_cache.get(), constraint_solver.get(), collison_configuration.get())}; + std::unique_ptr debug_drawer{new bullet_debug_drawer}; + + void update_bullet(float const dt, size_t const maximum_number_of_steps); + + void check_collisions(); + + //! If since the last time get_debug_draw_results was called, update was called. + //! ie. update sets it to false, get_debug_draw_results set it to true + bool debug_draw_up_to_date = true; + + // Contains objects that were colliding in the previous update + std::vector> bullet_objects_in_collision_previous_frame{}; + + std::vector> attractors; + + bool in_update{false}; +}; + +} diff --git a/src/physics_test_triangle_mesh.hpp b/src/physics_test_triangle_mesh.hpp new file mode 100644 index 0000000..8a51cc4 --- /dev/null +++ b/src/physics_test_triangle_mesh.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include "Base.hpp" + +namespace games { +using namespace motor; + +struct TestTriangleMesh : public Base { + static constexpr char bundle_name[] = "general"; + + struct Player { + moggle::vector3 const model_scale{1, 1, 1}; + std::shared_ptr model; + + Player(std::string filename); + }; + + Player player; + + float game_speed{1.0}; + + TestTriangleMesh(int window_width, int window_height, std::shared_ptr& active_base, std::string filename); + + virtual bool has_ended(); + virtual void update(float const dt, Input input); + + void reset_camera(); + void reset_players(); + void reset_effects(); + void reset(); + + void handle_players(float const dt, Input input); +}; + + +} // namespace games diff --git a/src/world.hpp b/src/world.hpp new file mode 100644 index 0000000..205ec43 --- /dev/null +++ b/src/world.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include "ai_world.hpp" +#include "physics.hpp" + +struct World { + std::shared_ptr scene = std::make_shared(); + std::shared_ptr hud = std::make_shared(); + motor::AIWorld ai{}; + motor::Physics physics{}; +};