diff --git a/CMakeLists.txt b/CMakeLists.txt index b80be9e..0b6fdd6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ set(STATIC_LIBS set(SHARED_LIBS ${OPENGL_gl_LIBRARY}) + # Generate configuration header file configure_file(${PROJECT_SOURCE_DIR}/src/configuration.hpp.in ${PROJECT_BINARY_DIR}/src/configuration.hpp) diff --git a/src/application.cpp b/src/application.cpp index 0c125d7..6733277 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -426,6 +426,11 @@ void application::set_window_opacity(float opacity) SDL_SetWindowOpacity(sdl_window, opacity); } +void application::swap_buffers() +{ + SDL_GL_SwapWindow(sdl_window); +} + void application::update(double t, double dt) { translate_sdl_events(); diff --git a/src/application.hpp b/src/application.hpp index b4356b0..a7c518c 100644 --- a/src/application.hpp +++ b/src/application.hpp @@ -161,6 +161,8 @@ public: void set_window_opacity(float opacity); + void swap_buffers(); + /// Returns the dimensions of the current display. const std::array& get_display_dimensions() const; diff --git a/src/game/bootloader.cpp b/src/game/bootloader.cpp index 655a7a3..e9c6e41 100644 --- a/src/game/bootloader.cpp +++ b/src/game/bootloader.cpp @@ -80,6 +80,7 @@ #include "event/event-dispatcher.hpp" #include "input/input-event-router.hpp" #include "input/input-mapper.hpp" +#include "input/input-listener.hpp" #include "input/game-controller.hpp" #include "input/mouse.hpp" #include "input/keyboard.hpp" @@ -868,6 +869,10 @@ void setup_controls(game_context* ctx) ctx->input_mapper = new input_mapper(); ctx->input_mapper->set_event_dispatcher(event_dispatcher); + // Setup input listener + ctx->input_listener = new input_listener(); + ctx->input_listener->set_event_dispatcher(event_dispatcher); + // Create toggle fullscreen control ctx->toggle_fullscreen_control = new control(); ctx->toggle_fullscreen_control->set_activated_callback @@ -1011,6 +1016,32 @@ void setup_controls(game_context* ctx) ctx->input_event_router->add_mapping(game_controller_axis_mapping(ctx->control_system->get_zoom_out_control(), nullptr, game_controller_axis::trigger_left, false)); ctx->input_event_router->add_mapping(game_controller_axis_mapping(ctx->control_system->get_zoom_in_control(), nullptr, game_controller_axis::trigger_right, false)); + ctx->input_event_router->add_mapping(key_mapping(ctx->control_system->get_equip_forceps_control(), nullptr, scancode::one)); + ctx->input_event_router->add_mapping(key_mapping(ctx->control_system->get_equip_brush_control(), nullptr, scancode::two)); + ctx->input_event_router->add_mapping(key_mapping(ctx->control_system->get_equip_lens_control(), nullptr, scancode::three)); + + ctx->control_system->get_equip_forceps_control()->set_activated_callback + ( + [ctx]() + { + ctx->tool_system->set_active_tool(ctx->forceps_entity); + } + ); + ctx->control_system->get_equip_brush_control()->set_activated_callback + ( + [ctx]() + { + ctx->tool_system->set_active_tool(ctx->brush_entity); + } + ); + ctx->control_system->get_equip_lens_control()->set_activated_callback + ( + [ctx]() + { + ctx->tool_system->set_active_tool(ctx->lens_entity); + } + ); + event_dispatcher->subscribe(ctx->control_system); event_dispatcher->subscribe(ctx->camera_system); event_dispatcher->subscribe(ctx->tool_system); diff --git a/src/game/game-context.hpp b/src/game/game-context.hpp index 2a3388d..0280c7f 100644 --- a/src/game/game-context.hpp +++ b/src/game/game-context.hpp @@ -51,6 +51,7 @@ class framebuffer; class locomotion_system; class logger; class material; +class input_listener; class material_pass; class nest_system; class orbit_cam; @@ -189,10 +190,13 @@ struct game_context screen_transition* fade_transition; screen_transition* radial_transition_inner; screen_transition* radial_transition_outer; + animation* equip_tool_animation; + animation* unequip_tool_animation; // Controls input_event_router* input_event_router; input_mapper* input_mapper; + input_listener* input_listener; control_set* application_controls; control_set* camera_controls; control_set* menu_controls; diff --git a/src/game/states/splash-state.cpp b/src/game/states/splash-state.cpp index 3c8f481..b1a2980 100644 --- a/src/game/states/splash-state.cpp +++ b/src/game/states/splash-state.cpp @@ -23,6 +23,9 @@ #include "application.hpp" #include "debug/logger.hpp" #include "game/game-context.hpp" +#include "input/input-listener.hpp" +#include "event/input-events.hpp" +#include "rasterizer/rasterizer.hpp" #include "game/states/game-states.hpp" #include "renderer/passes/sky-pass.hpp" #include "scene/billboard.hpp" @@ -72,6 +75,25 @@ void splash_state_enter(game_context* ctx) }; timeline->add_sequence(splash_sequence); + // Set up splash skipper + ctx->input_listener->set_callback + ( + [ctx](const event_base& event) + { + auto id = event.get_event_type_id(); + if (id != mouse_moved_event::event_type_id && id != mouse_wheel_scrolled_event::event_type_id && id != game_controller_axis_moved_event::event_type_id) + { + ctx->timeline->clear(); + ctx->fade_transition->get_animation()->stop(); + ctx->rasterizer->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f); + ctx->rasterizer->clear_framebuffer(true, false, false); + ctx->app->swap_buffers(); + ctx->app->change_state({std::bind(play_state_enter, ctx), std::bind(play_state_exit, ctx)}); + } + } + ); + ctx->input_listener->set_enabled(true); + logger->pop_task(EXIT_SUCCESS); } @@ -80,6 +102,10 @@ void splash_state_exit(game_context* ctx) logger* logger = ctx->logger; logger->push_task("Exiting splash state"); + // Disable splash skipper + ctx->input_listener->set_enabled(false); + ctx->input_listener->set_callback(nullptr); + // Remove splash billboard from UI scene ctx->ui_scene->remove_object(ctx->splash_billboard); diff --git a/src/game/systems/control-system.cpp b/src/game/systems/control-system.cpp index c792eb5..bbd20d1 100644 --- a/src/game/systems/control-system.cpp +++ b/src/game/systems/control-system.cpp @@ -51,6 +51,9 @@ control_system::control_system(entt::registry& registry): control_set.add_control(&descend_control); control_set.add_control(&toggle_view_control); control_set.add_control(&tool_menu_control); + control_set.add_control(&equip_lens_control); + control_set.add_control(&equip_brush_control); + control_set.add_control(&equip_forceps_control); // Set deadzone at 15% for all controls const std::list* controls = control_set.get_controls(); diff --git a/src/game/systems/control-system.hpp b/src/game/systems/control-system.hpp index a4d5dbf..10c22e0 100644 --- a/src/game/systems/control-system.hpp +++ b/src/game/systems/control-system.hpp @@ -212,5 +212,20 @@ inline control* control_system::get_tool_menu_control() return &tool_menu_control; } +inline control* control_system::get_equip_lens_control() +{ + return &equip_lens_control; +} + +inline control* control_system::get_equip_brush_control() +{ + return &equip_brush_control; +} + +inline control* control_system::get_equip_forceps_control() +{ + return &equip_forceps_control; +} + #endif // ANTKEEPER_CONTROL_SYSTEM_HPP diff --git a/src/game/systems/tool-system.cpp b/src/game/systems/tool-system.cpp index de97b48..aa83f0f 100644 --- a/src/game/systems/tool-system.cpp +++ b/src/game/systems/tool-system.cpp @@ -26,6 +26,7 @@ #include "geometry/mesh.hpp" #include "geometry/intersection.hpp" #include "math/math.hpp" +#include "game/entity-commands.hpp" using namespace ecs; @@ -49,6 +50,8 @@ tool_system::tool_system(entt::registry& registry): pick_spring.x1 = {0.0f, 0.0f, 0.0f}; pick_spring.x0 = pick_spring.x1; pick_spring.v = {0.0f, 0.0f, 0.0f}; + + active_tool = entt::null; } void tool_system::update(double t, double dt) @@ -112,7 +115,7 @@ void tool_system::update(double t, double dt) } // Determine target hand angle - hand_angle_spring.x1 = math::pi - std::min(0.5f, std::max(-0.5f, ((mouse_position[0] / viewport[2]) - 0.5f) * 3.0f)) * math::pi; + hand_angle_spring.x1 = math::pi - std::min(0.5f, std::max(-0.5f, ((mouse_position[0] / viewport[2]) - 0.5f) * 1.0f)) * (math::pi + math::half_pi); // Solve springs solve_numeric_spring(hand_angle_spring, dt); @@ -130,23 +133,28 @@ void tool_system::update(double t, double dt) if (!tool.active) return; + + active_tool = entity; if (intersection) { transform.transform.translation = pick_spring.x0 + float3{0, tool.hover_distance, 0}; } + // Interpolate between left and right hand + math::quaternion hand_rotation = math::angle_axis(orbit_cam->get_azimuth() + hand_angle_spring.x0, float3{0, 1, 0}); + if (tool.heliotropic) { math::quaternion solar_rotation = math::rotation(float3{0, -1, 0}, sun_direction); - transform.transform.translation = pick_spring.x0 + solar_rotation * float3{0, tool.hover_distance, 0}; - // Interpolate between left and right hand - math::quaternion hand_rotation = math::angle_axis(orbit_cam->get_azimuth() + hand_angle_spring.x0, float3{0, 1, 0}); - transform.transform.rotation = solar_rotation * hand_rotation; } + else + { + transform.transform.rotation = hand_rotation; + } //math::quaternion rotation = math::angle_axis(orbit_cam->get_azimuth() + pick_angle, float3{0, 1, 0}); //transform.transform.rotation = rotation; @@ -180,6 +188,26 @@ void tool_system::set_sun_direction(const float3& direction) sun_direction = direction; } +void tool_system::set_active_tool(entt::entity entity) +{ + if (active_tool != entt::null) + { + auto& tool = registry.get(active_tool); + tool.active = false; + ec::assign_render_layers(registry, active_tool, 0); + } + + active_tool = entity; + + if (active_tool != entt::null) + { + auto& tool = registry.get(active_tool); + tool.active = true; + ec::assign_render_layers(registry, active_tool, 1); + } +} + + void tool_system::handle_event(const mouse_moved_event& event) { if (pick_enabled && was_pick_enabled) diff --git a/src/game/systems/tool-system.hpp b/src/game/systems/tool-system.hpp index de60963..4c7cb10 100644 --- a/src/game/systems/tool-system.hpp +++ b/src/game/systems/tool-system.hpp @@ -44,6 +44,10 @@ public: void set_viewport(const float4& viewport); void set_pick(bool enabled); void set_sun_direction(const float3& direction); + + void set_active_tool(entt::entity entity); + + entt::entity get_active_tool() const; private: virtual void handle_event(const mouse_moved_event& event); @@ -56,9 +60,15 @@ private: bool was_pick_enabled; bool pick_enabled; float3 sun_direction; + entt::entity active_tool; numeric_spring hand_angle_spring; numeric_spring pick_spring; }; +inline entt::entity tool_system::get_active_tool() const +{ + return active_tool; +} + #endif // ANTKEEPER_TOOL_SYSTEM_HPP diff --git a/src/input/input-listener.cpp b/src/input/input-listener.cpp new file mode 100644 index 0000000..372de62 --- /dev/null +++ b/src/input/input-listener.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2020 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 . + */ + +#include "input-listener.hpp" +#include "event/event-dispatcher.hpp" + +input_listener::input_listener(): + event_dispatcher(nullptr), + callback(nullptr), + enabled(false) +{} + +input_listener::~input_listener() +{ + set_event_dispatcher(nullptr); +} + +void input_listener::set_event_dispatcher(::event_dispatcher* event_dispatcher) +{ + if (this->event_dispatcher) + { + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + } + + this->event_dispatcher = event_dispatcher; + + if (event_dispatcher) + { + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + } +} + +void input_listener::set_callback(std::function callback) +{ + this->callback = callback; +} + +void input_listener::set_enabled(bool enabled) +{ + this->enabled = enabled; +} + +void input_listener::handle_event(const key_pressed_event& event) +{ + if (!is_enabled() || !callback) + { + return; + } + + callback(event); +} + +void input_listener::handle_event(const mouse_moved_event& event) +{ + if (!is_enabled() || !callback) + { + return; + } + + callback(event); +} + +void input_listener::handle_event(const mouse_button_pressed_event& event) +{ + if (!is_enabled() || !callback) + { + return; + } + + callback(event); +} + +void input_listener::handle_event(const mouse_wheel_scrolled_event& event) +{ + if (!is_enabled() || !callback) + { + return; + } + + callback(event); +} + +void input_listener::handle_event(const game_controller_button_pressed_event& event) +{ + if (!is_enabled() || !callback) + { + return; + } + + callback(event); +} + +void input_listener::handle_event(const game_controller_axis_moved_event& event) +{ + if (!is_enabled() || !callback) + { + return; + } + + callback(event); +} diff --git a/src/input/input-listener.hpp b/src/input/input-listener.hpp new file mode 100644 index 0000000..3332acf --- /dev/null +++ b/src/input/input-listener.hpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 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 . + */ + +#ifndef ANTKEEPER_INPUT_LISTENER_HPP +#define ANTKEEPER_INPUT_LISTENER_HPP + +#include "event/input-events.hpp" +#include "event/event-handler.hpp" +#include + +class event_dispatcher; + +class input_listener: + public event_handler, + public event_handler, + public event_handler, + public event_handler, + public event_handler, + public event_handler +{ +public: + /** + * Creates an input listener. + */ + input_listener(); + + /** + * Destroys an input listener. + */ + virtual ~input_listener(); + + /** + * Sets the event dispatcher to which this input event router will subscribe itself. + */ + void set_event_dispatcher(event_dispatcher* event_dispatcher); + + /** + * Sets the input event callback function. + * + * @param callback Callback function which operates on an input event. + */ + void set_callback(std::function event); + + /** + * Enables or disables the input listening. + * + * @param enabled Whether to enable input listening or not. + */ + void set_enabled(bool enabled); + + /** + * Returns true if input listening is enabled. + */ + bool is_enabled() const; + +private: + void handle_event(const key_pressed_event& event); + void handle_event(const mouse_moved_event& event); + void handle_event(const mouse_wheel_scrolled_event& event); + void handle_event(const mouse_button_pressed_event& event); + void handle_event(const game_controller_axis_moved_event& event); + void handle_event(const game_controller_button_pressed_event& event); + + event_dispatcher* event_dispatcher; + std::function callback; + bool enabled; +}; + +inline bool input_listener::is_enabled() const +{ + return enabled; +} + +#endif // ANTKEEPER_INPUT_LISTENER_HPP +