diff --git a/assets/shaders/hud_drag_select.frag.glsl b/assets/shaders/hud_drag_select.frag.glsl new file mode 100644 index 0000000000..a6a36e5a1d --- /dev/null +++ b/assets/shaders/hud_drag_select.frag.glsl @@ -0,0 +1,10 @@ +#version 330 + +// Color of the drag rectangle +uniform vec4 in_col; + +layout(location=0) out vec4 out_col; + +void main() { + out_col = in_col; +} diff --git a/assets/shaders/hud_drag_select.vert.glsl b/assets/shaders/hud_drag_select.vert.glsl new file mode 100644 index 0000000000..527e0bb652 --- /dev/null +++ b/assets/shaders/hud_drag_select.vert.glsl @@ -0,0 +1,7 @@ +#version 330 + +layout(location=0) in vec2 position; + +void main() { + gl_Position = vec4(position, 0.0, 1.0); +} diff --git a/doc/code/renderer/level2.md b/doc/code/renderer/level2.md index f7bf8bbe2a..cec5950bfd 100644 --- a/doc/code/renderer/level2.md +++ b/doc/code/renderer/level2.md @@ -4,21 +4,21 @@ High-level renderer for transforming data from the gamestate to render objects f ## Overview -1. [Level 2](#level-2) - 1. [Overview](#overview) - 2. [Stages](#stages) - 1. [Updating Render Stages from the Gamestate](#updating-render-stages-from-the-gamestate) - 3. [Camera](#camera) +1. [Overview](#overview) +2. [Stages](#stages) + 1. [Updating Render Stages from the Gamestate](#updating-render-stages-from-the-gamestate) +3. [Camera](#camera) ## Stages Every stage has its own subrenderer that manages a `RenderPass` from the level 1 renderer and updates it with `Renderable`s created using update information from the gamestate. Stages also store the vertex and fragment shaders used for drawing the renderable objects. -There are currently 5 stages in the level 2 rendering pipeline: +There are currently 6 stages in the level 2 rendering pipeline: 1. `SkyboxRenderer`: Draws the background behind the terrain (as a single color). 1. `TerrainRenderer`: Draws the terrain. Terrains are handled as textured 3D meshes. 1. `WorldRenderer`: Draws animations and sprites for units/buildings and other 2D ingame objects. +1. `HudRenderer`: Draws "Head-Up Display" elements like health bars, selection boxes, and others. 1. `GuiRenderer`: Draws the GUI overlay. The drawing part in this stage is actually done by Qt, while the level 1 renderer only provides the framebuffer. 1. `ScreenRenderer`: Alpha composites the framebuffer data of previous stages and draws them onto the screen (i.e. it overlays the outputs from the other stages). diff --git a/libopenage/gamestate/component/api/CMakeLists.txt b/libopenage/gamestate/component/api/CMakeLists.txt index 16947be1ba..8588909bf2 100644 --- a/libopenage/gamestate/component/api/CMakeLists.txt +++ b/libopenage/gamestate/component/api/CMakeLists.txt @@ -2,5 +2,6 @@ add_sources(libopenage idle.cpp live.cpp move.cpp + selectable.cpp turn.cpp ) diff --git a/libopenage/gamestate/component/api/selectable.cpp b/libopenage/gamestate/component/api/selectable.cpp new file mode 100644 index 0000000000..c397a146de --- /dev/null +++ b/libopenage/gamestate/component/api/selectable.cpp @@ -0,0 +1,12 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#include "selectable.h" + + +namespace openage::gamestate::component { + +component_t Selectable::get_type() const { + return component_t::SELECTABLE; +} + +} // namespace openage::gamestate::component diff --git a/libopenage/gamestate/component/api/selectable.h b/libopenage/gamestate/component/api/selectable.h new file mode 100644 index 0000000000..d12e1522a7 --- /dev/null +++ b/libopenage/gamestate/component/api/selectable.h @@ -0,0 +1,20 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "gamestate/component/api_component.h" +#include "gamestate/component/types.h" + + +namespace openage::gamestate::component { + +class Selectable : public APIComponent { +public: + using APIComponent::APIComponent; + + component_t get_type() const override; +}; + +} // namespace openage::gamestate::component diff --git a/libopenage/gamestate/component/types.h b/libopenage/gamestate/component/types.h index 500b42692d..5e87b8ed23 100644 --- a/libopenage/gamestate/component/types.h +++ b/libopenage/gamestate/component/types.h @@ -19,6 +19,7 @@ enum class component_t { IDLE, TURN, MOVE, + SELECTABLE, LIVE }; diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index 831527c1ec..f2f489206c 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -26,6 +26,7 @@ #include "gamestate/component/api/idle.h" #include "gamestate/component/api/live.h" #include "gamestate/component/api/move.h" +#include "gamestate/component/api/selectable.h" #include "gamestate/component/api/turn.h" #include "gamestate/component/internal/activity.h" #include "gamestate/component/internal/command_queue.h" @@ -203,6 +204,10 @@ void EntityFactory::init_components(const std::shared_ptr(loop, ability_obj); + entity->add_component(selectable); + } } if (activity_ability) { diff --git a/libopenage/gamestate/event/CMakeLists.txt b/libopenage/gamestate/event/CMakeLists.txt index 309ba96909..4c885d82b1 100644 --- a/libopenage/gamestate/event/CMakeLists.txt +++ b/libopenage/gamestate/event/CMakeLists.txt @@ -1,4 +1,5 @@ add_sources(libopenage + drag_select.cpp process_command.cpp send_command.cpp spawn_entity.cpp diff --git a/libopenage/gamestate/event/drag_select.cpp b/libopenage/gamestate/event/drag_select.cpp new file mode 100644 index 0000000000..efb43b0ab1 --- /dev/null +++ b/libopenage/gamestate/event/drag_select.cpp @@ -0,0 +1,100 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#include "drag_select.h" + +#include + +#include "coord/phys.h" +#include "coord/pixel.h" +#include "coord/scene.h" +#include "curve/discrete.h" +#include "gamestate/component/internal/ownership.h" +#include "gamestate/component/internal/position.h" +#include "gamestate/game_entity.h" +#include "gamestate/game_state.h" +#include "gamestate/types.h" + + +namespace openage::gamestate::event { + +DragSelectHandler::DragSelectHandler() : + OnceEventHandler{"game.drag_select"} {} + +void DragSelectHandler::setup_event(const std::shared_ptr & /* event */, + const std::shared_ptr & /* state */) { + // TODO +} + +void DragSelectHandler::invoke(openage::event::EventLoop & /* loop */, + const std::shared_ptr & /* target */, + const std::shared_ptr &state, + const time::time_t &time, + const param_map ¶ms) { + auto gstate = std::dynamic_pointer_cast(state); + + size_t controlled_id = params.get("controlled", 0); + + Eigen::Matrix4f id_matrix = Eigen::Matrix4f::Identity(); + Eigen::Matrix4f cam_matrix = params.get("camera_matrix", id_matrix); + Eigen::Vector2f drag_start = params.get("drag_start", Eigen::Vector2f{0, 0}); + Eigen::Vector2f drag_end = params.get("drag_end", Eigen::Vector2f{0, 0}); + + // Boundaries of the rectangle + float top = std::max(drag_start.y(), drag_end.y()); + float bottom = std::min(drag_start.y(), drag_end.y()); + float left = std::min(drag_start.x(), drag_end.x()); + float right = std::max(drag_start.x(), drag_end.x()); + + log::log(SPAM << "Drag select rectangle (NDC):"); + log::log(SPAM << "\tTop: " << top); + log::log(SPAM << "\tBottom: " << bottom); + log::log(SPAM << "\tLeft: " << left); + log::log(SPAM << "\tRight: " << right); + + std::vector selected; + for (auto &entity : gstate->get_game_entities()) { + if (not entity.second->has_component(component::component_t::SELECTABLE)) { + // skip entities that are not selectable + continue; + } + + // Check if the entity is owned by the controlled player + // TODO: Check this using Selectable diplomatic property + auto owner = std::dynamic_pointer_cast( + entity.second->get_component(component::component_t::OWNERSHIP)); + if (owner->get_owners().get(time) != controlled_id) { + // only select entities of the controlled player + continue; + } + + // Get the position of the entity in the viewport + auto pos = std::dynamic_pointer_cast( + entity.second->get_component(component::component_t::POSITION)); + auto current_pos = pos->get_positions().get(time); + auto world_pos = current_pos.to_scene3().to_world_space(); + Eigen::Vector4f clip_pos = cam_matrix * Eigen::Vector4f{world_pos.x(), world_pos.y(), world_pos.z(), 1}; + + // Check if the entity is in the rectangle + if (clip_pos.x() > left + and clip_pos.x() < right + and clip_pos.y() > bottom + and clip_pos.y() < top) { + selected.push_back(entity.first); + } + } + + // Select the units + auto select_cb = params.get("select_cb", + std::function ids)>{ + [](const std::vector /* ids */) {}}); + select_cb(selected); +} + +time::time_t DragSelectHandler::predict_invoke_time(const std::shared_ptr & /* target */, + const std::shared_ptr & /* state */, + const time::time_t &at) { + return at; +} + + +} // namespace openage::gamestate::event diff --git a/libopenage/gamestate/event/drag_select.h b/libopenage/gamestate/event/drag_select.h new file mode 100644 index 0000000000..29a7338f27 --- /dev/null +++ b/libopenage/gamestate/event/drag_select.h @@ -0,0 +1,46 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include +#include + +#include "event/evententity.h" +#include "event/eventhandler.h" + + +namespace openage { + +namespace event { +class EventLoop; +class Event; +class State; +} // namespace event + +namespace gamestate::event { + +/** + * Drag select game entities. + */ +class DragSelectHandler : public openage::event::OnceEventHandler { +public: + DragSelectHandler(); + ~DragSelectHandler() = default; + + void setup_event(const std::shared_ptr &event, + const std::shared_ptr &state) override; + + void invoke(openage::event::EventLoop &loop, + const std::shared_ptr &target, + const std::shared_ptr &state, + const time::time_t &time, + const param_map ¶ms) override; + + time::time_t predict_invoke_time(const std::shared_ptr &target, + const std::shared_ptr &state, + const time::time_t &at) override; +}; + +} // namespace gamestate::event +} // namespace openage diff --git a/libopenage/gamestate/event/spawn_entity.cpp b/libopenage/gamestate/event/spawn_entity.cpp index 155441445f..9a728a71ef 100644 --- a/libopenage/gamestate/event/spawn_entity.cpp +++ b/libopenage/gamestate/event/spawn_entity.cpp @@ -196,11 +196,6 @@ void SpawnEntityHandler::invoke(openage::event::EventLoop & /* loop */, activity->init(time); entity->get_manager()->run_activity_system(time); - // TODO: Select the unit when it's created - // very dumb but it gets the job done - auto select_cb = params.get("select_cb", std::function{[](entity_id_t /* id */) {}}); - select_cb(entity->get_id()); - gstate->add_game_entity(entity); } diff --git a/libopenage/gamestate/game_entity.cpp b/libopenage/gamestate/game_entity.cpp index e8b7a9e086..a4e18a29e3 100644 --- a/libopenage/gamestate/game_entity.cpp +++ b/libopenage/gamestate/game_entity.cpp @@ -9,7 +9,7 @@ #include "gamestate/component/api/move.h" #include "gamestate/component/base_component.h" #include "gamestate/component/internal/position.h" -#include "renderer/stages/world/world_render_entity.h" +#include "renderer/stages/world/render_entity.h" namespace openage::gamestate { diff --git a/libopenage/gamestate/game_entity.h b/libopenage/gamestate/game_entity.h index d4b61202e9..f72544427d 100644 --- a/libopenage/gamestate/game_entity.h +++ b/libopenage/gamestate/game_entity.h @@ -134,6 +134,8 @@ class GameEntity { /** * Data components. + * + * TODO: Multiple components of the same type. */ std::unordered_map> components; diff --git a/libopenage/gamestate/simulation.cpp b/libopenage/gamestate/simulation.cpp index 4af77d2815..e883bd56f7 100644 --- a/libopenage/gamestate/simulation.cpp +++ b/libopenage/gamestate/simulation.cpp @@ -5,6 +5,7 @@ #include "assets/mod_manager.h" #include "event/event_loop.h" #include "gamestate/entity_factory.h" +#include "gamestate/event/drag_select.h" #include "gamestate/event/process_command.h" #include "gamestate/event/send_command.h" #include "gamestate/event/spawn_entity.h" @@ -133,11 +134,13 @@ void GameSimulation::set_modpacks(const std::vector &modpacks) { } void GameSimulation::init_event_handlers() { + auto drag_select_handler = std::make_shared(); auto spawn_handler = std::make_shared(this->event_loop, this->entity_factory); auto command_handler = std::make_shared(); auto manager_handler = std::make_shared(); auto wait_handler = std::make_shared(); + this->event_loop->add_event_handler(drag_select_handler); this->event_loop->add_event_handler(spawn_handler); this->event_loop->add_event_handler(command_handler); this->event_loop->add_event_handler(manager_handler); diff --git a/libopenage/gamestate/terrain_chunk.h b/libopenage/gamestate/terrain_chunk.h index df9091a321..e7abce08f5 100644 --- a/libopenage/gamestate/terrain_chunk.h +++ b/libopenage/gamestate/terrain_chunk.h @@ -6,7 +6,7 @@ #include "coord/tile.h" #include "gamestate/terrain_tile.h" -#include "renderer/stages/terrain/terrain_render_entity.h" +#include "renderer/stages/terrain/render_entity.h" #include "time/time.h" #include "util/vector.h" diff --git a/libopenage/gamestate/terrain_factory.cpp b/libopenage/gamestate/terrain_factory.cpp index 7276d73d57..128b92a043 100644 --- a/libopenage/gamestate/terrain_factory.cpp +++ b/libopenage/gamestate/terrain_factory.cpp @@ -11,7 +11,7 @@ #include "gamestate/terrain_chunk.h" #include "gamestate/terrain_tile.h" #include "renderer/render_factory.h" -#include "renderer/stages/terrain/terrain_render_entity.h" +#include "renderer/stages/terrain/render_entity.h" #include "time/time.h" #include "assets/mod_manager.h" diff --git a/libopenage/input/action.h b/libopenage/input/action.h index 4bad86c412..f6ca5296d3 100644 --- a/libopenage/input/action.h +++ b/libopenage/input/action.h @@ -23,8 +23,9 @@ enum class input_action_t { PUSH_CONTEXT, POP_CONTEXT, REMOVE_CONTEXT, - ENGINE, + GAME, CAMERA, + HUD, GUI, CUSTOM, }; diff --git a/libopenage/input/controller/CMakeLists.txt b/libopenage/input/controller/CMakeLists.txt index fd940942a7..eab16635b6 100644 --- a/libopenage/input/controller/CMakeLists.txt +++ b/libopenage/input/controller/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory("camera") add_subdirectory("game") +add_subdirectory("hud") diff --git a/libopenage/input/controller/camera/controller.h b/libopenage/input/controller/camera/controller.h index bdc64df28c..b668c18823 100644 --- a/libopenage/input/controller/camera/controller.h +++ b/libopenage/input/controller/camera/controller.h @@ -6,6 +6,7 @@ #include "input/event.h" + namespace openage { namespace renderer::camera { @@ -27,12 +28,13 @@ class Controller { ~Controller() = default; /** - * Process an input event from the input manager. - * - * @param ev Input event and arguments. - * - * @return true if the event is accepted, else false. - */ + * Process an input event from the input manager. + * + * @param ev Input event and arguments. + * @param ctx Binding context that maps input events to camera actions. + * + * @return true if the event is accepted, else false. + */ bool process(const event_arguments &ev_args, const std::shared_ptr &ctx); }; diff --git a/libopenage/input/controller/game/binding.cpp b/libopenage/input/controller/game/binding.cpp index abfc8b39b8..17f90f942a 100644 --- a/libopenage/input/controller/game/binding.cpp +++ b/libopenage/input/controller/game/binding.cpp @@ -4,4 +4,4 @@ namespace openage::input::game { -} // namespace openage::input::engine +} // namespace openage::input::game diff --git a/libopenage/input/controller/game/binding_context.cpp b/libopenage/input/controller/game/binding_context.cpp index 98a1a3d60e..9f963f4a11 100644 --- a/libopenage/input/controller/game/binding_context.cpp +++ b/libopenage/input/controller/game/binding_context.cpp @@ -36,4 +36,4 @@ const binding_action &BindingContext::lookup(const Event &ev) const { throw Error{MSG(err) << "Event is not bound in binding_action context."}; } -} // namespace openage::input::engine +} // namespace openage::input::game diff --git a/libopenage/input/controller/game/binding_context.h b/libopenage/input/controller/game/binding_context.h index d63b6e4c8c..84b331c690 100644 --- a/libopenage/input/controller/game/binding_context.h +++ b/libopenage/input/controller/game/binding_context.h @@ -69,4 +69,4 @@ class BindingContext { std::unordered_map by_class; }; -} // namespace openage::input::engine +} // namespace openage::input::game diff --git a/libopenage/input/controller/game/controller.cpp b/libopenage/input/controller/game/controller.cpp index 7a17de186a..96ccf53429 100644 --- a/libopenage/input/controller/game/controller.cpp +++ b/libopenage/input/controller/game/controller.cpp @@ -2,6 +2,8 @@ #include "controller.h" +#include "log/log.h" + #include "event/event_loop.h" #include "event/evententity.h" #include "event/state.h" @@ -16,6 +18,8 @@ #include "time/time_loop.h" #include "coord/phys.h" +#include "renderer/camera/camera.h" + namespace openage::input::game { @@ -45,9 +49,11 @@ const std::vector &Controller::get_selected() const { return this->selected; } -void Controller::set_selected(std::vector ids) { +void Controller::set_selected(const std::vector ids) { std::unique_lock lock{this->mutex}; + log::log(DBG << "Selected " << ids.size() << " entities"); + this->selected = ids; } @@ -86,6 +92,30 @@ bool Controller::process(const event_arguments &ev_args, const std::shared_ptrmutex}; + + log::log(DBG << "Drag select start at " << start); + this->drag_select_start = start; +} + +const coord::input Controller::get_drag_select_start() const { + std::unique_lock lock{this->mutex}; + + if (not this->drag_select_start.has_value()) { + throw Error{ERR << "Drag select start not set."}; + } + + return this->drag_select_start.value(); +} + +void Controller::reset_drag_select() { + std::unique_lock lock{this->mutex}; + + log::log(DBG << "Drag select start reset"); + this->drag_select_start = std::nullopt; +} + void setup_defaults(const std::shared_ptr &ctx, const std::shared_ptr &time_loop, const std::shared_ptr &simulation, @@ -96,10 +126,6 @@ void setup_defaults(const std::shared_ptr &ctx, event::EventHandler::param_map::map_t params{ {"position", mouse_pos}, {"owner", controller->get_controlled()}, - // TODO: Remove - {"select_cb", std::function{[controller](gamestate::entity_id_t id) { - controller->set_selected({id}); - }}}, }; auto event = simulation->get_event_loop()->create_event( @@ -112,9 +138,13 @@ void setup_defaults(const std::shared_ptr &ctx, }}; binding_action create_entity_action{forward_action_t::SEND, create_entity_event}; - Event ev_mouse_lmb{event_class::MOUSE_BUTTON, Qt::MouseButton::LeftButton, Qt::NoModifier, QEvent::MouseButtonRelease}; + Event ev_mouse_lmb_ctrl{ + event_class::MOUSE_BUTTON, + Qt::MouseButton::LeftButton, + Qt::KeyboardModifier::ControlModifier, + QEvent::MouseButtonRelease}; - ctx->bind(ev_mouse_lmb, create_entity_action); + ctx->bind(ev_mouse_lmb_ctrl, create_entity_action); binding_func_t move_entity{[&](const event_arguments &args, const std::shared_ptr controller) { @@ -135,9 +165,67 @@ void setup_defaults(const std::shared_ptr &ctx, }}; binding_action move_entity_action{forward_action_t::SEND, move_entity}; - Event ev_mouse_rmb{event_class::MOUSE_BUTTON, Qt::MouseButton::RightButton, Qt::NoModifier, QEvent::MouseButtonRelease}; + Event ev_mouse_rmb{ + event_class::MOUSE_BUTTON, + Qt::MouseButton::RightButton, + Qt::KeyboardModifier::NoModifier, + QEvent::MouseButtonRelease}; ctx->bind(ev_mouse_rmb, move_entity_action); + + binding_func_t init_drag_selection{[&](const event_arguments &args, + const std::shared_ptr controller) { + controller->set_drag_select_start(args.mouse); + return nullptr; + }}; + + binding_action init_drag_selection_action{forward_action_t::CLEAR, init_drag_selection}; + Event ev_mouse_lmb_press{ + event_class::MOUSE_BUTTON, + Qt::MouseButton::LeftButton, + Qt::KeyboardModifier::NoModifier, + QEvent::MouseButtonPress}; + + ctx->bind(ev_mouse_lmb_press, init_drag_selection_action); + + binding_func_t drag_selection{ + [&](const event_arguments &args, + const std::shared_ptr controller) { + Eigen::Matrix4f cam_matrix = camera->get_projection_matrix() * camera->get_view_matrix(); + event::EventHandler::param_map::map_t params{ + {"controlled", controller->get_controlled()}, + {"drag_start", controller->get_drag_select_start().to_viewport(camera).to_ndc_space(camera)}, + {"drag_end", args.mouse.to_viewport(camera).to_ndc_space(camera)}, + {"camera_matrix", cam_matrix}, + {"select_cb", + std::function ids)>{ + [controller]( + const std::vector ids) { + controller->set_selected(ids); + }}}, + }; + + auto event = simulation->get_event_loop()->create_event( + "game.drag_select", + simulation->get_commander(), + simulation->get_game()->get_state(), + time_loop->get_clock()->get_time(), + params); + + // Reset drag selection start + controller->reset_drag_select(); + + return event; + }}; + + binding_action drag_selection_action{forward_action_t::CLEAR, drag_selection}; + Event ev_mouse_lmb_release{ + event_class::MOUSE_BUTTON, + Qt::MouseButton::LeftButton, + Qt::KeyboardModifier::NoModifier, + QEvent::MouseButtonRelease}; + + ctx->bind(ev_mouse_lmb_release, drag_selection_action); } diff --git a/libopenage/input/controller/game/controller.h b/libopenage/input/controller/game/controller.h index 847a5999f5..a9d690f622 100644 --- a/libopenage/input/controller/game/controller.h +++ b/libopenage/input/controller/game/controller.h @@ -4,12 +4,15 @@ #include #include +#include #include +#include "coord/pixel.h" #include "curve/discrete.h" #include "gamestate/types.h" #include "input/event.h" + namespace openage { namespace gamestate { @@ -30,8 +33,6 @@ class BindingContext; * Controllers handle inputs from outside of a game (e.g. GUI, AI, scripts, ...) * and pass the resulting events to game entities. They also act as a form of * access control for using in-game functionality of game entities. - * - * TODO: Connection to engine */ class Controller : public std::enable_shared_from_this { public: @@ -41,75 +42,102 @@ class Controller : public std::enable_shared_from_this { ~Controller() = default; /** - * Switch the actively controlled faction by the controller. - * The ID must be in the list of controlled factions. - * - * @param faction_id ID of the new active faction. - */ + * Switch the actively controlled faction by the controller. + * The ID must be in the list of controlled factions. + * + * @param faction_id ID of the new active faction. + */ void set_control(size_t faction_id); /** - * Get the ID of the faction actively controlled by the controller. - * - * @return ID of the active faction. - */ + * Get the ID of the faction actively controlled by the controller. + * + * @return ID of the active faction. + */ size_t get_controlled() const; /** - * Get the currently selected entities. - * - * @return Selected entities. - */ + * Get the currently selected entities. + * + * @return Selected entities. + */ const std::vector &get_selected() const; /** - * Set the currently selected entities. - * - * @param ids Selected entities. - */ - void set_selected(std::vector ids); + * Set the currently selected entities. + * + * @param ids Selected entities. + */ + void set_selected(const std::vector ids); /** - * Process an input event from the input manager. - * - * @param ev_args Input event and arguments. - * @param ctx Binding context for looking up the event transformation. - * - * @return true if the event is accepted, else false. - */ + * Process an input event from the input manager. + * + * @param ev_args Input event and arguments. + * @param ctx Binding context for looking up the event transformation. + * + * @return true if the event is accepted, else false. + */ bool process(const event_arguments &ev_args, const std::shared_ptr &ctx); + /** + * Set the start position of a drag selection. + * + * @param start Start position of the drag selection. + */ + void set_drag_select_start(const coord::input &start); + + /** + * Get the start position of a drag selection. + * + * @return Start position of the drag selection. + */ + const coord::input get_drag_select_start() const; + + /** + * Reset the drag select start position. + */ + void reset_drag_select(); + private: /** - * List of factions controllable by this controller. - */ + * Factions controllable by this controller. + */ std::unordered_set controlled_factions; /** - * ID of the currently active faction. - */ + * ID of the currently active faction. + */ size_t active_faction_id; /** - * Currently selected entities. - */ + * Currently selected entities. + */ std::vector selected; /** - * Queue for gamestate events generated from inputs. - */ + * Queue for gamestate events generated from inputs. + */ std::vector> outqueue; /** - * Mutex for threaded access. - */ + * Start position of a drag selection. + * + * TODO: Move this into an input event. + */ + std::optional drag_select_start; + + /** + * Mutex for threaded access. + */ mutable std::recursive_mutex mutex; }; /** * Setup default controller action bindings: * - * - Mouse click: Create game entity. + * - CTRL + Left Mouse click: Create game entity. + * - Right Mouse click: Move game entity. * * @param ctx Binding context the actions are added to. * @param time_loop Time loop for getting simulation time. diff --git a/libopenage/input/controller/hud/CMakeLists.txt b/libopenage/input/controller/hud/CMakeLists.txt new file mode 100644 index 0000000000..0d348caf31 --- /dev/null +++ b/libopenage/input/controller/hud/CMakeLists.txt @@ -0,0 +1,5 @@ +add_sources(libopenage + binding_context.cpp + binding.cpp + controller.cpp +) diff --git a/libopenage/input/controller/hud/binding.cpp b/libopenage/input/controller/hud/binding.cpp new file mode 100644 index 0000000000..1c886a3d43 --- /dev/null +++ b/libopenage/input/controller/hud/binding.cpp @@ -0,0 +1,7 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#include "binding.h" + +namespace openage::input::hud { + +} // namespace openage::input::hud diff --git a/libopenage/input/controller/hud/binding.h b/libopenage/input/controller/hud/binding.h new file mode 100644 index 0000000000..716a7e4f39 --- /dev/null +++ b/libopenage/input/controller/hud/binding.h @@ -0,0 +1,30 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include + +#include "event/event.h" +#include "input/event.h" + +namespace openage::input::hud { +class Controller; + +using binding_flags_t = std::unordered_map; +using binding_func_t = std::function)>; + + +/** + * Action taken by the controller when receiving an input. + * + * @param action Maps an input event to a camera action. + * @param flags Additional parameters for the transformation. + */ +struct binding_action { + const binding_func_t action; + const binding_flags_t flags = {}; +}; + +} // namespace openage::input::hud diff --git a/libopenage/input/controller/hud/binding_context.cpp b/libopenage/input/controller/hud/binding_context.cpp new file mode 100644 index 0000000000..fb1d7a5e25 --- /dev/null +++ b/libopenage/input/controller/hud/binding_context.cpp @@ -0,0 +1,39 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#include "binding_context.h" + + +namespace openage::input::hud { + +BindingContext::BindingContext() : + by_event{} {} + +void BindingContext::bind(const Event &ev, const binding_action bind) { + this->by_event.emplace(std::make_pair(ev, bind)); +} + +void BindingContext::bind(const event_class &cl, const binding_action bind) { + this->by_class.emplace(std::make_pair(cl, bind)); +} + +bool BindingContext::is_bound(const Event &ev) const { + return this->by_event.contains(ev) || this->by_class.contains(ev.cc.cl); +} + +const binding_action &BindingContext::lookup(const Event &ev) const { + auto event_lookup = this->by_event.find(ev); + if (event_lookup != std::end(this->by_event)) { + return (*event_lookup).second; + } + + for (auto eclass : ev.cc.get_classes()) { + auto class_lookup = this->by_class.find(eclass); + if (class_lookup != std::end(this->by_class)) { + return (*class_lookup).second; + } + } + + throw Error{MSG(err) << "Event is not bound in binding_action context."}; +} + +} // namespace openage::input::hud diff --git a/libopenage/input/controller/hud/binding_context.h b/libopenage/input/controller/hud/binding_context.h new file mode 100644 index 0000000000..fac8283df6 --- /dev/null +++ b/libopenage/input/controller/hud/binding_context.h @@ -0,0 +1,72 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "input/controller/hud/binding.h" +#include "input/event.h" + +namespace openage::input::hud { + +/** + * Maps input events to HUD actons. + */ +class BindingContext { +public: + /** + * Create a new binding context. + */ + BindingContext(); + + ~BindingContext() = default; + + /** + * Bind a specific key combination to a binding. + * + * This is the first matching priority. + * + * @param ev Input event triggering the action. + * @param bind Binding for the event. + */ + void bind(const Event &ev, const binding_action bind); + + /** + * Bind an event class to an action. + * + * This is the second matching priority. + * + * @param ev Input event triggering the action. + * @param bind Binding for the event. + */ + void bind(const event_class &cl, const binding_action bind); + + /** + * Check whether a specific key event is bound in this context. + * + * @param ev Input event. + * + * @return true if event is bound, else false. + */ + bool is_bound(const Event &ev) const; + + /** + * Get the bindings for a specific event. + * + * @param ev Input event mapped to the binding. + */ + const binding_action &lookup(const Event &ev) const; + +private: + /** + * Maps specific input events to bindings. + */ + std::unordered_map by_event; + + /** + * Maps event classes to bindings. + */ + std::unordered_map by_class; +}; + +} // namespace openage::input::hud diff --git a/libopenage/input/controller/hud/controller.cpp b/libopenage/input/controller/hud/controller.cpp new file mode 100644 index 0000000000..b423ef79ae --- /dev/null +++ b/libopenage/input/controller/hud/controller.cpp @@ -0,0 +1,88 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#include "controller.h" + +#include + +#include "input/controller/hud/binding_context.h" + +#include "renderer/stages/hud/render_entity.h" +#include "renderer/stages/hud/render_stage.h" + + +namespace openage::input::hud { + +Controller::Controller() : + drag_entity{nullptr} {} + +bool Controller::process(const event_arguments &ev_args, + const std::shared_ptr &ctx) { + if (not ctx->is_bound(ev_args.e)) { + return false; + } + + auto bind = ctx->lookup(ev_args.e); + bind.action(ev_args, this->shared_from_this()); + + return true; +} + +void Controller::set_drag_entity(const std::shared_ptr &entity) { + this->drag_entity = entity; +} + +const std::shared_ptr &Controller::get_drag_entity() const { + return this->drag_entity; +} + +void setup_defaults(const std::shared_ptr &ctx, + const std::shared_ptr &hud_renderer) { + binding_func_t drag_selection_init{[&](const event_arguments &args, + const std::shared_ptr controller) { + auto render_entity = std::make_shared(args.mouse); + hud_renderer->add_drag_entity(render_entity); + controller->set_drag_entity(render_entity); + }}; + + binding_action drag_selection_init_action{drag_selection_init}; + Event ev_mouse_lmb_press{ + event_class::MOUSE_BUTTON, + Qt::MouseButton::LeftButton, + Qt::KeyboardModifier::NoModifier, + QEvent::MouseButtonPress}; + + ctx->bind(ev_mouse_lmb_press, drag_selection_init_action); + + binding_func_t drag_selection_move{[&](const event_arguments &args, + const std::shared_ptr controller) { + if (controller->get_drag_entity()) { + controller->get_drag_entity()->update(args.mouse); + } + }}; + + binding_action drag_selection_move_action{drag_selection_move}; + Event ev_mouse_move{ + event_class::MOUSE_MOVE, + Qt::MouseButton::NoButton, + Qt::KeyboardModifier::NoModifier, + QEvent::MouseMove}; + + ctx->bind(ev_mouse_move, drag_selection_move_action); + + binding_func_t drag_selection_end{[&](const event_arguments & /* args */, + const std::shared_ptr controller) { + hud_renderer->remove_drag_entity(); + controller->set_drag_entity(nullptr); + }}; + + binding_action drag_selection_end_action{drag_selection_end}; + Event ev_mouse_lmb_release{ + event_class::MOUSE_BUTTON, + Qt::MouseButton::LeftButton, + Qt::KeyboardModifier::NoModifier, + QEvent::MouseButtonRelease}; + + ctx->bind(ev_mouse_lmb_release, drag_selection_end_action); +} + +} // namespace openage::input::hud diff --git a/libopenage/input/controller/hud/controller.h b/libopenage/input/controller/hud/controller.h new file mode 100644 index 0000000000..e7c391846e --- /dev/null +++ b/libopenage/input/controller/hud/controller.h @@ -0,0 +1,75 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "input/event.h" + + +namespace openage { + +namespace renderer::hud { +class HudDragRenderEntity; +class HudRenderStage; +} // namespace renderer::hud + +namespace input::hud { +class BindingContext; + +/** + * Control athe HUD with input from the input manager. + */ +class Controller : public std::enable_shared_from_this { +public: + Controller(); + + ~Controller() = default; + + /** + * Process an input event from the input manager. + * + * @param ev Input event and arguments. + * @param ctx Binding context that maps input events to HUD actions. + * + * @return true if the event is accepted, else false. + */ + bool process(const event_arguments &ev_args, + const std::shared_ptr &ctx); + + /** + * Set the render entity for the selection box. + * + * @param entity New render entity. + */ + void set_drag_entity(const std::shared_ptr &entity); + + /** + * Get the render entity for the selection box. + * + * @return Render entity for the selection box. + */ + const std::shared_ptr &get_drag_entity() const; + +private: + /** + * Render entity for the selection box. + */ + std::shared_ptr drag_entity; +}; + +/** + * Setup default HUD action bindings: + * + * - Mouse drag: selection box draw + * + * TODO: Make this configurable. + * + * @param ctx Binding context the actions are added to. + * @param hud_renderer HUD render stage that is used to render the selection box. + */ +void setup_defaults(const std::shared_ptr &ctx, + const std::shared_ptr &hud_renderer); + +} // namespace input::hud +} // namespace openage diff --git a/libopenage/input/input_context.cpp b/libopenage/input/input_context.cpp index ef733b0e98..bdedf60f37 100644 --- a/libopenage/input/input_context.cpp +++ b/libopenage/input/input_context.cpp @@ -15,35 +15,53 @@ const std::string &InputContext::get_id() { return this->id; } -void InputContext::set_engine_bindings(const std::shared_ptr &bindings) { - this->engine_bindings = bindings; +void InputContext::set_game_bindings(const std::shared_ptr &bindings) { + this->game_bindings = bindings; } void InputContext::set_camera_bindings(const std::shared_ptr &bindings) { this->camera_bindings = bindings; } -const std::shared_ptr &InputContext::get_engine_bindings() { - return this->engine_bindings; +void InputContext::set_hud_bindings(const std::shared_ptr &bindings) { + this->hud_bindings = bindings; +} + +const std::shared_ptr &InputContext::get_game_bindings() { + return this->game_bindings; } const std::shared_ptr &InputContext::get_camera_bindings() { return this->camera_bindings; } +const std::shared_ptr &InputContext::get_hud_bindings() { + return this->hud_bindings; +} + void InputContext::bind(const Event &ev, const input_action act) { - this->by_event.emplace(std::make_pair(ev, act)); + std::vector actions{act}; + this->by_event.emplace(std::make_pair(ev, actions)); } void InputContext::bind(const event_class &cl, const input_action act) { - this->by_class.emplace(std::make_pair(cl, act)); + std::vector actions{act}; + this->by_class.emplace(std::make_pair(cl, actions)); +} + +void InputContext::bind(const Event &ev, const std::vector &&acts) { + this->by_event.emplace(std::make_pair(ev, std::move(acts))); +} + +void InputContext::bind(const event_class &cl, const std::vector &&acts) { + this->by_class.emplace(std::make_pair(cl, std::move(acts))); } bool InputContext::is_bound(const Event &ev) const { return this->by_event.contains(ev) || this->by_class.contains(ev.cc.cl); } -const input_action &InputContext::lookup(const Event &ev) const { +const std::vector &InputContext::lookup(const Event &ev) const { auto event_lookup = this->by_event.find(ev); if (event_lookup != std::end(this->by_event)) { return (*event_lookup).second; diff --git a/libopenage/input/input_context.h b/libopenage/input/input_context.h index f3d2d41903..55e3cd2974 100644 --- a/libopenage/input/input_context.h +++ b/libopenage/input/input_context.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include "input/action.h" #include "input/event.h" @@ -17,6 +18,10 @@ namespace game { class BindingContext; } +namespace hud { +class BindingContext; +} + /** * An input context contains all keybindings and actions * active in e.g. the HUD only. @@ -47,7 +52,7 @@ class InputContext { * * @param bindings Binding context for gamestate events. */ - void set_engine_bindings(const std::shared_ptr &bindings); + void set_game_bindings(const std::shared_ptr &bindings); /** * Set the associated context for binding input events to camera actions. @@ -56,12 +61,19 @@ class InputContext { */ void set_camera_bindings(const std::shared_ptr &bindings); + /** + * Set the associated context for binding input events to HUD actions. + * + * @param bindings Binding context for HUD actions. + */ + void set_hud_bindings(const std::shared_ptr &bindings); + /** * Get the associated context for binding input events to game events. * * @return Binding context of the input context. */ - const std::shared_ptr &get_engine_bindings(); + const std::shared_ptr &get_game_bindings(); /** * Get the associated context for binding input events to camera actions. @@ -71,25 +83,52 @@ class InputContext { const std::shared_ptr &get_camera_bindings(); /** - * Bind a specific key combination to an action. + * Get the associated context for binding input events to HUD actions. + * + * @return Binding context of the input context. + */ + const std::shared_ptr &get_hud_bindings(); + + /** + * Bind a specific key combination to a single action. * * This is the first matching priority. * * @param ev Input event triggering the action. - * @param act Function executing the action. + * @param act Action executed by the event. */ void bind(const Event &ev, const input_action act); /** - * Bind an event class to an action. + * Bind an event class to a single action. * * This is the second matching priority. * * @param ev Input event triggering the action. - * @param act Function executing the action. + * @param act Action executed by the event. */ void bind(const event_class &cl, const input_action act); + /** + * Bind a specific key combination to a list of actions. + * + * This is the first matching priority. + * + * @param ev Input event triggering the action. + * @param act Actions executed by the event. + */ + void bind(const Event &ev, const std::vector &&acts); + + /** + * Bind an event class to a list of actions. + * + * This is the second matching priority. + * + * @param ev Input event triggering the action. + * @param act Actions executed by the event. + */ + void bind(const event_class &cl, const std::vector &&acts); + /** * Check whether a specific key event is bound in this context. * @@ -104,7 +143,7 @@ class InputContext { * * @param ev Input event triggering the action. */ - const input_action &lookup(const Event &ev) const; + const std::vector &lookup(const Event &ev) const; /** * Get all event->action bindings in this context. @@ -130,22 +169,27 @@ class InputContext { /** * Maps specific input events to actions. */ - std::unordered_map by_event; + std::unordered_map, event_hash> by_event; /** * Maps event classes to actions. */ - std::unordered_map by_class; + std::unordered_map, event_class_hash> by_class; /** - * Additional context for engine events. + * Additional context for game simulation events. */ - std::shared_ptr engine_bindings; + std::shared_ptr game_bindings; /** * Additional context for camera actions. */ std::shared_ptr camera_bindings; + + /** + * Additional context for HUD actions. + */ + std::shared_ptr hud_bindings; }; } // namespace openage::input diff --git a/libopenage/input/input_manager.cpp b/libopenage/input/input_manager.cpp index 47ff5c1834..58bca7fb9e 100644 --- a/libopenage/input/input_manager.cpp +++ b/libopenage/input/input_manager.cpp @@ -4,6 +4,7 @@ #include "input/controller/camera/controller.h" #include "input/controller/game/controller.h" +#include "input/controller/hud/controller.h" #include "input/event.h" #include "input/input_context.h" #include "renderer/gui/guisys/public/gui_input.h" @@ -14,8 +15,9 @@ InputManager::InputManager() : global_context{std::make_shared("main")}, active_contexts{}, available_contexts{}, - engine_controller{nullptr}, + game_controller{nullptr}, camera_controller{nullptr}, + hud_controller{nullptr}, gui_input{nullptr} { } @@ -27,8 +29,12 @@ void InputManager::set_camera_controller(const std::shared_ptrcamera_controller = controller; } -void InputManager::set_engine_controller(const std::shared_ptr &controller) { - this->engine_controller = controller; +void InputManager::set_game_controller(const std::shared_ptr &controller) { + this->game_controller = controller; +} + +void InputManager::set_hud_controller(const std::shared_ptr controller) { + this->hud_controller = controller; } const std::shared_ptr &InputManager::get_global_context() { @@ -127,18 +133,20 @@ bool InputManager::process(const QEvent &ev) { // Check context list on top of the stack (most recent bound first) for (auto const &ctx : this->active_contexts) { if (ctx->is_bound(input_ev)) { - this->process_action(input_ev, - ctx->lookup(input_ev), - ctx); + auto &actions = ctx->lookup(input_ev); + for (auto const &action : actions) { + this->process_action(input_ev, action, ctx); + } return true; } } // If no local keybinds were bound, check the global keybinds if (this->global_context->is_bound(input_ev)) { - this->process_action(input_ev, - this->global_context->lookup(input_ev), - this->global_context); + auto &actions = this->global_context->lookup(input_ev); + for (auto const &action : actions) { + this->process_action(input_ev, action, this->global_context); + } return true; } @@ -173,14 +181,18 @@ void InputManager::process_action(const input::Event &ev, this->pop_context(ctx_id); break; } - case input_action_t::ENGINE: - this->engine_controller->process(args, ctx->get_engine_bindings()); + case input_action_t::GAME: + this->game_controller->process(args, ctx->get_game_bindings()); break; case input_action_t::CAMERA: this->camera_controller->process(args, ctx->get_camera_bindings()); break; + case input_action_t::HUD: + this->hud_controller->process(args, ctx->get_hud_bindings()); + break; + case input_action_t::GUI: this->gui_input->process(args.e.get_event()); break; @@ -196,6 +208,9 @@ void InputManager::process_action(const input::Event &ev, void setup_defaults(const std::shared_ptr &ctx) { + // hud + input_action hud_action{input_action_t::HUD}; + // camera input_action camera_action{input_action_t::CAMERA}; @@ -212,16 +227,19 @@ void setup_defaults(const std::shared_ptr &ctx) { ctx->bind(ev_down, camera_action); ctx->bind(ev_wheel_up, camera_action); ctx->bind(ev_wheel_down, camera_action); - ctx->bind(event_class::MOUSE_MOVE, camera_action); + ctx->bind(event_class::MOUSE_MOVE, {camera_action, hud_action}); - // engine - input_action engine_action{input_action_t::ENGINE}; + // game + input_action game_action{input_action_t::GAME}; Event ev_mouse_lmb{event_class::MOUSE_BUTTON, Qt::LeftButton, Qt::NoModifier, QEvent::MouseButtonRelease}; Event ev_mouse_rmb{event_class::MOUSE_BUTTON, Qt::RightButton, Qt::NoModifier, QEvent::MouseButtonRelease}; - ctx->bind(ev_mouse_lmb, engine_action); - ctx->bind(ev_mouse_rmb, engine_action); + ctx->bind(ev_mouse_lmb, {game_action, hud_action}); + ctx->bind(ev_mouse_rmb, {game_action, hud_action}); + + // also forward all other mouse button events + ctx->bind(event_class::MOUSE_BUTTON, {game_action, hud_action}); } diff --git a/libopenage/input/input_manager.h b/libopenage/input/input_manager.h index f81a5a5bc4..36bd158d59 100644 --- a/libopenage/input/input_manager.h +++ b/libopenage/input/input_manager.h @@ -26,6 +26,10 @@ namespace game { class Controller; } // namespace game +namespace hud { +class Controller; +} // namespace hud + class InputContext; /** @@ -53,11 +57,18 @@ class InputManager { void set_camera_controller(const std::shared_ptr &controller); /** - * Set the controller for the engine. + * Set the controller for the game simulation. * - * @param controller Engine controller. + * @param controller Game controller. */ - void set_engine_controller(const std::shared_ptr &controller); + void set_game_controller(const std::shared_ptr &controller); + + /** + * Set the controller for the HUD. + * + * @param controller HUD controller. + */ + void set_hud_controller(const std::shared_ptr controller); /** * returns the global keybind context. @@ -170,15 +181,20 @@ class InputManager { std::unordered_map> available_contexts; /** - * Interface to the engine. + * Interface to the game simulation. */ - std::shared_ptr engine_controller; + std::shared_ptr game_controller; /** * Interface to the camera. */ std::shared_ptr camera_controller; + /** + * Interface to the HUD. + */ + std::shared_ptr hud_controller; + /** * Interface to the GUI. */ diff --git a/libopenage/presenter/presenter.cpp b/libopenage/presenter/presenter.cpp index 06498e51c6..b8c034987a 100644 --- a/libopenage/presenter/presenter.cpp +++ b/libopenage/presenter/presenter.cpp @@ -12,6 +12,8 @@ #include "input/controller/camera/controller.h" #include "input/controller/game/binding_context.h" #include "input/controller/game/controller.h" +#include "input/controller/hud/binding_context.h" +#include "input/controller/hud/controller.h" #include "input/input_context.h" #include "input/input_manager.h" #include "log/log.h" @@ -23,10 +25,11 @@ #include "renderer/resources/shader_source.h" #include "renderer/resources/texture_info.h" #include "renderer/stages/camera/manager.h" -#include "renderer/stages/screen/screen_renderer.h" -#include "renderer/stages/skybox/skybox_renderer.h" -#include "renderer/stages/terrain/terrain_renderer.h" -#include "renderer/stages/world/world_renderer.h" +#include "renderer/stages/hud/render_stage.h" +#include "renderer/stages/screen/render_stage.h" +#include "renderer/stages/skybox/render_stage.h" +#include "renderer/stages/terrain/render_stage.h" +#include "renderer/stages/world/render_stage.h" #include "renderer/window.h" #include "time/time_loop.h" #include "util/path.h" @@ -75,8 +78,7 @@ void Presenter::run(bool debug_graphics) { void Presenter::set_simulation(const std::shared_ptr &simulation) { this->simulation = simulation; - auto render_factory = std::make_shared(this->terrain_renderer, - this->world_renderer); + auto render_factory = std::make_shared(this->terrain_renderer, this->world_renderer); this->simulation->attach_renderer(render_factory); } @@ -103,8 +105,7 @@ void Presenter::init_graphics(bool debug) { this->asset_manager->set_placeholder_animation(missing_tex); // Camera - this->camera = std::make_shared(this->renderer, - this->window->get_size()); + this->camera = std::make_shared(this->renderer, this->window->get_size()); this->window->add_resize_callback([this](size_t w, size_t h, double /*scale*/) { this->camera->resize(w, h); }); @@ -112,7 +113,7 @@ void Presenter::init_graphics(bool debug) { this->camera_manager = std::make_shared(this->camera); // Skybox - this->skybox_renderer = std::make_shared( + this->skybox_renderer = std::make_shared( this->window, this->renderer, this->root_dir["assets"]["shaders"]); @@ -120,7 +121,7 @@ void Presenter::init_graphics(bool debug) { this->render_passes.push_back(this->skybox_renderer->get_render_pass()); // Terrain - this->terrain_renderer = std::make_shared( + this->terrain_renderer = std::make_shared( this->window, this->renderer, this->camera, @@ -130,7 +131,7 @@ void Presenter::init_graphics(bool debug) { this->render_passes.push_back(this->terrain_renderer->get_render_pass()); // Units/buildings - this->world_renderer = std::make_shared( + this->world_renderer = std::make_shared( this->window, this->renderer, this->camera, @@ -139,12 +140,21 @@ void Presenter::init_graphics(bool debug) { this->time_loop->get_clock()); this->render_passes.push_back(this->world_renderer->get_render_pass()); + // HUD + this->hud_renderer = std::make_shared( + this->window, + this->renderer, + this->camera, + this->root_dir["assets"]["shaders"], + this->asset_manager, + this->time_loop->get_clock()); + this->render_passes.push_back(this->hud_renderer->get_render_pass()); + this->init_gui(); this->init_final_render_pass(); if (this->simulation) { - auto render_factory = std::make_shared(this->terrain_renderer, - this->world_renderer); + auto render_factory = std::make_shared(this->terrain_renderer, this->world_renderer); this->simulation->attach_renderer(render_factory); } @@ -215,12 +225,12 @@ void Presenter::init_input() { log::log(INFO << "Loading game simulation controls"); // TODO: Remove hardcoding - auto engine_controller = std::make_shared( + auto game_controller = std::make_shared( std::unordered_set{0, 1, 2, 3}, 0); auto engine_context = std::make_shared(); input::game::setup_defaults(engine_context, this->time_loop, this->simulation, this->camera); - this->input_manager->set_engine_controller(engine_controller); - input_ctx->set_engine_bindings(engine_context); + this->input_manager->set_game_controller(game_controller); + input_ctx->set_game_bindings(engine_context); } // attach GUI if it's initialized @@ -239,12 +249,22 @@ void Presenter::init_input() { input_ctx->set_camera_bindings(camera_context); } + // setup HUD controls + if (this->hud_renderer) { + log::log(INFO << "Loading HUD controls"); + auto hud_controller = std::make_shared(); + auto hud_context = std::make_shared(); + input::hud::setup_defaults(hud_context, this->hud_renderer); + this->input_manager->set_hud_controller(hud_controller); + input_ctx->set_hud_bindings(hud_context); + } + log::log(INFO << "Presenter: Input subsystem initialized"); } void Presenter::init_final_render_pass() { // Final output to window - this->screen_renderer = std::make_shared( + this->screen_renderer = std::make_shared( this->window, this->renderer, this->root_dir["assets"]["shaders"]); @@ -269,9 +289,11 @@ void Presenter::init_final_render_pass() { } void Presenter::render() { + // TODO: Pass current time to update() instead of fetching it in renderer this->camera_manager->update(); this->terrain_renderer->update(); this->world_renderer->update(); + this->hud_renderer->update(); this->gui->render(); for (auto &pass : this->render_passes) { diff --git a/libopenage/presenter/presenter.h b/libopenage/presenter/presenter.h index 6e7817ed2d..db28522a7c 100644 --- a/libopenage/presenter/presenter.h +++ b/libopenage/presenter/presenter.h @@ -41,20 +41,24 @@ namespace gui { class GUI; } +namespace hud { +class HudRenderStage; +} + namespace screen { -class ScreenRenderer; +class ScreenRenderStage; } namespace skybox { -class SkyboxRenderer; +class SkyboxRenderStage; } namespace terrain { -class TerrainRenderer; +class TerrainRenderStage; } namespace world { -class WorldRenderer; +class WorldRenderStage; } namespace resources { @@ -178,22 +182,27 @@ class Presenter { /** * Graphics output for the map background. */ - std::shared_ptr skybox_renderer; + std::shared_ptr skybox_renderer; /** * Graphics output for terrain. */ - std::shared_ptr terrain_renderer; + std::shared_ptr terrain_renderer; /** * Graphics output for units/buildings. */ - std::shared_ptr world_renderer; + std::shared_ptr world_renderer; + + /** + * Graphics output for the HUD. + */ + std::shared_ptr hud_renderer; /** * Final graphics output to the window screen. */ - std::shared_ptr screen_renderer; + std::shared_ptr screen_renderer; /** * Manager for loading/storing asset resources. diff --git a/libopenage/renderer/demo/demo_3.cpp b/libopenage/renderer/demo/demo_3.cpp index 9dbaa9b93e..835c13a364 100644 --- a/libopenage/renderer/demo/demo_3.cpp +++ b/libopenage/renderer/demo/demo_3.cpp @@ -13,12 +13,12 @@ #include "renderer/resources/assets/asset_manager.h" #include "renderer/resources/shader_source.h" #include "renderer/stages/camera/manager.h" -#include "renderer/stages/screen/screen_renderer.h" -#include "renderer/stages/skybox/skybox_renderer.h" -#include "renderer/stages/terrain/terrain_render_entity.h" -#include "renderer/stages/terrain/terrain_renderer.h" -#include "renderer/stages/world/world_render_entity.h" -#include "renderer/stages/world/world_renderer.h" +#include "renderer/stages/screen/render_stage.h" +#include "renderer/stages/skybox/render_stage.h" +#include "renderer/stages/terrain/render_entity.h" +#include "renderer/stages/terrain/render_stage.h" +#include "renderer/stages/world/render_entity.h" +#include "renderer/stages/world/render_stage.h" #include "renderer/uniform_buffer.h" #include "time/clock.h" @@ -58,14 +58,14 @@ void renderer_demo_3(const util::Path &path) { path["assets"]["test"]); // Renders the background - auto skybox_renderer = std::make_shared( + auto skybox_renderer = std::make_shared( window, renderer, path["assets"]["shaders"]); skybox_renderer->set_color(1.0f, 0.5f, 0.0f, 1.0f); // orange color // Renders the terrain in 3D - auto terrain_renderer = std::make_shared( + auto terrain_renderer = std::make_shared( window, renderer, camera, @@ -74,7 +74,7 @@ void renderer_demo_3(const util::Path &path) { clock); // Renders units/buildings/other objects - auto world_renderer = std::make_shared( + auto world_renderer = std::make_shared( window, renderer, camera, @@ -92,7 +92,7 @@ void renderer_demo_3(const util::Path &path) { // Final output on screen has its own subrenderer // It takes the outputs of all previous render passes // and blends them together - auto screen_renderer = std::make_shared( + auto screen_renderer = std::make_shared( window, renderer, path["assets"]["shaders"]); diff --git a/libopenage/renderer/demo/stresstest_0.cpp b/libopenage/renderer/demo/stresstest_0.cpp index b33abb9685..7167150ac9 100644 --- a/libopenage/renderer/demo/stresstest_0.cpp +++ b/libopenage/renderer/demo/stresstest_0.cpp @@ -12,12 +12,12 @@ #include "renderer/resources/assets/asset_manager.h" #include "renderer/resources/shader_source.h" #include "renderer/stages/camera/manager.h" -#include "renderer/stages/screen/screen_renderer.h" -#include "renderer/stages/skybox/skybox_renderer.h" -#include "renderer/stages/terrain/terrain_render_entity.h" -#include "renderer/stages/terrain/terrain_renderer.h" -#include "renderer/stages/world/world_render_entity.h" -#include "renderer/stages/world/world_renderer.h" +#include "renderer/stages/screen/render_stage.h" +#include "renderer/stages/skybox/render_stage.h" +#include "renderer/stages/terrain/render_entity.h" +#include "renderer/stages/terrain/render_stage.h" +#include "renderer/stages/world/render_entity.h" +#include "renderer/stages/world/render_stage.h" #include "renderer/uniform_buffer.h" #include "time/clock.h" #include "util/fps.h" @@ -59,14 +59,14 @@ void renderer_stresstest_0(const util::Path &path) { path["assets"]["test"]); // Renders the background - auto skybox_renderer = std::make_shared( + auto skybox_renderer = std::make_shared( window, renderer, path["assets"]["shaders"]); skybox_renderer->set_color(1.0f, 0.5f, 0.0f, 1.0f); // orange color // Renders the terrain in 3D - auto terrain_renderer = std::make_shared( + auto terrain_renderer = std::make_shared( window, renderer, camera, @@ -75,7 +75,7 @@ void renderer_stresstest_0(const util::Path &path) { clock); // Renders units/buildings/other objects - auto world_renderer = std::make_shared( + auto world_renderer = std::make_shared( window, renderer, camera, @@ -93,7 +93,7 @@ void renderer_stresstest_0(const util::Path &path) { // Final output on screen has its own subrenderer // It takes the outputs of all previous render passes // and blends them together - auto screen_renderer = std::make_shared( + auto screen_renderer = std::make_shared( window, renderer, path["assets"]["shaders"]); diff --git a/libopenage/renderer/opengl/renderer.cpp b/libopenage/renderer/opengl/renderer.cpp index 91cc5b35ee..a06cc16edf 100644 --- a/libopenage/renderer/opengl/renderer.cpp +++ b/libopenage/renderer/opengl/renderer.cpp @@ -27,8 +27,17 @@ GlRenderer::GlRenderer(const std::shared_ptr &ctx, display{std::make_shared(ctx, viewport_size[0], viewport_size[1])} { + // color used to clear the color buffers + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + // global GL alpha blending settings - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate( + GL_SRC_ALPHA, // source (overlaying) RGB factor + GL_ONE_MINUS_SRC_ALPHA, // destination (underlying) RGB factor + GL_ONE, // source (overlaying) alpha factor + GL_ONE_MINUS_SRC_ALPHA // destination (underlying) alpha factor + ); // global GL depth testing settings glDepthFunc(GL_LEQUAL); @@ -157,7 +166,6 @@ void GlRenderer::render(const std::shared_ptr &pass) { auto gl_target = std::dynamic_pointer_cast(pass->get_target()); gl_target->bind_write(); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: Option for face culling diff --git a/libopenage/renderer/render_factory.cpp b/libopenage/renderer/render_factory.cpp index c40cc46508..6d5e276479 100644 --- a/libopenage/renderer/render_factory.cpp +++ b/libopenage/renderer/render_factory.cpp @@ -3,14 +3,14 @@ #include "render_factory.h" #include "coord/phys.h" -#include "renderer/stages/terrain/terrain_render_entity.h" -#include "renderer/stages/terrain/terrain_renderer.h" -#include "renderer/stages/world/world_render_entity.h" -#include "renderer/stages/world/world_renderer.h" +#include "renderer/stages/terrain/render_entity.h" +#include "renderer/stages/terrain/render_stage.h" +#include "renderer/stages/world/render_entity.h" +#include "renderer/stages/world/render_stage.h" namespace openage::renderer { -RenderFactory::RenderFactory(const std::shared_ptr terrain_renderer, - const std::shared_ptr world_renderer) : +RenderFactory::RenderFactory(const std::shared_ptr terrain_renderer, + const std::shared_ptr world_renderer) : terrain_renderer{terrain_renderer}, world_renderer{world_renderer} { } diff --git a/libopenage/renderer/render_factory.h b/libopenage/renderer/render_factory.h index 5a2a36de59..113c9ac966 100644 --- a/libopenage/renderer/render_factory.h +++ b/libopenage/renderer/render_factory.h @@ -10,12 +10,12 @@ namespace openage::renderer { namespace terrain { -class TerrainRenderer; +class TerrainRenderStage; class TerrainRenderEntity; } // namespace terrain namespace world { -class WorldRenderer; +class WorldRenderStage; class WorldRenderEntity; } // namespace world @@ -32,8 +32,8 @@ class RenderFactory { * @param terrain_renderer Terrain renderer. * @param world_renderer World renderer. */ - RenderFactory(const std::shared_ptr terrain_renderer, - const std::shared_ptr world_renderer); + RenderFactory(const std::shared_ptr terrain_renderer, + const std::shared_ptr world_renderer); ~RenderFactory() = default; /** @@ -61,12 +61,12 @@ class RenderFactory { /** * Render stage for terrain drawing. */ - std::shared_ptr terrain_renderer; + std::shared_ptr terrain_renderer; /** * Render stage for game entity drawing. */ - std::shared_ptr world_renderer; + std::shared_ptr world_renderer; }; } // namespace openage::renderer diff --git a/libopenage/renderer/stages/CMakeLists.txt b/libopenage/renderer/stages/CMakeLists.txt index 5be8fb465b..c2ae5bf781 100644 --- a/libopenage/renderer/stages/CMakeLists.txt +++ b/libopenage/renderer/stages/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(camera/) +add_subdirectory(hud/) add_subdirectory(screen/) add_subdirectory(skybox/) add_subdirectory(terrain/) diff --git a/libopenage/renderer/stages/hud/CMakeLists.txt b/libopenage/renderer/stages/hud/CMakeLists.txt new file mode 100644 index 0000000000..811fd929ed --- /dev/null +++ b/libopenage/renderer/stages/hud/CMakeLists.txt @@ -0,0 +1,5 @@ +add_sources(libopenage + object.cpp + render_entity.cpp + render_stage.cpp +) diff --git a/libopenage/renderer/stages/hud/object.cpp b/libopenage/renderer/stages/hud/object.cpp new file mode 100644 index 0000000000..2b53bd1b3b --- /dev/null +++ b/libopenage/renderer/stages/hud/object.cpp @@ -0,0 +1,123 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#include "object.h" + +#include "renderer/geometry.h" +#include "renderer/stages/hud/render_entity.h" + + +namespace openage::renderer::hud { + +HudDragObject::HudDragObject(const std::shared_ptr &asset_manager) : + require_renderable{true}, + changed{false}, + camera{nullptr}, + asset_manager{asset_manager}, + render_entity{nullptr}, + drag_pos{nullptr, 0, "", nullptr, {0, 0}}, + drag_start{0, 0}, + uniforms{nullptr}, + geometry{nullptr}, + last_update{0.0} { +} + +void HudDragObject::set_render_entity(const std::shared_ptr &entity) { + this->render_entity = entity; + this->fetch_updates(); +} + +void HudDragObject::set_camera(const std::shared_ptr &camera) { + this->camera = camera; +} + +void HudDragObject::fetch_updates(const time::time_t &time) { + if (not this->render_entity->is_changed()) { + // exit early because there is nothing to do + return; + } + + // Get data from render entity + this->drag_start = this->render_entity->get_drag_start(); + this->drag_pos.sync(this->render_entity->get_drag_pos() /* , this->last_update */); + + // Set self to changed so that world renderer can update the renderable + this->changed = true; + this->render_entity->clear_changed_flag(); + this->last_update = time; +} + +void HudDragObject::update_uniforms(const time::time_t & /* time */) { + // TODO: Only update uniforms that changed since last update + if (this->uniforms == nullptr) [[unlikely]] { + return; + } + + // TODO: Do something with the uniforms +} + +void HudDragObject::update_geometry(const time::time_t &time) { + // TODO: Only update geometry that changed since last update + if (this->geometry == nullptr) [[unlikely]] { + return; + } + + auto drag_start_ndc = this->drag_start.to_viewport(this->camera).to_ndc_space(this->camera); + auto drag_pos_ndc = this->drag_pos.get(time).to_viewport(this->camera).to_ndc_space(this->camera); + + float top = std::max(drag_start_ndc.y(), drag_pos_ndc.y()); + float bottom = std::min(drag_start_ndc.y(), drag_pos_ndc.y()); + float left = std::min(drag_start_ndc.x(), drag_pos_ndc.x()); + float right = std::max(drag_start_ndc.x(), drag_pos_ndc.x()); + + log::log(SPAM << "top: " << top + << ", bottom: " << bottom + << ", left: " << left + << ", right: " << right); + + std::array quad_vertices{ + left, top, 0.0f, 1.0f, // top left corner + left, + bottom, + 0.0f, + 0.0f, // bottom left corner + right, + top, + 1.0f, + 1.0f, // top right corner + right, + bottom, + 1.0f, + 0.0f // bottom right corner + }; + + std::vector vertex_data(quad_vertices.size() * sizeof(float)); + std::memcpy(vertex_data.data(), quad_vertices.data(), vertex_data.size()); + + this->geometry->update_verts(vertex_data); +} + +bool HudDragObject::requires_renderable() { + return this->require_renderable; +} + +void HudDragObject::clear_requires_renderable() { + this->require_renderable = false; +} + +bool HudDragObject::is_changed() { + return this->changed; +} + +void HudDragObject::clear_changed_flag() { + this->changed = false; +} + +void HudDragObject::set_uniforms(const std::shared_ptr &uniforms) { + this->uniforms = uniforms; +} + +void HudDragObject::set_geometry(const std::shared_ptr &geometry) { + this->geometry = geometry; +} + +} // namespace openage::renderer::hud diff --git a/libopenage/renderer/stages/hud/object.h b/libopenage/renderer/stages/hud/object.h new file mode 100644 index 0000000000..b116821067 --- /dev/null +++ b/libopenage/renderer/stages/hud/object.h @@ -0,0 +1,178 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include +#include +#include + +#include "coord/pixel.h" +#include "curve/continuous.h" +#include "time/time.h" + + +namespace openage::renderer { +class Geometry; +class UniformInput; + +namespace camera { +class Camera; +} + +namespace resources { +class AssetManager; +class Animation2dInfo; +} // namespace resources + +namespace hud { +class HudDragRenderEntity; + +/** + * Stores the state of a renderable object in the HUD render stage. + */ +class HudDragObject { +public: + /** + * Create a new object for the HUD render stage. + * + * @param asset_manager Asset manager for loading resources. + */ + HudDragObject(const std::shared_ptr &asset_manager); + ~HudDragObject() = default; + + /** + * Set the world render entity. + * + * @param entity New world render entity. + */ + void set_render_entity(const std::shared_ptr &entity); + + /** + * Set the current camera of the scene. + * + * @param camera Camera object viewing the scene. + */ + void set_camera(const std::shared_ptr &camera); + + /** + * Fetch updates from the render entity. + * + * @param time Current simulation time. + */ + void fetch_updates(const time::time_t &time = 0.0); + + /** + * Update the uniforms of the renderable associated with this object. + * + * @param time Current simulation time. + */ + void update_uniforms(const time::time_t &time = 0.0); + + /** + * Update the geometry of the renderable associated with this object. + * + * @param time Current simulation time. + */ + void update_geometry(const time::time_t &time = 0.0); + + /** + * Check whether a new renderable needs to be created for this mesh. + * + * If true, the old renderable should be removed from the render pass. + * The updated uniforms and geometry should be passed to this mesh. + * Afterwards, clear the requirement flag with \p clear_requires_renderable(). + * + * @return true if a new renderable is required, else false. + */ + bool requires_renderable(); + + /** + * Indicate to this mesh that a new renderable has been created. + */ + void clear_requires_renderable(); + + /** + * Check whether the object was changed by \p update(). + * + * @return true if changes were made, else false. + */ + bool is_changed(); + + /** + * Clear the update flag by setting it to false. + */ + void clear_changed_flag(); + + /** + * Set the reference to the uniform inputs of the renderable + * associated with this object. Relevant uniforms are updated + * when calling \p update(). + * + * @param uniforms Uniform inputs of this object's renderable. + */ + void set_uniforms(const std::shared_ptr &uniforms); + + /** + * Set the geometry of the renderable associated with this object. + * + * The geometry is updated when calling \p update(). + * + * @param geometry Geometry of this object's renderable. + */ + void set_geometry(const std::shared_ptr &geometry); + +private: + /** + * Stores whether a new renderable for this object needs to be created + * for the render pass. + */ + bool require_renderable; + + /** + * Stores whether the \p update() call changed the object. + */ + bool changed; + + /** + * Camera for model uniforms. + */ + std::shared_ptr camera; + + /** + * Asset manager for central accessing and loading asset resources. + */ + std::shared_ptr asset_manager; + + /** + * Source for positional and texture data. + */ + std::shared_ptr render_entity; + + /** + * Position of the dragged corner. + */ + curve::Continuous drag_pos; + + /** + * Position of the start corner. + */ + coord::input drag_start; + + /** + * Shader uniforms for the renderable in the HUD render pass. + */ + std::shared_ptr uniforms; + + /** + * Geometry of the renderable in the HUD render pass. + */ + std::shared_ptr geometry; + + /** + * Time of the last update call. + */ + time::time_t last_update; +}; +} // namespace hud +} // namespace openage::renderer diff --git a/libopenage/renderer/stages/hud/render_entity.cpp b/libopenage/renderer/stages/hud/render_entity.cpp new file mode 100644 index 0000000000..f8cf19e692 --- /dev/null +++ b/libopenage/renderer/stages/hud/render_entity.cpp @@ -0,0 +1,53 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#include "render_entity.h" + +#include + + +namespace openage::renderer::hud { + +HudDragRenderEntity::HudDragRenderEntity(const coord::input drag_start) : + changed{false}, + last_update{0.0}, + drag_pos{nullptr, 0, "", nullptr, drag_start}, + drag_start{drag_start} { +} + +void HudDragRenderEntity::update(const coord::input drag_pos, + const time::time_t time) { + std::unique_lock lock{this->mutex}; + + this->drag_pos.set_insert(time, drag_pos); + + this->last_update = time; + this->changed = true; +} + +time::time_t HudDragRenderEntity::get_update_time() { + std::shared_lock lock{this->mutex}; + + return this->last_update; +} + +const curve::Continuous &HudDragRenderEntity::get_drag_pos() { + return this->drag_pos; +} + +const coord::input &HudDragRenderEntity::get_drag_start() { + return this->drag_start; +} + +bool HudDragRenderEntity::is_changed() { + std::shared_lock lock{this->mutex}; + + return this->changed; +} + +void HudDragRenderEntity::clear_changed_flag() { + std::unique_lock lock{this->mutex}; + + this->changed = false; +} + +} // namespace openage::renderer::hud diff --git a/libopenage/renderer/stages/hud/render_entity.h b/libopenage/renderer/stages/hud/render_entity.h new file mode 100644 index 0000000000..7bbdbd7366 --- /dev/null +++ b/libopenage/renderer/stages/hud/render_entity.h @@ -0,0 +1,100 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include +#include + +#include "coord/pixel.h" +#include "curve/continuous.h" +#include "time/time.h" + + +namespace openage::renderer::hud { + +/** + * Render entity for pushing drag selection updates to the HUD renderer. + */ +class HudDragRenderEntity { +public: + /** + * Create a new render entity for drag selection in the HUD. + * + * @param drag_start Position of the start corner. + */ + HudDragRenderEntity(const coord::input drag_start); + ~HudDragRenderEntity() = default; + + /** + * Update the render entity with information from the gamestate + * or input system. + * + * @param drag_pos Position of the dragged corner. + * @param time Current simulation time. + */ + void update(const coord::input drag_pos, + const time::time_t time = 0.0); + + /** + * Get the time of the last update. + * + * @return Time of last update. + */ + time::time_t get_update_time(); + + /** + * Get the position of the dragged corner. + * + * @return Coordinates of the dragged corner. + */ + const curve::Continuous &get_drag_pos(); + + /** + * Get the position of the start corner. + * + * @return Coordinates of the start corner. + */ + const coord::input &get_drag_start(); + + /** + * Check whether the render entity has received new updates. + * + * @return true if updates have been received, else false. + */ + bool is_changed(); + + /** + * Clear the update flag by setting it to false. + */ + void clear_changed_flag(); + +private: + /** + * Flag for determining if the render entity has been updated by the + * corresponding gamestate entity. Set to true every time \p update() + * is called. + */ + bool changed; + + /** + * Time of the last update call. + */ + time::time_t last_update; + + /** + * Position of the dragged corner. + */ + curve::Continuous drag_pos; + + /** + * Position of the start corner. + */ + coord::input drag_start; + + /** + * Mutex for protecting threaded access. + */ + std::shared_mutex mutex; +}; +} // namespace openage::renderer::hud diff --git a/libopenage/renderer/stages/hud/render_stage.cpp b/libopenage/renderer/stages/hud/render_stage.cpp new file mode 100644 index 0000000000..b7ecaef31e --- /dev/null +++ b/libopenage/renderer/stages/hud/render_stage.cpp @@ -0,0 +1,128 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#include "render_stage.h" + +#include "renderer/camera/camera.h" +#include "renderer/opengl/context.h" +#include "renderer/resources/assets/asset_manager.h" +#include "renderer/resources/shader_source.h" +#include "renderer/resources/texture_info.h" +#include "renderer/shader_program.h" +#include "renderer/stages/hud/object.h" +#include "renderer/texture.h" +#include "renderer/window.h" +#include "time/clock.h" + + +namespace openage::renderer::hud { + +HudRenderStage::HudRenderStage(const std::shared_ptr &window, + const std::shared_ptr &renderer, + const std::shared_ptr &camera, + const util::Path &shaderdir, + const std::shared_ptr &asset_manager, + const std::shared_ptr clock) : + renderer{renderer}, + camera{camera}, + asset_manager{asset_manager}, + drag_object{nullptr}, + clock{clock} { + renderer::opengl::GlContext::check_error(); + + auto size = window->get_size(); + this->initialize_render_pass(size[0], size[1], shaderdir); + + window->add_resize_callback([this](size_t width, size_t height, double /*scale*/) { + this->resize(width, height); + }); + + log::log(INFO << "Created render stage 'HUD'"); +} + +std::shared_ptr HudRenderStage::get_render_pass() { + return this->render_pass; +} + +void HudRenderStage::add_drag_entity(const std::shared_ptr entity) { + std::unique_lock lock{this->mutex}; + + auto hud_object = std::make_shared(this->asset_manager); + hud_object->set_render_entity(entity); + hud_object->set_camera(this->camera); + this->drag_object = hud_object; +} + +void HudRenderStage::remove_drag_entity() { + std::unique_lock lock{this->mutex}; + + this->drag_object = nullptr; + this->render_pass->clear_renderables(); +} + +void HudRenderStage::update() { + std::unique_lock lock{this->mutex}; + auto current_time = this->clock->get_real_time(); + + if (this->drag_object) { + this->drag_object->fetch_updates(current_time); + if (this->drag_object->requires_renderable()) { + auto geometry = this->renderer->add_mesh_geometry(resources::MeshData::make_quad()); + auto transform_unifs = this->drag_select_shader->new_uniform_input( + "in_col", + Eigen::Vector4f{0.0f, 0.0f, 0.0f, 0.2f}); + + Renderable display_obj{ + transform_unifs, + geometry, + true, + true, + }; + + this->render_pass->add_renderables(display_obj); + this->drag_object->clear_requires_renderable(); + + this->drag_object->set_uniforms(transform_unifs); + this->drag_object->set_geometry(geometry); + } + this->drag_object->update_uniforms(current_time); + this->drag_object->update_geometry(current_time); + } +} + +void HudRenderStage::resize(size_t width, size_t height) { + this->output_texture = renderer->add_texture(resources::Texture2dInfo(width, height, resources::pixel_format::rgba8)); + this->depth_texture = renderer->add_texture(resources::Texture2dInfo(width, height, resources::pixel_format::depth24)); + + auto fbo = this->renderer->create_texture_target({this->output_texture, this->depth_texture}); + this->render_pass->set_target(fbo); +} + +void HudRenderStage::initialize_render_pass(size_t width, + size_t height, + const util::Path &shaderdir) { + // Drag select shader + auto vert_shader_file = (shaderdir / "hud_drag_select.vert.glsl").open(); + auto vert_shader_src = renderer::resources::ShaderSource( + resources::shader_lang_t::glsl, + resources::shader_stage_t::vertex, + vert_shader_file.read()); + vert_shader_file.close(); + + auto frag_shader_file = (shaderdir / "hud_drag_select.frag.glsl").open(); + auto frag_shader_src = renderer::resources::ShaderSource( + resources::shader_lang_t::glsl, + resources::shader_stage_t::fragment, + frag_shader_file.read()); + frag_shader_file.close(); + + this->drag_select_shader = this->renderer->add_shader({vert_shader_src, frag_shader_src}); + + // Texture targets + this->output_texture = renderer->add_texture(resources::Texture2dInfo(width, height, resources::pixel_format::rgba8)); + this->depth_texture = renderer->add_texture(resources::Texture2dInfo(width, height, resources::pixel_format::depth24)); + + auto fbo = this->renderer->create_texture_target({this->output_texture, this->depth_texture}); + this->render_pass = this->renderer->add_render_pass({}, fbo); +} + +} // namespace openage::renderer::hud diff --git a/libopenage/renderer/stages/hud/render_stage.h b/libopenage/renderer/stages/hud/render_stage.h new file mode 100644 index 0000000000..6d3b68e9f6 --- /dev/null +++ b/libopenage/renderer/stages/hud/render_stage.h @@ -0,0 +1,163 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include +#include + +#include "util/path.h" + +namespace openage { + +namespace time { +class Clock; +} + +namespace renderer { +class Renderer; +class RenderPass; +class ShaderProgram; +class Texture2d; +class Window; + +namespace camera { +class Camera; +} + +namespace resources { +class AssetManager; +} + +namespace hud { +class HudDragObject; +class HudDragRenderEntity; + +/** + * Renderer for the "Heads-Up Display" (HUD). + * Draws UI elements that are not part of the GUI, e.g. health bars, selection boxes, minimap, etc. + * + * TODO: Currently only supports drag selection. + */ +class HudRenderStage { +public: + /** + * Create a new render stage for the HUD. + * + * @param window openage window targeted for rendering. + * @param renderer openage low-level renderer. + * @param camera Camera used for the rendered scene. + * @param shaderdir Directory containing the shader source files. + * @param asset_manager Asset manager for loading resources. + * @param clock Simulation clock for timing animations. + */ + HudRenderStage(const std::shared_ptr &window, + const std::shared_ptr &renderer, + const std::shared_ptr &camera, + const util::Path &shaderdir, + const std::shared_ptr &asset_manager, + const std::shared_ptr clock); + ~HudRenderStage() = default; + + /** + * Get the render pass of the HUD renderer. + * + * @return Render pass for HUD drawing. + */ + std::shared_ptr get_render_pass(); + + /** + * Add a new render entity for drag selection. + * + * @param render_entity New render entity. + */ + void add_drag_entity(const std::shared_ptr entity); + + /** + * Remove the render object for drag selection. + * + * @param render_entity Render entity to remove. + */ + void remove_drag_entity(); + + /** + * Update the render entities and render positions. + */ + void update(); + + /** + * Resize the FBO for the HUD rendering. This basically updates the output + * texture size. + * + * @param width New width of the FBO. + * @param height New height of the FBO. + */ + void resize(size_t width, size_t height); + +private: + /** + * Create the render pass for HUD drawing. + * + * Called during initialization of the HUD renderer. + * + * @param width Width of the FBO. + * @param height Height of the FBO. + * @param shaderdir Directory containg the shader source files. + */ + void initialize_render_pass(size_t width, + size_t height, + const util::Path &shaderdir); + + /** + * Reference to the openage renderer. + */ + std::shared_ptr renderer; + + /** + * Camera for model uniforms. + */ + std::shared_ptr camera; + + /** + * Texture manager for loading assets. + */ + std::shared_ptr asset_manager; + + /** + * Render pass for the HUD drawing. + */ + std::shared_ptr render_pass; + + /** + * Render object for the drag select rectangle. + */ + std::shared_ptr drag_object; + + /** + * Shader for rendering the drag select rectangle. + */ + std::shared_ptr drag_select_shader; + + /** + * Simulation clock for timing animations. + */ + std::shared_ptr clock; + + /** + * Output texture. + */ + std::shared_ptr output_texture; + + /** + * Depth texture. + */ + std::shared_ptr depth_texture; + + /** + * Mutex for protecting threaded access. + */ + std::shared_mutex mutex; +}; +} // namespace hud +} // namespace renderer +} // namespace openage diff --git a/libopenage/renderer/stages/screen/CMakeLists.txt b/libopenage/renderer/stages/screen/CMakeLists.txt index 59529a556b..589edbe681 100644 --- a/libopenage/renderer/stages/screen/CMakeLists.txt +++ b/libopenage/renderer/stages/screen/CMakeLists.txt @@ -1,4 +1,4 @@ add_sources(libopenage - screen_renderer.cpp + render_stage.cpp screenshot.cpp ) diff --git a/libopenage/renderer/stages/screen/screen_renderer.cpp b/libopenage/renderer/stages/screen/render_stage.cpp similarity index 81% rename from libopenage/renderer/stages/screen/screen_renderer.cpp rename to libopenage/renderer/stages/screen/render_stage.cpp index 1fa045abcf..786c547f62 100644 --- a/libopenage/renderer/stages/screen/screen_renderer.cpp +++ b/libopenage/renderer/stages/screen/render_stage.cpp @@ -1,6 +1,6 @@ // Copyright 2022-2023 the openage authors. See copying.md for legal info. -#include "screen_renderer.h" +#include "render_stage.h" #include "renderer/opengl/context.h" #include "renderer/renderer.h" @@ -15,9 +15,9 @@ namespace openage::renderer::screen { -ScreenRenderer::ScreenRenderer(const std::shared_ptr & /* window */, - const std::shared_ptr &renderer, - const util::Path &shaderdir) : +ScreenRenderStage::ScreenRenderStage(const std::shared_ptr & /* window */, + const std::shared_ptr &renderer, + const util::Path &shaderdir) : renderer{renderer}, render_targets{}, pass_outputs{} { @@ -28,16 +28,16 @@ ScreenRenderer::ScreenRenderer(const std::shared_ptr & /* window */, log::log(INFO << "Created render stage 'Screen'"); } -std::shared_ptr ScreenRenderer::get_render_pass() { +std::shared_ptr ScreenRenderStage::get_render_pass() { return this->render_pass; } -void ScreenRenderer::set_render_targets(const std::vector> &targets) { +void ScreenRenderStage::set_render_targets(const std::vector> &targets) { this->render_targets = targets; this->update_render_pass(); } -void ScreenRenderer::initialize_render_pass(const util::Path &shaderdir) { +void ScreenRenderStage::initialize_render_pass(const util::Path &shaderdir) { auto vert_shader_file = (shaderdir / "final.vert.glsl").open(); auto vert_shader_src = renderer::resources::ShaderSource( resources::shader_lang_t::glsl, @@ -59,7 +59,7 @@ void ScreenRenderer::initialize_render_pass(const util::Path &shaderdir) { this->render_pass = renderer->add_render_pass({}, renderer->get_display_target()); } -void ScreenRenderer::update_render_pass() { +void ScreenRenderStage::update_render_pass() { auto quad = renderer->add_mesh_geometry(renderer::resources::MeshData::make_quad()); std::vector output_layers{}; diff --git a/libopenage/renderer/stages/screen/screen_renderer.h b/libopenage/renderer/stages/screen/render_stage.h similarity index 82% rename from libopenage/renderer/stages/screen/screen_renderer.h rename to libopenage/renderer/stages/screen/render_stage.h index 7aae82a4be..f528850784 100644 --- a/libopenage/renderer/stages/screen/screen_renderer.h +++ b/libopenage/renderer/stages/screen/render_stage.h @@ -22,12 +22,19 @@ namespace screen { * (i.e. the renderers display target). This should always be the * last render stage. */ -class ScreenRenderer { +class ScreenRenderStage { public: - ScreenRenderer(const std::shared_ptr &window, - const std::shared_ptr &renderer, - const util::Path &shaderdir); - ~ScreenRenderer() = default; + /** + * Create a new render stage for drawing to the screen. + * + * @param window openage window targeted for rendering. + * @param renderer openage low-level renderer. + * @param shaderdir Directory containing the shader source files. + */ + ScreenRenderStage(const std::shared_ptr &window, + const std::shared_ptr &renderer, + const util::Path &shaderdir); + ~ScreenRenderStage() = default; /** * Get the render pass of the screen renderer. diff --git a/libopenage/renderer/stages/screen/screenshot.cpp b/libopenage/renderer/stages/screen/screenshot.cpp index e73c875a11..e910fb34ec 100644 --- a/libopenage/renderer/stages/screen/screenshot.cpp +++ b/libopenage/renderer/stages/screen/screenshot.cpp @@ -15,14 +15,14 @@ #include "log/log.h" #include "renderer/renderer.h" #include "renderer/resources/texture_data.h" -#include "renderer/stages/screen/screen_renderer.h" +#include "renderer/stages/screen/render_stage.h" #include "util/strings.h" namespace openage::renderer::screen { -ScreenshotManager::ScreenshotManager(std::shared_ptr &renderer, +ScreenshotManager::ScreenshotManager(std::shared_ptr &renderer, util::Path &outdir, std::shared_ptr &job_mgr) : outdir{outdir}, diff --git a/libopenage/renderer/stages/screen/screenshot.h b/libopenage/renderer/stages/screen/screenshot.h index df4ebcee48..718eabdf0d 100644 --- a/libopenage/renderer/stages/screen/screenshot.h +++ b/libopenage/renderer/stages/screen/screenshot.h @@ -16,7 +16,7 @@ class JobManager; } namespace renderer::screen { -class ScreenRenderer; +class ScreenRenderStage; /** * Takes screenshots, duh. @@ -30,7 +30,7 @@ class ScreenshotManager { * @param outdir Directory where the screenshots are saved. * @param job_mgr Job manager to use for writing the screenshot to disk. */ - ScreenshotManager(std::shared_ptr &renderer, + ScreenshotManager(std::shared_ptr &renderer, util::Path &outdir, std::shared_ptr &job_mgr); @@ -68,7 +68,7 @@ class ScreenshotManager { /** * Screen render stage to take the screenshot from. */ - std::shared_ptr renderer; + std::shared_ptr renderer; /** * Job manager to use for writing the screenshot to disk. diff --git a/libopenage/renderer/stages/skybox/CMakeLists.txt b/libopenage/renderer/stages/skybox/CMakeLists.txt index 0bafbc06db..2da05f4873 100644 --- a/libopenage/renderer/stages/skybox/CMakeLists.txt +++ b/libopenage/renderer/stages/skybox/CMakeLists.txt @@ -1,3 +1,3 @@ add_sources(libopenage - skybox_renderer.cpp + render_stage.cpp ) diff --git a/libopenage/renderer/stages/skybox/skybox_renderer.cpp b/libopenage/renderer/stages/skybox/render_stage.cpp similarity index 75% rename from libopenage/renderer/stages/skybox/skybox_renderer.cpp rename to libopenage/renderer/stages/skybox/render_stage.cpp index cc2777d324..c8b2fe76d2 100644 --- a/libopenage/renderer/stages/skybox/skybox_renderer.cpp +++ b/libopenage/renderer/stages/skybox/render_stage.cpp @@ -1,6 +1,6 @@ // Copyright 2022-2023 the openage authors. See copying.md for legal info. -#include "skybox_renderer.h" +#include "render_stage.h" #include "renderer/opengl/context.h" #include "renderer/renderer.h" @@ -14,9 +14,9 @@ namespace openage::renderer::skybox { -SkyboxRenderer::SkyboxRenderer(const std::shared_ptr &window, - const std::shared_ptr &renderer, - const util::Path &shaderdir) : +SkyboxRenderStage::SkyboxRenderStage(const std::shared_ptr &window, + const std::shared_ptr &renderer, + const util::Path &shaderdir) : renderer{renderer}, bg_color{0.0, 0.0, 0.0, 1.0} // black { @@ -32,11 +32,11 @@ SkyboxRenderer::SkyboxRenderer(const std::shared_ptr &window, log::log(INFO << "Created render stage 'Skybox'"); } -std::shared_ptr SkyboxRenderer::get_render_pass() { +std::shared_ptr SkyboxRenderStage::get_render_pass() { return this->render_pass; } -void SkyboxRenderer::set_color(const Eigen::Vector4i col) { +void SkyboxRenderStage::set_color(const Eigen::Vector4i col) { this->bg_color = Eigen::Vector4f( col[0] / 255, col[1] / 255, @@ -45,7 +45,7 @@ void SkyboxRenderer::set_color(const Eigen::Vector4i col) { this->color_unif->update("in_col", this->bg_color); } -void SkyboxRenderer::set_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { +void SkyboxRenderStage::set_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { this->bg_color = Eigen::Vector4f( r / 255, g / 255, @@ -54,26 +54,26 @@ void SkyboxRenderer::set_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { this->color_unif->update("in_col", this->bg_color); } -void SkyboxRenderer::set_color(const Eigen::Vector4f col) { +void SkyboxRenderStage::set_color(const Eigen::Vector4f col) { this->bg_color = col; this->color_unif->update("in_col", this->bg_color); } -void SkyboxRenderer::set_color(float r, float g, float b, float a) { +void SkyboxRenderStage::set_color(float r, float g, float b, float a) { this->bg_color = Eigen::Vector4f(r, g, b, a); this->color_unif->update("in_col", this->bg_color); } -void SkyboxRenderer::resize(size_t width, size_t height) { +void SkyboxRenderStage::resize(size_t width, size_t height) { this->output_texture = renderer->add_texture(resources::Texture2dInfo(width, height, resources::pixel_format::rgba8)); auto fbo = this->renderer->create_texture_target({this->output_texture}); this->render_pass->set_target(fbo); } -void SkyboxRenderer::initialize_render_pass(size_t width, - size_t height, - const util::Path &shaderdir) { +void SkyboxRenderStage::initialize_render_pass(size_t width, + size_t height, + const util::Path &shaderdir) { auto vert_shader_file = (shaderdir / "skybox.vert.glsl").open(); auto vert_shader_src = renderer::resources::ShaderSource( resources::shader_lang_t::glsl, diff --git a/libopenage/renderer/stages/skybox/skybox_renderer.h b/libopenage/renderer/stages/skybox/render_stage.h similarity index 82% rename from libopenage/renderer/stages/skybox/skybox_renderer.h rename to libopenage/renderer/stages/skybox/render_stage.h index 910b971ccd..fc2deb0a65 100644 --- a/libopenage/renderer/stages/skybox/skybox_renderer.h +++ b/libopenage/renderer/stages/skybox/render_stage.h @@ -24,12 +24,19 @@ namespace skybox { * a black devilish void of nothingness. Maybe "hellbox" is more * appropriate.) */ -class SkyboxRenderer { +class SkyboxRenderStage { public: - SkyboxRenderer(const std::shared_ptr &window, - const std::shared_ptr &renderer, - const util::Path &shaderdir); - ~SkyboxRenderer() = default; + /** + * Create a new render stage for the skybox. + * + * @param window openage window targeted for rendering. + * @param renderer openage low-level renderer. + * @param shaderdir Directory containing the shader source files. + */ + SkyboxRenderStage(const std::shared_ptr &window, + const std::shared_ptr &renderer, + const util::Path &shaderdir); + ~SkyboxRenderStage() = default; /** * Get the render pass of the skybox renderer. diff --git a/libopenage/renderer/stages/terrain/CMakeLists.txt b/libopenage/renderer/stages/terrain/CMakeLists.txt index f3d10e1354..7a55cd28ed 100644 --- a/libopenage/renderer/stages/terrain/CMakeLists.txt +++ b/libopenage/renderer/stages/terrain/CMakeLists.txt @@ -1,7 +1,7 @@ add_sources(libopenage - terrain_chunk.cpp - terrain_mesh.cpp - terrain_model.cpp - terrain_render_entity.cpp - terrain_renderer.cpp + chunk.cpp + mesh.cpp + model.cpp + render_entity.cpp + render_stage.cpp ) diff --git a/libopenage/renderer/stages/terrain/terrain_chunk.cpp b/libopenage/renderer/stages/terrain/chunk.cpp similarity index 96% rename from libopenage/renderer/stages/terrain/terrain_chunk.cpp rename to libopenage/renderer/stages/terrain/chunk.cpp index dcea61585e..2bb836a673 100644 --- a/libopenage/renderer/stages/terrain/terrain_chunk.cpp +++ b/libopenage/renderer/stages/terrain/chunk.cpp @@ -1,11 +1,11 @@ // Copyright 2023-2023 the openage authors. See copying.md for legal info. -#include "terrain_chunk.h" +#include "chunk.h" #include "renderer/resources/assets/asset_manager.h" #include "renderer/resources/mesh_data.h" -#include "renderer/stages/terrain/terrain_mesh.h" -#include "renderer/stages/terrain/terrain_render_entity.h" +#include "renderer/stages/terrain/mesh.h" +#include "renderer/stages/terrain/render_entity.h" namespace openage::renderer::terrain { diff --git a/libopenage/renderer/stages/terrain/terrain_chunk.h b/libopenage/renderer/stages/terrain/chunk.h similarity index 97% rename from libopenage/renderer/stages/terrain/terrain_chunk.h rename to libopenage/renderer/stages/terrain/chunk.h index 4e96619865..4506877230 100644 --- a/libopenage/renderer/stages/terrain/terrain_chunk.h +++ b/libopenage/renderer/stages/terrain/chunk.h @@ -21,6 +21,9 @@ namespace terrain { class TerrainRenderMesh; class TerrainRenderEntity; +/** + * Stores the state of a terrain chunk in the terrain render stage. + */ class TerrainChunk { public: /** diff --git a/libopenage/renderer/stages/terrain/terrain_mesh.cpp b/libopenage/renderer/stages/terrain/mesh.cpp similarity index 99% rename from libopenage/renderer/stages/terrain/terrain_mesh.cpp rename to libopenage/renderer/stages/terrain/mesh.cpp index b02f867050..ca8b19937c 100644 --- a/libopenage/renderer/stages/terrain/terrain_mesh.cpp +++ b/libopenage/renderer/stages/terrain/mesh.cpp @@ -1,6 +1,6 @@ // Copyright 2022-2023 the openage authors. See copying.md for legal info. -#include "terrain_mesh.h" +#include "mesh.h" #include #include diff --git a/libopenage/renderer/stages/terrain/terrain_mesh.h b/libopenage/renderer/stages/terrain/mesh.h similarity index 100% rename from libopenage/renderer/stages/terrain/terrain_mesh.h rename to libopenage/renderer/stages/terrain/mesh.h diff --git a/libopenage/renderer/stages/terrain/terrain_model.cpp b/libopenage/renderer/stages/terrain/model.cpp similarity index 91% rename from libopenage/renderer/stages/terrain/terrain_model.cpp rename to libopenage/renderer/stages/terrain/model.cpp index 329968f858..8d8cc4c727 100644 --- a/libopenage/renderer/stages/terrain/terrain_model.cpp +++ b/libopenage/renderer/stages/terrain/model.cpp @@ -1,6 +1,6 @@ // Copyright 2022-2023 the openage authors. See copying.md for legal info. -#include "terrain_model.h" +#include "model.h" #include #include @@ -12,8 +12,8 @@ #include "coord/scene.h" #include "renderer/resources/assets/asset_manager.h" #include "renderer/resources/mesh_data.h" -#include "renderer/stages/terrain/terrain_chunk.h" -#include "renderer/stages/terrain/terrain_render_entity.h" +#include "renderer/stages/terrain/chunk.h" +#include "renderer/stages/terrain/render_entity.h" #include "util/fixed_point.h" #include "util/vector.h" diff --git a/libopenage/renderer/stages/terrain/terrain_model.h b/libopenage/renderer/stages/terrain/model.h similarity index 94% rename from libopenage/renderer/stages/terrain/terrain_model.h rename to libopenage/renderer/stages/terrain/model.h index 6677f3c4c5..a24441f191 100644 --- a/libopenage/renderer/stages/terrain/terrain_model.h +++ b/libopenage/renderer/stages/terrain/model.h @@ -31,6 +31,11 @@ class TerrainChunk; */ class TerrainRenderModel { public: + /** + * Create a new model for the terrain. + * + * @param asset_manager Asset manager for loading resources. + */ TerrainRenderModel(const std::shared_ptr &asset_manager); ~TerrainRenderModel() = default; diff --git a/libopenage/renderer/stages/terrain/terrain_render_entity.cpp b/libopenage/renderer/stages/terrain/render_entity.cpp similarity index 99% rename from libopenage/renderer/stages/terrain/terrain_render_entity.cpp rename to libopenage/renderer/stages/terrain/render_entity.cpp index 2d83b018f7..fdfd7e486d 100644 --- a/libopenage/renderer/stages/terrain/terrain_render_entity.cpp +++ b/libopenage/renderer/stages/terrain/render_entity.cpp @@ -1,6 +1,6 @@ // Copyright 2022-2023 the openage authors. See copying.md for legal info. -#include "terrain_render_entity.h" +#include "render_entity.h" #include #include diff --git a/libopenage/renderer/stages/terrain/terrain_render_entity.h b/libopenage/renderer/stages/terrain/render_entity.h similarity index 97% rename from libopenage/renderer/stages/terrain/terrain_render_entity.h rename to libopenage/renderer/stages/terrain/render_entity.h index a7b044b874..dd95edda73 100644 --- a/libopenage/renderer/stages/terrain/terrain_render_entity.h +++ b/libopenage/renderer/stages/terrain/render_entity.h @@ -16,7 +16,9 @@ namespace openage::renderer::terrain { - +/** + * Render entity for pushing updates to the Terrain renderer. + */ class TerrainRenderEntity { public: TerrainRenderEntity(); diff --git a/libopenage/renderer/stages/terrain/terrain_renderer.cpp b/libopenage/renderer/stages/terrain/render_stage.cpp similarity index 72% rename from libopenage/renderer/stages/terrain/terrain_renderer.cpp rename to libopenage/renderer/stages/terrain/render_stage.cpp index 92b5933d4f..518ebd7c9b 100644 --- a/libopenage/renderer/stages/terrain/terrain_renderer.cpp +++ b/libopenage/renderer/stages/terrain/render_stage.cpp @@ -1,6 +1,6 @@ // Copyright 2022-2023 the openage authors. See copying.md for legal info. -#include "terrain_renderer.h" +#include "render_stage.h" #include "renderer/camera/camera.h" #include "renderer/opengl/context.h" @@ -8,21 +8,21 @@ #include "renderer/resources/shader_source.h" #include "renderer/resources/texture_info.h" #include "renderer/shader_program.h" -#include "renderer/stages/terrain/terrain_chunk.h" -#include "renderer/stages/terrain/terrain_mesh.h" -#include "renderer/stages/terrain/terrain_model.h" +#include "renderer/stages/terrain/chunk.h" +#include "renderer/stages/terrain/mesh.h" +#include "renderer/stages/terrain/model.h" #include "renderer/window.h" #include "time/clock.h" namespace openage::renderer::terrain { -TerrainRenderer::TerrainRenderer(const std::shared_ptr &window, - const std::shared_ptr &renderer, - const std::shared_ptr &camera, - const util::Path &shaderdir, - const std::shared_ptr &asset_manager, - const std::shared_ptr &clock) : +TerrainRenderStage::TerrainRenderStage(const std::shared_ptr &window, + const std::shared_ptr &renderer, + const std::shared_ptr &camera, + const util::Path &shaderdir, + const std::shared_ptr &asset_manager, + const std::shared_ptr &clock) : renderer{renderer}, camera{camera}, render_entity{nullptr}, @@ -42,13 +42,13 @@ TerrainRenderer::TerrainRenderer(const std::shared_ptr &window, log::log(INFO << "Created render stage 'Terrain'"); } -std::shared_ptr TerrainRenderer::get_render_pass() { +std::shared_ptr TerrainRenderStage::get_render_pass() { return this->render_pass; } -void TerrainRenderer::add_render_entity(const std::shared_ptr entity, - const util::Vector2s chunk_size, - const coord::scene2_delta chunk_offset) { +void TerrainRenderStage::add_render_entity(const std::shared_ptr entity, + const util::Vector2s chunk_size, + const coord::scene2_delta chunk_offset) { std::unique_lock lock{this->mutex}; this->render_entity = entity; @@ -56,7 +56,7 @@ void TerrainRenderer::add_render_entity(const std::shared_ptrupdate(); } -void TerrainRenderer::update() { +void TerrainRenderStage::update() { this->model->fetch_updates(); auto current_time = this->clock->get_real_time(); for (auto &chunk : this->model->get_chunks()) { @@ -85,7 +85,7 @@ void TerrainRenderer::update() { this->model->update_uniforms(current_time); } -void TerrainRenderer::resize(size_t width, size_t height) { +void TerrainRenderStage::resize(size_t width, size_t height) { this->output_texture = renderer->add_texture(resources::Texture2dInfo(width, height, resources::pixel_format::rgba8)); this->depth_texture = renderer->add_texture(resources::Texture2dInfo(width, height, resources::pixel_format::depth24)); @@ -93,9 +93,9 @@ void TerrainRenderer::resize(size_t width, size_t height) { this->render_pass->set_target(fbo); } -void TerrainRenderer::initialize_render_pass(size_t width, - size_t height, - const util::Path &shaderdir) { +void TerrainRenderStage::initialize_render_pass(size_t width, + size_t height, + const util::Path &shaderdir) { auto vert_shader_file = (shaderdir / "terrain.vert.glsl").open(); auto vert_shader_src = renderer::resources::ShaderSource( resources::shader_lang_t::glsl, diff --git a/libopenage/renderer/stages/terrain/terrain_renderer.h b/libopenage/renderer/stages/terrain/render_stage.h similarity index 76% rename from libopenage/renderer/stages/terrain/terrain_renderer.h rename to libopenage/renderer/stages/terrain/render_stage.h index 4986509c95..d8d1659329 100644 --- a/libopenage/renderer/stages/terrain/terrain_renderer.h +++ b/libopenage/renderer/stages/terrain/render_stage.h @@ -39,15 +39,25 @@ class TerrainRenderModel; /** * Manage and render terrain geometry and graphics. */ -class TerrainRenderer { +class TerrainRenderStage { public: - TerrainRenderer(const std::shared_ptr &window, - const std::shared_ptr &renderer, - const std::shared_ptr &camera, - const util::Path &shaderdir, - const std::shared_ptr &asset_manager, - const std::shared_ptr &clock); - ~TerrainRenderer() = default; + /** + * Create a new render stage for the terrain. + * + * @param window openage window targeted for rendering. + * @param renderer openage low-level renderer. + * @param camera Camera used for the rendered scene. + * @param shaderdir Directory containing the shader source files. + * @param asset_manager Asset manager for loading resources. + * @param clock Simulation clock for timing animations. + */ + TerrainRenderStage(const std::shared_ptr &window, + const std::shared_ptr &renderer, + const std::shared_ptr &camera, + const util::Path &shaderdir, + const std::shared_ptr &asset_manager, + const std::shared_ptr &clock); + ~TerrainRenderStage() = default; /** * Get the render pass of the terrain renderer. diff --git a/libopenage/renderer/stages/world/CMakeLists.txt b/libopenage/renderer/stages/world/CMakeLists.txt index 1bf4758b19..811fd929ed 100644 --- a/libopenage/renderer/stages/world/CMakeLists.txt +++ b/libopenage/renderer/stages/world/CMakeLists.txt @@ -1,5 +1,5 @@ add_sources(libopenage - world_object.cpp - world_render_entity.cpp - world_renderer.cpp + object.cpp + render_entity.cpp + render_stage.cpp ) diff --git a/libopenage/renderer/stages/world/world_object.cpp b/libopenage/renderer/stages/world/object.cpp similarity index 98% rename from libopenage/renderer/stages/world/world_object.cpp rename to libopenage/renderer/stages/world/object.cpp index 79960d236c..23915d420f 100644 --- a/libopenage/renderer/stages/world/world_object.cpp +++ b/libopenage/renderer/stages/world/object.cpp @@ -1,6 +1,6 @@ // Copyright 2022-2023 the openage authors. See copying.md for legal info. -#include "world_object.h" +#include "object.h" #include #include @@ -22,7 +22,7 @@ #include "renderer/resources/mesh_data.h" #include "renderer/resources/texture_info.h" #include "renderer/resources/texture_subinfo.h" -#include "renderer/stages/world/world_render_entity.h" +#include "renderer/stages/world/render_entity.h" #include "renderer/uniform_input.h" #include "util/fixed_point.h" #include "util/vector.h" diff --git a/libopenage/renderer/stages/world/world_object.h b/libopenage/renderer/stages/world/object.h similarity index 95% rename from libopenage/renderer/stages/world/world_object.h rename to libopenage/renderer/stages/world/object.h index 2893084ed3..c558e4f93f 100644 --- a/libopenage/renderer/stages/world/world_object.h +++ b/libopenage/renderer/stages/world/object.h @@ -31,8 +31,16 @@ class Animation2dInfo; namespace world { class WorldRenderEntity; +/** + * Stores the state of a renderable object in the World render stage. + */ class WorldObject { public: + /** + * Create a new object for the World render stage. + * + * @param asset_manager Asset manager for loading resources. + */ WorldObject(const std::shared_ptr &asset_manager); ~WorldObject() = default; diff --git a/libopenage/renderer/stages/world/world_render_entity.cpp b/libopenage/renderer/stages/world/render_entity.cpp similarity index 98% rename from libopenage/renderer/stages/world/world_render_entity.cpp rename to libopenage/renderer/stages/world/render_entity.cpp index 8cba90ca40..6909a6554a 100644 --- a/libopenage/renderer/stages/world/world_render_entity.cpp +++ b/libopenage/renderer/stages/world/render_entity.cpp @@ -1,6 +1,6 @@ // Copyright 2022-2023 the openage authors. See copying.md for legal info. -#include "world_render_entity.h" +#include "render_entity.h" #include #include diff --git a/libopenage/renderer/stages/world/world_render_entity.h b/libopenage/renderer/stages/world/render_entity.h similarity index 96% rename from libopenage/renderer/stages/world/world_render_entity.h rename to libopenage/renderer/stages/world/render_entity.h index ae656da20d..b1ca52c0d3 100644 --- a/libopenage/renderer/stages/world/world_render_entity.h +++ b/libopenage/renderer/stages/world/render_entity.h @@ -19,6 +19,9 @@ namespace openage::renderer::world { +/** + * Render entity for pushing updates to the World renderer. + */ class WorldRenderEntity { public: WorldRenderEntity(); @@ -40,7 +43,7 @@ class WorldRenderEntity { const time::time_t time = 0.0); /** - * Thus function is for DEBUGGING and should not be used. + * This function is for DEBUGGING and should not be used. * * Update the render entity with information from the gamestate. * diff --git a/libopenage/renderer/stages/world/world_renderer.cpp b/libopenage/renderer/stages/world/render_stage.cpp similarity index 80% rename from libopenage/renderer/stages/world/world_renderer.cpp rename to libopenage/renderer/stages/world/render_stage.cpp index 69bc67b54c..85e5da7048 100644 --- a/libopenage/renderer/stages/world/world_renderer.cpp +++ b/libopenage/renderer/stages/world/render_stage.cpp @@ -1,6 +1,6 @@ // Copyright 2022-2023 the openage authors. See copying.md for legal info. -#include "world_renderer.h" +#include "render_stage.h" #include "renderer/camera/camera.h" #include "renderer/opengl/context.h" @@ -8,7 +8,7 @@ #include "renderer/resources/shader_source.h" #include "renderer/resources/texture_info.h" #include "renderer/shader_program.h" -#include "renderer/stages/world/world_object.h" +#include "renderer/stages/world/object.h" #include "renderer/texture.h" #include "renderer/window.h" #include "time/clock.h" @@ -16,12 +16,12 @@ namespace openage::renderer::world { -WorldRenderer::WorldRenderer(const std::shared_ptr &window, - const std::shared_ptr &renderer, - const std::shared_ptr &camera, - const util::Path &shaderdir, - const std::shared_ptr &asset_manager, - const std::shared_ptr clock) : +WorldRenderStage::WorldRenderStage(const std::shared_ptr &window, + const std::shared_ptr &renderer, + const std::shared_ptr &camera, + const util::Path &shaderdir, + const std::shared_ptr &asset_manager, + const std::shared_ptr clock) : renderer{renderer}, camera{camera}, asset_manager{asset_manager}, @@ -41,11 +41,11 @@ WorldRenderer::WorldRenderer(const std::shared_ptr &window, log::log(INFO << "Created render stage 'World'"); } -std::shared_ptr WorldRenderer::get_render_pass() { +std::shared_ptr WorldRenderStage::get_render_pass() { return this->render_pass; } -void WorldRenderer::add_render_entity(const std::shared_ptr entity) { +void WorldRenderStage::add_render_entity(const std::shared_ptr entity) { std::unique_lock lock{this->mutex}; auto world_object = std::make_shared(this->asset_manager); @@ -54,7 +54,7 @@ void WorldRenderer::add_render_entity(const std::shared_ptr e this->render_objects.push_back(world_object); } -void WorldRenderer::update() { +void WorldRenderStage::update() { std::unique_lock lock{this->mutex}; auto current_time = this->clock->get_real_time(); for (auto &obj : this->render_objects) { @@ -92,7 +92,7 @@ void WorldRenderer::update() { } } -void WorldRenderer::resize(size_t width, size_t height) { +void WorldRenderStage::resize(size_t width, size_t height) { this->output_texture = renderer->add_texture(resources::Texture2dInfo(width, height, resources::pixel_format::rgba8)); this->depth_texture = renderer->add_texture(resources::Texture2dInfo(width, height, resources::pixel_format::depth24)); this->id_texture = renderer->add_texture(resources::Texture2dInfo(width, height, resources::pixel_format::r32ui)); @@ -101,9 +101,9 @@ void WorldRenderer::resize(size_t width, size_t height) { this->render_pass->set_target(fbo); } -void WorldRenderer::initialize_render_pass(size_t width, - size_t height, - const util::Path &shaderdir) { +void WorldRenderStage::initialize_render_pass(size_t width, + size_t height, + const util::Path &shaderdir) { auto vert_shader_file = (shaderdir / "world2d.vert.glsl").open(); auto vert_shader_src = renderer::resources::ShaderSource( resources::shader_lang_t::glsl, @@ -129,7 +129,7 @@ void WorldRenderer::initialize_render_pass(size_t width, this->render_pass = this->renderer->add_render_pass({}, fbo); } -void WorldRenderer::init_uniform_ids() { +void WorldRenderStage::init_uniform_ids() { WorldObject::obj_world_position = this->display_shader->get_uniform_id("obj_world_position"); WorldObject::flip_x = this->display_shader->get_uniform_id("flip_x"); WorldObject::flip_y = this->display_shader->get_uniform_id("flip_y"); diff --git a/libopenage/renderer/stages/world/world_renderer.h b/libopenage/renderer/stages/world/render_stage.h similarity index 73% rename from libopenage/renderer/stages/world/world_renderer.h rename to libopenage/renderer/stages/world/render_stage.h index e7e9e0c596..fe3cf1970b 100644 --- a/libopenage/renderer/stages/world/world_renderer.h +++ b/libopenage/renderer/stages/world/render_stage.h @@ -37,15 +37,25 @@ class WorldObject; /** * Renderer for drawing and displaying entities in the game world (units, buildings, etc.) */ -class WorldRenderer { +class WorldRenderStage { public: - WorldRenderer(const std::shared_ptr &window, - const std::shared_ptr &renderer, - const std::shared_ptr &camera, - const util::Path &shaderdir, - const std::shared_ptr &asset_manager, - const std::shared_ptr clock); - ~WorldRenderer() = default; + /** + * Create a new render stage for the game world. + * + * @param window openage window targeted for rendering. + * @param renderer openage low-level renderer. + * @param camera Camera used for the rendered scene. + * @param shaderdir Directory containing the shader source files. + * @param asset_manager Asset manager for loading resources. + * @param clock Simulation clock for timing animations. + */ + WorldRenderStage(const std::shared_ptr &window, + const std::shared_ptr &renderer, + const std::shared_ptr &camera, + const util::Path &shaderdir, + const std::shared_ptr &asset_manager, + const std::shared_ptr clock); + ~WorldRenderStage() = default; /** * Get the render pass of the world renderer. @@ -89,6 +99,13 @@ class WorldRenderer { size_t height, const util::Path &shaderdir); + /** + * Fetch the uniform IDs for the uniforms of the world shader from OpenGL + * and assign them to the WorldObject class. + * + * This method must be called after the shader program has been created but + * before any uniforms are set. + */ void init_uniform_ids(); /**