#include "base_data.h" #include "world_editor.h" #include "grid_misc.h" #include "graph_module.h" #include "queries.h" #include "region_tree.h" #include "grow_job.h" static inline int get_floor_index(flecs::entity e) { return e.get() ->index; } static inline flecs::entity get_grid_floor(flecs::entity grid_e, flecs::entity graph_floor_e) { assert(grid_e.is_valid()); assert(graph_floor_e.is_valid()); int floor_index = get_floor_index(graph_floor_e); String floor_name = "floor_" + itos(floor_index); // flecs::log::dbg("floor: %s", floor_name.ascii().ptr()); flecs::entity floor_e = grid_e.lookup(floor_name.ascii().ptr()); assert(floor_e.is_valid()); return floor_e; } grow_job_queue::grow_job_queue( flecs::entity grid_e, struct subregions &subregions, const WorldEditor::components::buildings_layout_grid_size &size, const String &module_name) : grid_e(grid_e) , grid_size(size.grid_size) , subregions(subregions) , module_name(module_name) { const List >::Element *me; List > job_create_queue; me = size.floors.front(); while (me) { // graph entity job_create_queue.push_back( { me->get().second, me->get().second }); flecs::entity_t base_floor_et = job_create_queue.front()->get().second; flecs::entity base_floor_e = grid_e.world().entity(base_floor_et); flecs::entity grid_floor_e = get_grid_floor(grid_e, base_floor_e); grid_floor_e.get_mut()->job_list = List(); me = me->next(); } while (!job_create_queue.empty()) { // graph entity flecs::entity_t et = job_create_queue.front()->get().first; // parent graph entity flecs::entity_t base_floor_et = job_create_queue.front()->get().second; job_create_queue.pop_front(); flecs::entity base_floor_e = grid_e.world().entity(base_floor_et); flecs::log::dbg("job_queue: base_floor: %s", base_floor_e.path().c_str()); flecs::entity grid_floor_e = get_grid_floor(grid_e, base_floor_e); flecs::log::dbg("job_queue: grid_floor: %s", grid_floor_e.path().c_str()); assert(!subregions.sub_subregions.empty()); if (subregions.sub_subregions.has(et) && !subregions.sub_subregions[et].empty()) { flecs::log::dbg( "subregions for: %s", grid_e.world().entity(et).path().c_str()); struct grow_job job; if (grid_floor_e.get()->job_list.empty()) job.job_type = grow_job::INITIAL; else job.job_type = grow_job::COMMON; job.parent_id = et; job.base_floor_id = base_floor_et; job.grid_floor_id = grid_floor_e.id(); job.subregions = subregions.sub_subregions[job.parent_id]; grid_floor_e.get_mut() ->job_list.push_back(job); List >::Element *se = job.subregions.front(); while (se) { job_create_queue.push_back( { se->get().first.id(), base_floor_et }); se = se->next(); } grid_floor_e.modified(); } else flecs::log::dbg( "no subregions for: %s", grid_e.world().entity(et).path().c_str()); } } /* Unlimited growth for squares */ void grow_job_queue::job_initial(struct grow_job *job) { int i, j; flecs::entity base_floor_e = grid_e.world().entity(job->base_floor_id); flecs::entity grid_floor_e = grid_e.world().entity(job->grid_floor_id); flecs::log::dbg("create: base_floor: %s", base_floor_e.path().c_str()); flecs::log::dbg("create: grid_floor: %s", grid_floor_e.path().c_str()); growth_regions *g = grid_floor_e.get_mut(); RegionRect2i clip_rect(1, 1, grid_size - 2, grid_size - 2); const List > &subregions = job->subregions; List initial_regions; if (subregions.size() == 0) { flecs::log::err("nothing to do"); return; } grid_floor_e.set( { { job->base_floor_id, job->base_floor_id, job->base_floor_id, clip_rect.grow(-1), grid_size * grid_size, false, false, true }, Vector(), nullptr }); struct region_tree *rtree = grid_floor_e.get_mut(); create_region_list(rtree, subregions, initial_regions); grid_floor_e.modified(); grid_floor_e.get_mut()->split(grid_floor_e, initial_regions); assert(rtree->check(grid_floor_e)); grid_floor_e.modified(); grid_floor_e.get()->dump(grid_floor_e); grid_floor_e.get_mut()->grow(grid_floor_e, true); grid_floor_e.modified(); grid_floor_e.get()->dump(grid_floor_e); // grid_floor_e.get()->place(grid_floor_e); List rects; List::Element *e, *e1; grid_floor_e.get()->get_rects(&rects); e = rects.front(); bool ok = true; while (e) { e1 = rects.front(); while (e1) { if (e->get() == e1->get()) { e1 = e1->next(); continue; } if (e->get().intersects(e1->get())) ok = false; if (e->get().encloses(e1->get())) ok = false; if (e1->get().intersects(e->get())) ok = false; if (e1->get().encloses(e->get())) ok = false; if (!ok) break; e1 = e1->next(); } e = e->next(); } assert(rtree->check(grid_floor_e)); } void grow_job_queue::job_common(struct grow_job *job) { int i, j; make_random r(172); flecs::entity base_floor_e = grid_e.world().entity(job->base_floor_id); flecs::entity grid_floor_e = grid_e.world().entity(job->grid_floor_id); flecs::log::dbg("create: base_floor: %s", base_floor_e.path().c_str()); flecs::log::dbg("create: grid_floor: %s", grid_floor_e.path().c_str()); flecs::entity parent_e = grid_e.world().entity(job->parent_id); growth_regions *g = grid_floor_e.get_mut(); const List > &subregions = job->subregions; const List >::Element *fe = subregions.front(); struct region_tree *base_rtree = grid_floor_e.get_mut(), *rtree; List region_list; base_rtree->dump(grid_floor_e); flecs::log::warn("search for: %s %lx", parent_e.path().c_str(), job->parent_id); rtree = base_rtree->find(job->parent_id); if (!rtree) { g->job_list.push_back(*job); flecs::log::warn("delay job fo %s (%d jobs)", parent_e.path().c_str(), g->job_list.size()); return; } create_region_list(rtree, subregions, region_list); assert(base_rtree->check(grid_floor_e)); rtree->split(grid_floor_e, region_list); assert(base_rtree->check(grid_floor_e)); grid_floor_e.modified(); rtree->dump(grid_floor_e); assert(base_rtree->check(grid_floor_e)); rtree->grow(grid_floor_e); assert(base_rtree->check(grid_floor_e)); rtree->dump(grid_floor_e); grid_floor_e.modified(); #if 0 flecs::log::dbg("common: region count: %d", g->regions.size()); assert(g->regions.size() > 0); bool restart = false, found = false; RegionRect2i clip_rect(0, 0, grid_size, grid_size); for (i = 0; i < (int)g->regions.size(); i++) { flecs::log::dbg("region %d: %s", i, grid_e.world() .entity(g->regions[i].region_et) .path() .c_str()); } int region_index = -1; for (i = 0; i < (int)g->regions.size(); i++) { if (g->regions[i].region_et == job->parent_id) { flecs::log::warn("parent is in regions %d", i); region_index = i; if (!g->regions[i].complete) { g->job_list.push_back(*job); restart = true; flecs::log::err( "parent is in regions %d incomplete", i); flecs::log::warn("delay job fo %s (%d jobs)", grid_e.world() .entity(job->parent_id) .path() .c_str(), g->job_list.size()); } found = true; break; } } if (!found) { // no parent region yet g->job_list.push_back(*job); flecs::log::warn("delay job fo %s (%d jobs)", parent_e.path().c_str(), g->job_list.size()); restart = true; } if (restart) return; #endif #if 0 for (i = 0; i < g->regions.size(); i++) flecs::log::warn( "before: region %d: %s", i, (g->regions[i].rect.operator String()).ascii().ptr()); // growth_regions::region parent_region = g->regions[region_index]; // const List >::Element *fe = // subregions.front(); List > region_list = subregions; g->split_region(grid_floor_e, region_index, region_list); #endif // g->regions.remove(region_index); // clip_rect = parent_region.rect; // int parent_region_index = g->parent_regions.size(); // g->parent_regions.push_back(parent_region); /* while (fe) { flecs::entity ce = fe->get().first; float area = fe->get().second; flecs::log::warn("generating positions for: %s", ce.path().c_str()); job->grid.filter_candidates(ce, area, clip_rect); fe = fe->next(); } job->grid.place_regions_common(parent_e, grid_floor_e, parent_region_index); */ #if 0 for (i = 0; i < g->regions.size(); i++) flecs::log::warn( "after: region %d: %s", i, (g->regions[i].rect.operator String()).ascii().ptr()); flecs::log::dbg("Running grow..."); g->grow_regions(grid_floor_e); #endif #if 0 // FIXME: fix this queries.get_qr().each( [this, parent_e]( flecs::entity grid_floor_e, WorldEditor::components::buildings_layout_grid_floor &fl, growth_regions &g) { if (g.complete) return; g.grow_regions(grid_floor_e); }); #endif #if 0 for (i = 0; i < g->regions.size(); i++) flecs::log::warn( "after2: region %d: %s", i, (g->regions[i].rect.operator String()).ascii().ptr()); #endif #if 0 queries.get_qr().each( [this, parent_e]( flecs::entity grid_floor_e, WorldEditor::components::buildings_layout_grid_floor &fl, growth_regions &g) { struct grid_misc grd; // if (g.complete) // return; //grd.grow_region_rects(grid_floor_e, fl, g); grd.update_region_cells(grid_floor_e, parent_e, fl, g); assert(false); }); #endif #if 0 update_region_cells(grid_floor_e); for (i = 0; i < (int)g->regions.size(); i++) g->regions.write[i].complete = true; g->complete = true; #endif commit_common_queue(); // assert(false); } void grow_job_queue::commit_common_queue() { grid_e.world() .query_builder< WorldEditor::components::buildings_layout_grid_floor, WorldEditor::components::buildings_layout_grid_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)) { flecs::log::err("bad cell %d", id); assert(false); } // fl.cells.insert(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.has()) { // flecs::log::dbg("cell: %s: %s", // e.path().c_str(), // c_e.path().c_str()); String type = seed_e.get() ->type; c_e.set( { type, id }); seed_e.each< WorldEditor::components::belongs>( [&c_e](flecs::entity second) { c_e.add( second); }); seed_e.each( [&c_e](flecs::entity second) { c_e.add( second); }); floor_e.modified< WorldEditor::components:: buildings_layout_grid_floor>(); } else { flecs::log::err("bad cell %s", c_name.ascii().ptr()); assert(false); } me = me->next(); } queue.queue.clear(); e.remove(); }); } void grow_job_queue::create_region_list( region_tree *rtree, const List > &subregions, List ®ion_list) { int i, j; const List >::Element *fe = subregions.front(); bool restart = false, found = false; make_random r(172); RegionRect2i clip_rect = rtree->region.rect; Vector2i current_position = clip_rect.get_center(); Vector2i velocity; Vector regions; LocalVector positions; Vector distances; LocalVector areas; // Drunk walk implementation int speed = MAX(1, grid_size * grid_size / 2 / subregions.size()); int bad_count = 0; while (fe) { Vector position_candidates; if (positions.empty()) { int target_distance = (int)(Math::sqrt(fe->get().second) * 0.7f) + 1; positions.push_back(current_position); distances.push_back(target_distance); regions.push_back(fe->get().first); areas.push_back(fe->get().second); fe = fe->next(); if (!fe) break; } for (i = 0; i < (int)positions.size(); i++) { int which = r.get() % 12; switch (which) { case 1: velocity = { 0, 1 }; break; case 3: velocity = { 1, 0 }; break; case 5: velocity = { 0, -1 }; break; case 7: velocity = { -1, 0 }; break; } if (clip_rect.has_point(current_position + velocity)) { position_candidates.push_back(current_position + velocity); current_position += velocity; } } int target_distance = (int)(Math::sqrt(fe->get().second) * 0.7f) + 1; assert(fe); if (bad_count > 100) target_distance = 1; bool any_match = false; for (i = 0; i < position_candidates.size(); i++) { bool bad = false; assert(fe); for (j = 0; j < (int)positions.size(); j++) { Vector2i l = position_candidates[i] - positions[j]; int distance = l.x * l.x + l.y * l.y - distances[j]; if (distance < target_distance) { bad = true; break; } } assert(fe); if (!bad) { assert(fe); positions.push_back(position_candidates[i]); distances.push_back(target_distance); regions.push_back(fe->get().first); areas.push_back(fe->get().second); any_match = true; bad_count = 0; if (fe) { fe = fe->next(); if (fe) target_distance = (int)(Math::sqrt( fe->get() .second) * 0.7f) + 1; } } if (!fe) break; } if (!any_match) { flecs::log::err( "No match found: %s: %s", (clip_rect.operator String()).ascii().ptr(), (current_position.operator String()) .ascii() .ptr()); bad_count++; } if (bad_count >= 100) { position_candidates.clear(); int x, y; for (x = clip_rect.position.x; x < clip_rect.position.x + clip_rect.size.x; x++) for (y = clip_rect.position.y; y < clip_rect.position.y + clip_rect.size.y; y++) { position_candidates.push_back( Vector2i(x, y)); } bad_count = 0; for (i = 0; i < position_candidates.size(); i++) { bool bad = false; assert(fe); for (j = 0; j < (int)positions.size(); j++) { Vector2i l = position_candidates[i] - positions[j]; int distance = l.x * l.x + l.y * l.y - distances[j]; if (distance > 0) { bad = true; break; } } assert(fe); if (!bad) { assert(fe); positions.push_back( position_candidates[i]); distances.push_back(target_distance); regions.push_back(fe->get().first); areas.push_back(fe->get().second); any_match = true; bad_count = 0; if (fe) fe = fe->next(); } if (!fe) break; } } assert(bad_count < 2000); } flecs::log::dbg("grid_size %d", grid_size); for (i = 0; i < (int)positions.size(); i++) { struct region region; flecs::log::dbg("region: %s", regions[i].path().c_str()); flecs::log::dbg("position: %d, %d", positions[i].x, positions[i].y); region.complete = false; region.can_grow = true; region.can_grow_square = true; region.parent = rtree->region.region_et; region.rect = RegionRect2i(positions[i], Vector2i(1, 1)); region.region_et = regions[i].id(); region.remains_area = MAX(1, (areas[i] / 16)); /* setting seed uninitialized */ region_list.push_back(region); } } void grow_job_queue::iterate() { assert(grid_e.is_valid()); assert(this); flecs::query q = grid_e.world().query_builder().build(); q.each([this](flecs::entity e, growth_regions &g) { flecs::log::warn("pre floor: %s", e.path().c_str()); }); q.each([this](flecs::entity e, growth_regions &g) { flecs::log::warn("floor: %s", e.path().c_str()); flecs::log::warn("job count: %d", g.job_list.size()); while (!g.job_list.empty()) { List::Element *job_e = g.job_list.front(); assert(job_e); flecs::entity base_floor_e = grid_e.world().entity( job_e->get().base_floor_id); assert(base_floor_e.is_valid()); flecs::entity grid_floor_e = grid_e.world().entity( job_e->get().grid_floor_id); assert(grid_floor_e.is_valid()); flecs::log::dbg("inerate: create: base_floor: %s", base_floor_e.path().c_str()); flecs::log::dbg("iterate: create: grid_floor: %s", grid_floor_e.path().c_str()); switch (job_e->get().job_type) { case grow_job::INITIAL: flecs::log::dbg("initial"); job_initial(&job_e->get()); { const region_tree *rtree = e.get(); assert(!rtree || (rtree && rtree->check(grid_floor_e))); } break; case grow_job::COMMON: flecs::log::dbg("common"); job_common(&job_e->get()); { const region_tree *rtree = e.get(); assert(!rtree || (rtree && rtree->check(grid_floor_e))); } break; default: assert(false); break; } g.job_list.pop_front(); // if (common_count > 0) // break; } g.job_list.clear(); flecs::query qm = grid_e.world().query_builder().build(); qm.each([this](flecs::entity em, region_tree &rt) { assert(rt.check(em)); }); qm.each([this](flecs::entity em, region_tree &rt) { rt.place(em); }); flecs::log::dbg( "processed jobs (created region initial positions): %d", g.job_list.size()); }); }