|
|
|
|
@@ -95,33 +95,66 @@ void CellGridSystem::update()
|
|
|
|
|
void CellGridSystem::buildCellGrid(flecs::entity entity, CellGridComponent& grid)
|
|
|
|
|
{
|
|
|
|
|
Ogre::LogManager::getSingleton().logMessage("CellGrid: buildCellGrid for entity " + std::to_string(entity.id()) +
|
|
|
|
|
" cells=" + std::to_string(grid.cells.size()));
|
|
|
|
|
" cells=" + std::to_string(grid.cells.size()) + " cellSize=" + std::to_string(grid.cellSize) +
|
|
|
|
|
" cellHeight=" + std::to_string(grid.cellHeight));
|
|
|
|
|
|
|
|
|
|
// Destroy existing meshes
|
|
|
|
|
destroyCellGridMeshes(entity);
|
|
|
|
|
|
|
|
|
|
// Find parent town for material
|
|
|
|
|
// Find material - prefer ProceduralMaterial from Town/District/Lot
|
|
|
|
|
std::string materialName = "Ogre/StandardFloor"; // default
|
|
|
|
|
flecs::entity townEntity = flecs::entity::null();
|
|
|
|
|
flecs::entity materialEntity = flecs::entity::null();
|
|
|
|
|
|
|
|
|
|
// Look for TownComponent in parent hierarchy
|
|
|
|
|
// Look for ProceduralMaterial in parent hierarchy (Lot -> District -> Town)
|
|
|
|
|
flecs::entity parent = entity.parent();
|
|
|
|
|
while (parent.is_alive()) {
|
|
|
|
|
if (parent.has<LotComponent>()) {
|
|
|
|
|
auto& lot = parent.get<LotComponent>();
|
|
|
|
|
if (lot.proceduralMaterialEntity.is_alive() &&
|
|
|
|
|
lot.proceduralMaterialEntity.has<ProceduralMaterialComponent>()) {
|
|
|
|
|
const auto& mat = lot.proceduralMaterialEntity.get<ProceduralMaterialComponent>();
|
|
|
|
|
if (mat.created && !mat.materialName.empty()) {
|
|
|
|
|
materialName = mat.materialName;
|
|
|
|
|
materialEntity = lot.proceduralMaterialEntity;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (parent.has<DistrictComponent>()) {
|
|
|
|
|
auto& district = parent.get<DistrictComponent>();
|
|
|
|
|
if (district.proceduralMaterialEntity.is_alive() &&
|
|
|
|
|
district.proceduralMaterialEntity.has<ProceduralMaterialComponent>()) {
|
|
|
|
|
const auto& mat = district.proceduralMaterialEntity.get<ProceduralMaterialComponent>();
|
|
|
|
|
if (mat.created && !mat.materialName.empty()) {
|
|
|
|
|
materialName = mat.materialName;
|
|
|
|
|
materialEntity = district.proceduralMaterialEntity;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (parent.has<TownComponent>()) {
|
|
|
|
|
townEntity = parent;
|
|
|
|
|
auto& town = parent.get<TownComponent>();
|
|
|
|
|
// Prefer ProceduralMaterial over generated material
|
|
|
|
|
if (town.proceduralMaterialEntity.is_alive() &&
|
|
|
|
|
town.proceduralMaterialEntity.has<ProceduralMaterialComponent>()) {
|
|
|
|
|
const auto& mat = town.proceduralMaterialEntity.get<ProceduralMaterialComponent>();
|
|
|
|
|
if (mat.created && !mat.materialName.empty()) {
|
|
|
|
|
materialName = mat.materialName;
|
|
|
|
|
materialEntity = town.proceduralMaterialEntity;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Fall back to generated material
|
|
|
|
|
materialName = town.materialName.empty() ? "TownMaterial_" + std::to_string(parent.id()) : town.materialName;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
parent = parent.parent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!townEntity.is_alive()) {
|
|
|
|
|
Ogre::LogManager::getSingleton().logMessage("CellGrid: No parent town found for entity " + std::to_string(entity.id()) + ", using default material");
|
|
|
|
|
} else {
|
|
|
|
|
Ogre::LogManager::getSingleton().logMessage("CellGrid: Found town " + std::to_string(townEntity.id()) +
|
|
|
|
|
" for entity " + std::to_string(entity.id()) + " material=" + materialName);
|
|
|
|
|
}
|
|
|
|
|
Ogre::LogManager::getSingleton().logMessage("CellGrid: Using material '" + materialName +
|
|
|
|
|
"' for entity " + std::to_string(entity.id()));
|
|
|
|
|
|
|
|
|
|
MeshData& meshData = m_entityMeshes[entity.id()];
|
|
|
|
|
|
|
|
|
|
@@ -136,6 +169,22 @@ void CellGridSystem::buildCellGrid(flecs::entity entity, CellGridComponent& grid
|
|
|
|
|
buildWindows(grid, windowTbs);
|
|
|
|
|
buildRoofs(entity, grid, roofTb);
|
|
|
|
|
|
|
|
|
|
// Apply UV mapping for each part (use rect if specified, otherwise default)
|
|
|
|
|
Ogre::LogManager::getSingleton().logMessage("CellGrid: UV mapping - floor: " + grid.floorRectName +
|
|
|
|
|
" ceiling: " + grid.ceilingRectName + " extWall: " + grid.extWallRectName);
|
|
|
|
|
applyUVMappingToBuffer(floorTb, grid.floorRectName, materialEntity);
|
|
|
|
|
applyUVMappingToBuffer(ceilingTb, grid.ceilingRectName, materialEntity);
|
|
|
|
|
applyUVMappingToBuffer(extWallTb, grid.extWallRectName, materialEntity);
|
|
|
|
|
applyUVMappingToBuffer(intWallTb, grid.intWallRectName, materialEntity);
|
|
|
|
|
applyUVMappingToBuffer(intWindowsTb, grid.intWallRectName, materialEntity);
|
|
|
|
|
for (auto& tb : doorTbs) {
|
|
|
|
|
applyUVMappingToBuffer(tb, grid.doorRectName, materialEntity);
|
|
|
|
|
}
|
|
|
|
|
for (auto& tb : windowTbs) {
|
|
|
|
|
applyUVMappingToBuffer(tb, grid.windowRectName, materialEntity);
|
|
|
|
|
}
|
|
|
|
|
applyUVMappingToBuffer(roofTb, grid.roofRectName, materialEntity);
|
|
|
|
|
|
|
|
|
|
// Generate unique base name
|
|
|
|
|
std::string baseName = "CellGrid_" + std::to_string(entity.id()) + "_" + std::to_string(m_meshCount++);
|
|
|
|
|
|
|
|
|
|
@@ -200,38 +249,99 @@ void CellGridSystem::buildCellGrid(flecs::entity entity, CellGridComponent& grid
|
|
|
|
|
meshData.entities.push_back(entity3d);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ogre::LogManager::getSingleton().logMessage("CellGrid: Built geometry for entity " + std::to_string(entity.id()));
|
|
|
|
|
// Doors
|
|
|
|
|
int doorIdx = 0;
|
|
|
|
|
for (auto& tb : doorTbs) {
|
|
|
|
|
if (tb.getVertices().size() >= 3) {
|
|
|
|
|
std::string meshName = baseName + "_door" + std::to_string(doorIdx++);
|
|
|
|
|
meshData.doorMeshes.push_back(meshName);
|
|
|
|
|
auto mesh = convertToMesh(meshName, tb, materialName);
|
|
|
|
|
auto entity3d = m_sceneMgr->createEntity(meshName);
|
|
|
|
|
transform.node->attachObject(entity3d);
|
|
|
|
|
meshData.entities.push_back(entity3d);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Windows
|
|
|
|
|
int windowIdx = 0;
|
|
|
|
|
for (auto& tb : windowTbs) {
|
|
|
|
|
if (tb.getVertices().size() >= 3) {
|
|
|
|
|
std::string meshName = baseName + "_window" + std::to_string(windowIdx++);
|
|
|
|
|
meshData.windowMeshes.push_back(meshName);
|
|
|
|
|
auto mesh = convertToMesh(meshName, tb, materialName);
|
|
|
|
|
auto entity3d = m_sceneMgr->createEntity(meshName);
|
|
|
|
|
transform.node->attachObject(entity3d);
|
|
|
|
|
meshData.entities.push_back(entity3d);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ogre::LogManager::getSingleton().logMessage("CellGrid: Built geometry for entity " + std::to_string(entity.id()) +
|
|
|
|
|
" floor=" + std::to_string(floorTb.getVertices().size()) +
|
|
|
|
|
" ceiling=" + std::to_string(ceilingTb.getVertices().size()) +
|
|
|
|
|
" extWall=" + std::to_string(extWallTb.getVertices().size()) +
|
|
|
|
|
" intWall=" + std::to_string(intWallTb.getVertices().size()) +
|
|
|
|
|
" intWindows=" + std::to_string(intWindowsTb.getVertices().size()) +
|
|
|
|
|
" doors=" + std::to_string(doorTbs.size()) +
|
|
|
|
|
" windows=" + std::to_string(windowTbs.size()) +
|
|
|
|
|
" roof=" + std::to_string(roofTb.getVertices().size()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CellGridSystem::buildFloorsAndCeilings(const CellGridComponent& grid,
|
|
|
|
|
Procedural::TriangleBuffer& floorTb,
|
|
|
|
|
// BitSet structure matching original town.cpp
|
|
|
|
|
struct BitSet {
|
|
|
|
|
uint64_t bit;
|
|
|
|
|
float sizeX;
|
|
|
|
|
float sizeY;
|
|
|
|
|
Ogre::Vector3 normal;
|
|
|
|
|
Ogre::Vector3 offset;
|
|
|
|
|
Procedural::TriangleBuffer* tb;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Plane generator matching original town.cpp
|
|
|
|
|
// The sizeX/sizeY values from original BitSet are the CORRECT full sizes for PlaneGenerator
|
|
|
|
|
// - sizeX = wall height (vertical dimension of the plane)
|
|
|
|
|
// - sizeY = wall half-width (horizontal dimension of the plane, gets doubled by PlaneGenerator internally)
|
|
|
|
|
static void genPlane(float sizeX, float sizeY, const Ogre::Vector3& normal,
|
|
|
|
|
const Ogre::Vector3& offset, Procedural::TriangleBuffer& tb,
|
|
|
|
|
const Ogre::Vector3& origin) {
|
|
|
|
|
// nsegY is based on the height (sizeX in the original, which is vertical)
|
|
|
|
|
int nsegY = (sizeX < 1.0f) ? 1 : std::max(1, (int)(sizeX / 2.0f));
|
|
|
|
|
Procedural::PlaneGenerator()
|
|
|
|
|
.setNormal(normal)
|
|
|
|
|
.setSizeX(sizeX) // This is the height of the wall
|
|
|
|
|
.setSizeY(sizeY) // This is already correct (half-width * 2 = full width)
|
|
|
|
|
.setNumSegX(1)
|
|
|
|
|
.setNumSegY(nsegY)
|
|
|
|
|
.setEnableNormals(true)
|
|
|
|
|
.setPosition(origin + offset)
|
|
|
|
|
.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CellGridSystem::buildFloorsAndCeilings(const CellGridComponent& grid,
|
|
|
|
|
Procedural::TriangleBuffer& floorTb,
|
|
|
|
|
Procedural::TriangleBuffer& ceilingTb)
|
|
|
|
|
{
|
|
|
|
|
const float cellSize = grid.cellSize;
|
|
|
|
|
const float floorHeight = 0.1f;
|
|
|
|
|
const float ceilingOffset = grid.cellHeight - 0.3f;
|
|
|
|
|
|
|
|
|
|
// Original uses 2.0f as half-size for 4.0f cell (cellSize/2)
|
|
|
|
|
// Use same hScale as walls for consistency
|
|
|
|
|
const float hScale = grid.cellSize / 2.0f;
|
|
|
|
|
// Scale factor for original 4.0f cell height reference
|
|
|
|
|
const float vScale = grid.cellHeight / 4.0f;
|
|
|
|
|
const float floorY = 0.1f * vScale;
|
|
|
|
|
const float ceilingY = grid.cellHeight - 0.3f * vScale;
|
|
|
|
|
|
|
|
|
|
for (const auto& cell : grid.cells) {
|
|
|
|
|
Ogre::Vector3 origin = grid.cellToWorld(cell.x, cell.y, cell.z);
|
|
|
|
|
|
|
|
|
|
// Floor
|
|
|
|
|
if (cell.hasFlag(CellFlags::Floor)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(cellSize - 0.1f);
|
|
|
|
|
box.setSizeY(floorHeight);
|
|
|
|
|
box.setSizeZ(cellSize - 0.1f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x, origin.y + floorHeight/2.0f, origin.z));
|
|
|
|
|
box.addToTriangleBuffer(floorTb);
|
|
|
|
|
uint64_t flags = cell.flags;
|
|
|
|
|
|
|
|
|
|
// Floor: original sizeX=2.0f, sizeY=2.0f (half-sizes), scaled by hScale
|
|
|
|
|
if (flags & CellFlags::Floor) {
|
|
|
|
|
genPlane(2.0f * hScale, 2.0f * hScale, Ogre::Vector3::UNIT_Y,
|
|
|
|
|
Ogre::Vector3(0, floorY, 0), floorTb, origin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ceiling
|
|
|
|
|
if (cell.hasFlag(CellFlags::Ceiling)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(cellSize - 0.1f);
|
|
|
|
|
box.setSizeY(0.1f);
|
|
|
|
|
box.setSizeZ(cellSize - 0.1f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x, origin.y + ceilingOffset, origin.z));
|
|
|
|
|
box.addToTriangleBuffer(ceilingTb);
|
|
|
|
|
|
|
|
|
|
// Ceiling: original sizeX=2.0f, sizeY=2.0f (half-sizes), scaled by hScale
|
|
|
|
|
if (flags & CellFlags::Ceiling) {
|
|
|
|
|
genPlane(2.0f * hScale, 2.0f * hScale, Ogre::Vector3::NEGATIVE_UNIT_Y,
|
|
|
|
|
Ogre::Vector3(0, ceilingY, 0), ceilingTb, origin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -241,168 +351,350 @@ void CellGridSystem::buildWalls(const CellGridComponent& grid,
|
|
|
|
|
Procedural::TriangleBuffer& intWallTb,
|
|
|
|
|
Procedural::TriangleBuffer& intWindowsTb)
|
|
|
|
|
{
|
|
|
|
|
const float cellSize = grid.cellSize;
|
|
|
|
|
// Original tables designed for cellSize=2.0 (2x2 base, 4.0 height)
|
|
|
|
|
// In original: sizeY=2.0f represents FULL cell width (not half!)
|
|
|
|
|
// Scale factor: cellSize / 2.0f (not 4.0f!)
|
|
|
|
|
const float hScale = grid.cellSize / 2.0f; // Horizontal scale
|
|
|
|
|
const float halfCell = 1.0f * hScale; // half of cellSize
|
|
|
|
|
const float cornerWidth = 0.2f; // Fixed corner width, don't scale
|
|
|
|
|
const float solidExtHeight = grid.cellHeight;
|
|
|
|
|
const float solidExtOffset = 0.0f;
|
|
|
|
|
const float solidIntOffset = 0.1f;
|
|
|
|
|
const float solidIntOffset = 0.1f * hScale;
|
|
|
|
|
const float solidIntHeight = grid.cellHeight - 0.3f - solidIntOffset;
|
|
|
|
|
|
|
|
|
|
// Tables matching original town.cpp
|
|
|
|
|
// Original cellSize=2.0: sizeY=2.0f (full width), offset=+/-(1.0 + 0.2)=+/-1.1?
|
|
|
|
|
// Wait, original offset is -2.2f which is -(2.0 + 0.2)
|
|
|
|
|
// So 2.0f in offset IS half-cell for original (cellSize=4.0)
|
|
|
|
|
// Let me re-read: original uses 4.0f cellSize, 2.0f half-cell
|
|
|
|
|
// But you said tables are for 2x2... so 2.0f in table = full width of 2.0
|
|
|
|
|
// Then for cellSize=4.0, wall width should be 4.0
|
|
|
|
|
// Scale = cellSize / 2.0f = 4.0 / 2.0 = 2.0
|
|
|
|
|
// sizeY = 2.0f * 2.0 = 4.0 ✓
|
|
|
|
|
std::vector<BitSet> bits_solid = {
|
|
|
|
|
// External walls: sizeX=height, sizeY=cellSize (full width = 2.0f * hScale)
|
|
|
|
|
{ CellFlags::WallXNeg, solidExtHeight, 2.0f * hScale,
|
|
|
|
|
Ogre::Vector3::NEGATIVE_UNIT_X,
|
|
|
|
|
{ -1.0f * hScale - cornerWidth, solidExtHeight / 2.0f + solidExtOffset, 0 }, &extWallTb },
|
|
|
|
|
{ CellFlags::WallXPos, solidExtHeight, 2.0f * hScale,
|
|
|
|
|
Ogre::Vector3::UNIT_X,
|
|
|
|
|
{ 1.0f * hScale + cornerWidth, solidExtHeight / 2.0f + solidExtOffset, 0 }, &extWallTb },
|
|
|
|
|
{ CellFlags::WallZPos, solidExtHeight, 2.0f * hScale,
|
|
|
|
|
Ogre::Vector3::UNIT_Z,
|
|
|
|
|
{ 0, solidExtHeight / 2.0f + solidExtOffset, 1.0f * hScale + cornerWidth }, &extWallTb },
|
|
|
|
|
{ CellFlags::WallZNeg, solidExtHeight, 2.0f * hScale,
|
|
|
|
|
Ogre::Vector3::NEGATIVE_UNIT_Z,
|
|
|
|
|
{ 0, solidExtHeight / 2.0f + solidExtOffset, -1.0f * hScale - cornerWidth }, &extWallTb },
|
|
|
|
|
// Internal walls: sizeY=2.0f-0.2f (in original units), offset=+/-(1.0f - 0.1f)
|
|
|
|
|
{ CellFlags::IntWallXNeg, solidIntHeight, (2.0f - 0.2f) * hScale,
|
|
|
|
|
Ogre::Vector3::UNIT_X,
|
|
|
|
|
{ -1.0f * hScale + cornerWidth/2, solidIntHeight / 2.0f + solidIntOffset, 0 }, &intWallTb },
|
|
|
|
|
{ CellFlags::IntWallXPos, solidIntHeight, (2.0f - 0.2f) * hScale,
|
|
|
|
|
Ogre::Vector3::NEGATIVE_UNIT_X,
|
|
|
|
|
{ 1.0f * hScale - cornerWidth/2, solidIntHeight / 2.0f + solidIntOffset, 0 }, &intWallTb },
|
|
|
|
|
{ CellFlags::IntWallZPos, solidIntHeight, (2.0f - 0.2f) * hScale,
|
|
|
|
|
Ogre::Vector3::NEGATIVE_UNIT_Z,
|
|
|
|
|
{ 0, solidIntHeight / 2.0f + solidIntOffset, 1.0f * hScale - cornerWidth/2 }, &intWallTb },
|
|
|
|
|
{ CellFlags::IntWallZNeg, solidIntHeight, (2.0f - 0.2f) * hScale,
|
|
|
|
|
Ogre::Vector3::UNIT_Z,
|
|
|
|
|
{ 0, solidIntHeight / 2.0f + solidIntOffset, -1.0f * hScale + cornerWidth/2 }, &intWallTb },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (const auto& cell : grid.cells) {
|
|
|
|
|
Ogre::Vector3 origin = grid.cellToWorld(cell.x, cell.y, cell.z);
|
|
|
|
|
uint64_t flags = cell.flags;
|
|
|
|
|
|
|
|
|
|
// External walls
|
|
|
|
|
if (cell.hasFlag(CellFlags::WallXNeg)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.2f);
|
|
|
|
|
box.setSizeY(solidExtHeight);
|
|
|
|
|
box.setSizeZ(cellSize);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x - cellSize/2.0f - 0.1f,
|
|
|
|
|
origin.y + solidExtHeight/2.0f, origin.z));
|
|
|
|
|
box.addToTriangleBuffer(extWallTb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::WallXPos)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.2f);
|
|
|
|
|
box.setSizeY(solidExtHeight);
|
|
|
|
|
box.setSizeZ(cellSize);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x + cellSize/2.0f + 0.1f,
|
|
|
|
|
origin.y + solidExtHeight/2.0f, origin.z));
|
|
|
|
|
box.addToTriangleBuffer(extWallTb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::WallZPos)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(cellSize);
|
|
|
|
|
box.setSizeY(solidExtHeight);
|
|
|
|
|
box.setSizeZ(0.2f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x, origin.y + solidExtHeight/2.0f,
|
|
|
|
|
origin.z + cellSize/2.0f + 0.1f));
|
|
|
|
|
box.addToTriangleBuffer(extWallTb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::WallZNeg)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(cellSize);
|
|
|
|
|
box.setSizeY(solidExtHeight);
|
|
|
|
|
box.setSizeZ(0.2f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x, origin.y + solidExtHeight/2.0f,
|
|
|
|
|
origin.z - cellSize/2.0f - 0.1f));
|
|
|
|
|
box.addToTriangleBuffer(extWallTb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Internal walls
|
|
|
|
|
if (cell.hasFlag(CellFlags::IntWallXNeg)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.1f);
|
|
|
|
|
box.setSizeY(solidIntHeight);
|
|
|
|
|
box.setSizeZ(cellSize - 0.4f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x - cellSize/2.0f + 0.05f,
|
|
|
|
|
origin.y + solidIntHeight/2.0f + solidIntOffset, origin.z));
|
|
|
|
|
box.addToTriangleBuffer(intWallTb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::IntWallXPos)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.1f);
|
|
|
|
|
box.setSizeY(solidIntHeight);
|
|
|
|
|
box.setSizeZ(cellSize - 0.4f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x + cellSize/2.0f - 0.05f,
|
|
|
|
|
origin.y + solidIntHeight/2.0f + solidIntOffset, origin.z));
|
|
|
|
|
box.addToTriangleBuffer(intWallTb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::IntWallZPos)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(cellSize - 0.4f);
|
|
|
|
|
box.setSizeY(solidIntHeight);
|
|
|
|
|
box.setSizeZ(0.1f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x, origin.y + solidIntHeight/2.0f + solidIntOffset,
|
|
|
|
|
origin.z + cellSize/2.0f - 0.05f));
|
|
|
|
|
box.addToTriangleBuffer(intWallTb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::IntWallZNeg)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(cellSize - 0.4f);
|
|
|
|
|
box.setSizeY(solidIntHeight);
|
|
|
|
|
box.setSizeZ(0.1f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x, origin.y + solidIntHeight/2.0f + solidIntOffset,
|
|
|
|
|
origin.z - cellSize/2.0f + 0.05f));
|
|
|
|
|
box.addToTriangleBuffer(intWallTb);
|
|
|
|
|
for (const auto& bit : bits_solid) {
|
|
|
|
|
if ((flags & bit.bit) == bit.bit) {
|
|
|
|
|
genPlane(bit.sizeX, bit.sizeY, bit.normal, bit.offset, *bit.tb, origin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CellGridSystem::buildCorners(const CellGridComponent& grid, Procedural::TriangleBuffer& extWallTb)
|
|
|
|
|
{
|
|
|
|
|
// Build external wall corners where walls meet
|
|
|
|
|
const float cellSize = grid.cellSize;
|
|
|
|
|
// Build external wall corners where walls meet using PlaneGenerator
|
|
|
|
|
// Original tables for cellSize=2.0 (2x2 base), so halfCell=1.0
|
|
|
|
|
const float hScale = grid.cellSize / 2.0f;
|
|
|
|
|
const float halfCell = 1.0f * hScale; // = cellSize / 2
|
|
|
|
|
const float cornerWidth = 0.2f; // Fixed corner width
|
|
|
|
|
const float solidExtHeight = grid.cellHeight;
|
|
|
|
|
const float solidExtOffset = 0.0f;
|
|
|
|
|
|
|
|
|
|
// Tables for corners matching original town.cpp bits_ext_corners
|
|
|
|
|
// Format: bit, sizeX (height), sizeY (corner width), normal, offset
|
|
|
|
|
// Corner width is FIXED (0.2f) - don't scale to avoid thin polygons
|
|
|
|
|
// Each corner plane is shifted by cornerWidth/2 toward the corner to close the gap
|
|
|
|
|
const float cornerOffset = cornerWidth / 2.0f;
|
|
|
|
|
std::vector<BitSet> bits_ext_corners = {
|
|
|
|
|
// X- with Z+ corners
|
|
|
|
|
// First corner plane: normal along -X, at X=-(halfCell+cornerWidth), Z=+halfCell+cornerOffset
|
|
|
|
|
{ CellFlags::WallXNeg | CellFlags::WallZPos, solidExtHeight, cornerWidth,
|
|
|
|
|
Ogre::Vector3::NEGATIVE_UNIT_X,
|
|
|
|
|
{ -halfCell - cornerWidth, solidExtHeight / 2.0f + solidExtOffset, halfCell + cornerOffset }, &extWallTb },
|
|
|
|
|
// Second corner plane: normal along +Z, at X=-halfCell-cornerOffset, Z=+(halfCell+cornerWidth)
|
|
|
|
|
{ CellFlags::WallXNeg | CellFlags::WallZPos, solidExtHeight, cornerWidth,
|
|
|
|
|
Ogre::Vector3::UNIT_Z,
|
|
|
|
|
{ -halfCell - cornerOffset, solidExtHeight / 2.0f + solidExtOffset, halfCell + cornerWidth }, &extWallTb },
|
|
|
|
|
// X- with Z- corners
|
|
|
|
|
{ CellFlags::WallXNeg | CellFlags::WallZNeg, solidExtHeight, cornerWidth,
|
|
|
|
|
Ogre::Vector3::NEGATIVE_UNIT_X,
|
|
|
|
|
{ -halfCell - cornerWidth, solidExtHeight / 2.0f + solidExtOffset, -halfCell - cornerOffset }, &extWallTb },
|
|
|
|
|
{ CellFlags::WallXNeg | CellFlags::WallZNeg, solidExtHeight, cornerWidth,
|
|
|
|
|
Ogre::Vector3::NEGATIVE_UNIT_Z,
|
|
|
|
|
{ -halfCell - cornerOffset, solidExtHeight / 2.0f + solidExtOffset, -halfCell - cornerWidth }, &extWallTb },
|
|
|
|
|
// X+ with Z+ corners
|
|
|
|
|
{ CellFlags::WallXPos | CellFlags::WallZPos, solidExtHeight, cornerWidth,
|
|
|
|
|
Ogre::Vector3::UNIT_X,
|
|
|
|
|
{ halfCell + cornerWidth, solidExtHeight / 2.0f + solidExtOffset, halfCell + cornerOffset }, &extWallTb },
|
|
|
|
|
{ CellFlags::WallXPos | CellFlags::WallZPos, solidExtHeight, cornerWidth,
|
|
|
|
|
Ogre::Vector3::UNIT_Z,
|
|
|
|
|
{ halfCell + cornerOffset, solidExtHeight / 2.0f + solidExtOffset, halfCell + cornerWidth }, &extWallTb },
|
|
|
|
|
// X+ with Z- corners
|
|
|
|
|
{ CellFlags::WallXPos | CellFlags::WallZNeg, solidExtHeight, cornerWidth,
|
|
|
|
|
Ogre::Vector3::UNIT_X,
|
|
|
|
|
{ halfCell + cornerWidth, solidExtHeight / 2.0f + solidExtOffset, -halfCell - cornerOffset }, &extWallTb },
|
|
|
|
|
{ CellFlags::WallXPos | CellFlags::WallZNeg, solidExtHeight, cornerWidth,
|
|
|
|
|
Ogre::Vector3::NEGATIVE_UNIT_Z,
|
|
|
|
|
{ halfCell + cornerOffset, solidExtHeight / 2.0f + solidExtOffset, -halfCell - cornerWidth }, &extWallTb },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (const auto& cell : grid.cells) {
|
|
|
|
|
Ogre::Vector3 origin = grid.cellToWorld(cell.x, cell.y, cell.z);
|
|
|
|
|
uint64_t wallFlags = cell.flags & CellFlags::AllExternalWalls;
|
|
|
|
|
|
|
|
|
|
// Corner cases: X- with Z+, X- with Z-, X+ with Z+, X+ with Z-
|
|
|
|
|
if ((cell.hasFlag(CellFlags::WallXNeg) || cell.hasFlag(CellFlags::WallZPos)) &&
|
|
|
|
|
(wallFlags & (CellFlags::WallXNeg | CellFlags::WallZPos)) == (CellFlags::WallXNeg | CellFlags::WallZPos)) {
|
|
|
|
|
// Corner at X-, Z+
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.2f);
|
|
|
|
|
box.setSizeY(solidExtHeight);
|
|
|
|
|
box.setSizeZ(0.2f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x - cellSize/2.0f - 0.1f,
|
|
|
|
|
origin.y + solidExtHeight/2.0f,
|
|
|
|
|
origin.z + cellSize/2.0f + 0.1f));
|
|
|
|
|
box.addToTriangleBuffer(extWallTb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((cell.hasFlag(CellFlags::WallXNeg) || cell.hasFlag(CellFlags::WallZNeg)) &&
|
|
|
|
|
(wallFlags & (CellFlags::WallXNeg | CellFlags::WallZNeg)) == (CellFlags::WallXNeg | CellFlags::WallZNeg)) {
|
|
|
|
|
// Corner at X-, Z-
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.2f);
|
|
|
|
|
box.setSizeY(solidExtHeight);
|
|
|
|
|
box.setSizeZ(0.2f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x - cellSize/2.0f - 0.1f,
|
|
|
|
|
origin.y + solidExtHeight/2.0f,
|
|
|
|
|
origin.z - cellSize/2.0f - 0.1f));
|
|
|
|
|
box.addToTriangleBuffer(extWallTb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((cell.hasFlag(CellFlags::WallXPos) || cell.hasFlag(CellFlags::WallZPos)) &&
|
|
|
|
|
(wallFlags & (CellFlags::WallXPos | CellFlags::WallZPos)) == (CellFlags::WallXPos | CellFlags::WallZPos)) {
|
|
|
|
|
// Corner at X+, Z+
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.2f);
|
|
|
|
|
box.setSizeY(solidExtHeight);
|
|
|
|
|
box.setSizeZ(0.2f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x + cellSize/2.0f + 0.1f,
|
|
|
|
|
origin.y + solidExtHeight/2.0f,
|
|
|
|
|
origin.z + cellSize/2.0f + 0.1f));
|
|
|
|
|
box.addToTriangleBuffer(extWallTb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((cell.hasFlag(CellFlags::WallXPos) || cell.hasFlag(CellFlags::WallZNeg)) &&
|
|
|
|
|
(wallFlags & (CellFlags::WallXPos | CellFlags::WallZNeg)) == (CellFlags::WallXPos | CellFlags::WallZNeg)) {
|
|
|
|
|
// Corner at X+, Z-
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.2f);
|
|
|
|
|
box.setSizeY(solidExtHeight);
|
|
|
|
|
box.setSizeZ(0.2f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x + cellSize/2.0f + 0.1f,
|
|
|
|
|
origin.y + solidExtHeight/2.0f,
|
|
|
|
|
origin.z - cellSize/2.0f - 0.1f));
|
|
|
|
|
box.addToTriangleBuffer(extWallTb);
|
|
|
|
|
for (const auto& bit : bits_ext_corners) {
|
|
|
|
|
// Check if both wall flags are set (corner needs both adjacent walls)
|
|
|
|
|
if ((wallFlags & bit.bit) == bit.bit) {
|
|
|
|
|
genPlane(bit.sizeX, bit.sizeY, bit.normal, bit.offset, *bit.tb, origin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CellGridSystem::buildDoors(const CellGridComponent& grid, std::vector<Procedural::TriangleBuffer>& doorTbs)
|
|
|
|
|
{
|
|
|
|
|
// Doors are cutouts in walls - we build the wall around them
|
|
|
|
|
// For now, use wall geometry with gaps
|
|
|
|
|
// This is simplified - full implementation needs more complex geometry
|
|
|
|
|
const float cellSize = grid.cellSize;
|
|
|
|
|
const float doorHeight = grid.cellHeight * 0.7f;
|
|
|
|
|
const float doorWidth = cellSize * 0.6f;
|
|
|
|
|
const float frameThickness = 0.1f;
|
|
|
|
|
|
|
|
|
|
for (const auto& cell : grid.cells) {
|
|
|
|
|
Ogre::Vector3 origin = grid.cellToWorld(cell.x, cell.y, cell.z);
|
|
|
|
|
|
|
|
|
|
// External doors
|
|
|
|
|
if (cell.hasFlag(CellFlags::DoorXNeg) || cell.hasFlag(CellFlags::DoorXPos) ||
|
|
|
|
|
cell.hasFlag(CellFlags::DoorZNeg) || cell.hasFlag(CellFlags::DoorZPos)) {
|
|
|
|
|
|
|
|
|
|
Procedural::TriangleBuffer tb;
|
|
|
|
|
|
|
|
|
|
// Door frame (simplified as thin boxes around the opening)
|
|
|
|
|
if (cell.hasFlag(CellFlags::DoorXNeg)) {
|
|
|
|
|
// Top frame
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.15f);
|
|
|
|
|
box.setSizeY(grid.cellHeight - doorHeight);
|
|
|
|
|
box.setSizeZ(doorWidth + 0.2f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x - cellSize/2.0f - 0.075f,
|
|
|
|
|
origin.y + doorHeight + (grid.cellHeight - doorHeight)/2.0f, origin.z));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::DoorXPos)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.15f);
|
|
|
|
|
box.setSizeY(grid.cellHeight - doorHeight);
|
|
|
|
|
box.setSizeZ(doorWidth + 0.2f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x + cellSize/2.0f + 0.075f,
|
|
|
|
|
origin.y + doorHeight + (grid.cellHeight - doorHeight)/2.0f, origin.z));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::DoorZNeg)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(doorWidth + 0.2f);
|
|
|
|
|
box.setSizeY(grid.cellHeight - doorHeight);
|
|
|
|
|
box.setSizeZ(0.15f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x,
|
|
|
|
|
origin.y + doorHeight + (grid.cellHeight - doorHeight)/2.0f,
|
|
|
|
|
origin.z - cellSize/2.0f - 0.075f));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::DoorZPos)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(doorWidth + 0.2f);
|
|
|
|
|
box.setSizeY(grid.cellHeight - doorHeight);
|
|
|
|
|
box.setSizeZ(0.15f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x,
|
|
|
|
|
origin.y + doorHeight + (grid.cellHeight - doorHeight)/2.0f,
|
|
|
|
|
origin.z + cellSize/2.0f + 0.075f));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tb.getVertices().size() > 0) {
|
|
|
|
|
doorTbs.push_back(std::move(tb));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Internal doors (similar but thinner)
|
|
|
|
|
if (cell.hasFlag(CellFlags::IntDoorXNeg) || cell.hasFlag(CellFlags::IntDoorXPos) ||
|
|
|
|
|
cell.hasFlag(CellFlags::IntDoorZNeg) || cell.hasFlag(CellFlags::IntDoorZPos)) {
|
|
|
|
|
|
|
|
|
|
Procedural::TriangleBuffer tb;
|
|
|
|
|
|
|
|
|
|
if (cell.hasFlag(CellFlags::IntDoorXNeg)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.08f);
|
|
|
|
|
box.setSizeY(grid.cellHeight - doorHeight);
|
|
|
|
|
box.setSizeZ(doorWidth + 0.2f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x - cellSize/2.0f + 0.04f,
|
|
|
|
|
origin.y + doorHeight + (grid.cellHeight - doorHeight)/2.0f, origin.z));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::IntDoorXPos)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.08f);
|
|
|
|
|
box.setSizeY(grid.cellHeight - doorHeight);
|
|
|
|
|
box.setSizeZ(doorWidth + 0.2f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x + cellSize/2.0f - 0.04f,
|
|
|
|
|
origin.y + doorHeight + (grid.cellHeight - doorHeight)/2.0f, origin.z));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::IntDoorZNeg)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(doorWidth + 0.2f);
|
|
|
|
|
box.setSizeY(grid.cellHeight - doorHeight);
|
|
|
|
|
box.setSizeZ(0.08f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x,
|
|
|
|
|
origin.y + doorHeight + (grid.cellHeight - doorHeight)/2.0f,
|
|
|
|
|
origin.z - cellSize/2.0f + 0.04f));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::IntDoorZPos)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(doorWidth + 0.2f);
|
|
|
|
|
box.setSizeY(grid.cellHeight - doorHeight);
|
|
|
|
|
box.setSizeZ(0.08f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x,
|
|
|
|
|
origin.y + doorHeight + (grid.cellHeight - doorHeight)/2.0f,
|
|
|
|
|
origin.z + cellSize/2.0f - 0.04f));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tb.getVertices().size() > 0) {
|
|
|
|
|
doorTbs.push_back(std::move(tb));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CellGridSystem::buildWindows(const CellGridComponent& grid, std::vector<Procedural::TriangleBuffer>& windowTbs)
|
|
|
|
|
{
|
|
|
|
|
// Windows are cutouts in walls - similar to doors
|
|
|
|
|
const float cellSize = grid.cellSize;
|
|
|
|
|
const float windowHeight = grid.cellHeight * 0.4f;
|
|
|
|
|
const float windowWidth = cellSize * 0.5f;
|
|
|
|
|
const float windowY = grid.cellHeight * 0.4f;
|
|
|
|
|
|
|
|
|
|
for (const auto& cell : grid.cells) {
|
|
|
|
|
Ogre::Vector3 origin = grid.cellToWorld(cell.x, cell.y, cell.z);
|
|
|
|
|
|
|
|
|
|
// External windows
|
|
|
|
|
if (cell.hasFlag(CellFlags::WindowXNeg) || cell.hasFlag(CellFlags::WindowXPos) ||
|
|
|
|
|
cell.hasFlag(CellFlags::WindowZNeg) || cell.hasFlag(CellFlags::WindowZPos)) {
|
|
|
|
|
|
|
|
|
|
Procedural::TriangleBuffer tb;
|
|
|
|
|
|
|
|
|
|
// Window frame (surrounding the window opening)
|
|
|
|
|
if (cell.hasFlag(CellFlags::WindowXNeg)) {
|
|
|
|
|
// Frame around the window
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.12f);
|
|
|
|
|
box.setSizeY(windowHeight + 0.2f);
|
|
|
|
|
box.setSizeZ(windowWidth + 0.2f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x - cellSize/2.0f - 0.06f,
|
|
|
|
|
origin.y + windowY, origin.z));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::WindowXPos)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.12f);
|
|
|
|
|
box.setSizeY(windowHeight + 0.2f);
|
|
|
|
|
box.setSizeZ(windowWidth + 0.2f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x + cellSize/2.0f + 0.06f,
|
|
|
|
|
origin.y + windowY, origin.z));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::WindowZNeg)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(windowWidth + 0.2f);
|
|
|
|
|
box.setSizeY(windowHeight + 0.2f);
|
|
|
|
|
box.setSizeZ(0.12f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x, origin.y + windowY,
|
|
|
|
|
origin.z - cellSize/2.0f - 0.06f));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::WindowZPos)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(windowWidth + 0.2f);
|
|
|
|
|
box.setSizeY(windowHeight + 0.2f);
|
|
|
|
|
box.setSizeZ(0.12f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x, origin.y + windowY,
|
|
|
|
|
origin.z + cellSize/2.0f + 0.06f));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tb.getVertices().size() > 0) {
|
|
|
|
|
windowTbs.push_back(std::move(tb));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Internal windows (thinner frames)
|
|
|
|
|
if (cell.hasFlag(CellFlags::IntWindowXNeg) || cell.hasFlag(CellFlags::IntWindowXPos) ||
|
|
|
|
|
cell.hasFlag(CellFlags::IntWindowZNeg) || cell.hasFlag(CellFlags::IntWindowZPos)) {
|
|
|
|
|
|
|
|
|
|
Procedural::TriangleBuffer tb;
|
|
|
|
|
|
|
|
|
|
if (cell.hasFlag(CellFlags::IntWindowXNeg)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.06f);
|
|
|
|
|
box.setSizeY(windowHeight + 0.15f);
|
|
|
|
|
box.setSizeZ(windowWidth + 0.15f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x - cellSize/2.0f + 0.03f,
|
|
|
|
|
origin.y + windowY, origin.z));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::IntWindowXPos)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(0.06f);
|
|
|
|
|
box.setSizeY(windowHeight + 0.15f);
|
|
|
|
|
box.setSizeZ(windowWidth + 0.15f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x + cellSize/2.0f - 0.03f,
|
|
|
|
|
origin.y + windowY, origin.z));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::IntWindowZNeg)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(windowWidth + 0.15f);
|
|
|
|
|
box.setSizeY(windowHeight + 0.15f);
|
|
|
|
|
box.setSizeZ(0.06f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x, origin.y + windowY,
|
|
|
|
|
origin.z - cellSize/2.0f + 0.03f));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
if (cell.hasFlag(CellFlags::IntWindowZPos)) {
|
|
|
|
|
Procedural::BoxGenerator box;
|
|
|
|
|
box.setSizeX(windowWidth + 0.15f);
|
|
|
|
|
box.setSizeY(windowHeight + 0.15f);
|
|
|
|
|
box.setSizeZ(0.06f);
|
|
|
|
|
box.setPosition(Ogre::Vector3(origin.x, origin.y + windowY,
|
|
|
|
|
origin.z + cellSize/2.0f - 0.03f));
|
|
|
|
|
box.addToTriangleBuffer(tb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tb.getVertices().size() > 0) {
|
|
|
|
|
windowTbs.push_back(std::move(tb));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CellGridSystem::buildRoofs(flecs::entity lotEntity, const CellGridComponent& grid, Procedural::TriangleBuffer& roofTb)
|
|
|
|
|
@@ -471,8 +763,11 @@ Ogre::MeshPtr CellGridSystem::convertToMesh(const std::string& name, Procedural:
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
Ogre::MeshPtr mesh = tb.transformToMesh(name);
|
|
|
|
|
Ogre::LogManager::getSingleton().logMessage("CellGrid: convertToMesh '" + name + "' vertices=" +
|
|
|
|
|
std::to_string(tb.getVertices().size()) + " material=" + materialName);
|
|
|
|
|
if (!materialName.empty() && mesh->getNumSubMeshes() > 0) {
|
|
|
|
|
mesh->getSubMesh(0)->setMaterialName(materialName);
|
|
|
|
|
Ogre::LogManager::getSingleton().logMessage("CellGrid: Set material on mesh '" + name + "' to '" + materialName + "'");
|
|
|
|
|
}
|
|
|
|
|
return mesh;
|
|
|
|
|
} catch (const Ogre::Exception& e) {
|
|
|
|
|
@@ -518,6 +813,12 @@ void CellGridSystem::destroyCellGridMeshes(flecs::entity entity)
|
|
|
|
|
removeMesh(it->second.intWallMesh);
|
|
|
|
|
removeMesh(it->second.intWindowsMesh);
|
|
|
|
|
removeMesh(it->second.roofMesh);
|
|
|
|
|
for (const auto& name : it->second.doorMeshes) {
|
|
|
|
|
removeMesh(name);
|
|
|
|
|
}
|
|
|
|
|
for (const auto& name : it->second.windowMeshes) {
|
|
|
|
|
removeMesh(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto& name : it->second.doorMeshes) {
|
|
|
|
|
removeMesh(name);
|
|
|
|
|
@@ -1030,3 +1331,86 @@ void CellGridSystem::buildLotBase(flecs::entity entity, LotComponent& lot)
|
|
|
|
|
"CellGrid: Failed to create lot base mesh: " + e.getDescription());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CellGridSystem::applyUVMappingToBuffer(Procedural::TriangleBuffer& tb, const std::string& rectName, flecs::entity materialEntity)
|
|
|
|
|
{
|
|
|
|
|
if (tb.getVertices().empty()) return;
|
|
|
|
|
|
|
|
|
|
// If no rect specified, use default UV mapping
|
|
|
|
|
if (rectName.empty()) {
|
|
|
|
|
// Default mapping - scale down UVs to a small atlas region
|
|
|
|
|
for (auto& v : tb.getVertices()) {
|
|
|
|
|
v.mUV *= 0.08f;
|
|
|
|
|
v.mUV.x += 0.41f;
|
|
|
|
|
v.mUV.x = Ogre::Math::Clamp(v.mUV.x, 0.4f, 0.5f);
|
|
|
|
|
v.mUV.y = Ogre::Math::Clamp(v.mUV.y, 0.0f, 0.1f);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ogre::LogManager::getSingleton().logMessage("CellGrid: Looking for texture rect: " + rectName);
|
|
|
|
|
|
|
|
|
|
// Find texture entity from material
|
|
|
|
|
flecs::entity textureEntity = flecs::entity::null();
|
|
|
|
|
if (materialEntity.is_alive() && materialEntity.has<ProceduralMaterialComponent>()) {
|
|
|
|
|
const auto& mat = materialEntity.get<ProceduralMaterialComponent>();
|
|
|
|
|
if (mat.diffuseTextureEntity.is_alive()) {
|
|
|
|
|
textureEntity = mat.diffuseTextureEntity;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!textureEntity.is_alive()) {
|
|
|
|
|
Ogre::LogManager::getSingleton().logMessage("CellGrid: WARNING - No texture entity for rect: " + rectName);
|
|
|
|
|
// Fall back to default UV mapping
|
|
|
|
|
for (auto& v : tb.getVertices()) {
|
|
|
|
|
v.mUV *= 0.08f;
|
|
|
|
|
v.mUV.x += 0.41f;
|
|
|
|
|
v.mUV.x = Ogre::Math::Clamp(v.mUV.x, 0.4f, 0.5f);
|
|
|
|
|
v.mUV.y = Ogre::Math::Clamp(v.mUV.y, 0.0f, 0.1f);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!textureEntity.has<ProceduralTextureComponent>()) {
|
|
|
|
|
Ogre::LogManager::getSingleton().logMessage("CellGrid: WARNING - Texture entity has no ProceduralTextureComponent for rect: " + rectName);
|
|
|
|
|
// Fall back to default UV mapping
|
|
|
|
|
for (auto& v : tb.getVertices()) {
|
|
|
|
|
v.mUV *= 0.08f;
|
|
|
|
|
v.mUV.x += 0.41f;
|
|
|
|
|
v.mUV.x = Ogre::Math::Clamp(v.mUV.x, 0.4f, 0.5f);
|
|
|
|
|
v.mUV.y = Ogre::Math::Clamp(v.mUV.y, 0.0f, 0.1f);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto& texture = textureEntity.get<ProceduralTextureComponent>();
|
|
|
|
|
const TextureRectInfo* rect = texture.getNamedRect(rectName);
|
|
|
|
|
if (!rect) {
|
|
|
|
|
Ogre::LogManager::getSingleton().logMessage("CellGrid: WARNING - Rect not found: " + rectName);
|
|
|
|
|
// Fall back to default UV mapping
|
|
|
|
|
for (auto& v : tb.getVertices()) {
|
|
|
|
|
v.mUV *= 0.08f;
|
|
|
|
|
v.mUV.x += 0.41f;
|
|
|
|
|
v.mUV.x = Ogre::Math::Clamp(v.mUV.x, 0.4f, 0.5f);
|
|
|
|
|
v.mUV.y = Ogre::Math::Clamp(v.mUV.y, 0.0f, 0.1f);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ogre::LogManager::getSingleton().logMessage("CellGrid: Applying rect " + rectName +
|
|
|
|
|
" u=" + std::to_string(rect->u1) + "-" + std::to_string(rect->u2) +
|
|
|
|
|
" v=" + std::to_string(rect->v1) + "-" + std::to_string(rect->v2));
|
|
|
|
|
|
|
|
|
|
// Apply texture rectangle UV mapping
|
|
|
|
|
const float margin = 0.01f;
|
|
|
|
|
float uRange = (rect->u2 - rect->u1) * (1.0f - 2.0f * margin);
|
|
|
|
|
float vRange = (rect->v2 - rect->v1) * (1.0f - 2.0f * margin);
|
|
|
|
|
float uOffset = rect->u1 + (rect->u2 - rect->u1) * margin;
|
|
|
|
|
float vOffset = rect->v1 + (rect->v2 - rect->v1) * margin;
|
|
|
|
|
|
|
|
|
|
for (auto& v : tb.getVertices()) {
|
|
|
|
|
v.mUV.x = uOffset + v.mUV.x * uRange;
|
|
|
|
|
v.mUV.y = vOffset + v.mUV.y * vRange;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|