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

623 lines
17 KiB
C++

#undef NDEBUG
#include <cassert>
#include <core/ustring.h>
#include <core/list.h>
#include <core/hashfuncs.h>
#include <core/hash_map.h>
#include "region_tree.h"
void region_tree::split(flecs::entity grid_floor_e,
const List<struct region> &regions)
{
assert(children.size() == 0);
const region_tree *base_rtree = grid_floor_e.get<region_tree>();
const List<struct region>::Element *e = regions.front();
int count = 0;
assert(base_rtree->check(grid_floor_e));
while (e) {
flecs::log::warn(
"%lx: %s -> %s", region.region_et,
(region.rect.operator String()).ascii().ptr(),
(e->get().rect.operator String()).ascii().ptr());
assert(region.rect.encloses(e->get().rect));
struct region_tree *child = memnew(struct region_tree);
child->parent = this;
child->region = e->get();
children.push_back(child);
base_rtree->dump(grid_floor_e);
flecs::log::dbg(
"added region: %d: %s", count,
(child->region.rect.operator String()).ascii().ptr());
if (!base_rtree->check(grid_floor_e))
flecs::log::err(
"bad region %d: %s: %s", count,
(region.rect.operator String()).ascii().ptr(),
(e->get().rect.operator String()).ascii().ptr());
base_rtree->dump(grid_floor_e);
assert(base_rtree->check(grid_floor_e));
count++;
e = e->next();
}
}
const region_tree *region_tree::find(flecs::entity_t which) const
{
int i;
List<const struct region_tree *> queue;
queue.push_back(this);
while (!queue.empty()) {
const struct region_tree *item = queue.front()->get();
if (item->region.region_et == which)
return item;
queue.pop_front();
for (i = 0; i < item->children.size(); i++)
queue.push_back(item->children[i]);
}
return nullptr;
}
region_tree *region_tree::find(flecs::entity_t which)
{
int i;
List<struct region_tree *> queue;
queue.push_back(this);
while (!queue.empty()) {
struct region_tree *item = queue.front()->get();
flecs::log::dbg("check %lx == %lx", item->region.region_et,
which);
if (item->region.region_et == which)
return item;
queue.pop_front();
for (i = 0; i < item->children.size(); i++)
queue.push_back(item->children[i]);
}
return nullptr;
}
void region_tree::dump(flecs::entity grid_floor_e) const
{
List<Pair<int, const struct region_tree *> > queue;
queue.push_back({ 0, this });
flecs::log::warn("tree dump:");
while (!queue.empty()) {
int tabs = queue.front()->get().first;
const struct region_tree *item = queue.front()->get().second;
queue.pop_front();
String stabs = "\t";
int i;
for (i = 0; i < tabs; i++)
stabs += "\t";
flecs::log::warn("%sregion: %s: %lx", stabs.ascii().ptr(),
grid_floor_e.world()
.entity(item->region.region_et)
.path()
.c_str(),
item->region.region_et);
flecs::log::warn(
"%srect: %s", stabs.ascii().ptr(),
(item->region.rect.operator String()).ascii().ptr());
flecs::log::warn("");
for (i = 0; i < item->children.size(); i++)
queue.push_back({ tabs + 1, item->children[i] });
}
}
void region_tree::grow(flecs::entity grid_floor_e)
{
List<struct region_tree *> grow_list;
List<struct region_tree *> queue;
List<struct region_tree *>::Element *e, *e1;
const struct region_tree *base_rtree = grid_floor_e.get<region_tree>();
assert(base_rtree);
#ifdef TESTS
flecs::log::warn("grow");
#endif
if (region.complete)
queue.push_back(this);
else
flecs::log::warn("incomplete");
while (!queue.empty()) {
int i;
struct region_tree *item = queue.front()->get();
queue.pop_front();
if (item->region.can_grow || item->region.can_grow_square)
continue;
for (i = 0; i < (int)item->children.size(); i++) {
if (item->children[i]->region.can_grow)
grow_list.push_back(item->children[i]);
queue.push_back(item->children[i]);
}
}
assert(base_rtree->check(grid_floor_e));
flecs::log::warn("grow list: %d", grow_list.size());
int grow_count = 0;
queue.clear();
struct pointer_hasher {
static _FORCE_INLINE_ uint32_t hash(void *ptr)
{
return hash_one_uint64((uint64_t)ptr);
}
};
HashMap<struct region_tree *, int, pointer_hasher> state;
while (1) {
e = grow_list.front();
while (e) {
struct region_tree *item = e->get();
flecs::log::warn("item: %s",
(item->region.rect.operator String())
.ascii()
.ptr());
if (!item->region.can_grow) {
e = e->next();
flecs::log::err("can't grow");
continue;
}
if (!state.has(item))
state[item] = 0;
RegionRect2i candidate = item->region.rect;
switch (state[item]) {
case 0:
candidate = item->region.rect.grow(1);
break;
case 1:
candidate = item->region.rect;
candidate.size.x += 1;
break;
case 2:
candidate = item->region.rect;
candidate.size.y += 1;
break;
case 3:
candidate = item->region.rect;
candidate.position.x -= 1;
candidate.size.x += 1;
break;
case 4:
candidate = item->region.rect;
candidate.position.y -= 1;
candidate.size.y += 1;
break;
default:
state[item]++;
}
e1 = grow_list.front();
bool ok = true;
while (e1) {
struct region_tree *check_item = e1->get();
if (item != check_item) {
flecs::log::warn(
"check item: %s candidate: %s",
(check_item->region.rect
.operator String())
.ascii()
.ptr(),
(candidate.operator String())
.ascii()
.ptr());
if (!check_item->check_candidate(
candidate)) {
ok = false;
flecs::log::err("check failed");
break;
}
}
e1 = e1->next();
}
if (ok) {
struct region backup = item->region;
item->update_candidate(candidate);
if (!base_rtree->check(grid_floor_e)) {
item->region = backup;
flecs::log::err(
"can't update candidate (after checking all rects)");
if (state[item] == 0)
item->region.can_grow_square =
false;
state[item]++;
}
assert(base_rtree->check(grid_floor_e));
} else {
flecs::log::err("can't update candidate");
if (state[item] == 0)
item->region.can_grow_square = false;
state[item]++;
}
if (item->region.remains_area < -2) {
item->region.can_grow = false;
item->region.can_grow_square = false;
item->region.complete = true;
}
grow_count++;
if (item->region.can_grow && state[item] < 5)
queue.push_back(item);
else {
item->region.can_grow = false;
item->region.complete = true;
}
e = e->next();
assert(base_rtree->check(grid_floor_e));
}
if (queue.empty())
break;
while (!queue.empty()) {
grow_list.push_back(queue.front()->get());
queue.pop_front();
}
queue.clear();
assert(base_rtree->check(grid_floor_e));
}
}
bool region_tree::check(flecs::entity grid_floor_e) const
{
List<const struct region_tree *> queue;
assert(!parent);
Vector<RegionRect2i> check_regions;
Vector<RegionRect2i> root_regions;
int i, j;
if (children.size() == 0)
return true;
int grid_size = grid_floor_e
.get<WorldEditor::components::
buildings_layout_grid_floor>()
->grid_size;
for (i = 0; i < children.size(); i++)
root_regions.push_back(children[i]->region.rect);
flecs::log::dbg("root regions count: %d", root_regions.size());
queue.push_back(this);
while (!queue.empty()) {
const struct region_tree *item = queue.front()->get();
queue.pop_front();
if (item->is_leaf()) {
const RegionRect2i &rect = item->region.rect;
check_regions.push_back(rect);
}
for (i = 0; i < (int)item->children.size(); i++) {
queue.push_back(item->children[i]);
}
}
flecs::log::dbg("regions to check: %d", check_regions.size());
for (i = 0; i < (int)root_regions.size(); i++)
flecs::log::dbg(
"root region: %d %s", i,
(root_regions[i].operator String()).ascii().ptr());
for (i = 0; i < (int)check_regions.size(); i++)
flecs::log::dbg(
"check region: %d %s", i,
(check_regions[i].operator String()).ascii().ptr());
int ok = true;
for (i = 0; i < (int)check_regions.size(); i++) {
if (!region.rect.encloses(check_regions[i])) {
flecs::log::err("region is not inside parent");
ok = false;
break;
}
}
if (root_regions.size() > 0) {
for (i = 0; i < (int)check_regions.size(); i++) {
int count = 0;
if (root_regions.find(check_regions[i]) >= 0)
continue;
for (j = 0; j < (int)root_regions.size(); j++)
if (root_regions[j].encloses(check_regions[i]))
count++;
if (count == 0) {
flecs::log::err(
"region is out of root regions");
ok = false;
break;
}
}
}
if (!ok)
return ok;
for (i = 0; i < (int)check_regions.size(); i++) {
for (j = 0; j < (int)check_regions.size(); j++) {
if (i == j)
continue;
if (check_regions[i].intersects(check_regions[j]))
ok = false;
if (check_regions[i].encloses(check_regions[j]))
ok = false;
if (check_regions[j].encloses(check_regions[i]))
ok = false;
if (!ok)
break;
}
if (!ok)
break;
}
if (!ok)
flecs::log::err(
"some leaf regions intersect other leaf regions");
return ok;
}
void region_tree::place(flecs::entity grid_floor_e) const
{
int i, j, k;
assert(!parent);
List<const struct region_tree *> queue;
int grid_size = grid_floor_e
.get<WorldEditor::components::
buildings_layout_grid_floor>()
->grid_size;
LocalVector<flecs::entity> delete_cells;
grid_floor_e.children([&delete_cells](flecs::entity e) {
if (e.has<WorldEditor::components::buildings_layout_grid_cell>())
delete_cells.push_back(e);
});
for (i = 0; i < (int)delete_cells.size(); i++)
delete_cells[i].destruct();
delete_cells.clear();
#if 0
for (i = region.rect.position.x;
i < region.rect.position.x + region.rect.size.x; i++)
for (j = region.rect.position.y;
j < region.rect.position.y + region.rect.size.y; j++) {
int cell_id = i + grid_size * j;
String cname = "cell_" + itos(cell_id);
flecs::entity cell_e =
grid_floor_e.lookup(cname.ascii().ptr());
if (cell_e.is_valid())
cell_e.destruct();
}
#endif
queue.push_back(this);
bool result = true;
while (!queue.empty()) {
const struct region_tree *item = queue.front()->get();
queue.pop_front();
if (item->is_leaf()) {
const RegionRect2i &rect = item->region.rect;
for (i = rect.position.x;
i < rect.position.x + rect.size.x; i++) {
for (j = rect.position.y;
j < rect.position.y + rect.size.y; j++) {
int cell_id = i + grid_size * j;
result = check_cell(
grid_floor_e,
item->region.parent,
item->region.region_et,
cell_id);
if (!result)
break;
}
if (!result)
break;
}
}
if (!result)
break;
for (i = 0; i < (int)item->children.size(); i++) {
queue.push_back(item->children[i]);
}
}
assert(result);
queue.clear();
queue.push_back(this);
while (!queue.empty()) {
const struct region_tree *item = queue.front()->get();
queue.pop_front();
if (item->is_leaf()) {
const RegionRect2i &rect = item->region.rect;
for (i = rect.position.x;
i < rect.position.x + rect.size.x; i++)
for (j = rect.position.y;
j < rect.position.y + rect.size.y; j++) {
int cell_id = i + grid_size * j;
update_cell(grid_floor_e,
item->region.parent,
item->region.region_et,
cell_id);
}
}
for (i = 0; i < (int)item->children.size(); i++) {
queue.push_back(item->children[i]);
}
}
for (i = 0; i < children.size(); i++) {
for (j = children[i]->region.rect.position.x;
j < children[i]->region.rect.position.x +
children[i]->region.rect.size.x;
j++)
for (k = children[i]->region.rect.position.y;
k < children[i]->region.rect.position.y +
children[i]->region.rect.size.y;
k++) {
int id = j + grid_size * k;
String cname("cell_" + itos(id));
flecs::entity cell_e = grid_floor_e.lookup(
cname.ascii().ptr());
if (cell_e.is_valid())
continue;
#if 0
update_cell(grid_floor_e,
grid_floor_e.parent().id(),
grid_floor_e.id(), id);
#endif
create_corridoor_cell(
grid_floor_e,
grid_floor_e.parent().id(), id);
}
}
}
void region_tree::get_rects(List<RegionRect2i> *rect_list) const
{
List<const struct region_tree *> queue;
queue.push_back(this);
while (!queue.empty()) {
int i;
const struct region_tree *item = queue.front()->get();
queue.pop_front();
if (item->children.size() == 0) {
flecs::log::warn("rect: %s",
(item->region.rect.operator String())
.ascii()
.ptr());
rect_list->push_back(item->region.rect);
}
for (i = 0; i < (int)item->children.size(); i++)
queue.push_back(item->children[i]);
}
}
bool region_tree::check_candidate(int i, const RegionRect2i &candidate) const
{
int j;
bool ok = true;
if (!region.rect.encloses(candidate))
return false;
for (j = 0; j < children.size(); j++) {
if (i == j)
continue;
if (candidate.intersects(children[j]->region.rect)) {
ok = false;
break;
}
if (candidate.encloses(children[j]->region.rect)) {
ok = false;
break;
}
if (children[j]->region.rect.encloses(candidate)) {
ok = false;
break;
}
if (children[j]->region.rect.intersects(candidate)) {
ok = false;
break;
}
}
return ok;
}
bool region_tree::check_candidate(const RegionRect2i &candidate) const
{
bool ok = true;
if (parent && !parent->region.rect.encloses(candidate))
return false;
if (candidate.intersects(region.rect))
ok = false;
if (candidate.encloses(region.rect))
ok = false;
if (region.rect.encloses(candidate))
ok = false;
if (region.rect.intersects(candidate))
ok = false;
return ok;
}
flecs::entity region_tree::update_cell(flecs::entity grid_floor_e,
flecs::entity_t parent_et,
flecs::entity_t region_et, int id) const
{
flecs::entity region_e = grid_floor_e.world().entity(region_et);
flecs::entity parent_e = grid_floor_e.world().entity(parent_et);
flecs::log::dbg("create_cell: %s %d", region_e.path().c_str(), id);
String pname("cell_" + itos(id));
flecs::entity cell_e = grid_floor_e.lookup(pname.ascii().ptr());
if (!cell_e.is_valid()) {
flecs::log::warn("creating new cell %s", pname.ascii().ptr());
cell_e = grid_floor_e.world()
.entity(pname.ascii().ptr())
.child_of(grid_floor_e);
assert(cell_e.is_valid());
}
// assert(cell_e.has<WorldEditor::components::belongs>(parent_e));
/* already there */
grid_floor_e
.get_mut<WorldEditor::components::buildings_layout_grid_floor>()
->cells.insert(id);
grid_floor_e.modified<
WorldEditor::components::buildings_layout_grid_floor>();
cell_e.set<WorldEditor::components::buildings_layout_grid_cell>(
{ String(region_e.name()), id });
/* cell_e.remove<WorldEditor::components::belongs>(parent_e); */
cell_e.add<WorldEditor::components::belongs>(region_e);
if (region_e.has<WorldEditor::components::buildings_layout_room>()) {
int mcount = 0;
cell_e.each<WorldEditor::components::belongs_room>(
[&mcount, region_e, cell_e](flecs::entity e) {
flecs::log::err("adding region %s to cell %s",
region_e.path().c_str(),
cell_e.path().c_str());
flecs::log::err("already belongs to %s",
e.path().c_str());
assert(mcount == 0);
assert(region_e.id() == e.id());
mcount++;
});
if (mcount == 0)
cell_e.add<WorldEditor::components::belongs_room>(
region_e);
}
return cell_e;
}
flecs::entity region_tree::create_corridoor_cell(flecs::entity grid_floor_e,
flecs::entity_t parent_et,
int id) const
{
// flecs::entity region_e = grid_floor_e.world().entity(region_et);
flecs::entity parent_e = grid_floor_e.world().entity(parent_et);
String pname("cell_" + itos(id));
flecs::entity cell_e = grid_floor_e.lookup(pname.ascii().ptr());
if (!cell_e.is_valid()) {
flecs::log::warn("creating new cell %s", pname.ascii().ptr());
cell_e = grid_floor_e.world()
.entity(pname.ascii().ptr())
.child_of(grid_floor_e);
assert(cell_e.is_valid());
}
// assert(cell_e.has<WorldEditor::components::belongs>(parent_e));
/* already there */
grid_floor_e
.get_mut<WorldEditor::components::buildings_layout_grid_floor>()
->cells.insert(id);
grid_floor_e.modified<
WorldEditor::components::buildings_layout_grid_floor>();
cell_e.set<WorldEditor::components::buildings_layout_grid_cell>(
{ "corridoor", id });
cell_e.add<WorldEditor::components::corridoor>();
return cell_e;
}
bool region_tree::check_cell(flecs::entity grid_floor_e,
flecs::entity_t parent_et,
flecs::entity_t region_et, int id) const
{
flecs::entity region_e = grid_floor_e.world().entity(region_et);
flecs::entity parent_e = grid_floor_e.world().entity(parent_et);
flecs::log::dbg("check_cell: %s %d", region_e.path().c_str(), id);
String pname("cell_" + itos(id));
flecs::entity cell_e = grid_floor_e.lookup(pname.ascii().ptr());
if (!cell_e.is_valid())
return true;
int mcount = 0;
if (region_e.has<WorldEditor::components::buildings_layout_room>()) {
cell_e.each<WorldEditor::components::belongs_room>(
[&mcount, region_e, cell_e](flecs::entity e) {
flecs::log::err(
"while adding region %s to cell %s",
region_e.path().c_str(),
cell_e.path().c_str());
flecs::log::err("already belongs to %s",
e.path().c_str());
mcount++;
});
}
if (mcount == 0)
return true;
return false;
}
void region_tree::update_candidate(const RegionRect2i &candidate)
{
int orig_area = region.rect.get_area();
region.rect = candidate;
int new_area = candidate.get_area();
int avalue = MAX(0, new_area - orig_area);
region.remains_area -= avalue;
}