Skip to content

Commit

Permalink
Merge pull request #1603 from heinezen/feature/drag-select
Browse files Browse the repository at this point in the history
Drag Selection
  • Loading branch information
TheJJ committed Jan 25, 2024
2 parents c4ccd41 + a51d2c3 commit 0dbf6c1
Show file tree
Hide file tree
Showing 77 changed files with 1,865 additions and 278 deletions.
10 changes: 10 additions & 0 deletions assets/shaders/hud_drag_select.frag.glsl
Original file line number Diff line number Diff line change
@@ -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;
}
7 changes: 7 additions & 0 deletions assets/shaders/hud_drag_select.vert.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#version 330

layout(location=0) in vec2 position;

void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
12 changes: 6 additions & 6 deletions doc/code/renderer/level2.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
1 change: 1 addition & 0 deletions libopenage/gamestate/component/api/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ add_sources(libopenage
idle.cpp
live.cpp
move.cpp
selectable.cpp
turn.cpp
)
12 changes: 12 additions & 0 deletions libopenage/gamestate/component/api/selectable.cpp
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions libopenage/gamestate/component/api/selectable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2023-2023 the openage authors. See copying.md for legal info.

#pragma once

#include <nyan/nyan.h>

#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
1 change: 1 addition & 0 deletions libopenage/gamestate/component/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ enum class component_t {
IDLE,
TURN,
MOVE,
SELECTABLE,
LIVE
};

Expand Down
5 changes: 5 additions & 0 deletions libopenage/gamestate/entity_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -203,6 +204,10 @@ void EntityFactory::init_components(const std::shared_ptr<openage::event::EventL
else if (ability_parent == "engine.ability.type.Activity") {
activity_ability = ability_obj;
}
else if (ability_parent == "engine.ability.type.Selectable") {
auto selectable = std::make_shared<component::Selectable>(loop, ability_obj);
entity->add_component(selectable);
}
}

if (activity_ability) {
Expand Down
1 change: 1 addition & 0 deletions libopenage/gamestate/event/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_sources(libopenage
drag_select.cpp
process_command.cpp
send_command.cpp
spawn_entity.cpp
Expand Down
100 changes: 100 additions & 0 deletions libopenage/gamestate/event/drag_select.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2023-2023 the openage authors. See copying.md for legal info.

#include "drag_select.h"

#include <eigen3/Eigen/Dense>

#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<openage::event::Event> & /* event */,
const std::shared_ptr<openage::event::State> & /* state */) {
// TODO
}

void DragSelectHandler::invoke(openage::event::EventLoop & /* loop */,
const std::shared_ptr<openage::event::EventEntity> & /* target */,
const std::shared_ptr<openage::event::State> &state,
const time::time_t &time,
const param_map &params) {
auto gstate = std::dynamic_pointer_cast<openage::gamestate::GameState>(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<entity_id_t> 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<component::Ownership>(
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<component::Position>(
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<void(const std::vector<entity_id_t> ids)>{
[](const std::vector<entity_id_t> /* ids */) {}});
select_cb(selected);
}

time::time_t DragSelectHandler::predict_invoke_time(const std::shared_ptr<openage::event::EventEntity> & /* target */,
const std::shared_ptr<openage::event::State> & /* state */,
const time::time_t &at) {
return at;
}


} // namespace openage::gamestate::event
46 changes: 46 additions & 0 deletions libopenage/gamestate/event/drag_select.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2023-2023 the openage authors. See copying.md for legal info.

#pragma once

#include <cstddef>
#include <memory>
#include <string>

#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<openage::event::Event> &event,
const std::shared_ptr<openage::event::State> &state) override;

void invoke(openage::event::EventLoop &loop,
const std::shared_ptr<openage::event::EventEntity> &target,
const std::shared_ptr<openage::event::State> &state,
const time::time_t &time,
const param_map &params) override;

time::time_t predict_invoke_time(const std::shared_ptr<openage::event::EventEntity> &target,
const std::shared_ptr<openage::event::State> &state,
const time::time_t &at) override;
};

} // namespace gamestate::event
} // namespace openage
5 changes: 0 additions & 5 deletions libopenage/gamestate/event/spawn_entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<void(entity_id_t id)>{[](entity_id_t /* id */) {}});
select_cb(entity->get_id());

gstate->add_game_entity(entity);
}

Expand Down
2 changes: 1 addition & 1 deletion libopenage/gamestate/game_entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
2 changes: 2 additions & 0 deletions libopenage/gamestate/game_entity.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ class GameEntity {

/**
* Data components.
*
* TODO: Multiple components of the same type.
*/
std::unordered_map<component::component_t, std::shared_ptr<component::Component>> components;

Expand Down
3 changes: 3 additions & 0 deletions libopenage/gamestate/simulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -133,11 +134,13 @@ void GameSimulation::set_modpacks(const std::vector<std::string> &modpacks) {
}

void GameSimulation::init_event_handlers() {
auto drag_select_handler = std::make_shared<gamestate::event::DragSelectHandler>();
auto spawn_handler = std::make_shared<gamestate::event::SpawnEntityHandler>(this->event_loop,
this->entity_factory);
auto command_handler = std::make_shared<gamestate::event::SendCommandHandler>();
auto manager_handler = std::make_shared<gamestate::event::ProcessCommandHandler>();
auto wait_handler = std::make_shared<gamestate::event::WaitHandler>();
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);
Expand Down
2 changes: 1 addition & 1 deletion libopenage/gamestate/terrain_chunk.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
2 changes: 1 addition & 1 deletion libopenage/gamestate/terrain_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
3 changes: 2 additions & 1 deletion libopenage/input/action.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ enum class input_action_t {
PUSH_CONTEXT,
POP_CONTEXT,
REMOVE_CONTEXT,
ENGINE,
GAME,
CAMERA,
HUD,
GUI,
CUSTOM,
};
Expand Down
1 change: 1 addition & 0 deletions libopenage/input/controller/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
add_subdirectory("camera")
add_subdirectory("game")
add_subdirectory("hud")
14 changes: 8 additions & 6 deletions libopenage/input/controller/camera/controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "input/event.h"


namespace openage {

namespace renderer::camera {
Expand All @@ -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<BindingContext> &ctx);
};

Expand Down
2 changes: 1 addition & 1 deletion libopenage/input/controller/game/binding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

namespace openage::input::game {

} // namespace openage::input::engine
} // namespace openage::input::game
2 changes: 1 addition & 1 deletion libopenage/input/controller/game/binding_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion libopenage/input/controller/game/binding_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,4 @@ class BindingContext {
std::unordered_map<event_class, binding_action, event_class_hash> by_class;
};

} // namespace openage::input::engine
} // namespace openage::input::game
Loading

0 comments on commit 0dbf6c1

Please sign in to comment.