#include #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(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); 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() .write() .write() .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() ->type; c_e.set( { type, id }); seed_e.each( [&c_e](flecs::entity second) { c_e.add( second); }); int mcount = 0; seed_e.each( [&c_e, &mcount](flecs::entity second) { assert(mcount == 0); c_e.add( second); mcount++; }); floor_e.get_mut() ->size_left--; floor_e.get_mut() ->cells.insert(id); floor_e.modified< WorldEditor::components::buildings_layout_grid_floor>(); } } void growth_module::commit_growth_queue(flecs::world &&ecs) { flecs::query grow_commit_queue = ecs.query_builder() .build(); grow_commit_queue.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(); }); } 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() ->grid_size; int i; e.each( [&room_e](flecs::entity fe) { room_e = fe; }); bool window = true; if (room_e.is_valid()) { int room = room_e.get() ->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 neighbors = { west_id, north_id, east_id, south_id, nw_id, sw_id, ne_id, se_id }; std::vector west_corners = { nw_id, sw_id }; std::vector north_corners = { nw_id, ne_id }; std::vector south_corners = { sw_id, se_id }; std::vector east_corners = { ne_id, se_id }; bool outside = false; int neighbor_flags = 0; HashMap neighbor_data; HashMap 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(); e.add(); if (window) e.add(); 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(); e.add(); if (window) e.add(); 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(); e.add(); if (window) e.add(); 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(); e.add(); if (window) e.add(); 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( { 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(); else if (id == east_id) e.add(); else if (id == north_id) e.add(); else if (id == south_id) e.add(); } } if (outside) { e.add(); flecs::log::dbg("outside wall cell %s", e.path().c_str()); } 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)); }); } 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() ->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()) cost += 500.0f; if (cell_e.has()) cost += 500.0f; return cost; } static int get_astar_id(Ref &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; List regions; Vector room_points; HashMap id2cell; HashMap id2entity; }; HashMap 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() ->grid_size; if (!data.has(e.parent().id())) { struct mark_doors_data mdata; mdata.astar.instance(); const struct region_tree *rtree = e.parent().get(); rtree->get_leaf_nodes(&mdata.regions); List::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( [&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 neighbors = { west_id, north_id, east_id, south_id }; bool outside = false; HashMap neighbor_data; HashMap 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 key_list; data.get_key_list(&key_list); List::Element *ke = key_list.front(); while (ke) { int j, k; int grid_size = ecs_.entity(ke->get()) .get() ->grid_size; HashMap > exits; Set 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 path = data[ke->get()].astar->get_id_path(p0, p1); PoolVector 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()) { cell_e.add(); next_cell_e.add< WorldEditor::components:: internal_door_east>(); } if (tx > 0 && cell_e.has()) { cell_e.add(); next_cell_e.add< WorldEditor::components:: internal_door_west>(); } if (ty < 0 && cell_e.has()) { cell_e.add(); next_cell_e.add< WorldEditor::components:: internal_door_south>(); } if (ty > 0 && cell_e.has()) { cell_e.add(); next_cell_e.add< WorldEditor::components:: internal_door_north>(); } } flecs::log::dbg("path: %s", elements.ascii().ptr()); } ke = ke->next(); } }