diff --git a/godot/astream/building_layout_data.conf b/godot/astream/building_layout_data.conf index eec90dc..d2eec58 100644 --- a/godot/astream/building_layout_data.conf +++ b/godot/astream/building_layout_data.conf @@ -1,7 +1,7 @@ [layouts] entries=[ { -"children": [ 3, 4 ], +"children": [ 4, 5 ], "commands": [ [ 3, [ ] ], [ 4, [ ] ] ], "floor_index": 0, "index": 0, @@ -9,7 +9,7 @@ entries=[ { "order": 0, "type": "layout" }, { -"children": [ 5, 6, 7 ], +"children": [ 6, 7, 8 ], "commands": [ [ 3, [ ] ], [ 4, [ ] ] ], "floor_index": 0, "index": 1, @@ -17,7 +17,7 @@ entries=[ { "order": 0, "type": "layout" }, { -"children": [ 8, 9, 10, 11, 12 ], +"children": [ 9, 10, 11, 12, 13 ], "commands": [ [ 3, [ ] ], [ 4, [ ] ] ], "floor_index": 0, "index": 2, @@ -25,136 +25,126 @@ entries=[ { "order": 0, "type": "layout" }, { -"children": [ 13, 14 ], -"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], +"children": [ 14, 15, 16, 17 ], +"commands": [ ], +"floor_index": 0, "index": 3, +"name": "v4", +"order": 4, +"type": "layout" +}, { +"children": [ 18, 19 ], +"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], +"index": 4, "name": "zone_0", "order": 0, "type": "zone", "zone_type": 1 }, { -"children": [ 15, 16, 17 ], +"children": [ 20, 21, 22 ], "commands": [ [ 4, [ ] ] ], -"index": 4, -"name": "zone_1", -"order": 1, -"type": "zone", -"zone_type": 0 -}, { -"children": [ 18, 19, 20, 21 ], -"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], "index": 5, "name": "zone_1", +"order": 1, +"type": "zone", +"zone_type": 0 +}, { +"children": [ 23, 24, 25, 26 ], +"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], +"index": 6, +"name": "zone_1", "order": 0, "type": "zone", "zone_type": 1 }, { -"children": [ 22, 23, 24 ], +"children": [ 27, 28, 29 ], "commands": [ [ 3, [ ] ], [ 4, [ ] ] ], -"index": 6, +"index": 7, "name": "zone_0", "order": 1, "type": "zone", "zone_type": 0 }, { -"children": [ 25, 26 ], +"children": [ 30, 31 ], "commands": [ [ 3, [ ] ], [ 4, [ ] ] ], -"index": 7, +"index": 8, "name": "unit_0", "order": 3, "type": "unit" }, { -"children": [ 27, 28 ], +"children": [ 32, 33 ], "commands": [ [ 3, [ ] ], [ 4, [ ] ] ], -"index": 8, +"index": 9, "name": "zone_0", "order": 0, "type": "zone", "zone_type": 1 }, { -"children": [ 29, 30 ], +"children": [ 34, 35 ], "commands": [ [ 3, [ ] ], [ 4, [ ] ] ], -"index": 9, +"index": 10, "name": "unit_0", "order": 0, "type": "unit" }, { -"children": [ 31, 32 ], +"children": [ 36, 37 ], "commands": [ [ 3, [ ] ], [ 4, [ ] ] ], -"index": 10, +"index": 11, "name": "unit_1", "order": 0, "type": "unit" }, { -"children": [ 33, 34 ], +"children": [ 38, 39 ], "commands": [ [ 3, [ ] ], [ 4, [ ] ] ], -"index": 11, +"index": 12, "name": "unit_2", "order": 0, "type": "unit" }, { -"children": [ 35, 36 ], +"children": [ 40, 41 ], "commands": [ [ 3, [ ] ], [ 4, [ ] ] ], -"index": 12, +"index": 13, "name": "unit_3", "order": 0, "type": "unit" }, { -"children": [ ], -"commands": [ [ 5, [ ] ] ], -"index": 13, -"name": "living_room_0", -"order": 1, -"room_area": 144.0, -"room_type": 300, -"type": "room", -"window": true -}, { -"children": [ ], -"commands": [ [ 5, [ ] ] ], +"children": [ 42, 43 ], +"commands": [ ], "index": 14, -"name": "kitchen_0", -"order": 2, -"room_area": 16.0, -"room_type": 302, -"type": "room", -"window": true -}, { -"children": [ ], -"commands": [ [ 5, [ ] ] ], -"index": 15, -"name": "wc_0", -"order": 0, -"room_area": 16.0, -"room_type": 200, -"type": "room", -"window": false -}, { -"children": [ ], -"commands": [ [ 5, [ ] ], [ 3, [ ] ] ], -"index": 16, -"name": "bathroom_0", +"name": "zone_0", "order": 1, -"room_area": 16.0, -"room_type": 201, -"type": "room", -"window": false +"type": "zone", +"zone_type": 0 }, { -"children": [ ], -"commands": [ [ 5, [ ] ] ], -"index": 17, -"name": "bedroom_0", +"children": [ 44, 45 ], +"commands": [ ], +"index": 15, +"name": "zone_1", "order": 2, -"room_area": 64.0, -"room_type": 202, -"type": "room", -"window": true +"type": "zone", +"zone_type": 0 +}, { +"children": [ 46, 47, 48 ], +"commands": [ ], +"index": 16, +"name": "zone_2", +"order": 3, +"type": "zone", +"zone_type": 0 +}, { +"children": [ 49, 50, 51, 52 ], +"commands": [ ], +"index": 17, +"name": "zone_3", +"order": 4, +"type": "zone", +"zone_type": 1 }, { "children": [ ], "commands": [ [ 5, [ ] ] ], "index": 18, "name": "living_room_0", -"order": 0, +"order": 1, "room_area": 144.0, "room_type": 300, "type": "room", @@ -164,6 +154,56 @@ entries=[ { "commands": [ [ 5, [ ] ] ], "index": 19, "name": "kitchen_0", +"order": 2, +"room_area": 16.0, +"room_type": 302, +"type": "room", +"window": true +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 20, +"name": "wc_0", +"order": 0, +"room_area": 16.0, +"room_type": 200, +"type": "room", +"window": false +}, { +"children": [ ], +"commands": [ [ 5, [ ] ], [ 3, [ ] ] ], +"index": 21, +"name": "bathroom_0", +"order": 1, +"room_area": 16.0, +"room_type": 201, +"type": "room", +"window": false +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 22, +"name": "bedroom_0", +"order": 2, +"room_area": 64.0, +"room_type": 202, +"type": "room", +"window": true +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 23, +"name": "living_room_0", +"order": 0, +"room_area": 144.0, +"room_type": 300, +"type": "room", +"window": true +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 24, +"name": "kitchen_0", "order": 1, "room_area": 64.0, "room_type": 302, @@ -172,7 +212,7 @@ entries=[ { }, { "children": [ ], "commands": [ [ 5, [ ] ] ], -"index": 20, +"index": 25, "name": "storage_room_0", "order": 2, "room_area": 16.0, @@ -182,7 +222,7 @@ entries=[ { }, { "children": [ ], "commands": [ [ 5, [ ] ], [ 4, [ ] ] ], -"index": 21, +"index": 26, "name": "enterance_0", "order": 3, "room_area": 16.0, @@ -192,7 +232,7 @@ entries=[ { }, { "children": [ ], "commands": [ [ 5, [ ] ] ], -"index": 22, +"index": 27, "name": "wc_0", "order": 0, "room_area": 16.0, @@ -202,7 +242,7 @@ entries=[ { }, { "children": [ ], "commands": [ [ 5, [ ] ] ], -"index": 23, +"index": 28, "name": "bathroom_0", "order": 1, "room_area": 16.0, @@ -212,7 +252,7 @@ entries=[ { }, { "children": [ ], "commands": [ [ 5, [ ] ] ], -"index": 24, +"index": 29, "name": "bedroom_0", "order": 2, "room_area": 64.0, @@ -220,17 +260,17 @@ entries=[ { "type": "room", "window": true }, { -"children": [ 37, 38, 39 ], +"children": [ 53, 54, 55 ], "commands": [ [ 3, [ ] ], [ 4, [ ] ] ], -"index": 25, +"index": 30, "name": "zone_0", "order": 0, "type": "zone", "zone_type": 0 }, { -"children": [ 40, 41 ], +"children": [ 56, 57 ], "commands": [ [ 3, [ ] ], [ 4, [ ] ] ], -"index": 26, +"index": 31, "name": "zone_1", "order": 0, "type": "zone", @@ -238,7 +278,7 @@ entries=[ { }, { "children": [ ], "commands": [ [ 5, [ ] ] ], -"index": 27, +"index": 32, "name": "enterance_0", "order": 0, "room_area": 64.0, @@ -246,9 +286,9 @@ entries=[ { "type": "room", "window": true }, { -"children": [ 42 ], +"children": [ 58 ], "commands": [ [ 5, [ ] ] ], -"index": 28, +"index": 33, "name": "stair_0", "order": 0, "room_area": 16.0, @@ -256,83 +296,73 @@ entries=[ { "type": "room", "window": true }, { -"children": [ 43, 44, 45 ], -"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], -"index": 29, -"name": "zone_0", -"order": 0, -"type": "zone", -"zone_type": 0 -}, { -"children": [ 46, 47 ], -"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], -"index": 30, -"name": "zone_1", -"order": 0, -"type": "zone", -"zone_type": 1 -}, { -"children": [ 48 ], -"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], -"index": 31, -"name": "zone_0", -"order": 0, -"type": "zone", -"zone_type": 0 -}, { -"children": [ 49, 50 ], -"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], -"index": 32, -"name": "zone_1", -"order": 0, -"type": "zone", -"zone_type": 1 -}, { -"children": [ 51 ], -"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], -"index": 33, -"name": "zone_0", -"order": 0, -"type": "zone", -"zone_type": 0 -}, { -"children": [ 52 ], +"children": [ 59, 60, 61 ], "commands": [ [ 3, [ ] ], [ 4, [ ] ] ], "index": 34, -"name": "zone_1", -"order": 1, -"type": "zone", -"zone_type": 1 -}, { -"children": [ 53 ], -"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], -"index": 35, "name": "zone_0", "order": 0, "type": "zone", "zone_type": 0 }, { -"children": [ 54 ], +"children": [ 62, 63 ], +"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], +"index": 35, +"name": "zone_1", +"order": 0, +"type": "zone", +"zone_type": 1 +}, { +"children": [ 64 ], "commands": [ [ 3, [ ] ], [ 4, [ ] ] ], "index": 36, +"name": "zone_0", +"order": 0, +"type": "zone", +"zone_type": 0 +}, { +"children": [ 65, 66 ], +"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], +"index": 37, +"name": "zone_1", +"order": 0, +"type": "zone", +"zone_type": 1 +}, { +"children": [ 67 ], +"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], +"index": 38, +"name": "zone_0", +"order": 0, +"type": "zone", +"zone_type": 0 +}, { +"children": [ 68 ], +"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], +"index": 39, +"name": "zone_1", +"order": 1, +"type": "zone", +"zone_type": 1 +}, { +"children": [ 69 ], +"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], +"index": 40, +"name": "zone_0", +"order": 0, +"type": "zone", +"zone_type": 0 +}, { +"children": [ 70 ], +"commands": [ [ 3, [ ] ], [ 4, [ ] ] ], +"index": 41, "name": "zone_1", "order": 1, "type": "zone", "zone_type": 1 }, { "children": [ ], -"commands": [ [ 5, [ ] ] ], -"index": 37, -"name": "bathroom_0", -"order": 0, -"room_area": 16.0, -"room_type": 201, -"type": "room", -"window": false -}, { -"children": [ ], -"commands": [ [ 5, [ ] ] ], -"index": 38, +"commands": [ ], +"index": 42, "name": "wc_0", "order": 1, "room_area": 16.0, @@ -341,65 +371,27 @@ entries=[ { "window": false }, { "children": [ ], -"commands": [ [ 5, [ ] ] ], -"index": 39, +"commands": [ ], +"index": 43, "name": "bedroom_0", "order": 2, -"room_area": 36.0, +"room_area": 64.0, "room_type": 202, "type": "room", "window": true }, { "children": [ ], -"commands": [ [ 5, [ ] ] ], -"index": 40, -"name": "living_room_0", -"order": 0, -"room_area": 36.0, -"room_type": 300, -"type": "room", -"window": true -}, { -"children": [ ], -"commands": [ [ 5, [ ] ] ], -"index": 41, -"name": "kitchen_0", -"order": 1, -"room_area": 16.0, -"room_type": 302, -"type": "room", -"window": true -}, { -"children": [ 55, 56 ], "commands": [ ], -"floor_index": 1, -"index": 42, -"name": "floor_0", -"order": 0, -"type": "floor" -}, { -"children": [ ], -"commands": [ [ 5, [ ] ] ], -"index": 43, +"index": 44, "name": "wc_0", -"order": 0, +"order": 1, "room_area": 16.0, "room_type": 200, "type": "room", "window": false }, { "children": [ ], -"commands": [ [ 5, [ ] ] ], -"index": 44, -"name": "bathroom_0", -"order": 1, -"room_area": 16.0, -"room_type": 201, -"type": "room", -"window": false -}, { -"children": [ ], -"commands": [ [ 5, [ ] ] ], +"commands": [ ], "index": 45, "name": "bedroom_0", "order": 2, @@ -409,18 +401,38 @@ entries=[ { "window": true }, { "children": [ ], -"commands": [ [ 5, [ ] ] ], +"commands": [ ], "index": 46, -"name": "kitchen_0", -"order": 0, +"name": "wc_0", +"order": 1, "room_area": 16.0, -"room_type": 302, +"room_type": 200, "type": "room", -"window": true +"window": false }, { "children": [ ], -"commands": [ [ 5, [ ] ] ], +"commands": [ ], "index": 47, +"name": "bathroom_0", +"order": 2, +"room_area": 16.0, +"room_type": 201, +"type": "room", +"window": false +}, { +"children": [ ], +"commands": [ ], +"index": 48, +"name": "bathroom_1", +"order": 3, +"room_area": 144.0, +"room_type": 201, +"type": "room", +"window": false +}, { +"children": [ ], +"commands": [ ], +"index": 49, "name": "living_room_0", "order": 1, "room_area": 144.0, @@ -429,52 +441,32 @@ entries=[ { "window": true }, { "children": [ ], -"commands": [ [ 5, [ ] ] ], -"index": 48, -"name": "bathroom_0", -"order": 0, -"room_area": 16.0, -"room_type": 201, -"type": "room", -"window": false -}, { -"children": [ ], -"commands": [ [ 5, [ ] ] ], -"index": 49, +"commands": [ ], +"index": 50, "name": "kitchen_0", -"order": 0, +"order": 2, "room_area": 64.0, "room_type": 302, "type": "room", "window": true }, { "children": [ ], -"commands": [ [ 5, [ ] ] ], -"index": 50, -"name": "living_room_0", -"order": 1, -"room_area": 64.0, -"room_type": 300, +"commands": [ ], +"index": 51, +"name": "dining_room_0", +"order": 3, +"room_area": 144.0, +"room_type": 303, "type": "room", "window": true }, { "children": [ ], -"commands": [ [ 5, [ ] ] ], -"index": 51, -"name": "wc_0", -"order": 0, -"room_area": 16.0, -"room_type": 200, -"type": "room", -"window": false -}, { -"children": [ ], -"commands": [ [ 5, [ ] ] ], +"commands": [ ], "index": 52, -"name": "living_room_0", -"order": 0, -"room_area": 64.0, -"room_type": 300, +"name": "enterance_0", +"order": 4, +"room_area": 16.0, +"room_type": 304, "type": "room", "window": true }, { @@ -491,6 +483,144 @@ entries=[ { "children": [ ], "commands": [ [ 5, [ ] ] ], "index": 54, +"name": "wc_0", +"order": 1, +"room_area": 16.0, +"room_type": 200, +"type": "room", +"window": false +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 55, +"name": "bedroom_0", +"order": 2, +"room_area": 36.0, +"room_type": 202, +"type": "room", +"window": true +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 56, +"name": "living_room_0", +"order": 0, +"room_area": 36.0, +"room_type": 300, +"type": "room", +"window": true +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 57, +"name": "kitchen_0", +"order": 1, +"room_area": 16.0, +"room_type": 302, +"type": "room", +"window": true +}, { +"children": [ 71, 72 ], +"commands": [ ], +"floor_index": 1, +"index": 58, +"name": "floor_0", +"order": 0, +"type": "floor" +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 59, +"name": "wc_0", +"order": 0, +"room_area": 16.0, +"room_type": 200, +"type": "room", +"window": false +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 60, +"name": "bathroom_0", +"order": 1, +"room_area": 16.0, +"room_type": 201, +"type": "room", +"window": false +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 61, +"name": "bedroom_0", +"order": 2, +"room_area": 64.0, +"room_type": 202, +"type": "room", +"window": true +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 62, +"name": "kitchen_0", +"order": 0, +"room_area": 16.0, +"room_type": 302, +"type": "room", +"window": true +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 63, +"name": "living_room_0", +"order": 1, +"room_area": 144.0, +"room_type": 300, +"type": "room", +"window": true +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 64, +"name": "bathroom_0", +"order": 0, +"room_area": 16.0, +"room_type": 201, +"type": "room", +"window": false +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 65, +"name": "kitchen_0", +"order": 0, +"room_area": 64.0, +"room_type": 302, +"type": "room", +"window": true +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 66, +"name": "living_room_0", +"order": 1, +"room_area": 64.0, +"room_type": 300, +"type": "room", +"window": true +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 67, +"name": "wc_0", +"order": 0, +"room_area": 16.0, +"room_type": 200, +"type": "room", +"window": false +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 68, "name": "living_room_0", "order": 0, "room_area": 64.0, @@ -498,24 +628,44 @@ entries=[ { "type": "room", "window": true }, { -"children": [ 57 ], +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 69, +"name": "bathroom_0", +"order": 0, +"room_area": 16.0, +"room_type": 201, +"type": "room", +"window": false +}, { +"children": [ ], +"commands": [ [ 5, [ ] ] ], +"index": 70, +"name": "living_room_0", +"order": 0, +"room_area": 64.0, +"room_type": 300, +"type": "room", +"window": true +}, { +"children": [ 73 ], "commands": [ ], -"index": 55, +"index": 71, "name": "zone_0", "order": 0, "type": "zone", "zone_type": 1 }, { -"children": [ 58, 59 ], +"children": [ 74, 75 ], "commands": [ ], -"index": 56, +"index": 72, "name": "unit_0", "order": 0, "type": "unit" }, { "children": [ ], "commands": [ ], -"index": 57, +"index": 73, "name": "storage_room_0", "order": 0, "room_area": 64.0, @@ -523,17 +673,17 @@ entries=[ { "type": "room", "window": true }, { -"children": [ 60 ], +"children": [ 76 ], "commands": [ ], -"index": 58, +"index": 74, "name": "zone_0", "order": 0, "type": "zone", "zone_type": 0 }, { -"children": [ 61 ], +"children": [ 77 ], "commands": [ ], -"index": 59, +"index": 75, "name": "zone_1", "order": 0, "type": "zone", @@ -541,7 +691,7 @@ entries=[ { }, { "children": [ ], "commands": [ ], -"index": 60, +"index": 76, "name": "wc_0", "order": 0, "room_area": 16.0, @@ -551,7 +701,7 @@ entries=[ { }, { "children": [ ], "commands": [ ], -"index": 61, +"index": 77, "name": "living_room_0", "order": 0, "room_area": 16.0, diff --git a/src/modules/stream/ui/building_layout_graph.h b/src/modules/stream/ui/building_layout_graph.h index f8eb2db..8ba7567 100644 --- a/src/modules/stream/ui/building_layout_graph.h +++ b/src/modules/stream/ui/building_layout_graph.h @@ -57,10 +57,17 @@ public: void queue_grow_cell(flecs::entity seed_e, int id); void growth_module(flecs::world &ecs, const String &module_name); + void room_growth_module(flecs::world &ecs, + const String &module_name); void create_floor_components( flecs::entity floor_e, flecs::entity base_floor_e, const WorldEditor::components::buildings_layout_grid_size &size); + void create_region(flecs::entity floor_e, flecs::entity seed_e, + flecs::entity region_e, + const Vector2i &position, float area); + bool check_region(flecs::entity floor_e, int index, + const Rect2i &rect); graph_module(flecs::world &ecs); }; }; \ No newline at end of file diff --git a/src/modules/stream/ui/building_layout_graph_ui.cpp b/src/modules/stream/ui/building_layout_graph_ui.cpp index a79bafa..97ac397 100644 --- a/src/modules/stream/ui/building_layout_graph_ui.cpp +++ b/src/modules/stream/ui/building_layout_graph_ui.cpp @@ -868,7 +868,48 @@ void BuildingLayoutGraphUI::draw_2d_grid_view(Control *draw) Math::randf(), 1); draw->draw_rect(Rect2(dx, dy, dsize, dsize), - colors[cell.type]); + colors[cell.type], true); + draw->draw_rect(Rect2(dx, dy, dsize, dsize), + Color(0.1f, 0.1f, 0.1f, 1.0f), + false); + if (fc.has()) + draw->draw_circle( + Vector2(dx + 0.5f * dsize, + dy + 0.5f * dsize), + dsize * 0.4f, + Color(0, 0, 1, 1)); + if (fc.has()) + draw->draw_circle( + Vector2(dx + 0.5f * dsize, + dy + 0.5f * dsize), + dsize * 0.2f, + Color(1, 0.3, 1, 1)); + int mcount = 0; + fc.each([dx, dy, dsize, + &mcount, &draw, + &colors]( + flecs::entity + re) { + assert(mcount == 0); + if (!colors.has(String(re.path()))) { + colors[String(re.path())] = + Color(Math::randf(), + Math::randf(), + Math::randf(), + 1.0f); + draw->draw_rect( + Rect2(dx + 0.1f * dsize, + dy + 0.1f * dsize, + 0.8f * dsize, + 0.8f * dsize), + colors[String( + re.path())], + true); + } + mcount++; + }); print_line( "draw cell: (" + itos(x) + ", " + itos(y) + ") (" + String::num(dx) + diff --git a/src/modules/stream/ui/graph_module.cpp b/src/modules/stream/ui/graph_module.cpp index 8d717aa..73a2cd2 100644 --- a/src/modules/stream/ui/graph_module.cpp +++ b/src/modules/stream/ui/graph_module.cpp @@ -3,6 +3,7 @@ #include "world_editor.h" #include "editor_event.h" #include "building_layout_graph.h" +#include "graph_module.h" #define MIN_ROOM_SIZE 16 /* 4 * 4 tiles */ @@ -265,584 +266,73 @@ void BuildingLayoutGraph::graph_module::queue_grow_cell(flecs::entity seed_e, WorldEditor::components::buildings_layout_grid_queue>(); } } - -void BuildingLayoutGraph::graph_module::growth_module(flecs::world &ecs, - const String &module_name) -{ - struct growth_regions { - struct region { - Rect2i rect; - bool can_grow_square; - }; - HashMap regions; - }; - ecs.component(); - flecs::entity GraphFilter = ecs.entity("GraphFilter") - .add(flecs::Phase) - .depends_on(flecs::OnUpdate); - GraphFilter.disable(); - - struct make_random { - int seed; - int next; - make_random(int seed) - : seed(seed) - , next(seed) - { - } - int get() - { - next = next * 1103515245 + 12345; - return (int)((unsigned)next >> 16) % 32768; - } - }; - struct grid_calc { - int grid_size; - int index2x(int index) - { - return index % grid_size; - } - int index2y(int index) - { - return index / grid_size; - } - void get_cadidates(int index, int *candidates) - { - int i, j, idx = 0; - int x = index2x(index); - int y = index2y(index); - - for (i = -1; i < 2; i++) { - for (j = -1; j < 2; j++) - if (i != 0 || j != 0) { - int cx = x + i, cy = y + j; - if (cx >= 0 && cx < grid_size && - cy >= 0 && cy < grid_size) { - int id = cx + - grid_size * cy; - candidates[idx++] = id; - } - } - } - } - grid_calc(flecs::entity layout_e) - : grid_size( - layout_e.get() - ->grid_size) - { - assert(layout_e.is_valid()); - } - grid_calc(int grid_size) - : grid_size(grid_size) - { - assert(grid_size > 0); - } - }; - struct grid_misc { - LocalVector > positions; - LocalVector accepted; - struct make_random r; - grid_misc() - : r(100) - { - } - void get_dim_candidates(const Vector2i &base, int dim, - Vector2i *candidates) - { - int i; - std::vector candidates_data = { - /* clang-format off */ - { base.x + dim, base.y }, - { base.x + dim, base.y + dim }, - { base.x, base.y + dim }, - { base.x - dim, base.y + dim }, - { base.x - dim, base.y }, - { base.x - dim, base.y - dim }, - { base.x, base.y - dim }, - { base.x + dim, base.y - dim }, - /* clang-format on */ - }; - for (i = 0; i < (int)candidates_data.size(); i++) - candidates[i] = candidates_data[i]; - } - void setup_floor( - flecs::entity grid_e, flecs::entity_t base_et, - const WorldEditor::components::buildings_layout_grid_size - &size, - BuildingLayoutGraph::graph_module *obj) - { - int i, j; - flecs::entity base_floor_e = - grid_e.world().entity(base_et); - flecs::log::warn("base_floor: %s", - base_floor_e.path().c_str()); - int floor_index = - base_floor_e - .get() - ->index; - String floor_name = "floor_" + itos(floor_index); - flecs::entity floor_e = - grid_e.lookup(floor_name.ascii().ptr()); - print_line("grid: " + String(grid_e.path())); - print_line("floor: " + floor_name); - assert(floor_e.is_valid()); - assert(grid_e.is_valid()); - obj->create_floor_components(floor_e, base_floor_e, - size); - flecs::log::warn("grid floor: %s", - floor_e.path().c_str()); - for (i = 0; i < (int)positions.size(); i++) - for (j = 0; j < (int)positions.size(); j++) { - if (i == j) - continue; - if (positions[i].second == - positions[j].second) - flecs::log::err( - "duplicate positions"); - assert(positions[i].second != - positions[j].second); - } - for (i = 0; i < (int)positions.size(); i++) { - int cell_id = - positions[i].second.x + - size.grid_size * positions[i].second.y; - flecs::entity region_e = grid_e.world().entity( - positions[i].first); - assert(region_e.is_valid()); - assert(floor_e.is_valid()); - flecs::entity cell_e = obj->create_cell( - floor_e, region_e, cell_id); - assert(cell_e.is_valid()); - flecs::log::warn("grid cell: %s", - cell_e.path().c_str()); - } - } - float get_entity_area(flecs::entity e) const - { - return e.get() - ->area; - } - float get_entity_area(flecs::world &&ecs, - flecs::entity_t et) const - { - flecs::entity e = ecs.entity(et); - return e.get() - ->area; - } - float get_entity_area(flecs::world &ecs, - flecs::entity_t et) const - { - flecs::entity e = ecs.entity(et); - return e.get() - ->area; - } - int get_base_radius(flecs::world &&ecs, - flecs::entity_t et) const - { - float base_area = get_entity_area(ecs, et); - int base_radius = - (int)((Math::sqrt(base_area) * 1.5f) / 2.0f) / - 4; /* grid conversion */ - return base_radius; - } - int get_base_radius(flecs::world &ecs, flecs::entity_t et) const - { - float base_area = get_entity_area(ecs, et); - int base_radius = - (int)((Math::sqrt(base_area) * 1.5f) / 2.0f) / - 4; /* grid conversion */ - return base_radius; - } - int distance_squared(const Vector2i &p1, - const Vector2i &p2) const - { - int lx = p2.x - p1.x; - int ly = p2.y - p1.y; - return lx * lx + ly * ly; - } - bool check_candidates_tolerance(const Vector2i check) - { - int i; - bool ret = true; - for (i = 0; i < (int)positions.size(); i++) - if (distance_squared(check, - positions[i].second) < 1) { - ret = false; - break; - } - return ret; - } - void filter_candidates( - flecs::entity ce, float area, - const WorldEditor::components::buildings_layout_grid_size - &size) - { - int which = which_position(); - if (positions.empty()) { - /* starting at grid center */ - Vector2i start_pos(size.grid_size / 2, - size.grid_size / 2); - positions.push_back( - Pair( - ce.id(), start_pos)); - return; - } - while (1) { - int j, k; - const Vector2i &base = positions[which].second; - flecs::entity_t base_et = - positions[which].first; - int base_radius = - get_base_radius(ce.world(), base_et); - base_radius = MAX(1, base_radius); - int local_radius = - (int)((Math::sqrt(area) * 1.5f) / - 2.0f) / - 4; /* grid conversion */ - local_radius = MAX(1, local_radius); - int dim = base_radius + local_radius; - dim = MAX(1, dim); - /* grid coordinates */ - Vector2i candidates[8]; - get_dim_candidates(base, dim, &candidates[0]); - for (j = 0; j < (int)(sizeof(candidates) / - sizeof(candidates[0])); - j++) { - print_line("candidate: " + - itos(candidates[j].x) + - ", " + - itos(candidates[j].y)); - if (candidates[j].x < 0 || - candidates[j].x >= size.grid_size) - continue; - if (candidates[j].y < 0 || - candidates[j].y >= size.grid_size) - continue; - if (!check_candidates_tolerance( - candidates[j])) - continue; - for (k = 0; k < (int)positions.size(); - k++) { - assert(k < - (int)positions.size()); - flecs::entity_t pbase_et = - positions[k].first; - float parea = get_entity_area( - ce.world(), pbase_et); - int pdim = - (int)((Math::sqrt( - parea) * - 1.5f) / - 2.0f) / - 4; /* radius converted to grid 4x4*/ - int radius = - pdim + local_radius; - int radius_sq = radius * radius; - if (distance_squared( - positions[k].second, - candidates[j]) < - radius_sq) - continue; - assert(check_candidates_tolerance( - candidates[j])); - accepted.push_back( - candidates[j]); - } - } - if (accepted.size() == 0) { - which = which_position(); - print_line( - "reset choice: " + itos(which) + - " " + itos(positions.size())); - continue; - } - assert(accepted.size() > 0); - const Vector2i &selected = - accepted[which_selected()]; - assert(check_candidates_tolerance(selected)); - Pair m(ce.id(), - selected); - positions.push_back(m); - flecs::log::warn("add position: %d, %d", - selected.x, selected.y); - accepted.clear(); - break; - } - } - int which_position() - { - int which; - if (positions.size() == 0) - return -1; - which = r.get() % positions.size(); - assert(which < (int)positions.size()); - return which; - } - int which_selected() - { - int which; - if (accepted.size() == 0) - return -1; - which = r.get() % accepted.size(); - assert(which < (int)accepted.size()); - return which; - } - }; - - ecs.system( - "CreateGrid") - .kind(GraphSolve) - .write() - .each([this](flecs::iter &it, size_t count, - const WorldEditor::components:: - buildings_layout_grid_size &size) { - struct grid_misc grid; - flecs::entity graph_e = it.entity(count); - graph_e.world().defer_suspend(); - flecs::log::warn("creating grid for: %s", - graph_e.path().c_str()); - flecs::entity grid_base_e = get_layout_grid_base(); - flecs::entity grid_e = - grid_base_e.lookup(graph_e.name()); - assert(grid_e.is_valid()); - flecs::log::warn("creating grid for: %s: %s", - graph_e.path().c_str(), - grid_e.path().c_str()); - /* starting at grid center */ - const List >::Element *me = - size.floors.front(); - while (me) { - flecs::query - q = graph_e.world() - .query_builder< - const WorldEditor::components:: - buildings_layout_area>() - .with(flecs::ChildOf, - me->get().second) - .scope_open() - .with() - .or_() - .with() - .scope_close() - .build(); - q.each([size, - &grid](flecs::entity ce, - const WorldEditor::components:: - buildings_layout_area - &area) { - flecs::log::warn( - "generating positions for: %s", - ce.path().c_str()); - grid.filter_candidates(ce, area.area, - size); - }); - grid.setup_floor(grid_e, me->get().second, size, - this); - - me = me->next(); - } - graph_e.world().defer_resume(); - }); - ecs.system("AssembleSkeletonEnd") - .kind(GraphSolve) - .run([module_name](flecs::iter &it) { - print_line("Assembling skeleton done..."); - it.world() - .lookup((module_name + "::GraphSolveZones") - .ascii() - .ptr()) - .disable(); - it.world() - .lookup((module_name + "::GraphFilter") - .ascii() - .ptr()) - .enable(); - EditorEvent::get_singleton()->event.emit( - "update_layout_view", varray()); - // assert(false); - }); - - ecs.system( - "GrowFloorRegions") - .kind(0) - .each([this](flecs::entity floor_e, - WorldEditor::components::buildings_layout_grid_floor - &fl) { - /* TODO: limit growth per region too */ - struct grid_calc grid(fl.grid_size); - if (fl.size_left <= 0) - return; - flecs::query - q = floor_e.world() - .query_builder< - const WorldEditor::components:: - buildings_layout_grid_cell>() - .with(flecs::ChildOf, floor_e) - .without() - .build(); - q.each([this, &floor_e, &fl, - &grid](flecs::entity et, - const WorldEditor::components:: - buildings_layout_grid_cell &cell) { - int index = cell.index; - assert(et.is_valid()); - int i; - LocalVector candidates; - candidates.resize(8); - grid.get_cadidates(cell.index, - candidates.ptr()); - int extended = 0; - for (i = 0; i < (int)candidates.size(); i++) { - if (have_cell(floor_e, candidates[i])) - continue; - queue_grow_cell(et, candidates[i]); - extended++; - } - if (extended == 0) - et.add(); - print_line("size: " + itos(grid.grid_size) + - " index: " + itos(index)); - }); - }); - ecs.system( - "GrowRegions") - .kind(0) - .without() - .read() - .write() - .write() - .write() - .each([this](flecs::entity e, - const WorldEditor::components:: - buildings_layout_grid_cell &cell) { - e.world().defer_suspend(); - int index = cell.index; - assert(e.is_valid()); - flecs::entity floor_e = e.parent(); - assert(floor_e.is_valid()); - - flecs::entity grid_e = floor_e.parent(); - assert(grid_e.is_valid()); - String layout_name(grid_e.name()); - flecs::entity base_e = get_layout_base(); - flecs::entity layout_e = - base_e.lookup(layout_name.ascii().ptr()); - assert(layout_e.is_valid()); - struct grid_calc grid(layout_e); - int i; - LocalVector candidates; - candidates.resize(8); - grid.get_cadidates(cell.index, candidates.ptr()); - int extended = 0; - for (i = 0; i < (int)candidates.size(); i++) { - if (have_cell(floor_e, candidates[i])) - continue; - queue_grow_cell(e, candidates[i]); -#if 0 - String c_name = "cell_" + itos(candidates[i]); - flecs::entity c_e = - e.parent().lookup(c_name.ascii().ptr()); -#endif - extended++; - } - if (extended == 0) - e.add(); - print_line("size: " + itos(grid.grid_size) + - " index: " + itos(index)); - e.world().defer_resume(); - }); - ecs.system( - "GrowCommitQueue") - .kind(0) - .each([this](flecs::entity e, - WorldEditor::components::buildings_layout_grid_floor - &fl, - WorldEditor::components::buildings_layout_grid_queue - &queue) { - List >::Element *me = - queue.queue.front(); - while (me) { - flecs::entity seed_e = - e.world().entity(me->get().first); - int id = me->get().second; - if (!fl.cells.has(id)) { - grow_cell(seed_e, id); - fl.cells.insert(id); - } - me = me->next(); - } - queue.queue.clear(); - e.remove(); - }); - ecs.system("RunGrow").kind(GraphFilter).run([module_name](flecs::iter &it) { - int i; - it.world().defer_suspend(); - print_line("Running grow..."); - for (i = 0; i < 10; i++) { - it.world() - .system(it.world().lookup( - (module_name + "::GrowFloorRegions") - .ascii() - .ptr())) - .run(); - it.world() - .system(it.world().lookup( - (module_name + "::GrowCommitQueue") - .ascii() - .ptr())) - .run(); - } - it.world().defer_resume(); - flecs::query - q = it.world() - .query_builder< - const WorldEditor::components:: - buildings_layout_grid_floor>() - .build(); - int count_left = 0; - int count_run = 0; - q.each([&count_left, - &count_run](flecs::entity e, - const WorldEditor::components:: - buildings_layout_grid_floor &fl) { - count_run++; - count_left += MAX(0, fl.size_left); - }); - // assert(false); - if (count_run > 0 && count_left <= 0) { - it.world() - .lookup((module_name + "::GraphFilter") - .ascii() - .ptr()) - .disable(); - print_line("Grow done"); - } - }); -} - void BuildingLayoutGraph::graph_module::create_floor_components( flecs::entity floor_e, flecs::entity base_floor_e, const WorldEditor::components::buildings_layout_grid_size &size) { floor_e.set( { Set(), size.grid_size, size.growth_size }); + floor_e.set( + { Vector() }); floor_e.add(base_floor_e); } +void BuildingLayoutGraph::graph_module::create_region(flecs::entity floor_e, + flecs::entity seed_e, + flecs::entity region_e, + const Vector2i &position, + float area) +{ + int i; + struct growth_regions::region r; + r.seed_et = seed_e.id(); + r.region_et = region_e.id(); + r.rect.position = position; + r.rect.size = Vector2i(1, 1); + r.remains_area = MAX((int)(area * 2.0f) / 16 + 1, 16); + r.can_grow_square = true; + bool ok = true; + assert(check_region(floor_e, -1, r.rect)); + const struct growth_regions *reg = floor_e.get(); + for (i = 0; i < reg->regions.size(); i++) { + if (reg->regions[i].region_et == r.region_et) { + ok = false; + break; + } + if (reg->regions[i].rect.position == r.rect.position) { + ok = false; + break; + } + } + r.remains_area -= 1; + assert(ok); + if (ok) { + floor_e.get_mut()->regions.push_back(r); + floor_e.modified(); + flecs::log::warn("region created for %s", + region_e.path().c_str()); + } +} + +bool BuildingLayoutGraph::graph_module::check_region(flecs::entity floor_e, + int index, + const Rect2i &rect) +{ + const Vector ®ions = + floor_e.get()->regions; + int i; + bool ret = true; + for (i = 0; i < regions.size(); i++) { + if (i == index) + continue; + if (rect.intersects(regions[i].rect)) { + ret = false; + break; + } + } + return ret; +} + BuildingLayoutGraph::graph_module::graph_module(flecs::world &ecs) { ecs.module(); @@ -893,6 +383,11 @@ BuildingLayoutGraph::graph_module::graph_module(flecs::world &ecs) GraphSolve = ecs.entity("GraphSolve") .add(flecs::Phase) .depends_on(GraphSolveFloors); + flecs::entity GraphAssembleSkeleton = + ecs.entity("GraphAssembleSkeleton") + .add(flecs::Phase) + .depends_on(GraphSolve); + GraphAssembleSkeleton.disable(); flecs::entity GraphPostSolve = ecs.entity("GraphPostSolve") .add(flecs::Phase) .depends_on(flecs::PostUpdate); @@ -1060,9 +555,36 @@ BuildingLayoutGraph::graph_module::graph_module(flecs::world &ecs) buildings_layout_area &area) { if (area.area < MIN_ROOM_SIZE) { badness++; - assert(false); } }); + if (badness == 0) { + it.world() + .lookup((module_name + + "::GraphSolveUnits") + .ascii() + .ptr()) + .enable(); + it.world() + .lookup((module_name + + "::GraphSolveFloors") + .ascii() + .ptr()) + .enable(); + } else { + it.world() + .lookup((module_name + + "::GraphSolveUnits") + .ascii() + .ptr()) + .disable(); + it.world() + .lookup((module_name + + "::GraphSolveFloors") + .ascii() + .ptr()) + .disable(); + } + print_line("Zones processing done..."); }); ecs.system("UnitsStart") @@ -1279,10 +801,19 @@ BuildingLayoutGraph::graph_module::graph_module(flecs::world &ecs) buildings_layout_area &area) { if (area.area < MIN_ROOM_SIZE) { badness++; - assert(false); } }); - print_line("Floor processing done..."); + if (badness == 0) { + flecs::world w = it.world(); + // create indices + w.lookup((module_name + + "::GraphAssembleSkeleton") + .ascii() + .ptr()) + .enable(); + print_line("Floor processing done..."); + } else + print_line("Still processing..."); }); #if 0 ecs.system( "AssembleSkeleton") - .kind(GraphSolve) + .kind(GraphAssembleSkeleton) .read() .read() .read() @@ -1486,7 +1017,7 @@ BuildingLayoutGraph::graph_module::graph_module(flecs::world &ecs) &area) { int grid_size = (int)(Math::sqrt(area.area) * 1.5) + 1; int growth_size = - (int)Math::ceil(area.area * 2.0f / 4.0f) + 1; + (int)Math::ceil(area.area * 1.3f / 16.0f) + 1; flecs::entity graph_e = it.entity(count); List queue; queue.push_back(graph_e); @@ -1608,6 +1139,7 @@ BuildingLayoutGraph::graph_module::graph_module(flecs::world &ecs) it.world().defer_resume(); }); growth_module(ecs, module_name); + room_growth_module(ecs, module_name); #if 0 ecs.system( "AssignGridSizeToFloor") diff --git a/src/modules/stream/ui/graph_module.h b/src/modules/stream/ui/graph_module.h new file mode 100644 index 0000000..8fe3e0e --- /dev/null +++ b/src/modules/stream/ui/graph_module.h @@ -0,0 +1,28 @@ +#ifndef GRAPH_MODULE_H_ +#define GRAPH_MODULE_H_ +struct growth_regions { + struct region { + flecs::entity_t seed_et; + flecs::entity_t region_et; + Rect2i rect; + int remains_area; + bool can_grow_square; + }; + Vector regions; +}; + +struct make_random { + int seed; + int next; + make_random(int seed) + : seed(seed) + , next(seed) + { + } + int get() + { + next = next * 1103515245 + 12345; + return (int)((unsigned)next >> 16) % 32768; + } +}; +#endif \ No newline at end of file diff --git a/src/modules/stream/ui/graph_module_growth.cpp b/src/modules/stream/ui/graph_module_growth.cpp new file mode 100644 index 0000000..ebdf02e --- /dev/null +++ b/src/modules/stream/ui/graph_module_growth.cpp @@ -0,0 +1,790 @@ +#include "base_data.h" +#include "editor_event.h" +#include "world_editor.h" +#include "building_layout_graph.h" +#include "graph_module.h" + +void BuildingLayoutGraph::graph_module::growth_module(flecs::world &ecs, + const String &module_name) +{ + ecs.component(); + flecs::entity GraphFilter = ecs.entity("GraphFilter") + .add(flecs::Phase) + .depends_on(flecs::OnUpdate); + GraphFilter.disable(); + + struct grid_calc { + int grid_size; + int index2x(int index) + { + return index % grid_size; + } + int index2y(int index) + { + return index / grid_size; + } + void get_cadidates(int index, int *candidates) + { + int i, j, idx = 0; + int x = index2x(index); + int y = index2y(index); + + for (i = -1; i < 2; i++) { + for (j = -1; j < 2; j++) + if (i != 0 || j != 0) { + int cx = x + i, cy = y + j; + if (cx >= 0 && cx < grid_size && + cy >= 0 && cy < grid_size) { + int id = cx + + grid_size * cy; + candidates[idx++] = id; + } + } + } + } + grid_calc(flecs::entity layout_e) + : grid_size( + layout_e.get() + ->grid_size) + { + assert(layout_e.is_valid()); + } + grid_calc(int grid_size) + : grid_size(grid_size) + { + assert(grid_size > 0); + } + }; + struct grid_misc { + LocalVector > positions; + LocalVector accepted; + struct make_random r; + grid_misc() + : r(100) + { + } + void get_dim_candidates(const Vector2i &base, int dim, + Vector2i *candidates) + { + int i; + std::vector candidates_data = { + /* clang-format off */ + { base.x + dim, base.y }, + { base.x, base.y + dim }, + { base.x - dim, base.y }, + { base.x, base.y - dim }, + { base.x + dim, base.y }, + { base.x, base.y + dim }, + { base.x - dim, base.y }, + { base.x, base.y - dim }, + /* clang-format on */ + }; + for (i = 0; i < (int)candidates_data.size(); i++) + candidates[i] = candidates_data[i]; + } + void setup_floor( + flecs::entity grid_e, flecs::entity_t base_et, + const WorldEditor::components::buildings_layout_grid_size + &size, + BuildingLayoutGraph::graph_module *obj) + { + int i, j; + flecs::entity base_floor_e = + grid_e.world().entity(base_et); + flecs::log::warn("base_floor: %s", + base_floor_e.path().c_str()); + int floor_index = + base_floor_e + .get() + ->index; + String floor_name = "floor_" + itos(floor_index); + flecs::entity floor_e = + grid_e.lookup(floor_name.ascii().ptr()); + print_line("grid: " + String(grid_e.path())); + print_line("floor: " + floor_name); + assert(floor_e.is_valid()); + assert(grid_e.is_valid()); + obj->create_floor_components(floor_e, base_floor_e, + size); + flecs::log::warn("grid floor: %s", + floor_e.path().c_str()); + for (i = 0; i < (int)positions.size(); i++) + for (j = 0; j < (int)positions.size(); j++) { + if (i == j) + continue; + if (positions[i].second == + positions[j].second) + flecs::log::err( + "duplicate positions"); + assert(positions[i].second != + positions[j].second); + } + for (i = 0; i < (int)positions.size(); i++) { + int cell_id = + positions[i].second.x + + size.grid_size * positions[i].second.y; + flecs::entity region_e = grid_e.world().entity( + positions[i].first); + float area = + region_e.get() + ->area; + assert(region_e.is_valid()); + assert(floor_e.is_valid()); + Rect2i check; + check.position = positions[i].second; + check.size = Vector2i(1, 1); + assert(obj->check_region(floor_e, -1, check)); + flecs::entity cell_e = obj->create_cell( + floor_e, region_e, cell_id); + assert(cell_e.is_valid()); + obj->create_region(floor_e, cell_e, region_e, + positions[i].second, area); + flecs::log::warn("grid cell: %s", + cell_e.path().c_str()); + } + } + float get_entity_area(flecs::entity e) const + { + return e.get() + ->area; + } + float get_entity_area(flecs::world &&ecs, + flecs::entity_t et) const + { + flecs::entity e = ecs.entity(et); + return e.get() + ->area; + } + float get_entity_area(flecs::world &ecs, + flecs::entity_t et) const + { + flecs::entity e = ecs.entity(et); + return e.get() + ->area; + } + int get_base_radius(flecs::world &&ecs, + flecs::entity_t et) const + { + float base_area = get_entity_area(ecs, et); + int base_radius = + (int)((Math::sqrt(base_area) * 1.6f) / 2.0f) / + 4; /* grid conversion */ + return base_radius; + } + int get_base_radius(flecs::world &ecs, flecs::entity_t et) const + { + float base_area = get_entity_area(ecs, et); + int base_radius = + (int)((Math::sqrt(base_area) * 1.6f) / 2.0f) / + 4; /* grid conversion */ + return base_radius; + } + int distance_squared(const Vector2i &p1, + const Vector2i &p2) const + { + int lx = p2.x - p1.x; + int ly = p2.y - p1.y; + return lx * lx + ly * ly; + } + bool check_candidates_tolerance(const Vector2i check) + { + int i; + bool ret = true; + for (i = 0; i < (int)positions.size(); i++) + if (distance_squared(check, + positions[i].second) < 1) { + ret = false; + break; + } + return ret; + } + bool check_candidate(const Vector2i &candidate) + { + int m; + bool bad = false; + for (m = 0; m < (int)positions.size(); m++) { + Rect2i check1, check2; + check1.position = positions[m].second; + check1.size = Vector2i(1, 1); + check2.position = candidate; + check2.size = Vector2i(1, 1); + if (check1.intersects(check2)) { + bad = true; + break; + } + } + return !bad; + } + bool accept_candidate(flecs::entity ce, + const Vector2i &candidate, float area) + { + int k; + int local_radius = + (int)((Math::sqrt(area) * 1.6f) / 2.0f) / + 4; /* grid conversion */ + local_radius = MAX(1, local_radius); + bool ok = false; + for (k = 0; k < (int)positions.size(); k++) { + assert(k < (int)positions.size()); + flecs::entity_t pbase_et = positions[k].first; + float parea = + get_entity_area(ce.world(), pbase_et); + int pdim = (int)((Math::sqrt(parea) * 1.5f) / + 2.0f) / + 4; /* radius converted to grid 4x4*/ + int radius = pdim + local_radius; + int radius_sq = radius * radius; + if (distance_squared(positions[k].second, + candidate) < radius_sq) + continue; + assert(check_candidates_tolerance(candidate)); + accepted.push_back(candidate); + ok = true; + } + return ok; + } + bool process_candidates( + flecs::entity ce, const Vector2i &base, float area, + const WorldEditor::components::buildings_layout_grid_size + &size, + flecs::entity_t base_et, int dim) + { + int j; + int base_radius = get_base_radius(ce.world(), base_et); + base_radius = MAX(1, base_radius); + int local_radius = + (int)((Math::sqrt(area) * 1.6f) / 2.0f) / + 4; /* grid conversion */ + local_radius = MAX(1, local_radius); + // int dim = base_radius + local_radius; + // dim = MAX(4, dim); + int md = 1; + if (size.grid_size > 30) + md = 8; + else if (size.grid_size > 20) + md = 6; + else if (size.grid_size > 15) + md = 4; + else if (size.grid_size > 10) + md = 2; + Rect2i clip(md, md, size.grid_size - md * 2, + size.grid_size - md * 2); + Vector2i candidates[8]; + get_dim_candidates(base, dim, &candidates[0]); + for (j = 0; j < (int)(sizeof(candidates) / + sizeof(candidates[0])); + j++) { + print_line("base: " + itos(base.x) + ", " + + itos(base.y)); + print_line("possible candidate: " + + itos(candidates[j].x) + ", " + + itos(candidates[j].y)); + if (!clip.has_point(candidates[j])) { + print_line("clipped by grid field"); + print_line(clip.operator String()); + continue; + } + if (!check_candidates_tolerance( + candidates[j])) { + print_line( + "too close to existing positions"); + continue; + } + if (!check_candidate(candidates[j])) { + print_line( + "too close to existing positions (rect)"); + continue; + } + print_line("valid candidate: " + + itos(candidates[j].x) + ", " + + itos(candidates[j].y)); + accept_candidate(ce, candidates[j], area); + } + if (accepted.size() > 0) + return true; + else + return false; + } + void filter_candidates( + flecs::entity ce, float area, + const WorldEditor::components::buildings_layout_grid_size + &size) + { + if (positions.empty()) { + /* starting at grid center */ + Vector2i start_pos(size.grid_size / 2, + size.grid_size / 2); + positions.push_back( + Pair( + ce.id(), start_pos)); + return; + } + while (1) { + int which = which_position(); + const Vector2i &base = positions[which].second; + flecs::entity_t base_et = + positions[which].first; + int base_radius = + get_base_radius(ce.world(), base_et); + base_radius = MAX(1, base_radius); + int local_radius = + (int)((Math::sqrt(area) * 1.6f) / + 2.0f) / + 4; /* grid conversion */ + local_radius = MAX(1, local_radius); + int dim = base_radius + local_radius; + dim = MAX(1, dim); + process_candidates(ce, base, area, size, + base_et, dim); + if (accepted.size() == 0) { + assert(positions.size() > 0); + continue; + } + assert(accepted.size() > 0); + const Vector2i &selected = + accepted[which_selected()]; + assert(check_candidates_tolerance(selected)); + Pair m(ce.id(), + selected); + check_candidate(selected); + positions.push_back(m); + flecs::log::warn("add position: %d, %d", + selected.x, selected.y); + accepted.clear(); + break; + } + } + int which_position() + { + int which; + if (positions.size() == 0) + return -1; + which = r.get() % positions.size(); + assert(which < (int)positions.size()); + return which; + } + int which_selected() + { + int which; + if (accepted.size() == 0) + return -1; + which = r.get() % accepted.size(); + assert(which < (int)accepted.size()); + return which; + } + }; + + ecs.system( + "CreateGrid") + .kind(GraphSolve) + .write() + .each([this](flecs::iter &it, size_t count, + const WorldEditor::components:: + buildings_layout_grid_size &size) { + struct grid_misc grid; + flecs::entity graph_e = it.entity(count); + graph_e.world().defer_suspend(); + flecs::log::warn("creating grid for: %s", + graph_e.path().c_str()); + flecs::entity grid_base_e = get_layout_grid_base(); + flecs::entity grid_e = + grid_base_e.lookup(graph_e.name()); + assert(grid_e.is_valid()); + flecs::log::warn("creating grid for: %s: %s", + graph_e.path().c_str(), + grid_e.path().c_str()); + /* starting at grid center */ + const List >::Element *me = + size.floors.front(); + while (me) { + flecs::query + q = graph_e.world() + .query_builder< + const WorldEditor::components:: + buildings_layout_area>() + .with(flecs::ChildOf, + me->get().second) + .scope_open() + .with() + .or_() + .with() + .scope_close() + .build(); + q.each([size, + &grid](flecs::entity ce, + const WorldEditor::components:: + buildings_layout_area + &area) { + flecs::log::warn( + "generating positions for: %s", + ce.path().c_str()); + grid.filter_candidates(ce, area.area, + size); + }); + grid.setup_floor(grid_e, me->get().second, size, + this); + + me = me->next(); + } + graph_e.world().defer_resume(); + }); + ecs.system("AssembleSkeletonEnd") + .kind(GraphSolve) + .run([module_name](flecs::iter &it) { + print_line("Assembling skeleton done..."); + it.world() + .lookup((module_name + "::GraphSolveZones") + .ascii() + .ptr()) + .disable(); + it.world() + .lookup((module_name + "::GraphFilter") + .ascii() + .ptr()) + .enable(); + EditorEvent::get_singleton()->event.emit( + "update_layout_view", varray()); + // assert(false); + }); + + ecs.system("GrowFloorRectRegions") + .kind(0) + .each([this](flecs::entity floor_e, + WorldEditor::components::buildings_layout_grid_floor + &fl, + growth_regions &g) { + int i; + bool grown = true; + int state = 0; + while (1) { + while (grown) { + grown = false; + for (i = 0; i < g.regions.size(); i++) { + Rect2i mrect = + g.regions[i].rect; + if (state == 0) { + if (!g.regions[i] + .can_grow_square) + continue; + if (g.regions[i] + .remains_area <= + 0) { + g.regions + .write[i] + .can_grow_square = + false; + continue; + } + } + mrect = g.regions[i].rect; + int old_area = mrect.get_area(); + if (state == 0) { + mrect = g.regions[i] + .rect; + + assert(check_region( + floor_e, i, + mrect)); + mrect = mrect.grow(1); + Rect2i clip( + 0, 0, + fl.grid_size, + fl.grid_size); + if (!clip.encloses( + mrect)) + continue; + bool ok = check_region( + floor_e, i, + mrect); + if (!ok) + continue; + int new_area = + mrect.get_area(); + int area_diff = + new_area - + old_area; + if (area_diff > 0) { + g.regions + .write[i] + .rect = + mrect; + g.regions + .write[i] + .remains_area -= + area_diff; + grown = true; + } + } else if (state == 1) { + int d; + mrect = g.regions[i] + .rect; + if (g.regions[i] + .remains_area <= + 0) + break; + bool ok = true; + for (d = 0; d < 4; + d++) { + mrect = g.regions[i] + .rect; + assert(check_region( + floor_e, + i, + mrect)); + switch (d) { + case 0: + mrect.position + .y -= + 1; + mrect.size + .y += + 1; + break; + case 1: + mrect.size + .y += + 1; + break; + case 2: + mrect.position + .x -= + 1; + mrect.size + .x += + 1; + break; + case 3: + mrect.size + .x += + 1; + break; + } + Rect2i clip( + 0, 0, + fl.grid_size, + fl.grid_size); + if (!clip.encloses( + mrect)) { + ok = false; + continue; + } + + ok = check_region( + floor_e, + i, + mrect); + if (ok) { + int new_area = + mrect.get_area(); + int area_diff = + new_area - + old_area; + if (area_diff > + 0) { + g.regions + .write[i] + .rect = + mrect; + g.regions + .write[i] + .remains_area -= + area_diff; + grown = true; + } + } + } + } + } + } + state++; + grown = true; + if (state == 2) + break; + } + for (i = 0; i < g.regions.size(); i++) { + int x, y; + Rect2i rect = g.regions[i].rect; + for (x = rect.position.x; + x <= rect.position.x + rect.size.x; x++) + for (y = rect.position.y; + y <= rect.position.y + rect.size.y; + y++) { + int id = x + fl.grid_size * y; + if (!fl.cells.has(id)) { + flecs::entity seed_e = + floor_e.world().entity( + g.regions[i] + .seed_et); + assert(seed_e.is_valid()); + queue_grow_cell(seed_e, + id); + } + } + } + }); + ecs.system( + "GrowFloorRegions") + .kind(0) + .each([this](flecs::entity floor_e, + WorldEditor::components::buildings_layout_grid_floor + &fl) { + /* TODO: limit growth per region too */ + struct grid_calc grid(fl.grid_size); + if (fl.size_left <= 0) + return; + flecs::query + q = floor_e.world() + .query_builder< + const WorldEditor::components:: + buildings_layout_grid_cell>() + .with(flecs::ChildOf, floor_e) + .without() + .build(); + q.each([this, &floor_e, &fl, + &grid](flecs::entity et, + const WorldEditor::components:: + buildings_layout_grid_cell &cell) { + int index = cell.index; + assert(et.is_valid()); + int i; + LocalVector candidates; + candidates.resize(8); + grid.get_cadidates(cell.index, + candidates.ptr()); + int extended = 0; + for (i = 0; i < (int)candidates.size(); i++) { + if (have_cell(floor_e, candidates[i])) + continue; + queue_grow_cell(et, candidates[i]); + extended++; + } + if (extended == 0) + et.add(); + print_line("size: " + itos(grid.grid_size) + + " index: " + itos(index)); + }); + }); + ecs.system( + "GrowRegions") + .kind(0) + .without() + .read() + .write() + .write() + .write() + .each([this](flecs::entity e, + const WorldEditor::components:: + buildings_layout_grid_cell &cell) { + e.world().defer_suspend(); + int index = cell.index; + assert(e.is_valid()); + flecs::entity floor_e = e.parent(); + assert(floor_e.is_valid()); + + flecs::entity grid_e = floor_e.parent(); + assert(grid_e.is_valid()); + String layout_name(grid_e.name()); + flecs::entity base_e = get_layout_base(); + flecs::entity layout_e = + base_e.lookup(layout_name.ascii().ptr()); + assert(layout_e.is_valid()); + struct grid_calc grid(layout_e); + int i; + LocalVector candidates; + candidates.resize(8); + grid.get_cadidates(cell.index, candidates.ptr()); + int extended = 0; + for (i = 0; i < (int)candidates.size(); i++) { + if (have_cell(floor_e, candidates[i])) + continue; + queue_grow_cell(e, candidates[i]); +#if 0 + String c_name = "cell_" + itos(candidates[i]); + flecs::entity c_e = + e.parent().lookup(c_name.ascii().ptr()); +#endif + extended++; + } + if (extended == 0) + e.add(); + print_line("size: " + itos(grid.grid_size) + + " index: " + itos(index)); + e.world().defer_resume(); + }); + ecs.system( + "GrowCommitQueue") + .kind(0) + .each([this](flecs::entity e, + WorldEditor::components::buildings_layout_grid_floor + &fl, + WorldEditor::components::buildings_layout_grid_queue + &queue) { + List >::Element *me = + queue.queue.front(); + while (me) { + flecs::entity seed_e = + e.world().entity(me->get().first); + int id = me->get().second; + if (!fl.cells.has(id)) { + grow_cell(seed_e, id); + fl.cells.insert(id); + } + me = me->next(); + } + queue.queue.clear(); + e.remove(); + }); + ecs.system("RunGrow") + .kind(GraphFilter) + .run([module_name](flecs::iter &it) { + int i; + it.world().defer_suspend(); + print_line("Running grow..."); + for (i = 0; i < 100; i++) { + it.world() + .system(it.world().lookup( + (module_name + + "::GrowFloorRectRegions") + .ascii() + .ptr())) + .run(); + it.world() + .system(it.world().lookup( + (module_name + + "::GrowCommitQueue") + .ascii() + .ptr())) + .run(); + } +#if 0 + it.world() + .system(it.world().lookup( + (module_name + "::GrowFloorRegions") + .ascii() + .ptr())) + .run(); + it.world() + .system(it.world().lookup( + (module_name + "::GrowCommitQueue") + .ascii() + .ptr())) + .run(); +#endif + it.world().defer_resume(); + }); +} diff --git a/src/modules/stream/ui/graph_module_rooms.cpp b/src/modules/stream/ui/graph_module_rooms.cpp new file mode 100644 index 0000000..b2d275e --- /dev/null +++ b/src/modules/stream/ui/graph_module_rooms.cpp @@ -0,0 +1,380 @@ +#include "base_data.h" +#include "world_editor.h" +#include "building_layout_graph.h" +#include "graph_module.h" + +void BuildingLayoutGraph::graph_module::room_growth_module( + flecs::world &ecs, const String &module_name) +{ + flecs::entity GraphFilter = ecs.entity("GraphFilter"); + assert(GraphFilter.is_valid()); + flecs::entity GraphMarkData = ecs.entity("GraphMarkData") + .add(flecs::Phase) + .depends_on(flecs::OnUpdate); + GraphMarkData.disable(); + flecs::entity GraphProcessRooms = ecs.entity("GraphProcessRooms") + .add(flecs::Phase) + .depends_on(flecs::OnUpdate); + GraphProcessRooms.disable(); + + ecs.system("CheckGrow").kind(GraphFilter).run([module_name](flecs::iter &it) { + flecs::query + q = it.world() + .query_builder< + const WorldEditor::components:: + buildings_layout_grid_floor>() + .build(); + int count_left = 0; + int count_run = 0; + q.each([&count_left, + &count_run](flecs::entity e, + const WorldEditor::components:: + buildings_layout_grid_floor &fl) { + count_run++; + count_left += MAX(0, fl.size_left); + }); + // assert(false); + if (count_run > 0 && count_left <= 0) { + it.world() + .lookup((module_name + "::GraphFilter") + .ascii() + .ptr()) + .disable(); + print_line("Grow done"); + it.world() + .lookup((module_name + "::GraphMarkData") + .ascii() + .ptr()) + .enable(); + print_line("Mark started..."); + } + }); + ecs.system( + "MarkExternalWall") + .kind(GraphMarkData) + .without() + .read() + .write() + .write() + .each([](flecs::entity e, + const WorldEditor::components::buildings_layout_grid_cell + &cell) { + int grid_size = + e.parent() + .get() + ->grid_size; + int i; + Vector2i position(cell.index % grid_size, + cell.index / grid_size); + Vector2 west(position.x - 1, position.y); + Vector2 north(position.x, position.y - 1); + Vector2 east(position.x + 1, position.y); + Vector2 south(position.x, position.y + 1); + int west_id = west.x + grid_size * west.y; + int north_id = north.x + grid_size * north.y; + int east_id = east.x + grid_size * east.y; + int south_id = south.x + grid_size * south.y; + std::vector neighbors = { west_id, north_id, + east_id, south_id }; + bool outside = false; + bool border = false; + for (i = 0; i < (int)neighbors.size(); i++) { + int id = neighbors[i]; + print_line("id=" + itos(id)); + if (!e.parent() + .get() + ->cells.has(id)) { + outside = true; + break; + } + } + for (i = 0; i < (int)neighbors.size(); i++) { + int id = neighbors[i]; + print_line("id=" + itos(id)); + String neighbor_name = "cell_" + itos(id); + flecs::entity neighbor_e = e.parent().lookup( + neighbor_name.ascii().ptr()); + if (!neighbor_e.is_valid()) + continue; + const WorldEditor::components::buildings_layout_grid_cell + *neighbor_cell = neighbor_e.get< + WorldEditor::components:: + buildings_layout_grid_cell>(); + if (cell.type != neighbor_cell->type) { + border = true; + break; + } + } + if (outside) + e.add(); + else + e.add(); + if (border) + e.add(); + print_line("outside: " + itos(outside)); + print_line("position: " + (position.operator String())); + print_line("grid size: " + itos(grid_size)); + print_line("tile index: " + itos(cell.index)); + }); + ecs.system("CheckMark") + .kind(GraphMarkData) + .run([module_name](flecs::iter &it) { + int tile_count = 0, inside_count = 0, wall_count = 0; + it.world() + .query_builder< + const WorldEditor::components:: + buildings_layout_grid_cell>() + .build() + .each([&tile_count, &inside_count, &wall_count]( + flecs::entity e, + const WorldEditor::components:: + buildings_layout_grid_cell + &cell) { + tile_count++; + if (e.has()) + wall_count++; + if (e.has()) + inside_count++; + }); + if (tile_count > 0 && wall_count > 0 && + inside_count > 0 && + tile_count == wall_count + inside_count) { + it.world() + .lookup((module_name + + "::GraphMarkData") + .ascii() + .ptr()) + .disable(); + it.world() + .lookup((module_name + + "::GraphProcessRooms") + .ascii() + .ptr()) + .enable(); + } + }); + struct prio_queue { + HashMap entities; + Set tile_selection[4]; + Vector tile_selection_array[4]; + struct make_random r; + bool dirty; + prio_queue() + : r(make_random(100)) + , dirty(true) + { + } + void add_tile(int prio, int id, flecs::entity ec) + { + if (!tile_selection[prio].has(id)) + tile_selection[prio].insert(id); + if (!entities.has(id)) + entities[id] = ec; + dirty = true; + } + void update() + { + int i, j; + for (i = 0; i < 4; i++) { + tile_selection_array[i].resize( + tile_selection[i].size()); + j = 0; + Set::Element *m = + tile_selection[i].front(); + while (m) { + tile_selection_array[i].write[j++] = + m->get(); + m = m->next(); + } + } + } + void dump() + { + if (dirty) { + update(); + dirty = false; + } + print_line("0: " + + itos(tile_selection_array[0].size())); + print_line("1: " + + itos(tile_selection_array[1].size())); + print_line("2: " + + itos(tile_selection_array[2].size())); + } + int get_random_tile(int prio) + { + assert(tile_selection[prio].size() > 0); + int sel = r.get() % tile_selection[prio].size(); + int id = tile_selection_array[prio][sel]; + assert(entities.has(id)); + tile_selection_array[prio].erase(id); + tile_selection[prio].erase(id); + return id; + } + void delete_neighbors(int id) + { + int i, j; + flecs::entity selected_e = entities[id]; + int grid_size = + selected_e.parent() + .get() + ->grid_size; + Vector2i base(id % grid_size, id / grid_size); + for (i = -1; i < 2; i++) + for (j = -1; j < 2; j++) { + if (i == 0 && j == 0) + continue; + Vector2i neighbor = + base + Vector2i(i, j); + int neighbor_id = + neighbor.x + + grid_size * neighbor.y; + tile_selection_array[0].erase( + neighbor_id); + tile_selection[0].erase(neighbor_id); + if (!tile_selection[3].has( + neighbor_id)) { + tile_selection_array[3] + .push_back(neighbor_id); + tile_selection[3].insert( + neighbor_id); + } + } + } + int take_tile() + { + if (dirty) { + update(); + dirty = false; + } + if (tile_selection[0].size() > 0) { + int id = get_random_tile(0); + delete_neighbors(id); + return id; + } else { + int prio; + flecs::log::warn( + "out of normal tiles, using outside wall and zone borders"); + for (prio = 1; prio < 4; prio++) { + if (tile_selection[prio].size() > 0) { + int id = get_random_tile(prio); + return id; + } + } + } + flecs::log::err("failed to allocate room initial tile"); + return -1; + } + flecs::entity get_entity(int id) + { + assert(entities.has(id)); + return entities[id]; + } + int get_total_size() + { + int ret = 0, i; + for (i = 0; i < 4; i++) + ret += tile_selection[i].size(); + return ret; + } + }; + ecs.system( + "ProcessFloor0Rooms") + .kind(GraphProcessRooms) + .read() + .read() + .read() + .each([](flecs::entity e, + const WorldEditor::components::buildings_layout_zone + &zone, + const WorldEditor::components:: + buildings_layout_floor_index &idx) { + if (idx.index != 0) + return; + // make_random r(1001); + struct prio_queue prio; + e.world() + .query_builder< + const WorldEditor::components:: + buildings_layout_grid_cell>() + .with(e) + .without() + .without() + .build() + .each([&prio](flecs::entity ec, + const WorldEditor::components:: + buildings_layout_grid_cell + &cell) { + prio.add_tile(0, cell.index, ec); + }); + e.world() + .query_builder< + const WorldEditor::components:: + buildings_layout_grid_cell>() + .with(e) + .without() + .with() + .build() + .each([&prio](flecs::entity ec, + const WorldEditor::components:: + buildings_layout_grid_cell + &cell) { + prio.add_tile(1, cell.index, ec); + }); + e.world() + .query_builder< + const WorldEditor::components:: + buildings_layout_grid_cell>() + .with(e) + .with() + .build() + .each([&prio](flecs::entity ec, + const WorldEditor::components:: + buildings_layout_grid_cell + &cell) { + prio.add_tile(2, cell.index, ec); + }); + prio.update(); + prio.dump(); + if (prio.get_total_size() == 0) { + flecs::log::err( + "no tiles allocated for zone: %s", + e.path().c_str()); + return; + } + int count = 0, assigned_count = 0; + e.world() + .query_builder() + .with(flecs::ChildOf, e) + .build() + .each([&prio, &count, &assigned_count]( + flecs::entity ec, + const WorldEditor::components:: + buildings_layout_room + &room) { + int id = prio.take_tile(); + if (id >= 0) { + flecs::entity selected_e = + prio.get_entity(id); + selected_e.add< + WorldEditor::components:: + belongs_room>( + ec); + assigned_count++; + } + count++; + }); + if (assigned_count < count) + flecs::log::err( + "not all rooms were assigned: %d < %d", + assigned_count, count); + }); +} \ No newline at end of file diff --git a/src/modules/stream/world_editor.h b/src/modules/stream/world_editor.h index 6654acf..a222338 100644 --- a/src/modules/stream/world_editor.h +++ b/src/modules/stream/world_editor.h @@ -120,6 +120,9 @@ public: int growth_size; }; struct belongs {}; + struct border {}; + struct belongs_room {}; + struct outside_wall {}; struct buildings_layout_grid_floor { Set cells; int grid_size;