Quality of life in procedural town creation improved
This commit is contained in:
@@ -1091,6 +1091,112 @@ class EdgeEditorHandler {
|
||||
result = 1;
|
||||
return result;
|
||||
}
|
||||
bool pack_buildings(
|
||||
const AABB &lot_aabb,
|
||||
std::vector<struct RoadLinesData::road_edge_side::buildings>
|
||||
&buildings,
|
||||
float gap)
|
||||
{
|
||||
int i;
|
||||
Rect2 rect_lot(lot_aabb.position.x, lot_aabb.position.z,
|
||||
lot_aabb.size.x, lot_aabb.size.z);
|
||||
std::vector<Rect2> inputs, outputs;
|
||||
List<Rect2> parts;
|
||||
List<Rect2> parts_next;
|
||||
rect_lot.grow_by(-4);
|
||||
print_line("lot rect: " + (rect_lot.operator String()));
|
||||
parts.push_back(rect_lot);
|
||||
Vector<int> bad_indices;
|
||||
for (i = 0; i < (int)buildings.size(); i++) {
|
||||
if (buildings[i].id.length() == 0) {
|
||||
if (bad_indices.find(i) < 0)
|
||||
bad_indices.push_back(i);
|
||||
continue;
|
||||
}
|
||||
const AABB &aabb_building =
|
||||
BuildingsData::get_singleton()
|
||||
->building_aabbs[buildings[i].id];
|
||||
Transform building_rot(
|
||||
Basis().rotated(
|
||||
Vector3(0, 1, 0),
|
||||
Math::deg2rad(buildings[i].y_rotation)),
|
||||
Vector3());
|
||||
const AABB &aabb_building_rot =
|
||||
building_rot.xform(aabb_building);
|
||||
Rect2 input(aabb_building_rot.position.x,
|
||||
aabb_building_rot.position.z,
|
||||
aabb_building_rot.size.x,
|
||||
aabb_building_rot.size.z);
|
||||
if (input.size.x < 0.1f) {
|
||||
if (bad_indices.find(i) < 0)
|
||||
bad_indices.push_back(i);
|
||||
continue;
|
||||
}
|
||||
print_line("id: " + buildings[i].id +
|
||||
" input: " + (input.operator String()));
|
||||
input.grow_by(gap);
|
||||
inputs.push_back(input);
|
||||
}
|
||||
bad_indices.invert();
|
||||
for (i = 0; i < (int)bad_indices.size(); i++)
|
||||
buildings.erase(buildings.begin() + bad_indices[i]);
|
||||
if (buildings.size() < 2)
|
||||
return false;
|
||||
for (i = 0; i < (int)inputs.size(); i++) {
|
||||
bool found = false;
|
||||
List<Rect2>::Element *e = parts.front();
|
||||
while (e) {
|
||||
Rect2 part = e->get();
|
||||
if (part.size.x >= inputs[i].size.x &&
|
||||
part.size.y >= inputs[i].size.y) {
|
||||
found = true;
|
||||
Rect2 output(part.position.x,
|
||||
part.position.y,
|
||||
inputs[i].size.x,
|
||||
inputs[i].size.y);
|
||||
outputs.push_back(output);
|
||||
Rect2 part1(part.position.x,
|
||||
part.position.y +
|
||||
inputs[i].size.y,
|
||||
inputs[i].size.x,
|
||||
part.size.y -
|
||||
inputs[i].size.y);
|
||||
Rect2 part2(part.position.x +
|
||||
inputs[i].size.x,
|
||||
part.position.y,
|
||||
part.size.x -
|
||||
inputs[i].size.x,
|
||||
part.size.y);
|
||||
parts_next.push_back(part1);
|
||||
parts_next.push_back(part2);
|
||||
break;
|
||||
} else
|
||||
parts_next.push_back(part);
|
||||
e = e->next();
|
||||
}
|
||||
if (!found) {
|
||||
print_line("can't fit " + itos(i));
|
||||
break;
|
||||
}
|
||||
parts = parts_next;
|
||||
}
|
||||
if (outputs.size() < buildings.size())
|
||||
return false;
|
||||
Rect2 result;
|
||||
for (i = 0; i < (int)outputs.size(); i++) {
|
||||
print_line("output: " + itos(i) +
|
||||
(outputs[i].operator String()));
|
||||
result = result.merge(outputs[i]);
|
||||
}
|
||||
for (i = 0; i < (int)outputs.size(); i++) {
|
||||
/* FIXME: why ? */
|
||||
Vector2 center =
|
||||
outputs[i].get_center() - result.get_center();
|
||||
buildings[i].offsets.x = center.x;
|
||||
buildings[i].offsets.z = center.y;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void event_handler(const String &event, const Vector<Variant> &args)
|
||||
{
|
||||
if (event == "road_lines_edge_editor::edit") {
|
||||
@@ -1133,6 +1239,9 @@ class EdgeEditorHandler {
|
||||
case 200:
|
||||
pname = "clear";
|
||||
break;
|
||||
case 201:
|
||||
pname = "clear-buildings";
|
||||
break;
|
||||
default:
|
||||
pname = menu->get_item_metadata(item_index);
|
||||
break;
|
||||
@@ -1150,6 +1259,10 @@ class EdgeEditorHandler {
|
||||
get_edge_conf<float>(
|
||||
pname, "left",
|
||||
"lot_y_rotation");
|
||||
rl.edges[index].left.lot_offset =
|
||||
get_edge_conf<float>(
|
||||
pname, "left",
|
||||
"lot_offset");
|
||||
float dir_offt =
|
||||
rl.points[index + 1]
|
||||
.origin.distance_to(
|
||||
@@ -1159,6 +1272,32 @@ class EdgeEditorHandler {
|
||||
rl.edges[index].left.lot_dir_offset =
|
||||
dir_offt;
|
||||
}
|
||||
} else if (pname.begins_with("residental-")) {
|
||||
struct RoadLinesData::road_edge_side::buildings
|
||||
b;
|
||||
b.id = pname;
|
||||
b.offsets = Vector3();
|
||||
b.y_rotation = 0.0f;
|
||||
|
||||
rl.edges[index].left.buildings.push_back(b);
|
||||
if (rl.edges[index].left.lot > 0) {
|
||||
String lot_id =
|
||||
rl.edges[index].left.lot_type;
|
||||
if (rl.edges[index]
|
||||
.left.buildings.size() >
|
||||
1) {
|
||||
const AABB &aabb_lot =
|
||||
BuildingsData::get_singleton()
|
||||
->building_aabbs
|
||||
["lot-" +
|
||||
lot_id];
|
||||
pack_buildings(
|
||||
aabb_lot,
|
||||
rl.edges[index]
|
||||
.left.buildings,
|
||||
2.0f);
|
||||
}
|
||||
}
|
||||
} else if (pname == "clear") {
|
||||
rl.edges[index].left.lot_type = "";
|
||||
rl.edges[index].left.lot = 0;
|
||||
@@ -1175,6 +1314,9 @@ class EdgeEditorHandler {
|
||||
case 200:
|
||||
pname = "clear";
|
||||
break;
|
||||
case 201:
|
||||
pname = "clear-buildings";
|
||||
break;
|
||||
default:
|
||||
pname = menu->get_item_metadata(item_index);
|
||||
break;
|
||||
@@ -1195,6 +1337,10 @@ class EdgeEditorHandler {
|
||||
get_edge_conf<float>(
|
||||
pname, "right",
|
||||
"lot_y_rotation");
|
||||
rl.edges[index].right.lot_offset =
|
||||
get_edge_conf<float>(
|
||||
pname, "right",
|
||||
"lot_offset");
|
||||
float dir_offt =
|
||||
rl.points[index + 1]
|
||||
.origin.distance_to(
|
||||
@@ -1204,6 +1350,33 @@ class EdgeEditorHandler {
|
||||
rl.edges[index].right.lot_dir_offset =
|
||||
dir_offt;
|
||||
}
|
||||
} else if (pname.begins_with("residental-") ||
|
||||
pname.begins_with("business-")) {
|
||||
struct RoadLinesData::road_edge_side::buildings
|
||||
b;
|
||||
b.id = pname;
|
||||
b.offsets = Vector3();
|
||||
b.y_rotation = 0.0f;
|
||||
|
||||
rl.edges[index].right.buildings.push_back(b);
|
||||
if (rl.edges[index].right.lot > 0) {
|
||||
String lot_id =
|
||||
rl.edges[index].right.lot_type;
|
||||
if (rl.edges[index]
|
||||
.right.buildings.size() >
|
||||
1) {
|
||||
const AABB &aabb_lot =
|
||||
BuildingsData::get_singleton()
|
||||
->building_aabbs
|
||||
["lot-" +
|
||||
lot_id];
|
||||
pack_buildings(
|
||||
aabb_lot,
|
||||
rl.edges[index]
|
||||
.right.buildings,
|
||||
2.0f);
|
||||
}
|
||||
}
|
||||
} else if (pname == "clear") {
|
||||
rl.edges[index].right.lot_type = "";
|
||||
rl.edges[index].right.lot = 0;
|
||||
@@ -1253,74 +1426,124 @@ public:
|
||||
v->call_deferred("add_child", sep);
|
||||
v->call_deferred("add_child", h2);
|
||||
side_handler(v, right, "right");
|
||||
MenuButton *mb = memnew(MenuButton);
|
||||
v->call_deferred("add_child", mb);
|
||||
mb->set_text("Action...");
|
||||
MenuButton *mb_left = memnew(MenuButton);
|
||||
v->call_deferred("add_child", mb_left);
|
||||
mb_left->set_text("Select left lot...");
|
||||
MenuButton *mb_right = memnew(MenuButton);
|
||||
mb_right->set_text("Select right lot...");
|
||||
v->call_deferred("add_child", mb_right);
|
||||
Error err = stream_conf.load("res://config/stream.conf");
|
||||
assert(err == OK);
|
||||
struct menu_button_config {
|
||||
String caption;
|
||||
std::vector<std::pair<String, int> > *menu_items;
|
||||
String filter;
|
||||
String msg;
|
||||
String event;
|
||||
};
|
||||
std::vector<std::pair<String, int> > menu_items = {
|
||||
{ "Move lots starting from current edge to next edges",
|
||||
100 },
|
||||
};
|
||||
std::vector<std::pair<String, int> > menu_items_leftright = {
|
||||
{ "Clear current lot and building(s)", 200 },
|
||||
{ "Clear current building(s)", 201 },
|
||||
};
|
||||
|
||||
for (i = 0; i < (int)menu_items.size(); i++)
|
||||
mb->get_popup()->add_item(menu_items[i].first,
|
||||
menu_items[i].second);
|
||||
for (i = 0; i < (int)menu_items_leftright.size(); i++) {
|
||||
mb_left->get_popup()->add_item(
|
||||
menu_items_leftright[i].first,
|
||||
menu_items_leftright[i].second);
|
||||
mb_right->get_popup()->add_item(
|
||||
menu_items_leftright[i].first,
|
||||
menu_items_leftright[i].second);
|
||||
}
|
||||
int item_id = 1000;
|
||||
struct menu_button_config buttons[] = {
|
||||
{
|
||||
.caption = "Action...",
|
||||
.menu_items = &menu_items,
|
||||
.filter = "",
|
||||
.event = event_prefix + "::menu",
|
||||
},
|
||||
{
|
||||
.caption = "Select left lot...",
|
||||
.menu_items = &menu_items_leftright,
|
||||
.filter = "lot-",
|
||||
.msg = "Create lot ",
|
||||
.event = event_prefix + "::menu::left",
|
||||
},
|
||||
{
|
||||
.caption = "Add left house...",
|
||||
.menu_items = nullptr,
|
||||
.filter = "residental-",
|
||||
.msg = "Add house ",
|
||||
.event = event_prefix + "::menu::left",
|
||||
},
|
||||
{
|
||||
.caption = "Add left business...",
|
||||
.menu_items = nullptr,
|
||||
.filter = "business-",
|
||||
.msg = "Add business ",
|
||||
.event = event_prefix + "::menu::left",
|
||||
},
|
||||
{
|
||||
.caption = "Select right lot...",
|
||||
.menu_items = &menu_items_leftright,
|
||||
.filter = "lot-",
|
||||
.msg = "Create lot ",
|
||||
.event = event_prefix + "::menu::right",
|
||||
},
|
||||
{
|
||||
.caption = "Add right house...",
|
||||
.menu_items = nullptr,
|
||||
.filter = "residental-",
|
||||
.msg = "Add house ",
|
||||
.event = event_prefix + "::menu::right",
|
||||
},
|
||||
{
|
||||
.caption = "Add right business...",
|
||||
.menu_items = nullptr,
|
||||
.filter = "business-",
|
||||
.msg = "Add business ",
|
||||
.event = event_prefix + "::menu::right",
|
||||
},
|
||||
};
|
||||
Error err = stream_conf.load("res://config/stream.conf");
|
||||
assert(err == OK);
|
||||
Dictionary bdata =
|
||||
stream_conf.get_value("buildings", "building_data");
|
||||
int item_id = 1000;
|
||||
List<Variant> building_list;
|
||||
List<Variant>::Element *el;
|
||||
bdata.get_key_list(&building_list);
|
||||
el = building_list.front();
|
||||
while (el) {
|
||||
String pname = el->get();
|
||||
String msg;
|
||||
if (pname.begins_with("lot-"))
|
||||
msg = "Create lot: " + pname;
|
||||
else if (pname.begins_with("residental-"))
|
||||
msg = "Add house: " + pname;
|
||||
if (msg.length() > 0 && pname.begins_with("lot-")) {
|
||||
std::vector<PopupMenu *> menus = {
|
||||
mb_left->get_popup(),
|
||||
mb_right->get_popup()
|
||||
};
|
||||
for (i = 0; i < (int)menus.size(); i++) {
|
||||
menus[i]->add_item(msg, item_id);
|
||||
int item_index =
|
||||
menus[i]->get_item_index(
|
||||
item_id);
|
||||
menus[i]->set_item_metadata(item_index,
|
||||
pname);
|
||||
item_id++;
|
||||
for (i = 0; i < (int)sizeof(buttons) / (int)sizeof(buttons[0]);
|
||||
i++) {
|
||||
int j;
|
||||
MenuButton *mb = memnew(MenuButton);
|
||||
v->call_deferred("add_child", mb);
|
||||
mb->set_text(buttons[i].caption);
|
||||
if (buttons[i].menu_items) {
|
||||
for (j = 0;
|
||||
j < (int)buttons[i].menu_items->size();
|
||||
j++)
|
||||
mb->get_popup()->add_item(
|
||||
buttons[i]
|
||||
.menu_items->data()[j]
|
||||
.first,
|
||||
buttons[i]
|
||||
.menu_items->data()[j]
|
||||
.second);
|
||||
}
|
||||
if (buttons[i].filter.length() > 0) {
|
||||
List<Variant> building_list;
|
||||
bdata.get_key_list(&building_list);
|
||||
el = building_list.front();
|
||||
while (el) {
|
||||
String pname = el->get();
|
||||
if (pname.begins_with(
|
||||
buttons[i].filter)) {
|
||||
String message =
|
||||
buttons[i].msg + " " +
|
||||
pname;
|
||||
mb->get_popup()->add_item(
|
||||
message, item_id);
|
||||
int item_index =
|
||||
mb->get_popup()
|
||||
->get_item_index(
|
||||
item_id);
|
||||
mb->get_popup()
|
||||
->set_item_metadata(
|
||||
item_index,
|
||||
pname);
|
||||
item_id++;
|
||||
}
|
||||
el = el->next();
|
||||
}
|
||||
}
|
||||
el = el->next();
|
||||
menu_handlers.push_back(memnew(PopupMenuSelectHandler(
|
||||
mb->get_popup(), buttons[i].event)));
|
||||
}
|
||||
menu_handlers.push_back(memnew(PopupMenuSelectHandler(
|
||||
mb->get_popup(), event_prefix + "::menu")));
|
||||
menu_handlers.push_back(memnew(PopupMenuSelectHandler(
|
||||
mb_left->get_popup(), event_prefix + "::menu::left")));
|
||||
menu_handlers.push_back(memnew(PopupMenuSelectHandler(
|
||||
mb_right->get_popup(),
|
||||
event_prefix + "::menu::right")));
|
||||
EditorEvent::get_singleton()->event.add_listener(
|
||||
this, &EdgeEditorHandler::event_handler);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user