Furiture placement works
This commit is contained in:
@@ -207,6 +207,8 @@ struct RoomComponent {
|
||||
bool createCeiling = true; // Create ceiling cells
|
||||
bool createInteriorWalls = true; // Create iwallx-/+, iwallz-/+ around the room
|
||||
bool createWindows = false; // Convert exterior-facing walls to windows
|
||||
bool fillRoomWithFurniture = false; // Automatically place furniture based on tags
|
||||
unsigned int furnitureSeed = 42; // Seed for deterministic furniture placement
|
||||
|
||||
// Dirty flag - triggers regeneration of cell grid
|
||||
bool dirty = true;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <OgreDataStream.h>
|
||||
#include <OgreLogManager.h>
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
FurnitureLibrary &FurnitureLibrary::getInstance()
|
||||
{
|
||||
@@ -112,15 +111,13 @@ std::vector<const FurnitureDefinition *> FurnitureLibrary::findByTags(
|
||||
|
||||
const FurnitureDefinition *FurnitureLibrary::selectByTags(
|
||||
const std::vector<std::string> &requiredTags,
|
||||
std::mt19937 &rng,
|
||||
const std::vector<std::string> &forbiddenTags) const
|
||||
{
|
||||
auto candidates = findByTags(requiredTags, forbiddenTags);
|
||||
if (candidates.empty())
|
||||
return nullptr;
|
||||
|
||||
static std::mt19937 rng((unsigned)std::chrono::steady_clock::now()
|
||||
.time_since_epoch()
|
||||
.count());
|
||||
std::uniform_int_distribution<size_t> dist(0, candidates.size() - 1);
|
||||
return candidates[dist(rng)];
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
|
||||
struct FurnitureDefinition {
|
||||
std::string name;
|
||||
@@ -30,6 +31,7 @@ public:
|
||||
|
||||
const FurnitureDefinition *selectByTags(
|
||||
const std::vector<std::string> &requiredTags,
|
||||
std::mt19937 &rng,
|
||||
const std::vector<std::string> &forbiddenTags = {}) const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -860,6 +860,8 @@ void RoomLayoutSystem::generateRoomFurniture(RoomComponent& room,
|
||||
if (room.tags.empty())
|
||||
return;
|
||||
|
||||
std::mt19937 rng(room.furnitureSeed);
|
||||
|
||||
// Clear existing furniture in room bounds
|
||||
for (auto it = grid.furnitureCells.begin();
|
||||
it != grid.furnitureCells.end();) {
|
||||
@@ -889,12 +891,6 @@ void RoomLayoutSystem::generateRoomFurniture(RoomComponent& room,
|
||||
CellFlags::IntWindowXNeg | CellFlags::WindowXNeg,
|
||||
CellFlags::IntWindowXPos | CellFlags::WindowXPos
|
||||
};
|
||||
const uint64_t DOOR_FLAGS_BY_SIDE[4] = {
|
||||
CellFlags::IntDoorZNeg | CellFlags::DoorZNeg,
|
||||
CellFlags::IntDoorZPos | CellFlags::DoorZPos,
|
||||
CellFlags::IntDoorXNeg | CellFlags::DoorXNeg,
|
||||
CellFlags::IntDoorXPos | CellFlags::DoorXPos
|
||||
};
|
||||
|
||||
auto cellHasAnyDoor = [&](int x, int y, int z) -> bool {
|
||||
const Cell *cell = grid.findCell(x, y, z);
|
||||
@@ -908,61 +904,61 @@ void RoomLayoutSystem::generateRoomFurniture(RoomComponent& room,
|
||||
return false;
|
||||
return (cell->flags & CellFlags::AllWindows) != 0;
|
||||
};
|
||||
auto cellHasAnyWall = [&](int x, int y, int z) -> bool {
|
||||
const Cell *cell = grid.findCell(x, y, z);
|
||||
if (!cell)
|
||||
return false;
|
||||
return (cell->flags & CellFlags::AllWalls) != 0;
|
||||
};
|
||||
|
||||
auto canPlaceFurniture = [&](const FurnitureDefinition *def,
|
||||
int x, int y, int z) -> bool {
|
||||
if (!def)
|
||||
return false;
|
||||
// If cell has any door, furniture must have "door" tag
|
||||
if (cellHasAnyDoor(x, y, z)) {
|
||||
if (std::find(def->tags.begin(), def->tags.end(),
|
||||
"door") == def->tags.end())
|
||||
return false;
|
||||
}
|
||||
// If cell has any window, furniture must NOT have "nowindow" tag
|
||||
if (cellHasAnyWindow(x, y, z)) {
|
||||
if (std::find(def->tags.begin(), def->tags.end(),
|
||||
"nowindow") != def->tags.end())
|
||||
return false;
|
||||
}
|
||||
// If cell has no walls, furniture must have "nowall" tag
|
||||
if (!cellHasAnyWall(x, y, z)) {
|
||||
if (std::find(def->tags.begin(), def->tags.end(),
|
||||
"nowall") == def->tags.end())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
auto tryPlace = [&](int cx, int cy, int cz, int side,
|
||||
const std::vector<std::string> &tags,
|
||||
const std::vector<std::string> ¬ags,
|
||||
bool onWindow,
|
||||
std::mt19937 &rng) -> const FurnitureDefinition * {
|
||||
if (grid.findFurnitureCell(cx, cy, cz))
|
||||
return nullptr;
|
||||
const Cell *cell = grid.findCell(cx, cy, cz);
|
||||
if (!cell)
|
||||
return nullptr;
|
||||
uint64_t sideFlags = onWindow ? WINDOW_FLAGS_BY_SIDE[side] :
|
||||
WALL_FLAGS_BY_SIDE[side];
|
||||
if ((cell->flags & sideFlags) == 0)
|
||||
return nullptr;
|
||||
auto *def = FurnitureLibrary::getInstance().selectByTags(
|
||||
tags, rng, notags);
|
||||
if (!def)
|
||||
return nullptr;
|
||||
if (!canPlaceFurniture(def, cx, cy, cz))
|
||||
return nullptr;
|
||||
return def;
|
||||
};
|
||||
|
||||
auto placeOne = [&](int cx, int cy, int cz, int side,
|
||||
const std::vector<std::string> &tags,
|
||||
const std::vector<std::string> &fallbackTags,
|
||||
const std::vector<std::string> ¬ags,
|
||||
bool onWindow) -> bool {
|
||||
if (grid.findFurnitureCell(cx, cy, cz))
|
||||
return false;
|
||||
const Cell *cell = grid.findCell(cx, cy, cz);
|
||||
if (!cell)
|
||||
return false;
|
||||
|
||||
uint64_t sideFlags = onWindow ? WINDOW_FLAGS_BY_SIDE[side] :
|
||||
WALL_FLAGS_BY_SIDE[side];
|
||||
if ((cell->flags & sideFlags) == 0)
|
||||
return false;
|
||||
|
||||
auto *def = FurnitureLibrary::getInstance().selectByTags(
|
||||
tags, notags);
|
||||
bool onWindow,
|
||||
std::mt19937 &rng) -> bool {
|
||||
auto *def = tryPlace(cx, cy, cz, side, tags, notags,
|
||||
onWindow, rng);
|
||||
if (!def && !fallbackTags.empty()) {
|
||||
def = tryPlace(cx, cy, cz, side, fallbackTags,
|
||||
notags, onWindow, rng);
|
||||
}
|
||||
if (!def)
|
||||
return false;
|
||||
|
||||
if (!canPlaceFurniture(def, cx, cy, cz))
|
||||
return false;
|
||||
|
||||
auto &fcell = grid.getOrCreateFurnitureCell(cx, cy, cz);
|
||||
fcell.furnitureType = def->name;
|
||||
fcell.rotation = ROTATION_BY_SIDE[side];
|
||||
@@ -982,60 +978,91 @@ void RoomLayoutSystem::generateRoomFurniture(RoomComponent& room,
|
||||
|
||||
struct TagData {
|
||||
std::vector<std::string> tags;
|
||||
std::vector<std::string> fallbackTags;
|
||||
std::vector<std::string> notagsWalls;
|
||||
std::vector<std::string> notagsWindows;
|
||||
bool essential;
|
||||
bool filler;
|
||||
};
|
||||
TagData taglist[] = {
|
||||
{ etags, { "nowall", "nowindow" }, { "nowindow" }, true,
|
||||
std::vector<TagData> taglist = {
|
||||
{ etags, {}, { "nowall", "nowindow" }, { "nowindow" }, true,
|
||||
false },
|
||||
{ itags, { "nowall", "nowindow" }, { "nowindow" }, false,
|
||||
{ itags, {}, { "nowall", "nowindow" }, { "nowindow" }, false,
|
||||
false },
|
||||
{ otags, { "nowall", "nowindow" }, { "nowindow" }, false,
|
||||
{ otags, {}, { "nowall", "nowindow" }, { "nowindow" }, false,
|
||||
false },
|
||||
{ ftags, { "nowall", "nowindow" }, { "nowindow" }, false,
|
||||
true },
|
||||
{ ftags,
|
||||
room.fillRoomWithFurniture ? baseTags :
|
||||
std::vector<std::string>(),
|
||||
{ "nowall", "nowindow" },
|
||||
{ "nowindow" }, false, true },
|
||||
};
|
||||
|
||||
// Midpoints for each side
|
||||
std::tuple<int, int, int> mpoints[4] = {
|
||||
// Midpoints for each side - original has 8 entries (each wall twice)
|
||||
std::vector<std::tuple<int, int, int>> mpoints = {
|
||||
{ midX, room.minY, room.minZ }, // side 0 (Z-)
|
||||
{ midX, room.minY, room.maxZ - 1 }, // side 1 (Z+)
|
||||
{ room.minX, room.minY, midZ }, // side 2 (X-)
|
||||
{ room.maxX - 1, room.minY, midZ }, // side 3 (X+)
|
||||
{ midX, room.minY, room.minZ }, // side 0 again
|
||||
{ midX, room.minY, room.maxZ - 1 }, // side 1 again
|
||||
{ room.minX, room.minY, midZ }, // side 2 again
|
||||
{ room.maxX - 1, room.minY, midZ }, // side 3 again
|
||||
};
|
||||
|
||||
for (const auto &mtags : taglist) {
|
||||
bool placed = false;
|
||||
|
||||
// Try wall midpoints
|
||||
for (int side = 0; side < 4 && !placed; ++side) {
|
||||
int cx = std::get<0>(mpoints[side]);
|
||||
int cy = std::get<1>(mpoints[side]);
|
||||
int cz = std::get<2>(mpoints[side]);
|
||||
placed = placeOne(cx, cy, cz, side, mtags.tags,
|
||||
mtags.notagsWalls, false);
|
||||
if (placed && !mtags.filler)
|
||||
break;
|
||||
for (auto &p : mpoints) {
|
||||
int cx = std::get<0>(p);
|
||||
int cy = std::get<1>(p);
|
||||
int cz = std::get<2>(p);
|
||||
int side = -1;
|
||||
if (cz == room.minZ)
|
||||
side = 0;
|
||||
else if (cz == room.maxZ - 1)
|
||||
side = 1;
|
||||
else if (cx == room.minX)
|
||||
side = 2;
|
||||
else if (cx == room.maxX - 1)
|
||||
side = 3;
|
||||
if (side < 0)
|
||||
continue;
|
||||
if (!cellHasAnyDoor(cx, cy, cz)) {
|
||||
placed = placeOne(cx, cy, cz, side,
|
||||
mtags.tags,
|
||||
mtags.fallbackTags,
|
||||
mtags.notagsWalls,
|
||||
false,
|
||||
rng);
|
||||
if (placed && !mtags.filler)
|
||||
break;
|
||||
}
|
||||
if (mtags.filler)
|
||||
placed = false;
|
||||
}
|
||||
|
||||
// Try wall ranges (all edge cells on each side)
|
||||
if (!placed) {
|
||||
for (int side = 0; side < 4 && !placed; ++side) {
|
||||
if (!placed || mtags.filler) {
|
||||
for (int side = 0; side < 4; ++side) {
|
||||
auto edgeCells = getRoomEdgeCells(room, side);
|
||||
for (const auto &cell : edgeCells) {
|
||||
int cx = cell.first;
|
||||
int cz = cell.second;
|
||||
int cy = room.minY;
|
||||
placed = placeOne(cx, cy, cz, side,
|
||||
mtags.tags,
|
||||
mtags.notagsWalls,
|
||||
false);
|
||||
if (placed && !mtags.filler)
|
||||
break;
|
||||
if (!cellHasAnyDoor(cx, cy, cz)) {
|
||||
placed = placeOne(cx, cy, cz, side,
|
||||
mtags.tags,
|
||||
mtags.fallbackTags,
|
||||
mtags.notagsWalls,
|
||||
false,
|
||||
rng);
|
||||
if (placed && !mtags.filler)
|
||||
break;
|
||||
}
|
||||
if (mtags.filler)
|
||||
placed = false;
|
||||
}
|
||||
if (placed && !mtags.filler)
|
||||
break;
|
||||
@@ -1046,42 +1073,68 @@ void RoomLayoutSystem::generateRoomFurniture(RoomComponent& room,
|
||||
placed = false;
|
||||
|
||||
// Try window midpoints
|
||||
if (!placed) {
|
||||
for (int side = 0; side < 4 && !placed; ++side) {
|
||||
int cx = std::get<0>(mpoints[side]);
|
||||
int cy = std::get<1>(mpoints[side]);
|
||||
int cz = std::get<2>(mpoints[side]);
|
||||
placed = placeOne(cx, cy, cz, side, mtags.tags,
|
||||
mtags.notagsWindows, true);
|
||||
if (placed && !mtags.filler)
|
||||
break;
|
||||
if (!placed || mtags.filler) {
|
||||
for (auto &p : mpoints) {
|
||||
int cx = std::get<0>(p);
|
||||
int cy = std::get<1>(p);
|
||||
int cz = std::get<2>(p);
|
||||
int side = -1;
|
||||
if (cz == room.minZ)
|
||||
side = 0;
|
||||
else if (cz == room.maxZ - 1)
|
||||
side = 1;
|
||||
else if (cx == room.minX)
|
||||
side = 2;
|
||||
else if (cx == room.maxX - 1)
|
||||
side = 3;
|
||||
if (side < 0)
|
||||
continue;
|
||||
if (!cellHasAnyDoor(cx, cy, cz)) {
|
||||
placed = placeOne(cx, cy, cz, side,
|
||||
mtags.tags,
|
||||
mtags.fallbackTags,
|
||||
mtags.notagsWindows,
|
||||
true,
|
||||
rng);
|
||||
if (placed && !mtags.filler)
|
||||
break;
|
||||
}
|
||||
if (mtags.filler)
|
||||
placed = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Try window ranges
|
||||
if (!placed) {
|
||||
for (int side = 0; side < 4 && !placed; ++side) {
|
||||
if (!placed || mtags.filler) {
|
||||
for (int side = 0; side < 4; ++side) {
|
||||
auto edgeCells = getRoomEdgeCells(room, side);
|
||||
for (const auto &cell : edgeCells) {
|
||||
int cx = cell.first;
|
||||
int cz = cell.second;
|
||||
int cy = room.minY;
|
||||
placed = placeOne(cx, cy, cz, side,
|
||||
mtags.tags,
|
||||
mtags.notagsWindows,
|
||||
true);
|
||||
if (placed && !mtags.filler)
|
||||
break;
|
||||
if (!cellHasAnyDoor(cx, cy, cz)) {
|
||||
placed = placeOne(cx, cy, cz, side,
|
||||
mtags.tags,
|
||||
mtags.fallbackTags,
|
||||
mtags.notagsWindows,
|
||||
true,
|
||||
rng);
|
||||
if (placed && !mtags.filler)
|
||||
break;
|
||||
}
|
||||
if (mtags.filler)
|
||||
placed = false;
|
||||
}
|
||||
if (placed && !mtags.filler)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!placed && !mtags.filler) {
|
||||
break; // do not place anything if essentials were not placed
|
||||
// Only break if essential was not placed.
|
||||
// For important/optional/filler, continue to next stage
|
||||
// even if nothing was placed.
|
||||
if (!placed && mtags.essential) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <flecs.h>
|
||||
#include <Ogre.h>
|
||||
|
||||
|
||||
/**
|
||||
* @brief Room Layout System - processes Room components and generates cell grid
|
||||
*
|
||||
|
||||
@@ -1405,6 +1405,8 @@ nlohmann::json SceneSerializer::serializeRoom(flecs::entity entity)
|
||||
json["createCeiling"] = room.createCeiling;
|
||||
json["createInteriorWalls"] = room.createInteriorWalls;
|
||||
json["createWindows"] = room.createWindows;
|
||||
json["fillRoomWithFurniture"] = room.fillRoomWithFurniture;
|
||||
json["furnitureSeed"] = room.furnitureSeed;
|
||||
|
||||
// Serialize exits (bool array for Z-, Z+, X-, X+)
|
||||
nlohmann::json exits = nlohmann::json::array();
|
||||
@@ -1656,6 +1658,8 @@ void SceneSerializer::deserializeRoom(flecs::entity entity, const nlohmann::json
|
||||
room.createCeiling = json.value("createCeiling", true);
|
||||
room.createInteriorWalls = json.value("createInteriorWalls", true);
|
||||
room.createWindows = json.value("createWindows", false);
|
||||
room.fillRoomWithFurniture = json.value("fillRoomWithFurniture", false);
|
||||
room.furnitureSeed = json.value("furnitureSeed", 42u);
|
||||
|
||||
// Load exits (bool array for Z-, Z+, X-, X+)
|
||||
if (json.contains("exits") && json["exits"].is_array()) {
|
||||
|
||||
@@ -44,6 +44,13 @@ bool RoomEditor::renderComponent(flecs::entity entity, RoomComponent& room)
|
||||
if (ImGui::Checkbox("Create Ceiling", &room.createCeiling)) modified = true;
|
||||
if (ImGui::Checkbox("Create Interior Walls", &room.createInteriorWalls)) modified = true;
|
||||
if (ImGui::Checkbox("Create Windows", &room.createWindows)) modified = true;
|
||||
if (ImGui::Checkbox("Fill Room with Furniture", &room.fillRoomWithFurniture)) modified = true;
|
||||
int seed = static_cast<int>(room.furnitureSeed);
|
||||
if (ImGui::InputInt("Furniture Seed", &seed)) {
|
||||
room.furnitureSeed = static_cast<unsigned int>(seed);
|
||||
modified = true;
|
||||
}
|
||||
ImGui::TextDisabled("Change seed for different furniture arrangement");
|
||||
ImGui::TextDisabled("(Windows are created after all doors)");
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
Reference in New Issue
Block a user