Files
streaming_world/src/modules/stream/ui/growth_module.cpp

643 lines
20 KiB
C++

#include <core/math/a_star.h>
#include "growth_regions.h"
#include "region_tree.h"
#include "editor_event.h"
#include "queries.h"
#include "building_layout_graph.h"
#include "growth_module.h"
// TODO: make sure Enterance is at outside wall, can do this on region level or on cell level
growth_module::growth_module(flecs::world &ecs)
{
ecs.module<growth_module>();
ecs.component<growth_regions>();
ecs.component<region_tree>();
ecs.component<WorldEditor::components::internal_wall_east>();
ecs.component<WorldEditor::components::internal_wall_west>();
ecs.component<WorldEditor::components::internal_wall_north>();
ecs.component<WorldEditor::components::internal_wall_south>();
ecs.component<WorldEditor::components::internal_door_east>();
ecs.component<WorldEditor::components::internal_door_west>();
ecs.component<WorldEditor::components::internal_door_north>();
ecs.component<WorldEditor::components::internal_door_south>();
ecs.component<WorldEditor::components::outside_wall_east>();
ecs.component<WorldEditor::components::outside_wall_west>();
ecs.component<WorldEditor::components::outside_wall_north>();
ecs.component<WorldEditor::components::outside_wall_south>();
ecs.component<WorldEditor::components::window_east>();
ecs.component<WorldEditor::components::window_west>();
ecs.component<WorldEditor::components::window_north>();
ecs.component<WorldEditor::components::window_south>();
const String &module_name = "::growth_module";
flecs::entity GraphFilter = ecs.entity("GraphFilter")
.add(flecs::Phase)
.depends_on(flecs::OnUpdate);
flecs::entity GraphSolve = ecs.lookup("::graph_module::GraphSolve");
assert(GraphSolve.is_valid());
GraphFilter.disable();
assert(ecs.lookup("::growth_module::window_east").is_valid());
ecs.system("RunGrow")
.immediate()
.write<WorldEditor::components::buildings_layout_grid_floor>()
.write<WorldEditor::components::buildings_layout_grid_size>()
.write<growth_regions>()
.kind(GraphSolve)
.run([module_name, this, GraphFilter](flecs::iter &it) {
flecs::world &&ecs_ = it.world();
it.world().defer_suspend();
flecs::log::dbg("Assembling skeleton done...");
flecs::entity GraphSolveZones = it.world().lookup(
"::graph_module::GraphSolveZones");
assert(GraphSolveZones.is_valid());
GraphSolveZones.disable();
assert(GraphFilter.is_valid());
GraphFilter.enable();
EditorEvent::get_singleton()->event.emit(
"update_layout_view", varray());
// assert(false);
struct subregions subregions(ecs_);
struct queries queries(ecs_);
flecs::log::dbg("Creating regions grow...");
queries.get_qp().each(
[this, &subregions, &queries,
module_name](flecs::iter &it2, size_t count,
const WorldEditor::components::
buildings_layout_grid_size
&size) {
struct grid_misc grid;
// grid.subregions_init(it2.world());
flecs::entity graph_e =
it2.entity(count);
flecs::log::warn(
"creating grid for: %s",
graph_e.path().c_str());
flecs::entity grid_base_e =
queries.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 */
subregions.build_subregions(grid_e,
grid, size);
struct grow_job_queue job_queue(
grid_e, subregions, size,
module_name);
job_queue.iterate();
});
commit_growth_queue(it.world());
mark_cells(it.world());
mark_doors(it.world());
it.world()
.lookup("::growth_module::GraphFilter")
.disable();
it.world().defer_resume();
});
}
void growth_module::grow_cell(flecs::entity seed_e, int id)
{
flecs::entity floor_e = seed_e.parent();
String c_name = "cell_" + itos(id);
flecs::entity c_e = floor_e.lookup(c_name.ascii().ptr());
if (!c_e.is_valid()) {
c_e = seed_e.world()
.entity(c_name.ascii().ptr())
.child_of(floor_e);
assert(c_e.is_valid());
String type = seed_e.get<WorldEditor::components::
buildings_layout_grid_cell>()
->type;
c_e.set<WorldEditor::components::buildings_layout_grid_cell>(
{ type, id });
seed_e.each<WorldEditor::components::belongs>(
[&c_e](flecs::entity second) {
c_e.add<WorldEditor::components::belongs>(
second);
});
int mcount = 0;
seed_e.each<WorldEditor::components::belongs_room>(
[&c_e, &mcount](flecs::entity second) {
assert(mcount == 0);
c_e.add<WorldEditor::components::belongs_room>(
second);
mcount++;
});
floor_e.get_mut<WorldEditor::components::
buildings_layout_grid_floor>()
->size_left--;
floor_e.get_mut<WorldEditor::components::
buildings_layout_grid_floor>()
->cells.insert(id);
floor_e.modified<
WorldEditor::components::buildings_layout_grid_floor>();
}
}
void growth_module::commit_growth_queue(flecs::world &&ecs)
{
flecs::query<WorldEditor::components::buildings_layout_grid_floor,
WorldEditor::components::buildings_layout_grid_queue>
grow_commit_queue =
ecs.query_builder<WorldEditor::components::
buildings_layout_grid_floor,
WorldEditor::components::
buildings_layout_grid_queue>()
.build();
grow_commit_queue.each(
[this](flecs::entity e,
WorldEditor::components::buildings_layout_grid_floor &fl,
WorldEditor::components::buildings_layout_grid_queue
&queue) {
List<Pair<flecs::entity_t, int> >::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<WorldEditor::components::
buildings_layout_grid_queue>();
});
}
void growth_module::mark_cells(flecs::world &&ecs_)
{
struct queries queries(ecs_);
queries.get_mark_cells().each([](flecs::entity e,
const WorldEditor::components::
buildings_layout_grid_cell
&cell) {
flecs::entity room_e;
int grid_size =
e.parent()
.get<WorldEditor::components::
buildings_layout_grid_floor>()
->grid_size;
int i;
e.each<WorldEditor::components::belongs_room>(
[&room_e](flecs::entity fe) { room_e = fe; });
bool window = true;
if (room_e.is_valid()) {
int room = room_e.get<WorldEditor::components::
buildings_layout_room>()
->room_type;
flecs::log::dbg("room id: %d", room);
window = BuildingLayoutGraph::get_singleton()
->get_room_type_property(room,
"window");
flecs::log::dbg("room id: %d: window: %d", room,
window);
}
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);
Vector2 nw(position.x - 1, position.y - 1);
Vector2 sw(position.x - 1, position.y + 1);
Vector2 ne(position.x + 1, position.y - 1);
Vector2 se(position.x + 1, position.y + 1);
#define CELL_ID(v) (v.x + grid_size * v.y);
int west_id = CELL_ID(west);
int north_id = CELL_ID(north);
int east_id = CELL_ID(east);
int south_id = CELL_ID(south);
int nw_id = CELL_ID(nw);
int sw_id = CELL_ID(sw);
int ne_id = CELL_ID(ne);
int se_id = CELL_ID(se);
std::vector<int> neighbors = { west_id, north_id, east_id,
south_id, nw_id, sw_id,
ne_id, se_id };
std::vector<int> west_corners = { nw_id, sw_id };
std::vector<int> north_corners = { nw_id, ne_id };
std::vector<int> south_corners = { sw_id, se_id };
std::vector<int> east_corners = { ne_id, se_id };
bool outside = false;
int neighbor_flags = 0;
HashMap<int, flecs::entity> neighbor_data;
HashMap<int, bool> neighbor_flags_data;
for (i = 0; i < (int)neighbors.size(); i++) {
int id = neighbors[i];
String neighbor_cell = "cell_" + itos(id);
flecs::entity neighbor_e =
e.parent().lookup(neighbor_cell.ascii().ptr());
neighbor_data[id] = neighbor_e;
if (neighbor_e.is_valid()) {
neighbor_flags |= (1 << i);
neighbor_flags_data[id] = true;
}
}
int internal_corner_flags = 0;
for (i = 0; i < (int)neighbors.size(); i++) {
int id = neighbors[i];
if ((neighbor_flags & (1 << i)) == 0) {
outside = true;
if (id == west_id) {
e.add<WorldEditor::components::
outside_wall_west>();
e.add<WorldEditor::components::
internal_wall_west>();
if (window)
e.add<WorldEditor::components::
window_west>();
if (neighbor_flags_data.has(nw_id))
internal_corner_flags |=
WorldEditor::components::
internal_corner::
NW;
if (neighbor_flags_data.has(sw_id))
internal_corner_flags |=
WorldEditor::components::
internal_corner::
SW;
} else if (id == east_id) {
e.add<WorldEditor::components::
outside_wall_east>();
e.add<WorldEditor::components::
internal_wall_east>();
if (window)
e.add<WorldEditor::components::
window_east>();
if (neighbor_flags_data.has(ne_id))
internal_corner_flags |=
WorldEditor::components::
internal_corner::
NE;
if (neighbor_flags_data.has(se_id))
internal_corner_flags |=
WorldEditor::components::
internal_corner::
SE;
} else if (id == north_id) {
e.add<WorldEditor::components::
outside_wall_north>();
e.add<WorldEditor::components::
internal_wall_north>();
if (window)
e.add<WorldEditor::components::
window_north>();
if (neighbor_flags_data.has(nw_id))
internal_corner_flags |=
WorldEditor::components::
internal_corner::
NW;
if (neighbor_flags_data.has(ne_id))
internal_corner_flags |=
WorldEditor::components::
internal_corner::
NE;
} else if (id == south_id) {
e.add<WorldEditor::components::
outside_wall_south>();
e.add<WorldEditor::components::
internal_wall_south>();
if (window)
e.add<WorldEditor::components::
window_south>();
if (neighbor_flags_data.has(sw_id))
internal_corner_flags |=
WorldEditor::components::
internal_corner::
SW;
if (neighbor_flags_data.has(se_id))
internal_corner_flags |=
WorldEditor::components::
internal_corner::
SE;
}
}
}
if (internal_corner_flags > 0)
e.set<WorldEditor::components::internal_corner>(
{ internal_corner_flags });
bool border = false;
flecs::log::dbg("outside: %d", outside);
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;
if (id == west_id)
e.add<WorldEditor::components::
internal_wall_west>();
else if (id == east_id)
e.add<WorldEditor::components::
internal_wall_east>();
else if (id == north_id)
e.add<WorldEditor::components::
internal_wall_north>();
else if (id == south_id)
e.add<WorldEditor::components::
internal_wall_south>();
}
}
if (outside) {
e.add<WorldEditor::components::outside_wall>();
flecs::log::dbg("outside wall cell %s",
e.path().c_str());
} else
e.add<WorldEditor::components::final_cell>();
if (border)
e.add<WorldEditor::components::border>();
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));
});
}
static float get_cell_cost(flecs::entity room_e, flecs::entity cell_e)
{
float cost = 1.0f;
if (!room_e.is_valid())
return cost;
int room = room_e.get<WorldEditor::components::buildings_layout_room>()
->room_type;
flecs::log::dbg("room id: %d", room);
bool priv =
BuildingLayoutGraph::get_singleton()->get_room_type_property(
room, "private");
flecs::log::dbg("room id: %d: private: %d", room, priv);
if (priv)
cost += 10000.0f;
if (cell_e.has<WorldEditor::components::border>())
cost += 500.0f;
if (cell_e.has<WorldEditor::components::outside_wall>())
cost += 500.0f;
return cost;
}
static int get_astar_id(Ref<AStar> &astar, int grid_size, int cell_id,
float cost)
{
int astar_id = -1;
Vector2i position(cell_id % grid_size, cell_id / grid_size);
Vector3 pos3(position.x, 0, position.y);
astar_id = astar->get_closest_point(pos3, true);
if (astar_id >= 0 &&
astar->get_point_position(astar_id).distance_squared_to(pos3) <
1.0f)
return astar_id;
astar_id = astar->get_available_point_id();
astar->add_point(astar_id, pos3, cost);
return astar_id;
}
void growth_module::mark_doors(flecs::world &&ecs_)
{
struct queries queries(ecs_);
struct mark_doors_data {
Ref<AStar> astar;
List<const struct region_tree *> regions;
Vector<Vector3> room_points;
HashMap<int, int> id2cell;
HashMap<int, flecs::entity> id2entity;
};
HashMap<flecs::entity_t, struct mark_doors_data> data;
queries.get_mark_cells().each([&data](flecs::entity e,
const WorldEditor::components::
buildings_layout_grid_cell
&cell) {
flecs::entity room_e;
int i;
int grid_size =
e.parent()
.get<WorldEditor::components::
buildings_layout_grid_floor>()
->grid_size;
if (!data.has(e.parent().id())) {
struct mark_doors_data mdata;
mdata.astar.instance();
const struct region_tree *rtree =
e.parent().get<region_tree>();
rtree->get_leaf_nodes(&mdata.regions);
List<const struct region_tree *>::Element *reg_E =
mdata.regions.front();
while (reg_E) {
const struct region_tree *rt = reg_E->get();
Vector2i center = rt->region.rect.get_center();
Vector3 center3(center.x, 0, center.y);
mdata.room_points.push_back(center3);
flecs::log::dbg("room_point: %s",
(center3.operator String())
.ascii()
.ptr());
reg_E = reg_E->next();
}
data[e.parent().id()] = mdata;
}
e.each<WorldEditor::components::belongs_room>(
[&room_e](flecs::entity fe) { room_e = fe; });
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 p0 = get_astar_id(data[e.parent().id()].astar, grid_size,
cell.index, get_cell_cost(room_e, e));
data[e.parent().id()].id2cell[p0] = cell.index;
data[e.parent().id()].id2entity[p0] = e;
#define CELL_ID(v) (v.x + grid_size * v.y);
int west_id = CELL_ID(west);
int north_id = CELL_ID(north);
int east_id = CELL_ID(east);
int south_id = CELL_ID(south);
std::vector<int> neighbors = { west_id, north_id, east_id,
south_id };
bool outside = false;
HashMap<int, flecs::entity> neighbor_data;
HashMap<int, bool> neighbor_flags_data;
for (i = 0; i < (int)neighbors.size(); i++) {
int id = neighbors[i];
String neighbor_cell = "cell_" + itos(id);
flecs::entity neighbor_e =
e.parent().lookup(neighbor_cell.ascii().ptr());
if (neighbor_e.is_valid()) {
flecs::entity neighbor_room_e;
neighbor_e.each<
WorldEditor::components::belongs_room>(
[&neighbor_room_e](flecs::entity fe) {
neighbor_room_e = fe;
});
neighbor_flags_data[id] = true;
int p1 = get_astar_id(
data[e.parent().id()].astar, grid_size,
id,
get_cell_cost(neighbor_room_e,
neighbor_e));
assert(p1 >= 0 && p0 >= 0);
data[e.parent().id()].astar->connect_points(
p0, p1, true);
data[e.parent().id()].id2cell[p1] = id;
data[e.parent().id()].id2entity[p1] =
neighbor_e;
}
}
});
List<flecs::entity_t> key_list;
data.get_key_list(&key_list);
List<flecs::entity_t>::Element *ke = key_list.front();
while (ke) {
int j, k;
int grid_size =
ecs_.entity(ke->get())
.get<WorldEditor::components::
buildings_layout_grid_floor>()
->grid_size;
HashMap<flecs::entity_t, List<flecs::entity_t> > exits;
Set<flecs::entity_t> corridoor_exits;
Vector3 xdata(grid_size / 2, 0, grid_size + 1);
int p0 = data[ke->get()].astar->get_closest_point(xdata);
for (j = 0; j < data[ke->get()].room_points.size(); j++) {
int p1 = data[ke->get()].astar->get_closest_point(
data[ke->get()].room_points[j]);
PoolVector<int> path =
data[ke->get()].astar->get_id_path(p0, p1);
PoolVector<int> cell_path;
cell_path.resize(path.size());
String elements;
for (k = 0; k < path.size(); k++)
cell_path.write()[k] =
data[ke->get()].id2cell[path[k]];
for (k = 0; k < cell_path.size(); k++) {
elements += itos(cell_path[k]);
if (k < cell_path.size() - 1)
elements += ", ";
}
for (k = 0; k < cell_path.size() - 1; k++) {
int px = cell_path[k] % grid_size;
int py = cell_path[k] / grid_size;
int next_px = cell_path[k + 1] % grid_size;
int next_py = cell_path[k + 1] / grid_size;
int tx = next_px - px;
int ty = next_py - py;
Vector3 position(px, 0, py),
next_position(next_px, 0, next_py);
int point = data[ke->get()]
.astar->get_closest_point(
position, true),
point_next =
data[ke->get()]
.astar->get_closest_point(
next_position,
true);
data[ke->get()].astar->set_point_weight_scale(
point, 0.5f);
data[ke->get()].astar->set_point_weight_scale(
point_next, 0.5f);
flecs::entity room_e, next_room_e;
flecs::entity cell_e =
data[ke->get()].id2entity[path[k]];
cell_e.each<
WorldEditor::components::belongs_room>(
[&room_e](flecs::entity re) {
room_e = re;
});
flecs::entity next_cell_e =
data[ke->get()].id2entity[path[k + 1]];
next_cell_e.each<
WorldEditor::components::belongs_room>(
[&next_room_e](flecs::entity re) {
next_room_e = re;
});
if (room_e.is_valid() &&
next_room_e.is_valid()) {
if (exits.has(room_e.id()) &&
exits[room_e.id()].find(
next_room_e.id()) !=
nullptr)
continue;
if (exits.has(next_room_e.id()) &&
exits[next_room_e.id()].find(
room_e.id()) != nullptr)
continue;
exits[room_e.id()].push_back(
next_room_e.id());
exits[next_room_e.id()].push_back(
room_e.id());
} else if (room_e.is_valid()) {
if (corridoor_exits.has(room_e.id()))
continue;
corridoor_exits.insert(room_e.id());
} else if (next_room_e.is_valid()) {
if (corridoor_exits.has(
next_room_e.id()))
continue;
corridoor_exits.insert(
next_room_e.id());
}
flecs::log::dbg("path point %d: %d %d", k, tx,
ty);
assert((tx == 0 || ty == 0) &&
(tx != 0 || ty != 0));
if (tx < 0 &&
cell_e.has<WorldEditor::components::
internal_wall_west>()) {
cell_e.add<WorldEditor::components::
internal_door_west>();
next_cell_e.add<
WorldEditor::components::
internal_door_east>();
}
if (tx > 0 &&
cell_e.has<WorldEditor::components::
internal_wall_east>()) {
cell_e.add<WorldEditor::components::
internal_door_east>();
next_cell_e.add<
WorldEditor::components::
internal_door_west>();
}
if (ty < 0 &&
cell_e.has<WorldEditor::components::
internal_wall_north>()) {
cell_e.add<WorldEditor::components::
internal_door_north>();
next_cell_e.add<
WorldEditor::components::
internal_door_south>();
}
if (ty > 0 &&
cell_e.has<WorldEditor::components::
internal_wall_south>()) {
cell_e.add<WorldEditor::components::
internal_door_south>();
next_cell_e.add<
WorldEditor::components::
internal_door_north>();
}
}
flecs::log::dbg("path: %s", elements.ascii().ptr());
}
ke = ke->next();
}
}