Skip to content

Commit

Permalink
feat: Implement fill bucket edition mode
Browse files Browse the repository at this point in the history
  • Loading branch information
piiertho committed Jul 26, 2024
1 parent 5bec612 commit d53732d
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 5 deletions.
109 changes: 105 additions & 4 deletions src/containers/grid_3d.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
#ifndef ISOMETRIC_MAPS_GRID_3D_H
#define ISOMETRIC_MAPS_GRID_3D_H

#include <core/templates/vector.h>
#include <core/variant/array.h>
#include "logging.h"

#include <core/math/aabb.h>
#include <core/math/vector3.h>
#include <core/math/vector3i.h>
#include <core/templates/hash_set.h>
#include <core/templates/vector.h>
#include <core/variant/array.h>

namespace containers {

Expand All @@ -25,6 +27,9 @@ namespace containers {

Vector3 plane_square_and_jumps_from(const Vector3& size) const;

template<bool (*condition)(T)>
bool match_condition_on_one_of(const AABB& aabb) const;

inline int compute_array_size() const { return width * depth * height; }

static int index_increment_from(const Vector3& planeSquareAndJumps, const Vector3& size, int iteration);
Expand Down Expand Up @@ -76,6 +81,13 @@ namespace containers {

Array to_array() const;
void from_array(const Array& array);

template<bool (*condition)(T)> Vector<Vector3> dfs(
const Vector3& p_starting_position,
Vector3::Axis p_axis,
const Vector3& p_unit_size,
HashSet<Vector3>* p_visited = nullptr
) const;
};

template<class T, T default_value>
Expand Down Expand Up @@ -200,19 +212,30 @@ namespace containers {
return ret;
}

template<class T, T default_value>
static bool element_is_not_null(T element) {
return element != nullptr;
}

template<class T, T default_value>
bool Grid3D<T, default_value>::is_overlapping(const AABB& aabb) const {
return match_condition_on_one_of<&element_is_not_null<T, default_value>>(aabb);
}

template<class T, T default_value>
template<bool (*condition)(T)>
bool Grid3D<T, default_value>::match_condition_on_one_of(const AABB& aabb) const {
int index {get_index_from_position(aabb.position)};
const Vector3& size {aabb.size};

if (index >= 0 && index < internal_array.size()) {
T element = internal_array[index];
if (element) { return true; }
if (condition(element)) { return true; }
for (int i = 1; i < static_cast<int>(size.x) * static_cast<int>(size.y) * static_cast<int>(size.z); ++i) {
index += Grid3D::index_increment_from(plane_square_and_jumps_from(size), size, i);
if (index >= 0 && index < internal_array.size()) {
element = internal_array[index];
if (element) { return true; }
if (condition(element)) { return true; }
}
}
}
Expand Down Expand Up @@ -331,6 +354,84 @@ namespace containers {
set_internal_array(new_internal_array);
}

template<class T, bool (*condition)(T)>
bool revert_condition(T element) {
return !condition(element);
}

template<class T, T default_value>
template<bool (*condition)(T)>
Vector<Vector3> Grid3D<T, default_value>::dfs(
const Vector3& p_starting_position,
Vector3::Axis p_axis,
const Vector3& p_unit_size,
HashSet<Vector3>* p_visited
) const {
Vector<Vector3> connected_positions;

HashSet<Vector3> visited_fallback;
HashSet<Vector3>* visited { p_visited == nullptr ? &visited_fallback : p_visited };

Vector<int> dx;
Vector<int> dy;
Vector<int> dz;

auto size_on_x {static_cast<int>(p_unit_size.x)};
auto size_on_y {static_cast<int>(p_unit_size.y)};
auto size_on_z {static_cast<int>(p_unit_size.z)};

switch (p_axis) {
case Vector3::AXIS_X:
dx.append_array({0, 0, 0, 0});
dy.append_array({-size_on_y, size_on_y, 0, 0});
dz.append_array({0, 0, -size_on_z, size_on_z});
break;
case Vector3::AXIS_Y:
dx.append_array({-size_on_x, size_on_x, 0, 0});
dy.append_array({0, 0, 0, 0});
dz.append_array({0, 0, -size_on_z, size_on_z});
break;
case Vector3::AXIS_Z:
dx.append_array({-size_on_x, size_on_x, 0, 0});
dy.append_array({0, 0, -size_on_y, size_on_y});
dz.append_array({0, 0, 0, 0});
break;
}

Vector<Vector3> stack;
stack.push_back(p_starting_position);
visited->insert(p_starting_position);

while (!stack.is_empty()) {
Vector3 current_position {stack[stack.size() - 1]};
stack.remove_at(stack.size() - 1);

if (current_position.x < 0 || current_position.y < 0 || current_position.z < 0 ||
current_position.x + size_on_x - 1 >= width || current_position.y + size_on_y - 1 >= depth || current_position.z + size_on_z - 1 >= height) {
continue;
}

AABB aabb {current_position, p_unit_size};
if (match_condition_on_one_of<&revert_condition<T, condition>>(aabb)) {
continue;
}
connected_positions.append(current_position);

for (int i = 0; i < 4; ++i) {
Vector3 neighbor { current_position + Vector3(dx[i], dy[i], dz[i]) };

if (visited->has(neighbor)) {
continue;
}

stack.push_back(neighbor);
visited->insert(neighbor);
}
}

return connected_positions;
}

template<class T, T default_value>
Grid3D<T, default_value>::Grid3D() {
update_array_size(Vector3(1, 1, 1));
Expand Down
71 changes: 71 additions & 0 deletions src/editor/commands/emitters/fill_plan_command_emitter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#ifdef TOOLS_ENABLED

#include "fill_plan_command_emitter.h"

#include "editor/isometric_editor_plugin.h"
#include "isometric_server.h"
#include "utils/isometric_maths.h"

using namespace editor::commands::emitters;

static constexpr bool (*condition)(node::IsometricPositionable*) = [](node::IsometricPositionable* positionable) -> bool {
return positionable == nullptr;
};

Vector<Ref<editor::commands::Command<node::IsometricMap>>> FillPlanCommandEmitter::from_gui_input_to_command_impl(Ref<InputEventMouse> p_event) {
Vector<Ref<Command<node::IsometricMap>>> commands;

if (!Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
return commands;
}

IsometricEditorPlugin* isometric_editor_plugin { IsometricEditorPlugin::get_instance() };

int selected_tile_id {isometric_editor_plugin->get_selection_pane()->get_selected_positionable_id()};

if (selected_tile_id == resource::PositionableSet::NONE_POSITIONABLE_ID) { return commands; }

node::IsometricMap* map { isometric_editor_plugin->get_selected_map() };
const EditorPlane& editor_plane {
isometric_editor_plugin->get_editor_plane_for_map(map, EditorPlane::PlaneType::EDITOR_DRAWER)
};

Vector3::Axis editor_plane_axis { editor_plane.get_axis() };

const data::IsometricParameters* parameters {IsometricServer::get_instance()->space_get_configuration(map->get_space_RID())};

const Vector3& position {
utils::from_screen_to_3D(
*parameters,
map->get_local_mouse_position(),
editor_plane_axis,
static_cast<float>(editor_plane.get_position())
)
};

Vector3 positionable_size;
if (auto* positionable {Object::cast_to<node::IsometricPositionable>(
map->get_positionable_set()->get_positionable_scene_for_id(selected_tile_id)->instantiate()
)}) {
positionable_size = positionable->get_size();
memdelete(positionable);
} else {
return commands;
}

Vector<Vector3> connected_positions_with_no_elements { map->dfs<condition>(position, editor_plane_axis, positionable_size) };

for (const Vector3& connected_position : connected_positions_with_no_elements) {
Ref<editor::commands::AddPositionableCommand> add_command;
add_command.instantiate();
add_command->set_aabb({connected_position, positionable_size});
add_command->set_positionable_id(selected_tile_id);
add_command->set_layer_id(IsometricEditorPlugin::get_instance()->get_selected_layer());
commands.push_back(add_command);
}

return commands;
}


#endif
32 changes: 32 additions & 0 deletions src/editor/commands/emitters/fill_plan_command_emitter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef ISOMETRIC_MAPS_FILL_PLAN_COMMAND_EMITTER_H
#define ISOMETRIC_MAPS_FILL_PLAN_COMMAND_EMITTER_H

#ifdef TOOLS_ENABLED

#include "command_emitter.h"
#include "node/isometric_map.h"

#include <core/input/input_event.h>

namespace editor {
namespace commands {
namespace emitters {
static constexpr const char fill_plan_action_name[]{"Fill plan with elements"};

class FillPlanCommandEmitter : public CommandEmitter<FillPlanCommandEmitter, InputEventMouse, node::IsometricMap, fill_plan_action_name> {
friend class CommandEmitter<FillPlanCommandEmitter, InputEventMouse, node::IsometricMap, fill_plan_action_name>;

public:
FillPlanCommandEmitter() = default;
~FillPlanCommandEmitter() = default;

private:
Vector<Ref<Command<node::IsometricMap>>> from_gui_input_to_command_impl([[maybe_unused]] Ref<InputEventMouse> p_event);
};
}
}
}

#endif

#endif// ISOMETRIC_MAPS_FILL_PLAN_COMMAND_EMITTER_H
6 changes: 6 additions & 0 deletions src/editor/isometric_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ static constexpr const char* NONE_EDITION_LABEL {"None"};
static constexpr const char* SELECT_EDITION_LABEL {"Select"};
static constexpr const char* PAINT_EDITION_LABEL {"Paint"};
static constexpr const char* DRAG_AND_DROP_EDITION_LABEL {"Drag & Drop"};
static constexpr const char* FILL_EDITION_LABEL {"Fill"};
static constexpr const char* GRID_COLOR_PICKER_TITLE {"Grid color:"};

static constexpr const char* DEBUG_BUTTON_TITLE {"Debug"};
Expand Down Expand Up @@ -94,6 +95,7 @@ void IsometricEditorPlugin::_notification(int p_notification) {
edition_mode_button->add_item(SELECT_EDITION_LABEL);
edition_mode_button->add_item(PAINT_EDITION_LABEL);
edition_mode_button->add_item(DRAG_AND_DROP_EDITION_LABEL);
edition_mode_button->add_item(FILL_EDITION_LABEL);
edition_mode_button->set_flat(true);
edition_mode_button->connect("item_selected", Callable(this, "_on_edition_mode_changed"));
toolbar->add_child(edition_mode_button);
Expand Down Expand Up @@ -248,6 +250,8 @@ bool IsometricEditorPlugin::forward_canvas_gui_input(const Ref<InputEvent>& p_ev
case DRAG_AND_DROP:
drag_and_drop_command_emitter.on_gui_input(p_event, selected_map);
break;
case FILL:
fill_plan_command_emitter.on_gui_input(p_event, selected_map);
}
move_editor_drawer_command_emitter.on_gui_input(p_event, selected_map);
rotate_editor_plane_command_emitter.on_gui_input(p_event, selected_map);
Expand Down Expand Up @@ -369,6 +373,8 @@ void IsometricEditorPlugin::_on_edition_mode_changed(int selected_index) {
current_mode = Mode::PAINT;
} else if (selected_label == DRAG_AND_DROP_EDITION_LABEL) {
current_mode = Mode::DRAG_AND_DROP;
} else if (selected_label == FILL_EDITION_LABEL) {
current_mode = Mode::FILL;
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/editor/isometric_editor_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "edition_grid_drawer.h"
#include "editor/commands/emitters/delete_command_emitter.h"
#include "editor/commands/emitters/drag_and_drop_command_emitter.h"
#include "editor/commands/emitters/fill_plan_command_emitter.h"
#include "editor/commands/emitters/move_editor_grid_command_emitter.h"
#include "editor/commands/emitters/move_editor_view_limiter_command_emitter.h"
#include "editor/commands/emitters/move_selection_command_emitter.h"
Expand Down Expand Up @@ -34,7 +35,8 @@ namespace editor {
NONE,
SELECT,
PAINT,
DRAG_AND_DROP
DRAG_AND_DROP,
FILL
};

editor::inspector::PositionableSelectionPane* get_selection_pane() const;
Expand Down Expand Up @@ -106,6 +108,7 @@ namespace editor {
commands::emitters::DeleteCommandEmitter delete_command_emitter;
commands::emitters::MoveSelectionCommandEmitter move_selection_command_emitter;
commands::emitters::DragAndDropCommandEmitter drag_and_drop_command_emitter;
commands::emitters::FillPlanCommandEmitter fill_plan_command_emitter;
commands::emitters::MoveEditorGridCommandEmitter move_editor_drawer_command_emitter;
commands::emitters::RotateEditorPlaneCommandEmitter rotate_editor_plane_command_emitter;
commands::emitters::MoveEditorViewLimiterCommandEmitter plane_view_limiter_command_emitter;
Expand Down
10 changes: 10 additions & 0 deletions src/node/isometric_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ namespace node {
void set_layer_visible(uint32_t p_layer_id, bool is_visible);
void set_layer_color(uint32_t p_layer_id, const Color& p_color);
Vector<Vector3> get_layer_positions(uint32_t p_layer_id) const;

template<bool (*condition)(node::IsometricPositionable*)>
Vector<Vector3> dfs(const Vector3& p_starting_position, Vector3::Axis p_axis, const Vector3& p_unit_size) const;
#endif
#ifdef DEBUG_ENABLED
void set_debug_modulate(const Color &p_modulate) const override;
Expand All @@ -91,5 +94,12 @@ namespace node {
protected:
static void _bind_methods();
};

#ifdef TOOLS_ENABLED
template<bool (*condition)(node::IsometricPositionable*)>
Vector<Vector3> IsometricMap::dfs(const Vector3& p_starting_position, Vector3::Axis p_axis, const Vector3& p_unit_size) const {
return instances_grid_3d.dfs<condition>(p_starting_position, p_axis, p_unit_size);
}
#endif
}// namespace node
#endif// ISOMETRIC_MAPS_ISOMETRIC_MAP_H

0 comments on commit d53732d

Please sign in to comment.