623 lines
17 KiB
C++
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> ®ions)
|
|
{
|
|
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;
|
|
}
|