Furniture placement

This commit is contained in:
2026-01-09 22:27:51 +03:00
parent 272e202774
commit f86e7fd96c
11 changed files with 1089 additions and 326 deletions

View File

@@ -199,9 +199,9 @@ void setupLods(Ogre::LodConfig &config)
config.advanced.preventPunchingHoles = true;
config.advanced.preventBreakingLines = true;
config.createGeneratedLodLevel(10, 0.15f);
config.createGeneratedLodLevel(50, 0.25f);
config.createGeneratedLodLevel(100, 0.36f);
config.createGeneratedLodLevel(200, 0.50f);
// config.createGeneratedLodLevel(50, 0.25f);
// config.createGeneratedLodLevel(100, 0.36f);
// config.createGeneratedLodLevel(200, 0.50f);
config.createGeneratedLodLevel(500, 0.85f);
config.advanced.useBackgroundQueue = false;
Ogre::MeshLodGenerator::getSingleton().generateLodLevels(config);

View File

@@ -19,7 +19,7 @@
#include "items.h"
#include "town.h"
/* TODO: Create roofs with the same Lua script as tiles.
/*
* TODO: Create doors and handle them via script.
* TODO: Handle doors direction.
* TODO: Hide hero mesh if camera is too close.
@@ -174,6 +174,7 @@ struct CellsScript {
Ogre::String cellScript;
nlohmann::json &lot;
nlohmann::json &cells;
nlohmann::json &fucells;
CellsScript(const Ogre::String &cellScript, nlohmann::json &lot)
: L(luaL_newstate())
, currentX(0)
@@ -182,6 +183,7 @@ struct CellsScript {
, cellScript(cellScript)
, lot(lot)
, cells(lot["cells"])
, fucells(lot["furniture_cells"])
{
lua_pushlightuserdata(L, this);
lua_pushcclosure(
@@ -277,6 +279,19 @@ struct CellsScript {
1);
lua_setglobal(L, "roof");
lua_pushlightuserdata(L, this);
lua_pushcclosure(
L,
[](lua_State *L) -> int {
CellsScript *_this = static_cast<CellsScript *>(
lua_touserdata(L, lua_upvalueindex(1)));
int room = lua_tointeger(L, 1);
Ogre::String tags = lua_tostring(L, 2);
_this->place_furniture(room, tags);
return 0;
},
1);
lua_setglobal(L, "place_furniture");
lua_pushlightuserdata(L, this);
lua_pushcclosure(
L,
[](lua_State *L) -> int {
@@ -292,6 +307,22 @@ struct CellsScript {
1);
lua_setglobal(L, "clear_area");
lua_pushlightuserdata(L, this);
lua_pushcclosure(
L,
[](lua_State *L) -> int {
CellsScript *_this = static_cast<CellsScript *>(
lua_touserdata(L, lua_upvalueindex(1)));
int minX = lua_tointeger(L, 1);
int minZ = lua_tointeger(L, 2);
int sizeX = lua_tointeger(L, 3);
int sizeZ = lua_tointeger(L, 4);
_this->clear_furniture_area(minX, minZ, sizeX,
sizeZ);
return 0;
},
1);
lua_setglobal(L, "clear_furniture_area");
lua_pushlightuserdata(L, this);
lua_pushcclosure(
L,
[](lua_State *L) -> int {
@@ -430,6 +461,11 @@ struct CellsScript {
for (auto &cell : cells)
cell["id"] = makeCellKey(cell);
}
void buildFCellIndex()
{
for (auto &fucell : fucells)
fucell["id"] = makeCellKey(fucell);
}
int findCell(int X, int Y, int Z)
{
int64_t key = makeCellKey(X, Y, Z);
@@ -444,6 +480,20 @@ struct CellsScript {
}
return result;
}
int findFCell(int X, int Y, int Z)
{
int64_t key = makeCellKey(X, Y, Z);
int result = -1;
int i;
for (i = 0; i < fucells.size(); i++) {
int64_t ckey = makeCellKey(fucells[i]);
if (key == ckey) {
result = i;
break;
}
}
return result;
}
void bitCmd(const Ogre::String &cmd, const Ogre::String &bit)
{
uint64_t flags = 0;
@@ -560,6 +610,22 @@ struct CellsScript {
clear();
}
}
void clear_furniture()
{
int index = findFCell(currentX, currentY, currentZ);
if (index >= 0)
fucells.erase(index);
}
void clear_furniture_area(int minX, int minZ, int sizeX, int sizeZ)
{
int i, j;
for (i = 0; i < sizeZ; i++)
for (j = 0; j < sizeX; j++) {
currentX = minX + j;
currentZ = minZ + i;
clear_furniture();
}
}
int room(int minX, int minZ, int sizeX, int sizeZ)
{
int i, j, ret;
@@ -621,6 +687,250 @@ struct CellsScript {
{
lot["roofs"] = nlohmann::json::array();
}
void fcell(const std::vector<Ogre::String> &tags,
nlohmann::json furniture, int rotation)
{
nlohmann::json jtags = nlohmann::json::array();
for (const auto &tag : tags)
jtags.push_back(tag);
if (findFCell(currentX, currentY, currentZ) == -1) {
nlohmann::json fucell;
buildFCellIndex();
fucell["x"] = currentX;
fucell["y"] = currentY;
fucell["z"] = currentZ;
fucell["tags"] = jtags;
fucell["id"] = makeCellKey(fucell);
fucell["furniture"] = furniture;
fucell["rotation"] = rotation;
fucells.push_back(fucell);
} else {
int index = findFCell(currentX, currentY, currentZ);
if (index >= 0) {
fucells[index]["tags"] = jtags;
fucells[index]["furniture"] = furniture;
fucells[index]["rotation"] = rotation;
}
}
}
nlohmann::json select_furniture(const std::vector<Ogre::String> &tags)
{
nlohmann::json adata = nlohmann::json::array();
ECS::get().query_builder<const FurnitureItem>().build().each(
[&](flecs::entity e, const FurnitureItem &item) {
bool apply = true;
for (auto &t : tags) {
if (std::find(item.tags.begin(),
item.tags.end(),
t) == item.tags.end()) {
apply = false;
break;
}
}
if (apply) {
nlohmann::json jdata =
nlohmann::json::parse(
item.properties);
adata.push_back(jdata);
}
});
// FIXME: use noise function
return adata[0];
}
void place_furniture(int room, Ogre::String tags)
{
std::vector<Ogre::String> atags = split_and_trim(tags, ',');
int i, j;
int minX = rooms[room].minX;
int minZ = rooms[room].minZ;
int sizeX = rooms[room].sizeX;
int sizeZ = rooms[room].sizeZ;
int midX = minX + sizeX / 2;
int midZ = minZ + sizeZ / 2;
std::cout << "room: " << room << " " << minX << " " << minZ
<< " " << sizeX << " " << sizeZ << std::endl;
clear_furniture_area(minX, minZ, sizeX, sizeZ);
currentX = midX;
currentZ = minZ;
bool placed = false;
if (isBit("iwallz-") && !isBit("idoorz+") &&
!isBit("idoorz-") && !isBit("idoorx+") &&
!isBit("idoorx-") && !placed) {
std::vector<Ogre::String> ptags = atags;
ptags.push_back("essential");
nlohmann::json furniture = select_furniture(ptags);
std::cout << currentX << " " << currentY << " "
<< currentZ << std::endl;
fcell(ptags, furniture, 2);
placed = true;
}
currentX = midX;
currentZ = minZ + sizeZ - 1;
if (isBit("iwallz+") && !isBit("idoorz+") &&
!isBit("idoorz-") && !isBit("idoorx+") &&
!isBit("idoorx-") && !placed) {
std::vector<Ogre::String> ptags = atags;
ptags.push_back("essential");
nlohmann::json furniture = select_furniture(ptags);
std::cout << currentX << " " << currentY << " "
<< currentZ << std::endl;
fcell(ptags, furniture, 0);
placed = true;
}
currentX = midX;
currentZ = midZ;
if (isBit("iwallx-") && !isBit("idoorz+") &&
!isBit("idoorz-") && !isBit("idoorx+") &&
!isBit("idoorx-") && !placed) {
std::vector<Ogre::String> ptags = atags;
ptags.push_back("essential");
nlohmann::json furniture = select_furniture(ptags);
std::cout << currentX << " " << currentY << " "
<< currentZ << std::endl;
fcell(ptags, furniture, 1);
placed = true;
}
currentX = midX + sizeX - 1;
currentZ = midZ;
if (isBit("iwallx+") && !isBit("idoorz+") &&
!isBit("idoorz-") && !isBit("idoorx+") &&
!isBit("idoorx-") && !placed) {
std::vector<Ogre::String> ptags = atags;
ptags.push_back("essential");
nlohmann::json furniture = select_furniture(ptags);
std::cout << currentX << " " << currentY << " "
<< currentZ << std::endl;
fcell(ptags, furniture, 3);
placed = true;
}
if (placed)
return;
if (isBit("windowz-") && !isBit("idoorz+") &&
!isBit("idoorz-") && !isBit("idoorx+") &&
!isBit("idoorx-") && !placed) {
std::vector<Ogre::String> ptags = atags;
ptags.push_back("essential");
nlohmann::json furniture = select_furniture(ptags);
std::cout << currentX << " " << currentY << " "
<< currentZ << std::endl;
fcell(ptags, furniture, 2);
placed = true;
}
currentX = midX;
currentZ = minZ + sizeZ - 1;
if (isBit("windowz+") && !isBit("idoorz+") &&
!isBit("idoorz-") && !isBit("idoorx+") &&
!isBit("idoorx-") && !placed) {
std::vector<Ogre::String> ptags = atags;
ptags.push_back("essential");
nlohmann::json furniture = select_furniture(ptags);
std::cout << currentX << " " << currentY << " "
<< currentZ << std::endl;
fcell(ptags, furniture, 0);
placed = true;
}
currentX = midX;
currentZ = midZ;
if (isBit("windowx-") && !isBit("idoorz+") &&
!isBit("idoorz-") && !isBit("idoorx+") &&
!isBit("idoorx-") && !placed) {
std::vector<Ogre::String> ptags = atags;
ptags.push_back("essential");
nlohmann::json furniture = select_furniture(ptags);
std::cout << currentX << " " << currentY << " "
<< currentZ << std::endl;
fcell(ptags, furniture, 1);
placed = true;
}
currentX = midX + sizeX - 1;
currentZ = midZ;
if (isBit("windowx+") && !isBit("idoorz+") &&
!isBit("idoorz-") && !isBit("idoorx+") &&
!isBit("idoorx-") && !placed) {
std::vector<Ogre::String> ptags = atags;
ptags.push_back("essential");
nlohmann::json furniture = select_furniture(ptags);
std::cout << currentX << " " << currentY << " "
<< currentZ << std::endl;
fcell(ptags, furniture, 3);
placed = true;
}
if (placed)
return;
for (i = 1; i < sizeX - 1; i++) {
currentX = minX + i;
currentZ = minZ;
if (isBit("iwallz-") && !isBit("idoorz+") &&
!isBit("idoorz-") && !isBit("idoorx+") &&
!isBit("idoorx-") && !placed) {
std::vector<Ogre::String> ptags = atags;
ptags.push_back("essential");
nlohmann::json furniture =
select_furniture(ptags);
std::cout << currentX << " " << currentY << " "
<< currentZ << std::endl;
fcell(ptags, furniture, 2);
placed = true;
break;
}
}
if (placed)
return;
for (i = 1; i < sizeX - 1; i++) {
currentX = minX + i;
currentZ = minZ + sizeZ - 1;
if (isBit("iwallz+") && !isBit("idoorz+") &&
!isBit("idoorz-") && !isBit("idoorx+") &&
!isBit("idoorx-") && !placed) {
std::vector<Ogre::String> ptags = atags;
ptags.push_back("essential");
nlohmann::json furniture =
select_furniture(ptags);
std::cout << currentX << " " << currentY << " "
<< currentZ << std::endl;
fcell(ptags, furniture, 0);
placed = true;
break;
}
}
if (placed)
return;
for (i = 1; i < sizeZ - 1; i++) {
currentX = minX;
currentZ = minZ + i;
if (isBit("iwallx-") && !isBit("idoorz+") &&
!isBit("idoorz-") && !isBit("idoorx+") &&
!isBit("idoorx-") && !placed) {
std::vector<Ogre::String> ptags = atags;
ptags.push_back("essential");
nlohmann::json furniture =
select_furniture(ptags);
std::cout << currentX << " " << currentY << " "
<< currentZ << std::endl;
fcell(ptags, furniture, 1);
placed = true;
break;
}
}
for (i = 1; i < sizeZ - 1; i++) {
currentX = minX + sizeX - 1;
currentZ = minZ + i;
if (isBit("iwallx+") && !isBit("idoorz+") &&
!isBit("idoorz-") && !isBit("idoorx+") &&
!isBit("idoorx-") && !placed) {
std::vector<Ogre::String> ptags = atags;
ptags.push_back("essential");
nlohmann::json furniture =
select_furniture(ptags);
std::cout << currentX << " " << currentY << " "
<< currentZ << std::endl;
fcell(ptags, furniture, 3);
placed = true;
break;
}
}
}
std::vector<std::pair<int, int> > roomEdge(int room)
{
std::vector<std::pair<int, int> > seg;
@@ -1235,16 +1545,21 @@ bool editLot(const Ogre::String &lotLabel, nlohmann::json &lot)
nlohmann::json cells = nlohmann::json::array();
static char text_buffer[16384];
strcpy(text_buffer, "");
/* copy script data from json to a buffer */
if (lot.find("cellScript") != lot.end()) {
Ogre::String script = lot["cellScript"].get<Ogre::String>();
strncpy(text_buffer, script.c_str(), sizeof(text_buffer));
}
/* edit a buffer and if changed, copy a buffer back to json */
if (ImGui::InputTextMultiline(("Cells Script##" + lotLabel).c_str(),
text_buffer, IM_ARRAYSIZE(text_buffer),
ImVec2(1000,
ImGui::GetTextLineHeight() * 20),
ImGuiInputTextFlags_AllowTabInput)) {
lot["cellScript"] = Ogre::String(text_buffer);
}
if (ImGui::IsItemDeactivatedAfterEdit()) {
lot["cellScript"] = Ogre::String(text_buffer);
changed = true;
}
if (ImGui::SmallButton(("Execute script...##" + lotLabel).c_str())) {
@@ -1252,6 +1567,7 @@ bool editLot(const Ogre::String &lotLabel, nlohmann::json &lot)
script.run();
changed = true;
}
for (const auto &cell : lot["cells"])
cells.push_back(cell);
if (ImGui::CollapsingHeader(("Cells...##" + lotLabel).c_str())) {
@@ -1380,11 +1696,15 @@ bool editDistrict(const Ogre::String &districtLabel, nlohmann::json &district)
districtLabel + "_lot" +
Ogre::StringConverter::toString(lotCount);
ImGui::Text("%s", lotLabel.c_str());
changed = changed || editLot(lotLabel, lot);
ImGui::Separator();
if (ImGui::SmallButton(("Lot Delete##" + lotLabel).c_str())) {
lots.erase(lotCount);
break;
if (ImGui::CollapsingHeader(
("Edit lot... ##" + lotLabel).c_str())) {
changed = changed || editLot(lotLabel, lot);
ImGui::Separator();
if (ImGui::SmallButton(
("Lot Delete##" + lotLabel).c_str())) {
lots.erase(lotCount);
break;
}
}
lotCount++;
}
@@ -1400,9 +1720,12 @@ bool editDistrict(const Ogre::String &districtLabel, nlohmann::json &district)
changed = true;
}
district["lots"] = lots;
float radius = 50.0f;
bool plazza = false;
if (district.find("plazza") != district.end())
plazza = district["plazza"].get<bool>();
if (district.find("radius") != district.end())
radius = district["radius"].get<float>();
if (ImGui::Checkbox("Plazza", &plazza)) {
changed = true;
district["plazza"] = plazza;
@@ -1412,6 +1735,10 @@ bool editDistrict(const Ogre::String &districtLabel, nlohmann::json &district)
elevation = district["elevation"].get<float>();
if (district.find("height") != district.end())
height = district["height"].get<float>();
if (ImGui::SliderFloat("Radius", &radius, 20.0f, 100.0f)) {
district["radius"] = radius;
changed = true;
}
if (ImGui::SliderFloat("Height", &height, 0.1f, 10.0f)) {
district["height"] = height;
changed = true;
@@ -1535,8 +1862,10 @@ void createTownPopup(const std::pair<flecs::entity, Ogre::String> item)
nlohmann::json colorRects = nlohmann::json::object();
if (j.find("colorRects") != j.end())
colorRects = j["colorRects"];
changed = changed || editColorRects(colorRects);
ImGui::Separator();
if (ImGui::CollapsingHeader("Edit Color Rects")) {
changed = changed || editColorRects(colorRects);
ImGui::Separator();
}
nlohmann::json districts = nlohmann::json::array();
for (auto &district : j["districts"])
districts.push_back(district);
@@ -2565,6 +2894,122 @@ void createTownDoors(flecs::entity e)
}
}
}
void createDecorateFurniture(flecs::entity e, const nlohmann::json &jdistrict,
int index, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
Ogre::MaterialPtr townMaterial = createTownMaterial(e);
const nlohmann::json &jp = jdistrict;
nlohmann::json jlots = nlohmann::json::array();
float baseHeight = 4.0f;
Ogre::Vector3 localPosition(0, 0, 0);
Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY;
Ogre::Vector3 centerPosition = sceneNode->_getDerivedPosition();
Ogre::Quaternion centerOrientation =
sceneNode->_getDerivedOrientation();
float delevation = 0.0f;
float radius = 50.0f;
if (jp.find("elevation") != jp.end())
delevation = jp["elevation"].get<float>();
if (jp.find("radius") != jp.end())
radius = jp["radius"].get<float>();
from_json(jp["position"], localPosition);
from_json(jp["rotation"], localRotation);
centerPosition = centerPosition + localPosition +
Ogre::Vector3(0, delevation, 0);
centerOrientation = centerOrientation * localRotation;
if (jdistrict.find("lots") != jdistrict.end())
jlots = jdistrict["lots"];
for (const auto &jb : jlots) {
float angle = 0.0f;
int depth = 10;
int width = 10;
float distance = radius;
float elevation = 0.0f;
std::cout << jb.dump() << std::endl;
if (jb.find("angle") != jb.end())
angle = jb["angle"].get<float>();
if (jb.find("depth") != jb.end())
depth = jb["depth"].get<int>();
if (jb.find("width") != jb.end())
width = jb["width"].get<int>();
if (jb.find("elevation") != jb.end())
elevation = jb["elevation"].get<float>();
OgreAssert(width > 1 && depth > 1 && baseHeight > 1,
"Bad stuff happen");
Ogre::Quaternion rotation = Ogre::Quaternion(
Ogre::Degree(angle), Ogre::Vector3::UNIT_Y);
Ogre::Vector3 offset = centerOrientation * rotation *
(Ogre::Vector3::UNIT_Z * distance);
Ogre::Vector3 worldCenterPosition =
centerPosition + offset +
Ogre::Vector3(0, elevation, 0);
Ogre::Quaternion worldCenterOrientation =
centerOrientation * rotation;
float outOffset = 1.05f;
if (jb.find("furniture_cells") != jb.end()) {
for (auto &jfcell : jb["furniture_cells"]) {
int x = jfcell["x"].get<int>();
int y = jfcell["y"].get<int>();
int z = jfcell["z"].get<int>();
nlohmann::json furniture = jfcell["furniture"];
Ogre::Vector3 cellOffset(x * 2.0f, y * 4.0f,
z * 2.0f);
Ogre::Vector3 offsetX = worldCenterOrientation *
Ogre::Vector3::UNIT_X *
(float)x * 2.0f;
Ogre::Vector3 offsetZ = worldCenterOrientation *
Ogre::Vector3::UNIT_Z *
(float)z * 2.0f;
Ogre::Vector3 offsetY(0, y * 4.0f, 0);
if (furniture.find("mesh") != furniture.end()) {
Ogre::String meshName =
furniture["mesh"]
.get<Ogre::String>();
Ogre::MeshPtr mesh =
Ogre::MeshManager::getSingleton()
.getByName(meshName);
if (!mesh) {
mesh = Ogre::MeshManager::getSingleton()
.load(meshName,
"General");
Ogre::LodConfig meshconf(mesh);
setupLods(meshconf);
}
int rotation = 2;
if (jfcell.find("rotation") !=
jfcell.end())
rotation = jfcell["rotation"]
.get<int>();
Ogre::Vector3 offset =
worldCenterOrientation *
Ogre::Vector3::UNIT_Z * 0.0f;
Ogre::Entity *ent =
ECS::get<EngineData>()
.mScnMgr->createEntity(
meshName);
geo->addEntity(
ent,
worldCenterPosition + offsetX +
offsetZ + offsetY +
offset,
worldCenterOrientation *
Ogre::Quaternion(
Ogre::Degree(
90.0f *
(float)rotation),
Ogre::Vector3::
UNIT_Y));
ECS::get<EngineData>()
.mScnMgr->destroyEntity(ent);
}
}
}
}
}
void createDecorateDoors(flecs::entity e, const nlohmann::json &jdistrict,
int index, Ogre::SceneNode *sceneNode,
@@ -3049,6 +3494,8 @@ void createTown(flecs::entity e, Ogre::SceneNode *sceneNode,
geo);
createDecorateDoors(e, jdistrict, index, sceneNode,
geo);
createDecorateFurniture(e, jdistrict, index, sceneNode,
geo);
index++;
}
}