Fixed tile growing

This commit is contained in:
2024-10-10 01:30:57 +03:00
parent 69b62c8c97
commit 2597802469
5 changed files with 991 additions and 744 deletions

View File

@@ -3,7 +3,7 @@
element_types={ element_types={
"corner1": { "corner1": {
"name": "corner1", "name": "corner1",
"sockets": [ Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 0, -2 ), Transform( 7.54979e-08, 0, -1, 0, 1, 0, 1, 0, 7.54979e-08, 2, 0, -4 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 0, -2 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 0, -2 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ) ] "sockets": [ Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 7.54979e-08, 0, -1, 0, 1, 0, 1, 0, 7.54979e-08, 0, 0, -2 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ) ]
}, },
"e1": { "e1": {
"name": "e1", "name": "e1",
@@ -15,11 +15,11 @@ element_types={
}, },
"just_floor": { "just_floor": {
"name": "just_floor", "name": "just_floor",
"sockets": [ Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ) ] "sockets": [ Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2, 0, 2 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ) ]
}, },
"side_wall": { "side_wall": {
"name": "side_wall", "name": "side_wall",
"sockets": [ Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -2 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -2 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ) ] "sockets": [ Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ), Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ) ]
} }
} }
elements={ elements={
@@ -384,9 +384,9 @@ grid_layouts={
"index": 78, "index": 78,
"rotation": 0 "rotation": 0
}, { }, {
"element": "empty", "element": "corner",
"index": 79, "index": 79,
"rotation": 0 "rotation": 1
}, { }, {
"element": "empty", "element": "empty",
"index": 80, "index": 80,
@@ -1159,11 +1159,11 @@ grid_layouts={
"index": 28, "index": 28,
"rotation": 0 "rotation": 0
}, { }, {
"element": "empty", "element": "normal_floor",
"index": 29, "index": 29,
"rotation": 0 "rotation": 0
}, { }, {
"element": "empty", "element": "normal_floor",
"index": 30, "index": 30,
"rotation": 0 "rotation": 0
}, { }, {
@@ -1359,9 +1359,9 @@ grid_layouts={
"index": 78, "index": 78,
"rotation": 0 "rotation": 0
}, { }, {
"element": "empty", "element": "corner",
"index": 79, "index": 79,
"rotation": 0 "rotation": 1
}, { }, {
"element": "empty", "element": "empty",
"index": 80, "index": 80,
@@ -1432,13 +1432,13 @@ grid_layouts={
"index": 15, "index": 15,
"rotation": 0 "rotation": 0
}, { }, {
"element": "empty", "element": "side_wall",
"index": 16, "index": 16,
"rotation": 0 "rotation": 2
}, { }, {
"element": "empty", "element": "corner",
"index": 17, "index": 17,
"rotation": 0 "rotation": 2
}, { }, {
"element": "empty", "element": "empty",
"index": 18, "index": 18,
@@ -1504,9 +1504,9 @@ grid_layouts={
"index": 33, "index": 33,
"rotation": 0 "rotation": 0
}, { }, {
"element": "empty", "element": "normal_floor",
"index": 34, "index": 34,
"rotation": 0 "rotation": 2
}, { }, {
"element": "empty", "element": "empty",
"index": 35, "index": 35,
@@ -1612,9 +1612,9 @@ grid_layouts={
"index": 60, "index": 60,
"rotation": 0 "rotation": 0
}, { }, {
"element": "empty", "element": "side_wall",
"index": 61, "index": 61,
"rotation": 0 "rotation": 1
}, { }, {
"element": "empty", "element": "empty",
"index": 62, "index": 62,
@@ -1646,15 +1646,15 @@ grid_layouts={
}, { }, {
"element": "empty", "element": "empty",
"index": 69, "index": 69,
"rotation": 0 "rotation": 1
}, { }, {
"element": "empty", "element": "side_window",
"index": 70, "index": 70,
"rotation": 0 "rotation": 1
}, { }, {
"element": "empty", "element": "empty",
"index": 71, "index": 71,
"rotation": 0 "rotation": 3
}, { }, {
"element": "corner", "element": "corner",
"index": 72, "index": 72,
@@ -1684,9 +1684,9 @@ grid_layouts={
"index": 78, "index": 78,
"rotation": 0 "rotation": 0
}, { }, {
"element": "empty", "element": "corner",
"index": 79, "index": 79,
"rotation": 0 "rotation": 1
}, { }, {
"element": "empty", "element": "empty",
"index": 80, "index": 80,

View File

@@ -330,43 +330,89 @@ columns = 2
unique_name_in_owner = true unique_name_in_owner = true
margin_top = 719.0 margin_top = 719.0
margin_right = 314.0 margin_right = 314.0
margin_bottom = 847.0 margin_bottom = 999.0
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/layout_editor"] [node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/layout_editor"]
margin_left = 7.0 margin_left = 7.0
margin_top = 7.0 margin_top = 7.0
margin_right = 307.0 margin_right = 307.0
margin_bottom = 121.0 margin_bottom = 273.0
[node name="grid_elements" type="OptionButton" parent="VBoxContainer/layout_editor/VBoxContainer"] [node name="grid_elements" type="OptionButton" parent="VBoxContainer/layout_editor/VBoxContainer"]
unique_name_in_owner = true unique_name_in_owner = true
margin_right = 300.0 margin_right = 300.0
margin_bottom = 20.0 margin_bottom = 20.0
[node name="clear_grid_cell" type="Button" parent="VBoxContainer/layout_editor/VBoxContainer"] [node name="select_rotation" type="OptionButton" parent="VBoxContainer/layout_editor/VBoxContainer"]
unique_name_in_owner = true unique_name_in_owner = true
margin_top = 24.0 margin_top = 24.0
margin_right = 300.0 margin_right = 300.0
margin_bottom = 44.0 margin_bottom = 44.0
text = "Clear cell" text = "0"
items = [ "0", null, false, 0, null, "90", null, false, 1, null, "180", null, false, 2, null, "270", null, false, 3, null ]
selected = 0
[node name="Label" type="Label" parent="VBoxContainer/layout_editor/VBoxContainer"] [node name="clear_grid_cell" type="Button" parent="VBoxContainer/layout_editor/VBoxContainer"]
unique_name_in_owner = true
margin_top = 48.0 margin_top = 48.0
margin_right = 300.0 margin_right = 300.0
margin_bottom = 62.0 margin_bottom = 68.0
text = "Clear cell"
[node name="grow_cell_button" type="Button" parent="VBoxContainer/layout_editor/VBoxContainer"]
unique_name_in_owner = true
margin_top = 72.0
margin_right = 300.0
margin_bottom = 92.0
text = "Grow"
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/layout_editor/VBoxContainer"]
margin_top = 96.0
margin_right = 300.0
margin_bottom = 100.0
[node name="grid_elements_corner" type="OptionButton" parent="VBoxContainer/layout_editor/VBoxContainer"]
unique_name_in_owner = true
margin_top = 104.0
margin_right = 300.0
margin_bottom = 124.0
[node name="grid_elements_side" type="OptionButton" parent="VBoxContainer/layout_editor/VBoxContainer"]
unique_name_in_owner = true
margin_top = 128.0
margin_right = 300.0
margin_bottom = 148.0
[node name="grid_elements_floor" type="OptionButton" parent="VBoxContainer/layout_editor/VBoxContainer"]
unique_name_in_owner = true
margin_top = 152.0
margin_right = 300.0
margin_bottom = 172.0
[node name="make_room_button" type="Button" parent="VBoxContainer/layout_editor/VBoxContainer"]
unique_name_in_owner = true
margin_top = 176.0
margin_right = 300.0
margin_bottom = 196.0
text = "Make Room"
[node name="Label" type="Label" parent="VBoxContainer/layout_editor/VBoxContainer"]
margin_top = 200.0
margin_right = 300.0
margin_bottom = 214.0
text = "Level" text = "Level"
[node name="level_value" type="SpinBox" parent="VBoxContainer/layout_editor/VBoxContainer"] [node name="level_value" type="SpinBox" parent="VBoxContainer/layout_editor/VBoxContainer"]
unique_name_in_owner = true unique_name_in_owner = true
margin_top = 66.0 margin_top = 218.0
margin_right = 300.0 margin_right = 300.0
margin_bottom = 90.0 margin_bottom = 242.0
[node name="layout_selector" type="OptionButton" parent="VBoxContainer/layout_editor/VBoxContainer"] [node name="layout_selector" type="OptionButton" parent="VBoxContainer/layout_editor/VBoxContainer"]
unique_name_in_owner = true unique_name_in_owner = true
margin_top = 94.0 margin_top = 246.0
margin_right = 300.0 margin_right = 300.0
margin_bottom = 114.0 margin_bottom = 266.0
[node name="BuildingLayoutEditor" type="BuildingLayoutEditor" parent="."] [node name="BuildingLayoutEditor" type="BuildingLayoutEditor" parent="."]
source = ExtResource( 2 ) source = ExtResource( 2 )
@@ -375,7 +421,7 @@ source = ExtResource( 2 )
unique_name_in_owner = true unique_name_in_owner = true
[node name="refcube" type="MeshInstance" parent="refcube"] [node name="refcube" type="MeshInstance" parent="refcube"]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 4, -2 ) transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4, 0 )
mesh = SubResource( 3 ) mesh = SubResource( 3 )
skeleton = NodePath("../..") skeleton = NodePath("../..")

View File

@@ -16,718 +16,9 @@
#include <editor/editor_node.h> #include <editor/editor_node.h>
#include <flecs/flecs.h> #include <flecs/flecs.h>
#include "editor_event.h" #include "editor_event.h"
#include "element_data.h"
#include "building_layout_editor.h" #include "building_layout_editor.h"
template <class T> T *get_as_node(const String &path)
{
Node *scene;
if (Engine::get_singleton()->is_editor_hint())
scene = EditorNode::get_singleton()->get_edited_scene();
else
scene = SceneTree::get_singleton()->get_current_scene();
assert(scene);
Node *node = scene->get_node(NodePath(path));
if (!node)
print_error("Failed to get " + path);
assert(node);
T *ret = Object::cast_to<T>(node);
if (!ret)
print_error("Failed to assign " + path);
assert(ret);
return ret;
}
template <class T>
void select_control_item(const String &path, const String &item)
{
int i;
T *ctl = get_as_node<T>(path);
int selected = -1;
for (i = 0; i < ctl->get_item_count(); i++)
if (ctl->get_item_text(i) == item) {
selected = i;
break;
}
if (selected >= 0) {
ctl->select(selected);
print_line("selected: " + item);
}
}
template <class T> void select_control_item(T *ctl, const String &item)
{
int i;
int selected = -1;
for (i = 0; i < ctl->get_item_count(); i++)
if (ctl->get_item_text(i) == item) {
selected = i;
break;
}
if (selected >= 0) {
ctl->select(selected);
print_line("selected: " + item);
}
}
#define ELEMENT_SOCKETS 16
class ElementData {
flecs::world ecs;
static ElementData *singleton;
public:
static ElementData *get_singleton()
{
if (!singleton)
singleton = memnew(ElementData);
return singleton;
}
static void cleanup()
{
if (singleton)
memdelete(singleton);
}
struct grid_element_type {
String name;
Transform sockets[ELEMENT_SOCKETS];
};
struct grid_element {
String name;
String type;
String mesh_names[ELEMENT_SOCKETS];
};
int grid_step{ 4 };
int grid_size{ 9 };
struct grid_cell {
String element;
int rotation;
};
struct grid_layouts {};
struct grid_layout {};
struct grid_layout_base {
int floor_count;
};
struct grid_layout_exterior {};
struct grid_layout_interior {};
struct grid_floor {};
struct grid_floor_item {
int index;
String element;
int rotation;
};
struct grid_floor_item_node {
Spatial *node;
};
protected:
ElementData()
{
ecs.component<struct grid_layouts>();
ecs.component<struct grid_layout>();
ecs.component<struct grid_layout_exterior>();
ecs.component<struct grid_layout_interior>();
ecs.component<struct grid_floor>();
ecs.component<struct grid_floor_item>();
ecs.component<struct grid_floor_item_node>();
flecs::entity e = ecs.entity("grid_layouts");
e.add<grid_layouts>();
create_new_layout("default");
create_new_exterior_floor("default");
create_new_interior_floor("default");
load_data();
struct grid_element_type t_empty;
struct grid_element e_empty;
t_empty.name = "empty";
e_empty.name = "empty";
e_empty.type = "empty";
element_type["empty"] = t_empty;
elements["empty"] = e_empty;
ecs.observer<struct grid_floor_item_node>()
.event(flecs::OnRemove)
.each([](flecs::entity em,
struct grid_floor_item_node &s) {
if (s.node)
s.node->queue_delete();
s.node = nullptr;
});
}
HashMap<String, struct grid_element_type> element_type;
HashMap<String, struct grid_element> elements;
public:
void create_new_layout(const String &key)
{
flecs::entity top = ecs.lookup("grid_layouts");
assert(top.is_valid());
flecs::entity layout =
ecs.entity(key.ascii().ptr()).child_of(top);
// one floor by default
layout.add<struct grid_layout>();
flecs::entity extr = ecs.entity("exterior").child_of(layout);
extr.add<grid_layout_exterior>();
flecs::entity intr = ecs.entity("interior").child_of(layout);
intr.add<grid_layout_interior>();
intr.set<grid_layout_base>({ 0 });
extr.set<grid_layout_base>({ 0 });
}
void create_new_exterior_floor(const String &key)
{
int i;
flecs::entity ext = get_base(key, true);
assert(ext.is_valid());
struct grid_layout_base *l = ext.get_mut<grid_layout_base>();
int floor = l->floor_count;
flecs::entity fl =
ecs.entity(("floor_" + itos(floor)).ascii().ptr())
.child_of(ext);
assert(fl.is_valid());
for (i = 0; i < grid_size * grid_size; i++) {
flecs::entity item =
ecs.entity(("item_" + itos(i)).ascii().ptr())
.child_of(fl);
item.set<grid_floor_item>({ i, "empty", 0 });
}
l->floor_count++;
}
void create_new_interior_floor(const String &key)
{
int i;
flecs::entity intr = get_base(key, false);
assert(intr.is_valid());
struct grid_layout_base *l = intr.get_mut<grid_layout_base>();
int floor = l->floor_count;
flecs::entity fl =
ecs.entity(("floor_" + itos(floor)).ascii().ptr())
.child_of(intr);
assert(fl.is_valid());
for (i = 0; i < grid_size * grid_size; i++) {
flecs::entity item =
ecs.entity(("item_" + itos(i)).ascii().ptr())
.child_of(fl);
item.set<grid_floor_item>({ i, "empty", 0 });
}
l->floor_count++;
}
inline int constexpr get_grid_size()
{
return grid_size;
}
flecs::entity get_base(const String &key, bool exterior) const
{
flecs::entity top = ecs.lookup("grid_layouts");
assert(top.is_valid());
flecs::entity layout = top.lookup(key.ascii().ptr());
assert(layout.is_valid());
flecs::entity base;
if (exterior)
base = layout.lookup("exterior");
else
base = layout.lookup("interior");
return base;
}
flecs::entity get_floor(const String &key, bool exterior, int fl) const
{
flecs::entity base = get_base(key, exterior);
assert(base.is_valid());
String floor_key = "floor_" + itos(fl);
flecs::entity floor_e = base.lookup(floor_key.ascii().ptr());
return floor_e;
}
bool has_floor(const String &key, bool exterior, int fl) const
{
flecs::entity floor_e = get_floor(key, exterior, fl);
if (!floor_e.is_valid())
return false;
flecs::entity item_e = floor_e.lookup("item_0");
return item_e.is_valid();
}
void ensure_floor(const String &key, bool exterior, int fl)
{
int i;
if (has_floor(key, exterior, fl))
return;
flecs::entity base = get_base(key, exterior);
flecs::entity floor_e =
ecs.entity(("floor_" + itos(fl)).ascii().ptr())
.child_of(base);
assert(floor_e.is_valid());
for (i = 0; i < grid_size * grid_size; i++) {
flecs::entity item =
ecs.entity(("item_" + itos(i)).ascii().ptr())
.child_of(floor_e);
item.set<grid_floor_item>({ i, "empty", 0 });
}
assert(has_floor(key, exterior, fl));
print_line("ensured floor: " + itos(fl));
}
inline flecs::entity get_grid_entity(const String &key, bool exterior,
int fl, int i) const
{
flecs::entity base = get_floor(key, exterior, fl);
assert(base.is_valid());
String item_key = "item_" + itos(i);
flecs::entity item_data = base.lookup(item_key.ascii().ptr());
if (!item_data.is_valid())
print_error("lookup failed: " + item_key);
assert(item_data.is_valid());
return item_data;
}
inline const String &get_grid_element(const String &key, bool exterior,
int fl, int i) const
{
flecs::entity item_data = get_grid_entity(key, exterior, fl, i);
const String &element =
item_data.get<grid_floor_item>()->element;
return element;
}
inline int get_grid_rotation(const String &key, bool exterior, int fl,
int i) const
{
flecs::entity item_data = get_grid_entity(key, exterior, fl, i);
int rotation = item_data.get<grid_floor_item>()->rotation;
return rotation;
}
void set_grid_element(const String &key, bool exterior, int fl, int i,
const String &element)
{
flecs::entity item_data = get_grid_entity(key, exterior, fl, i);
item_data.get_mut<grid_floor_item>()->element = element;
}
void set_grid_rotation(const String &key, bool exterior, int fl, int i,
int rotation)
{
flecs::entity item_data = get_grid_entity(key, exterior, fl, i);
item_data.get_mut<grid_floor_item>()->rotation = rotation;
}
Spatial *get_grid_node(const String &key, bool exterior, int fl, int i)
{
assert(has_floor(key, exterior, fl));
flecs::entity item_data = get_grid_entity(key, exterior, fl, i);
if (!item_data.has<struct grid_floor_item_node>()) {
Spatial *sp = memnew(Spatial);
get_as_node<Spatial>("%bg_floor")
->call_deferred("add_child", sp);
item_data.set<struct grid_floor_item_node>({ sp });
int x = i % grid_size;
int z = i / grid_size;
int rotation = get_grid_rotation(key, exterior, fl, i);
sp->set_transform(Transform(
Basis().rotated(Vector3(0, 1, 0),
Math_PI * rotation / 2.0f),
Vector3((x - 4) * 4, 1 + 5 * fl, (z - 4) * 4) -
get_as_node<Spatial>("%bg_floor")
->get_transform()
.origin));
}
return item_data.get<struct grid_floor_item_node>()->node;
}
int get_closest_node_on_floor(const String &key, bool exterior, int fl,
const Vector3 &position)
{
int i;
assert(has_floor(key, exterior, fl));
flecs::entity floor_e = get_floor(key, exterior, fl);
assert(floor_e.is_valid());
int selected = -1;
float dst = Math_INF;
for (i = 0; i < grid_size * grid_size; i++) {
Spatial *node = get_grid_node(key, exterior, fl, i);
Vector3 pos = node->get_transform().origin;
float mdst = position.distance_squared_to(pos);
if (dst > mdst) {
dst = mdst;
selected = i;
}
}
return selected;
}
void get_grid_layouts_key_list(List<String> *keys)
{
flecs::entity top = ecs.lookup("grid_layouts");
assert(top.is_valid());
flecs::query_builder<const struct grid_layout> qb =
ecs.query_builder<const struct grid_layout>().with(
flecs::ChildOf, top);
flecs::query<const struct grid_layout> q = qb.build();
q.each([keys](flecs::entity e, const struct grid_layout &data) {
keys->push_back(String(e.name()));
});
}
void serialize_layouts(Dictionary &store)
{
flecs::entity top = ecs.lookup("grid_layouts");
assert(top.is_valid());
top.children([this, &store](flecs::entity l) {
Dictionary layout, exterior_layout, interior_layout;
if (l.has<struct grid_layout>()) {
flecs::entity intr = l.lookup("interior");
assert(intr.is_valid());
flecs::entity extr = l.lookup("exterior");
assert(extr.is_valid());
intr.children([this, &interior_layout](
flecs::entity intr_fl) {
if (intr_fl.has<struct grid_floor>()) {
Array items;
intr_fl.children([&items](
flecs::entity
floor_item) {
if (floor_item.has<
struct grid_floor_item>()) {
const struct grid_floor_item *item =
floor_item
.get<struct grid_floor_item>();
Dictionary sitem;
sitem["index"] =
item->index;
sitem["element"] =
item->element;
sitem["rotation"] =
item->rotation;
items.push_back(
sitem);
}
});
String floor_key(
intr_fl.name());
interior_layout[floor_key] =
items;
}
});
extr.children([this, &exterior_layout](
flecs::entity extr_fl) {
Array items;
extr_fl.children([&items](
flecs::entity
floor_item) {
if (floor_item.has<
struct grid_floor_item>()) {
const struct grid_floor_item *item =
floor_item.get<
struct grid_floor_item>();
Dictionary sitem;
sitem["index"] =
item->index;
sitem["element"] =
item->element;
sitem["rotation"] =
item->rotation;
items.push_back(sitem);
}
});
String floor_key(extr_fl.name());
exterior_layout[floor_key] = items;
});
layout["interior"] = interior_layout;
layout["exterior"] = exterior_layout;
}
String layout_name(l.name());
store[layout_name] = layout;
});
}
void unserialize_layouts(const Dictionary &store)
{
int i;
flecs::entity top = ecs.lookup("grid_layouts");
assert(top.is_valid());
// delete all layouts
top.children([this](flecs::entity l) { l.destruct(); });
List<Variant> layout_keys;
store.get_key_list(&layout_keys);
List<Variant>::Element *e;
e = layout_keys.front();
while (e) {
String layout_name = e->get();
flecs::entity layout =
ecs.entity(layout_name.ascii().ptr())
.child_of(top);
layout.add<grid_layout>();
flecs::entity extr =
ecs.entity("exterior").child_of(layout);
extr.add<grid_layout_exterior>();
extr.set<grid_layout_base>({ 0 });
flecs::entity intr =
ecs.entity("interior").child_of(layout);
intr.add<grid_layout_interior>();
intr.set<grid_layout_base>({ 0 });
Dictionary store_layout = store[e->get()];
Dictionary store_interior = store_layout["interior"];
Dictionary store_exterior = store_layout["exterior"];
List<Variant>::Element *ve;
List<Variant> interior_keys;
List<Variant> exterior_keys;
store_interior.get_key_list(&interior_keys);
store_exterior.get_key_list(&exterior_keys);
for (ve = interior_keys.front(); ve; ve = ve->next()) {
String floor_key = ve->get();
if (floor_key.begins_with("floor_")) {
flecs::entity floor_e =
ecs.entity(floor_key.ascii()
.ptr())
.child_of(intr);
assert(floor_e.is_valid());
floor_e.add<struct grid_floor>();
const Array &floor_interior =
store_interior[floor_key];
for (i = 0; i < floor_interior.size();
i++) {
const Dictionary &item =
floor_interior[i];
int index = item["index"];
String element =
item["element"];
int rotation = item["rotation"];
String item_key =
"item_" + itos(index);
flecs::entity item_e =
ecs.entity(item_key.ascii()
.ptr())
.child_of(
floor_e);
item_e.set<grid_floor_item>(
{ index, element,
rotation });
}
struct grid_layout_base *l = intr.get_mut<
struct grid_layout_base>();
l->floor_count++;
}
}
for (ve = exterior_keys.front(); ve; ve = ve->next()) {
String floor_key = ve->get();
if (floor_key.begins_with("floor_")) {
flecs::entity floor_e =
ecs.entity(floor_key.ascii()
.ptr())
.child_of(extr);
assert(floor_e.is_valid());
floor_e.add<struct grid_floor>();
const Array &floor_exterior =
store_exterior[floor_key];
for (i = 0; i < floor_exterior.size();
i++) {
const Dictionary &item =
floor_exterior[i];
int index = item["index"];
String element =
item["element"];
int rotation = item["rotation"];
String item_key =
"item_" + itos(index);
flecs::entity item_e =
ecs.entity(item_key.ascii()
.ptr())
.child_of(
floor_e);
item_e.set<grid_floor_item>(
{ index, element,
rotation });
}
struct grid_layout_base *l = extr.get_mut<
struct grid_layout_base>();
l->floor_count++;
}
}
e = e->next();
}
}
void get_element_type_key_list(List<String> *keys)
{
element_type.get_key_list(keys);
}
void create_element_type(const String &key)
{
assert(!element_type.has(key));
struct grid_element_type g;
g.name = key;
element_type[key] = g;
}
void set_element_type_socket(const String &key, int socket,
const Transform &xform)
{
assert(element_type.has(key));
assert(socket >= 0 && socket < ELEMENT_SOCKETS);
element_type[key].sockets[socket] = xform;
}
void set_element_type_name(const String &key, const String &name)
{
assert(element_type.has(key));
element_type[key].name = name;
}
bool has_element_type(const String &key)
{
return element_type.has(key);
}
Transform get_element_type_socket(const String &key, int socket)
{
assert(element_type.has(key));
assert(socket >= 0 && socket < ELEMENT_SOCKETS);
return element_type[key].sockets[socket];
}
int get_element_type_size()
{
return element_type.size();
}
void create_element(const String &key, const String &type)
{
struct grid_element g;
assert(!elements.has(key));
assert(element_type.has(type));
g.name = key;
g.type = type;
elements[key] = g;
}
void set_element_type(const String &key, const String &type)
{
assert(elements.has(key));
assert(element_type.has(type));
elements[key].type = type;
}
const String &get_element_type(const String &key) const
{
assert(elements.has(key));
return elements[key].type;
}
void set_element_mesh_name(const String &key, int socket,
const String &mesh_name)
{
assert(elements.has(key));
assert(socket >= 0 && socket < ELEMENT_SOCKETS);
elements[key].mesh_names[socket] = mesh_name;
}
const String &get_element_mesh_name(const String &key, int socket)
{
assert(elements.has(key));
assert(socket >= 0 && socket < ELEMENT_SOCKETS);
return elements[key].mesh_names[socket];
}
int get_element_size()
{
return elements.size();
}
void get_element_key_list(List<String> *keys)
{
elements.get_key_list(keys);
}
bool has_element(const String &key)
{
return elements.has(key);
}
void save_data()
{
int i;
ConfigFile config;
Dictionary conf_element_types;
Dictionary conf_elements;
Dictionary conf_exterior_grid;
List<String> keys;
List<String>::Element *e;
element_type.get_key_list(&keys);
e = keys.front();
while (e) {
Dictionary item;
const struct grid_element_type &g =
element_type[e->get()];
if (e->get() == "empty") {
e = e->next();
continue;
}
item["name"] = e->get();
Array sockets;
sockets.resize(ELEMENT_SOCKETS);
for (i = 0; i < ELEMENT_SOCKETS; i++)
sockets[i] = g.sockets[i];
item["sockets"] = sockets;
conf_element_types[e->get()] = item;
e = e->next();
}
keys.clear();
elements.get_key_list(&keys);
e = keys.front();
while (e) {
Dictionary item;
const struct grid_element &g = elements[e->get()];
if (e->get() == "empty") {
e = e->next();
continue;
}
item["name"] = e->get();
item["type"] = g.type;
Array mesh_names;
mesh_names.resize(ELEMENT_SOCKETS);
for (i = 0; i < ELEMENT_SOCKETS; i++)
mesh_names[i] = g.mesh_names[i];
item["mesh_names"] = mesh_names;
conf_elements[e->get()] = item;
e = e->next();
}
// TODO: support multiple layouts;
serialize_layouts(conf_exterior_grid);
config.set_value("buildings_layout", "element_types",
conf_element_types);
config.set_value("buildings_layout", "elements", conf_elements);
config.set_value("buildings_layout", "grid_layouts",
conf_exterior_grid);
config.save("res://astream/blayout.conf");
}
void load_data()
{
int i;
ConfigFile config;
Dictionary conf_element_types;
Dictionary conf_elements;
Dictionary conf_grid_layouts;
List<Variant> keys;
List<Variant>::Element *e;
elements.clear();
element_type.clear();
config.load("res://astream/blayout.conf");
conf_element_types = config.get_value(
"buildings_layout", "element_types", Dictionary());
conf_elements = config.get_value("buildings_layout", "elements",
Dictionary());
conf_grid_layouts = config.get_value(
"buildings_layout", "grid_layouts", Dictionary());
conf_element_types.get_key_list(&keys);
e = keys.front();
while (e) {
String key = e->get();
Dictionary item = conf_element_types[key];
assert(item.has("sockets"));
create_element_type(key);
Array sockets = item["sockets"];
for (i = 0; i < sockets.size(); i++)
set_element_type_socket(key, i, sockets[i]);
e = e->next();
}
keys.clear();
conf_elements.get_key_list(&keys);
e = keys.front();
while (e) {
String key = e->get();
Dictionary item = conf_elements[key];
assert(item.has("type"));
assert(item.has("mesh_names"));
String type = item["type"];
print_line("loading element: " + key +
" type: " + type);
if (key == "empty") {
e = e->next();
continue;
}
create_element(key, type);
Array mesh_names = item["mesh_names"];
for (i = 0; i < mesh_names.size(); i++)
set_element_mesh_name(key, i, mesh_names[i]);
e = e->next();
}
unserialize_layouts(conf_grid_layouts);
}
};
ElementData *ElementData::singleton = nullptr;
class LayoutEditor : public Object { class LayoutEditor : public Object {
GDCLASS(LayoutEditor, Object) GDCLASS(LayoutEditor, Object)
BuildingLayoutEditor *editor; BuildingLayoutEditor *editor;
@@ -737,6 +28,7 @@ class LayoutEditor : public Object {
String current_layout; String current_layout;
bool is_exterior; bool is_exterior;
int current_floor; int current_floor;
String select_rotation;
public: public:
LayoutEditor(BuildingLayoutEditor *editor) LayoutEditor(BuildingLayoutEditor *editor)
@@ -748,6 +40,7 @@ public:
, current_layout("default") , current_layout("default")
, is_exterior(true) , is_exterior(true)
, current_floor(0) , current_floor(0)
, select_rotation("%select_rotation")
{ {
get_as_node<OptionButton>(grid_elements) get_as_node<OptionButton>(grid_elements)
->connect("item_selected", this, "select_grid_element"); ->connect("item_selected", this, "select_grid_element");
@@ -755,6 +48,11 @@ public:
sb->connect("value_changed", this, "set_current_floor"); sb->connect("value_changed", this, "set_current_floor");
sb->set_min(-6); sb->set_min(-6);
sb->set_max(6); sb->set_max(6);
get_as_node<OptionButton>(select_rotation)
->connect("item_selected", this,
"select_grid_rotation");
get_as_node<Button>("%grow_cell_button")
->connect("pressed", this, "grow_cell");
} }
virtual ~LayoutEditor() virtual ~LayoutEditor()
{ {
@@ -892,6 +190,16 @@ public:
current_cell); current_cell);
select_control_item<OptionButton>( select_control_item<OptionButton>(
"%grid_elements", element); "%grid_elements", element);
int rotation =
ElementData::get_singleton()
->get_grid_rotation(
current_layout,
is_exterior,
current_floor,
current_cell);
get_as_node<OptionButton>(
"%select_rotation")
->select(rotation);
} }
} }
} }
@@ -922,12 +230,53 @@ public:
current_layout, is_exterior, current_layout, is_exterior,
current_floor, current_cell)); current_floor, current_cell));
} }
void select_grid_rotation(int index)
{
print_line("rotation: " + itos(index));
const String &element =
ElementData::get_singleton()->get_grid_element(
current_layout, is_exterior, current_floor,
current_cell);
ElementData::get_singleton()->set_grid_rotation(
current_layout, is_exterior, current_floor,
current_cell, index);
editor->visualize_element_at(
element, ElementData::get_singleton()->get_grid_node(
current_layout, is_exterior,
current_floor, current_cell));
}
void grow_cell()
{
int i;
ElementData::get_singleton()->grow_cell(current_layout,
is_exterior,
current_floor,
current_cell);
int grid_size = ElementData::get_singleton()->get_grid_size();
for (i = 0; i < grid_size * grid_size; i++) {
Spatial *sp =
ElementData::get_singleton()->get_grid_node(
current_layout, is_exterior,
current_floor, i);
const String &element =
ElementData::get_singleton()->get_grid_element(
current_layout, is_exterior,
current_floor, i);
editor->visualize_element_at(element, sp);
}
print_line("grow_cell");
}
static void _bind_methods() static void _bind_methods()
{ {
ClassDB::bind_method(D_METHOD("select_grid_element", "index"), ClassDB::bind_method(D_METHOD("select_grid_element", "index"),
&LayoutEditor::select_grid_element); &LayoutEditor::select_grid_element);
ClassDB::bind_method(D_METHOD("set_current_floor", "value"), ClassDB::bind_method(D_METHOD("set_current_floor", "value"),
&LayoutEditor::set_current_floor); &LayoutEditor::set_current_floor);
ClassDB::bind_method(D_METHOD("select_grid_rotation", "index"),
&LayoutEditor::select_grid_rotation);
ClassDB::bind_method(D_METHOD("grow_cell"),
&LayoutEditor::grow_cell);
} }
}; };

View File

@@ -0,0 +1,123 @@
#undef NDEBUG
#include "element_data.h"
ElementData *ElementData::singleton = nullptr;
List<int> ElementData::get_grow_cells(const String &key, bool exterior, int fl,
int cell) const
{
List<int> grow_cells;
int x = cell % grid_size;
int z = cell / grid_size;
bool grow_west = true, grow_south = true, grow_east = true,
grow_north = true;
if (x == 0)
grow_west = false;
else if (x >= grid_size - 1)
grow_east = false;
if (z == 0)
grow_south = false;
else if (z >= grid_size - 1)
grow_north = false;
if (grow_west) {
int cell_ = (x - 1) + z * grid_size;
grow_cells.push_back(cell_);
}
if (grow_west && grow_south) {
int cell_ = (x - 1) + (z - 1) * grid_size;
grow_cells.push_back(cell_);
}
if (grow_south) {
int cell_ = x + (z - 1) * grid_size;
grow_cells.push_back(cell_);
}
if (grow_south && grow_east) {
int cell_ = (x + 1) + (z - 1) * grid_size;
grow_cells.push_back(cell_);
}
if (grow_east) {
int cell_ = (x + 1) + z * grid_size;
grow_cells.push_back(cell_);
}
if (grow_east && grow_north) {
int cell_ = (x + 1) + (z + 1) * grid_size;
grow_cells.push_back(cell_);
}
if (grow_north) {
int cell_ = x + (z + 1) * grid_size;
grow_cells.push_back(cell_);
}
if (grow_north && grow_west) {
int cell_ = (x - 1) + (z + 1) * grid_size;
grow_cells.push_back(cell_);
}
return grow_cells;
}
void ElementData::grow_cell(const String &key, bool exterior, int fl, int cell)
{
List<int> queue;
List<int> input_cells;
List<int> output_cells;
const String &element = get_grid_element(key, exterior, fl, cell);
print_line("grow_cell: " + element);
if (element == "empty")
return;
int rotation = get_grid_rotation(key, exterior, fl, cell);
queue.push_back(cell);
// collect all the same elements adjacent to current one
while (!queue.empty()) {
int c = queue.front()->get();
queue.pop_front();
const String &el = get_grid_element(key, exterior, fl, c);
int rot = get_grid_rotation(key, exterior, fl, c);
if (el == element && rot == rotation) {
print_line("adding cell: " + itos(c));
// do not place original cell in inputs
if (input_cells.find(c) == nullptr)
input_cells.push_back(c);
List<int> cells = get_grow_cells(key, exterior, fl, c);
while (!cells.empty()) {
int item = cells.front()->get();
cells.pop_front();
if (input_cells.find(item) == nullptr &&
queue.find(item) == nullptr)
queue.push_back(item);
}
}
}
print_line("input_cells: " + itos(input_cells.size()));
queue = input_cells;
while (!queue.empty()) {
int c = queue.front()->get();
queue.pop_front();
const String &el = get_grid_element(key, exterior, fl, c);
if (el == element) {
List<int> cells = get_grow_cells(key, exterior, fl, c);
while (!cells.empty()) {
int g = cells.front()->get();
cells.pop_front();
const String &em =
get_grid_element(key, exterior, fl, g);
if (em == "empty" && queue.find(g) == nullptr)
queue.push_back(g);
}
} else if (el == "empty") {
if (output_cells.find(c) == nullptr)
output_cells.push_back(c);
}
}
queue = output_cells;
while (!queue.empty()) {
int c = queue.front()->get();
queue.pop_front();
const String &cell_element =
get_grid_element(key, exterior, fl, c);
assert(cell_element == "empty");
if (cell_element == "empty") {
set_grid_element(key, exterior, fl, c, element);
set_grid_rotation(key, exterior, fl, c, rotation);
}
}
print_line("initial cell: " + itos(cell));
print_line("input_cells: " + itos(input_cells.size()));
print_line("output_cells: " + itos(output_cells.size()));
}

View File

@@ -0,0 +1,729 @@
#ifndef ELEMENT_DATA_H
#define ELEMENT_DATA_H
#include <vector>
#include <core/ustring.h>
#include <scene/main/node.h>
#include <core/engine.h>
#include <editor/editor_node.h>
#include <flecs/flecs.h>
template <class T> T *get_as_node(const String &path)
{
Node *scene;
if (Engine::get_singleton()->is_editor_hint())
scene = EditorNode::get_singleton()->get_edited_scene();
else
scene = SceneTree::get_singleton()->get_current_scene();
assert(scene);
Node *node = scene->get_node(NodePath(path));
if (!node)
print_error("Failed to get " + path);
assert(node);
T *ret = Object::cast_to<T>(node);
if (!ret)
print_error("Failed to assign " + path);
assert(ret);
return ret;
}
template <class T>
void select_control_item(const String &path, const String &item)
{
int i;
T *ctl = get_as_node<T>(path);
int selected = -1;
for (i = 0; i < ctl->get_item_count(); i++)
if (ctl->get_item_text(i) == item) {
selected = i;
break;
}
if (selected >= 0) {
ctl->select(selected);
print_line("selected: " + item);
}
}
template <class T> void select_control_item(T *ctl, const String &item)
{
int i;
int selected = -1;
for (i = 0; i < ctl->get_item_count(); i++)
if (ctl->get_item_text(i) == item) {
selected = i;
break;
}
if (selected >= 0) {
ctl->select(selected);
print_line("selected: " + item);
}
}
#define ELEMENT_SOCKETS 16
class ElementData {
flecs::world ecs;
static ElementData *singleton;
public:
static ElementData *get_singleton()
{
if (!singleton)
singleton = memnew(ElementData);
return singleton;
}
static void cleanup()
{
if (singleton)
memdelete(singleton);
}
struct grid_element_type {
String name;
Transform sockets[ELEMENT_SOCKETS];
};
struct grid_element {
String name;
String type;
String mesh_names[ELEMENT_SOCKETS];
};
int grid_step{ 4 };
int grid_size{ 9 };
struct grid_cell {
String element;
int rotation;
};
struct grid_layouts {};
struct grid_layout {};
struct grid_layout_base {
int floor_count;
};
struct grid_layout_exterior {};
struct grid_layout_interior {};
struct grid_floor {};
struct grid_floor_item {
int index;
String element;
int rotation;
};
struct grid_floor_item_node {
Spatial *node;
};
protected:
ElementData()
{
ecs.component<struct grid_layouts>();
ecs.component<struct grid_layout>();
ecs.component<struct grid_layout_exterior>();
ecs.component<struct grid_layout_interior>();
ecs.component<struct grid_floor>();
ecs.component<struct grid_floor_item>();
ecs.component<struct grid_floor_item_node>();
flecs::entity e = ecs.entity("grid_layouts");
e.add<grid_layouts>();
create_new_layout("default");
create_new_exterior_floor("default");
create_new_interior_floor("default");
load_data();
struct grid_element_type t_empty;
struct grid_element e_empty;
t_empty.name = "empty";
e_empty.name = "empty";
e_empty.type = "empty";
element_type["empty"] = t_empty;
elements["empty"] = e_empty;
ecs.observer<struct grid_floor_item_node>()
.event(flecs::OnRemove)
.each([](flecs::entity em,
struct grid_floor_item_node &s) {
if (s.node)
s.node->queue_delete();
s.node = nullptr;
});
}
HashMap<String, struct grid_element_type> element_type;
HashMap<String, struct grid_element> elements;
public:
List<int> get_grow_cells(const String &key, bool exterior, int fl,
int cell) const;
void grow_cell(const String &key, bool exterior, int fl, int cell);
void create_new_layout(const String &key)
{
flecs::entity top = ecs.lookup("grid_layouts");
assert(top.is_valid());
flecs::entity layout =
ecs.entity(key.ascii().ptr()).child_of(top);
// one floor by default
layout.add<struct grid_layout>();
flecs::entity extr = ecs.entity("exterior").child_of(layout);
extr.add<grid_layout_exterior>();
flecs::entity intr = ecs.entity("interior").child_of(layout);
intr.add<grid_layout_interior>();
intr.set<grid_layout_base>({ 0 });
extr.set<grid_layout_base>({ 0 });
}
void create_new_exterior_floor(const String &key)
{
int i;
flecs::entity ext = get_base(key, true);
assert(ext.is_valid());
struct grid_layout_base *l = ext.get_mut<grid_layout_base>();
int floor = l->floor_count;
flecs::entity fl =
ecs.entity(("floor_" + itos(floor)).ascii().ptr())
.child_of(ext);
assert(fl.is_valid());
for (i = 0; i < grid_size * grid_size; i++) {
flecs::entity item =
ecs.entity(("item_" + itos(i)).ascii().ptr())
.child_of(fl);
item.set<grid_floor_item>({ i, "empty", 0 });
}
l->floor_count++;
}
void create_new_interior_floor(const String &key)
{
int i;
flecs::entity intr = get_base(key, false);
assert(intr.is_valid());
struct grid_layout_base *l = intr.get_mut<grid_layout_base>();
int floor = l->floor_count;
flecs::entity fl =
ecs.entity(("floor_" + itos(floor)).ascii().ptr())
.child_of(intr);
assert(fl.is_valid());
for (i = 0; i < grid_size * grid_size; i++) {
flecs::entity item =
ecs.entity(("item_" + itos(i)).ascii().ptr())
.child_of(fl);
item.set<grid_floor_item>({ i, "empty", 0 });
}
l->floor_count++;
}
inline int constexpr get_grid_size()
{
return grid_size;
}
flecs::entity get_base(const String &key, bool exterior) const
{
flecs::entity top = ecs.lookup("grid_layouts");
assert(top.is_valid());
flecs::entity layout = top.lookup(key.ascii().ptr());
assert(layout.is_valid());
flecs::entity base;
if (exterior)
base = layout.lookup("exterior");
else
base = layout.lookup("interior");
return base;
}
flecs::entity get_floor(const String &key, bool exterior, int fl) const
{
flecs::entity base = get_base(key, exterior);
assert(base.is_valid());
String floor_key = "floor_" + itos(fl);
flecs::entity floor_e = base.lookup(floor_key.ascii().ptr());
return floor_e;
}
bool has_floor(const String &key, bool exterior, int fl) const
{
flecs::entity floor_e = get_floor(key, exterior, fl);
if (!floor_e.is_valid())
return false;
flecs::entity item_e = floor_e.lookup("item_0");
return item_e.is_valid();
}
void ensure_floor(const String &key, bool exterior, int fl)
{
int i;
if (has_floor(key, exterior, fl))
return;
flecs::entity base = get_base(key, exterior);
flecs::entity floor_e =
ecs.entity(("floor_" + itos(fl)).ascii().ptr())
.child_of(base);
assert(floor_e.is_valid());
for (i = 0; i < grid_size * grid_size; i++) {
flecs::entity item =
ecs.entity(("item_" + itos(i)).ascii().ptr())
.child_of(floor_e);
item.set<grid_floor_item>({ i, "empty", 0 });
}
assert(has_floor(key, exterior, fl));
print_line("ensured floor: " + itos(fl));
}
inline flecs::entity get_grid_entity(const String &key, bool exterior,
int fl, int i) const
{
flecs::entity base = get_floor(key, exterior, fl);
assert(base.is_valid());
String item_key = "item_" + itos(i);
flecs::entity item_data = base.lookup(item_key.ascii().ptr());
if (!item_data.is_valid())
print_error("lookup failed: " + item_key);
assert(item_data.is_valid());
return item_data;
}
inline const String &get_grid_element(const String &key, bool exterior,
int fl, int i) const
{
flecs::entity item_data = get_grid_entity(key, exterior, fl, i);
const String &element =
item_data.get<grid_floor_item>()->element;
return element;
}
inline int get_grid_rotation(const String &key, bool exterior, int fl,
int i) const
{
flecs::entity item_data = get_grid_entity(key, exterior, fl, i);
int rotation = item_data.get<grid_floor_item>()->rotation;
return rotation;
}
void set_grid_element(const String &key, bool exterior, int fl, int i,
const String &element)
{
flecs::entity item_data = get_grid_entity(key, exterior, fl, i);
item_data.get_mut<grid_floor_item>()->element = element;
}
void set_grid_rotation(const String &key, bool exterior, int fl, int i,
int rotation)
{
flecs::entity item_data = get_grid_entity(key, exterior, fl, i);
item_data.get_mut<grid_floor_item>()->rotation = rotation;
}
Spatial *get_grid_node(const String &key, bool exterior, int fl, int i)
{
assert(has_floor(key, exterior, fl));
flecs::entity item_data = get_grid_entity(key, exterior, fl, i);
if (!item_data.has<struct grid_floor_item_node>()) {
Spatial *sp = memnew(Spatial);
get_as_node<Spatial>("%bg_floor")
->call_deferred("add_child", sp);
item_data.set<struct grid_floor_item_node>({ sp });
int x = i % grid_size;
int z = i / grid_size;
int rotation = get_grid_rotation(key, exterior, fl, i);
sp->set_transform(Transform(
Basis().rotated(Vector3(0, 1, 0),
Math_PI * rotation / 2.0f),
Vector3((x - 4) * 4, 1 + 5 * fl, (z - 4) * 4) -
get_as_node<Spatial>("%bg_floor")
->get_transform()
.origin));
} else {
int rotation = get_grid_rotation(key, exterior, fl, i);
const struct grid_floor_item_node *item =
item_data.get<struct grid_floor_item_node>();
Spatial *node = item->node;
Transform xform = node->get_transform();
xform.basis = Basis().rotated(
Vector3(0, 1, 0), rotation * Math_PI / 2.0f);
node->set_transform(xform);
}
return item_data.get<struct grid_floor_item_node>()->node;
}
int get_closest_node_on_floor(const String &key, bool exterior, int fl,
const Vector3 &position)
{
int i;
assert(has_floor(key, exterior, fl));
flecs::entity floor_e = get_floor(key, exterior, fl);
assert(floor_e.is_valid());
int selected = -1;
float dst = Math_INF;
for (i = 0; i < grid_size * grid_size; i++) {
Spatial *node = get_grid_node(key, exterior, fl, i);
Vector3 pos = node->get_transform().origin;
float mdst = position.distance_squared_to(pos);
if (dst > mdst) {
dst = mdst;
selected = i;
}
}
return selected;
}
void get_grid_layouts_key_list(List<String> *keys)
{
flecs::entity top = ecs.lookup("grid_layouts");
assert(top.is_valid());
flecs::query_builder<const struct grid_layout> qb =
ecs.query_builder<const struct grid_layout>().with(
flecs::ChildOf, top);
flecs::query<const struct grid_layout> q = qb.build();
q.each([keys](flecs::entity e, const struct grid_layout &data) {
keys->push_back(String(e.name()));
});
}
void serialize_layouts(Dictionary &store)
{
flecs::entity top = ecs.lookup("grid_layouts");
assert(top.is_valid());
top.children([this, &store](flecs::entity l) {
Dictionary layout, exterior_layout, interior_layout;
if (l.has<struct grid_layout>()) {
flecs::entity intr = l.lookup("interior");
assert(intr.is_valid());
flecs::entity extr = l.lookup("exterior");
assert(extr.is_valid());
intr.children([this, &interior_layout](
flecs::entity intr_fl) {
if (intr_fl.has<struct grid_floor>()) {
Array items;
intr_fl.children([&items](
flecs::entity
floor_item) {
if (floor_item.has<
struct grid_floor_item>()) {
const struct grid_floor_item *item =
floor_item
.get<struct grid_floor_item>();
Dictionary sitem;
sitem["index"] =
item->index;
sitem["element"] =
item->element;
sitem["rotation"] =
item->rotation;
items.push_back(
sitem);
}
});
String floor_key(
intr_fl.name());
interior_layout[floor_key] =
items;
}
});
extr.children([this, &exterior_layout](
flecs::entity extr_fl) {
Array items;
extr_fl.children([&items](
flecs::entity
floor_item) {
if (floor_item.has<
struct grid_floor_item>()) {
const struct grid_floor_item *item =
floor_item.get<
struct grid_floor_item>();
Dictionary sitem;
sitem["index"] =
item->index;
sitem["element"] =
item->element;
sitem["rotation"] =
item->rotation;
items.push_back(sitem);
}
});
String floor_key(extr_fl.name());
exterior_layout[floor_key] = items;
});
layout["interior"] = interior_layout;
layout["exterior"] = exterior_layout;
}
String layout_name(l.name());
store[layout_name] = layout;
});
}
void unserialize_layouts(const Dictionary &store)
{
int i;
flecs::entity top = ecs.lookup("grid_layouts");
assert(top.is_valid());
// delete all layouts
top.children([this](flecs::entity l) { l.destruct(); });
List<Variant> layout_keys;
store.get_key_list(&layout_keys);
List<Variant>::Element *e;
e = layout_keys.front();
while (e) {
String layout_name = e->get();
flecs::entity layout =
ecs.entity(layout_name.ascii().ptr())
.child_of(top);
layout.add<grid_layout>();
flecs::entity extr =
ecs.entity("exterior").child_of(layout);
extr.add<grid_layout_exterior>();
extr.set<grid_layout_base>({ 0 });
flecs::entity intr =
ecs.entity("interior").child_of(layout);
intr.add<grid_layout_interior>();
intr.set<grid_layout_base>({ 0 });
Dictionary store_layout = store[e->get()];
Dictionary store_interior = store_layout["interior"];
Dictionary store_exterior = store_layout["exterior"];
List<Variant>::Element *ve;
List<Variant> interior_keys;
List<Variant> exterior_keys;
store_interior.get_key_list(&interior_keys);
store_exterior.get_key_list(&exterior_keys);
for (ve = interior_keys.front(); ve; ve = ve->next()) {
String floor_key = ve->get();
if (floor_key.begins_with("floor_")) {
flecs::entity floor_e =
ecs.entity(floor_key.ascii()
.ptr())
.child_of(intr);
assert(floor_e.is_valid());
floor_e.add<struct grid_floor>();
const Array &floor_interior =
store_interior[floor_key];
for (i = 0; i < floor_interior.size();
i++) {
const Dictionary &item =
floor_interior[i];
int index = item["index"];
String element =
item["element"];
int rotation = item["rotation"];
String item_key =
"item_" + itos(index);
flecs::entity item_e =
ecs.entity(item_key.ascii()
.ptr())
.child_of(
floor_e);
item_e.set<grid_floor_item>(
{ index, element,
rotation });
}
struct grid_layout_base *l = intr.get_mut<
struct grid_layout_base>();
l->floor_count++;
}
}
for (ve = exterior_keys.front(); ve; ve = ve->next()) {
String floor_key = ve->get();
if (floor_key.begins_with("floor_")) {
flecs::entity floor_e =
ecs.entity(floor_key.ascii()
.ptr())
.child_of(extr);
assert(floor_e.is_valid());
floor_e.add<struct grid_floor>();
const Array &floor_exterior =
store_exterior[floor_key];
for (i = 0; i < floor_exterior.size();
i++) {
const Dictionary &item =
floor_exterior[i];
int index = item["index"];
String element =
item["element"];
int rotation = item["rotation"];
String item_key =
"item_" + itos(index);
flecs::entity item_e =
ecs.entity(item_key.ascii()
.ptr())
.child_of(
floor_e);
item_e.set<grid_floor_item>(
{ index, element,
rotation });
}
struct grid_layout_base *l = extr.get_mut<
struct grid_layout_base>();
l->floor_count++;
}
}
e = e->next();
}
}
void get_element_type_key_list(List<String> *keys)
{
element_type.get_key_list(keys);
}
void create_element_type(const String &key)
{
assert(!element_type.has(key));
struct grid_element_type g;
g.name = key;
element_type[key] = g;
}
void set_element_type_socket(const String &key, int socket,
const Transform &xform)
{
assert(element_type.has(key));
assert(socket >= 0 && socket < ELEMENT_SOCKETS);
element_type[key].sockets[socket] = xform;
}
void set_element_type_name(const String &key, const String &name)
{
assert(element_type.has(key));
element_type[key].name = name;
}
bool has_element_type(const String &key)
{
return element_type.has(key);
}
Transform get_element_type_socket(const String &key, int socket)
{
assert(element_type.has(key));
assert(socket >= 0 && socket < ELEMENT_SOCKETS);
return element_type[key].sockets[socket];
}
int get_element_type_size()
{
return element_type.size();
}
void create_element(const String &key, const String &type)
{
struct grid_element g;
assert(!elements.has(key));
assert(element_type.has(type));
g.name = key;
g.type = type;
elements[key] = g;
}
void set_element_type(const String &key, const String &type)
{
assert(elements.has(key));
assert(element_type.has(type));
elements[key].type = type;
}
const String &get_element_type(const String &key) const
{
assert(elements.has(key));
return elements[key].type;
}
void set_element_mesh_name(const String &key, int socket,
const String &mesh_name)
{
assert(elements.has(key));
assert(socket >= 0 && socket < ELEMENT_SOCKETS);
elements[key].mesh_names[socket] = mesh_name;
}
const String &get_element_mesh_name(const String &key, int socket)
{
assert(elements.has(key));
assert(socket >= 0 && socket < ELEMENT_SOCKETS);
return elements[key].mesh_names[socket];
}
int get_element_size()
{
return elements.size();
}
void get_element_key_list(List<String> *keys)
{
elements.get_key_list(keys);
}
bool has_element(const String &key)
{
return elements.has(key);
}
void save_data()
{
int i;
ConfigFile config;
Dictionary conf_element_types;
Dictionary conf_elements;
Dictionary conf_exterior_grid;
List<String> keys;
List<String>::Element *e;
element_type.get_key_list(&keys);
e = keys.front();
while (e) {
Dictionary item;
const struct grid_element_type &g =
element_type[e->get()];
if (e->get() == "empty") {
e = e->next();
continue;
}
item["name"] = e->get();
Array sockets;
sockets.resize(ELEMENT_SOCKETS);
for (i = 0; i < ELEMENT_SOCKETS; i++)
sockets[i] = g.sockets[i];
item["sockets"] = sockets;
conf_element_types[e->get()] = item;
e = e->next();
}
keys.clear();
elements.get_key_list(&keys);
e = keys.front();
while (e) {
Dictionary item;
const struct grid_element &g = elements[e->get()];
if (e->get() == "empty") {
e = e->next();
continue;
}
item["name"] = e->get();
item["type"] = g.type;
Array mesh_names;
mesh_names.resize(ELEMENT_SOCKETS);
for (i = 0; i < ELEMENT_SOCKETS; i++)
mesh_names[i] = g.mesh_names[i];
item["mesh_names"] = mesh_names;
conf_elements[e->get()] = item;
e = e->next();
}
// TODO: support multiple layouts;
serialize_layouts(conf_exterior_grid);
config.set_value("buildings_layout", "element_types",
conf_element_types);
config.set_value("buildings_layout", "elements", conf_elements);
config.set_value("buildings_layout", "grid_layouts",
conf_exterior_grid);
config.save("res://astream/blayout.conf");
}
void load_data()
{
int i;
ConfigFile config;
Dictionary conf_element_types;
Dictionary conf_elements;
Dictionary conf_grid_layouts;
List<Variant> keys;
List<Variant>::Element *e;
elements.clear();
element_type.clear();
config.load("res://astream/blayout.conf");
conf_element_types = config.get_value(
"buildings_layout", "element_types", Dictionary());
conf_elements = config.get_value("buildings_layout", "elements",
Dictionary());
conf_grid_layouts = config.get_value(
"buildings_layout", "grid_layouts", Dictionary());
conf_element_types.get_key_list(&keys);
e = keys.front();
while (e) {
String key = e->get();
Dictionary item = conf_element_types[key];
assert(item.has("sockets"));
create_element_type(key);
Array sockets = item["sockets"];
for (i = 0; i < sockets.size(); i++)
set_element_type_socket(key, i, sockets[i]);
e = e->next();
}
keys.clear();
conf_elements.get_key_list(&keys);
e = keys.front();
while (e) {
String key = e->get();
Dictionary item = conf_elements[key];
assert(item.has("type"));
assert(item.has("mesh_names"));
String type = item["type"];
print_line("loading element: " + key +
" type: " + type);
if (key == "empty") {
e = e->next();
continue;
}
create_element(key, type);
Array mesh_names = item["mesh_names"];
for (i = 0; i < mesh_names.size(); i++)
set_element_mesh_name(key, i, mesh_names[i]);
e = e->next();
}
unserialize_layouts(conf_grid_layouts);
}
};
#endif