roof color editing

This commit is contained in:
2026-04-11 14:59:43 +03:00
parent 3c47a87768
commit 03f72bdd77
5 changed files with 181 additions and 24 deletions

View File

@@ -140,6 +140,10 @@ struct CellGridComponent {
std::string extWindowFrameRectName;
std::string intWindowFrameRectName;
// Roof texture rectangles
std::string roofTopRectName;
std::string roofSideRectName;
// Dirty flag - triggers rebuild
bool dirty = true;
unsigned int version = 0;

View File

@@ -216,7 +216,7 @@ void CellGridSystem::buildCellGrid(flecs::entity entity,
// Build geometry parts
Procedural::TriangleBuffer floorTb, ceilingTb, extWallTb, intWallTb,
extWindowsTb, intWindowsTb, extDoorsTb, intDoorsTb, roofTb;
extWindowsTb, intWindowsTb, extDoorsTb, intDoorsTb, roofTopTb, roofSideTb;
buildFloorsAndCeilings(grid, floorTb, ceilingTb);
buildWalls(grid, extWallTb, intWallTb, intWindowsTb);
@@ -224,7 +224,7 @@ void CellGridSystem::buildCellGrid(flecs::entity entity,
buildInternalCorners(grid, intWallTb);
buildDoors(grid, extDoorsTb, intDoorsTb);
buildWindows(grid, extWindowsTb, intWindowsTb);
buildRoofs(entity, grid, roofTb);
buildRoofs(entity, grid, roofTopTb, roofSideTb);
// Apply UV mapping for each part (use rect if specified, otherwise default)
Ogre::LogManager::getSingleton().logMessage(
@@ -243,7 +243,8 @@ void CellGridSystem::buildCellGrid(flecs::entity entity,
materialEntity);
applyUVMappingToBuffer(intDoorsTb, grid.intWallRectName,
materialEntity);
applyUVMappingToBuffer(roofTb, grid.extWallRectName, materialEntity);
applyUVMappingToBuffer(roofTopTb, grid.roofTopRectName, materialEntity);
applyUVMappingToBuffer(roofSideTb, grid.roofSideRectName, materialEntity);
// Generate unique base name
std::string baseName = "CellGrid_" + std::to_string(entity.id()) + "_" +
@@ -311,15 +312,25 @@ void CellGridSystem::buildCellGrid(flecs::entity entity,
meshData.entities.push_back(entity3d);
}
// Roof
if (roofTb.getVertices().size() >= 3) {
meshData.roofMesh = baseName + "_roof";
// Roof top pieces
if (roofTopTb.getVertices().size() >= 3) {
meshData.roofMesh = baseName + "_roofTop";
auto mesh =
convertToMesh(meshData.roofMesh, roofTb, materialName);
convertToMesh(meshData.roofMesh, roofTopTb, materialName);
auto entity3d = m_sceneMgr->createEntity(meshData.roofMesh);
transform.node->attachObject(entity3d);
meshData.entities.push_back(entity3d);
}
// Roof side pieces (trim/gable ends)
if (roofSideTb.getVertices().size() >= 3) {
meshData.roofSideMesh = baseName + "_roofSide";
auto mesh =
convertToMesh(meshData.roofSideMesh, roofSideTb, materialName);
auto entity3d = m_sceneMgr->createEntity(meshData.roofSideMesh);
transform.node->attachObject(entity3d);
meshData.entities.push_back(entity3d);
}
// External doors
if (extDoorsTb.getVertices().size() >= 3) {
@@ -378,7 +389,8 @@ void CellGridSystem::buildCellGrid(flecs::entity entity,
std::to_string(extWindowsTb.getVertices().size()) +
" extDoors=" + std::to_string(extDoorsTb.getVertices().size()) +
" intDoors=" + std::to_string(intDoorsTb.getVertices().size()) +
" roof=" + std::to_string(roofTb.getVertices().size()));
" roofTop=" + std::to_string(roofTopTb.getVertices().size()) +
" roofSide=" + std::to_string(roofSideTb.getVertices().size()));
// Build window and door frames
try {
@@ -1734,9 +1746,60 @@ void CellGridSystem::buildWindows(const CellGridComponent &grid,
}
}
/**
* Deform roof side vertices to create triangular gable ends
*
* For Normal roof (deformXAxis=true): Deforms side pieces along X axis
* For Normal2 roof (deformXAxis=false): Deforms side pieces along Z axis
*/
static void deformRoofSideVertices(Procedural::TriangleBuffer &tb,
size_t startVertexIndex,
bool deformXAxis)
{
auto &vertices = tb.getVertices();
if (vertices.size() <= startVertexIndex)
return;
// Find min/max Y and coord (X or Z depending on axis)
float minY = std::numeric_limits<float>::max();
float maxY = std::numeric_limits<float>::lowest();
float minCoord = std::numeric_limits<float>::max();
float maxCoord = std::numeric_limits<float>::lowest();
for (size_t i = startVertexIndex; i < vertices.size(); ++i) {
const auto &v = vertices[i];
minY = std::min(minY, v.mPosition.y);
maxY = std::max(maxY, v.mPosition.y);
float coord = deformXAxis ? v.mPosition.x : v.mPosition.z;
minCoord = std::min(minCoord, coord);
maxCoord = std::max(maxCoord, coord);
}
float midCoord = minCoord + (maxCoord - minCoord) / 2.0f;
float coordRange = (maxCoord - minCoord) / 2.0f;
if (coordRange < 0.001f)
return;
// Deform vertices to create triangular shape
for (size_t i = startVertexIndex; i < vertices.size(); ++i) {
auto &v = vertices[i];
float md = maxY - v.mPosition.y;
float hm = (deformXAxis ? v.mPosition.x : v.mPosition.z) -
midCoord;
float he = (hm / coordRange) * md;
if (deformXAxis) {
v.mPosition.x = he + midCoord;
} else {
v.mPosition.z = he + midCoord;
}
}
}
void CellGridSystem::buildRoofs(flecs::entity cellGridEntity,
const CellGridComponent &grid,
Procedural::TriangleBuffer &roofTb)
Procedural::TriangleBuffer &roofTopTb,
Procedural::TriangleBuffer &roofSideTb)
{
// Get roof components from CellGrid's children
// Roof components are children of the CellGrid entity
@@ -1776,7 +1839,7 @@ void CellGridSystem::buildRoofs(flecs::entity cellGridEntity,
switch (roof.type) {
case RoofComponent::Flat: {
// Main roof box
// Main roof box -> roofTopTb
float baseY = origin.y + roof.baseHeight / 2.0f;
Procedural::BoxGenerator()
.setSizeX(width)
@@ -1788,11 +1851,12 @@ void CellGridSystem::buildRoofs(flecs::entity cellGridEntity,
.setEnableNormals(true)
.setPosition(Ogre::Vector3(
origin.x, baseY, origin.z))
.addToTriangleBuffer(roofTb);
.addToTriangleBuffer(roofTopTb);
// Edge trim pieces (Z+ edge)
// Edge trim pieces -> roofSideTb
float trimBaseY =
origin.y + roof.baseHeight / 2.0f;
// Z+ edge
Procedural::BoxGenerator()
.setSizeX(width)
.setSizeY(roof.baseHeight +
@@ -1806,7 +1870,7 @@ void CellGridSystem::buildRoofs(flecs::entity cellGridEntity,
origin.x, trimBaseY + EXTEND,
origin.z + depth / 2.0f +
EXTEND / 2.0f))
.addToTriangleBuffer(roofTb);
.addToTriangleBuffer(roofSideTb);
// Z- edge
Procedural::BoxGenerator()
@@ -1822,7 +1886,7 @@ void CellGridSystem::buildRoofs(flecs::entity cellGridEntity,
origin.x, trimBaseY + EXTEND,
origin.z - depth / 2.0f -
EXTEND / 2.0f))
.addToTriangleBuffer(roofTb);
.addToTriangleBuffer(roofSideTb);
// X+ edge
Procedural::BoxGenerator()
@@ -1838,7 +1902,7 @@ void CellGridSystem::buildRoofs(flecs::entity cellGridEntity,
origin.x + width / 2.0f +
EXTEND / 2.0f,
trimBaseY + EXTEND, origin.z))
.addToTriangleBuffer(roofTb);
.addToTriangleBuffer(roofSideTb);
// X- edge
Procedural::BoxGenerator()
@@ -1854,12 +1918,12 @@ void CellGridSystem::buildRoofs(flecs::entity cellGridEntity,
origin.x - width / 2.0f -
EXTEND / 2.0f,
trimBaseY + EXTEND, origin.z))
.addToTriangleBuffer(roofTb);
.addToTriangleBuffer(roofSideTb);
break;
}
case RoofComponent::Normal: {
// Gable roof along X axis (ridge runs along Z)
// Two angled boxes forming a roof
// Two angled boxes -> roofTopTb
float q = width / 2.0f;
float m = 1.0f;
float d = Ogre::Math::Sqrt(2.0f) * (q + m);
@@ -1883,7 +1947,7 @@ void CellGridSystem::buildRoofs(flecs::entity cellGridEntity,
baseY + roof.baseHeight +
q / 2.0f - m / 2.0f,
origin.z))
.addToTriangleBuffer(roofTb);
.addToTriangleBuffer(roofTopTb);
// Second angled box
Procedural::BoxGenerator()
@@ -1903,11 +1967,48 @@ void CellGridSystem::buildRoofs(flecs::entity cellGridEntity,
baseY + roof.baseHeight +
q / 2.0f - m / 2.0f,
origin.z))
.addToTriangleBuffer(roofTb);
.addToTriangleBuffer(roofTopTb);
// Side trim pieces (gable ends) -> roofSideTb
size_t sideVertexStart = roofSideTb.getVertices().size();
float sideY = baseY + q / 2.0f;
float sideHeight = q + roof.baseHeight * 3.0f;
// Z+ edge
Procedural::BoxGenerator()
.setSizeX(width + EXTEND * 2.0f)
.setSizeY(sideHeight)
.setSizeZ(EXTEND)
.setNumSegX(3)
.setNumSegY(3)
.setNumSegZ(1)
.setEnableNormals(true)
.setPosition(Ogre::Vector3(
origin.x, sideY + roof.baseHeight,
origin.z + depth / 2.0f + EXTEND / 2.0f))
.addToTriangleBuffer(roofSideTb);
// Z- edge
Procedural::BoxGenerator()
.setSizeX(width + EXTEND * 2.0f)
.setSizeY(sideHeight)
.setSizeZ(EXTEND)
.setNumSegX(3)
.setNumSegY(3)
.setNumSegZ(1)
.setEnableNormals(true)
.setPosition(Ogre::Vector3(
origin.x, sideY + roof.baseHeight,
origin.z - depth / 2.0f - EXTEND / 2.0f))
.addToTriangleBuffer(roofSideTb);
// Apply vertex deformation to side pieces
deformRoofSideVertices(roofSideTb, sideVertexStart, true);
break;
}
case RoofComponent::Normal2: {
// Gable roof along Z axis (ridge runs along X)
// Two angled boxes -> roofTopTb
float q = depth / 2.0f;
float m = 1.0f;
float d = Ogre::Math::Sqrt(2.0f) * (q + m);
@@ -1931,7 +2032,7 @@ void CellGridSystem::buildRoofs(flecs::entity cellGridEntity,
q / 2.0f - m / 2.0f,
origin.z - depth / 2.0f +
q / 2.0f - m / 2.0f))
.addToTriangleBuffer(roofTb);
.addToTriangleBuffer(roofTopTb);
// Second angled box
Procedural::BoxGenerator()
@@ -1951,19 +2052,58 @@ void CellGridSystem::buildRoofs(flecs::entity cellGridEntity,
q / 2.0f - m / 2.0f,
origin.z - depth / 2.0f + q +
q / 2.0f + m / 2.0f))
.addToTriangleBuffer(roofTb);
.addToTriangleBuffer(roofTopTb);
// Side trim pieces (gable ends) -> roofSideTb
size_t sideVertexStart = roofSideTb.getVertices().size();
float sideY = baseY + q / 2.0f;
float sideHeight = q + roof.baseHeight * 3.0f;
float sideDepth = depth + 2.0f * roof.baseHeight * 3.0f;
// X+ edge
Procedural::BoxGenerator()
.setSizeX(EXTEND)
.setSizeY(sideHeight)
.setSizeZ(sideDepth)
.setNumSegX(2)
.setNumSegY(2)
.setNumSegZ(1)
.setEnableNormals(true)
.setPosition(Ogre::Vector3(
origin.x + width / 2.0f + EXTEND / 2.0f,
sideY + roof.baseHeight, origin.z))
.addToTriangleBuffer(roofSideTb);
// X- edge
Procedural::BoxGenerator()
.setSizeX(EXTEND)
.setSizeY(sideHeight)
.setSizeZ(sideDepth)
.setNumSegX(2)
.setNumSegY(2)
.setNumSegZ(1)
.setEnableNormals(true)
.setPosition(Ogre::Vector3(
origin.x - width / 2.0f - EXTEND / 2.0f,
sideY + roof.baseHeight, origin.z))
.addToTriangleBuffer(roofSideTb);
// Apply vertex deformation to side pieces
deformRoofSideVertices(roofSideTb, sideVertexStart, false);
break;
}
case RoofComponent::Cone: {
// Cone -> roofTopTb
Procedural::ConeGenerator cone;
cone.setRadius(width / 2.0f);
cone.setHeight(roof.baseHeight + height);
cone.setPosition(Ogre::Vector3(
origin.x, origin.y, origin.z));
cone.addToTriangleBuffer(roofTb);
cone.addToTriangleBuffer(roofTopTb);
break;
}
case RoofComponent::Cylinder: {
// Cylinder -> roofTopTb
Procedural::CylinderGenerator cyl;
cyl.setRadius(width / 2.0f);
cyl.setHeight(roof.baseHeight);
@@ -1971,7 +2111,7 @@ void CellGridSystem::buildRoofs(flecs::entity cellGridEntity,
origin.x,
origin.y + roof.baseHeight / 2.0f,
origin.z));
cyl.addToTriangleBuffer(roofTb);
cyl.addToTriangleBuffer(roofTopTb);
break;
}
}
@@ -2043,6 +2183,7 @@ void CellGridSystem::destroyCellGridMeshes(flecs::entity entity)
removeMesh(it->second.intWallMesh);
removeMesh(it->second.intWindowsMesh);
removeMesh(it->second.roofMesh);
removeMesh(it->second.roofSideMesh);
for (const auto &name : it->second.doorMeshes) {
removeMesh(name);
}

View File

@@ -49,7 +49,8 @@ private:
void buildInternalCorners(const struct CellGridComponent& grid, Procedural::TriangleBuffer& intWallTb);
// Build roofs
void buildRoofs(flecs::entity lotEntity, const struct CellGridComponent& grid, Procedural::TriangleBuffer& roofTb);
void buildRoofs(flecs::entity lotEntity, const struct CellGridComponent& grid,
Procedural::TriangleBuffer& roofTopTb, Procedural::TriangleBuffer& roofSideTb);
// Build plaza for district
void buildDistrictPlaza(flecs::entity entity, struct DistrictComponent& district);
@@ -90,6 +91,7 @@ private:
std::vector<std::string> doorMeshes;
std::vector<std::string> windowMeshes;
std::string roofMesh;
std::string roofSideMesh;
std::vector<Ogre::Entity*> entities;
// Frame meshes (unique per CellGrid for cellSize/cellHeight adaptation)

View File

@@ -1278,6 +1278,8 @@ nlohmann::json SceneSerializer::serializeCellGrid(flecs::entity entity)
json["intDoorFrameRectName"] = grid.intDoorFrameRectName;
json["extWindowFrameRectName"] = grid.extWindowFrameRectName;
json["intWindowFrameRectName"] = grid.intWindowFrameRectName;
json["roofTopRectName"] = grid.roofTopRectName;
json["roofSideRectName"] = grid.roofSideRectName;
// Serialize cells
nlohmann::json cellsJson = nlohmann::json::array();
@@ -1452,6 +1454,8 @@ void SceneSerializer::deserializeCellGrid(flecs::entity entity, const nlohmann::
grid.intDoorFrameRectName = json.value("intDoorFrameRectName", "");
grid.extWindowFrameRectName = json.value("extWindowFrameRectName", "");
grid.intWindowFrameRectName = json.value("intWindowFrameRectName", "");
grid.roofTopRectName = json.value("roofTopRectName", "");
grid.roofSideRectName = json.value("roofSideRectName", "");
// Deserialize cells
if (json.contains("cells") && json["cells"].is_array()) {

View File

@@ -352,6 +352,12 @@ void CellGridEditor::renderTextureRectEditor(flecs::entity entity, CellGridCompo
if (renderRectCombo("Int Window Frames", grid.intWindowFrameRectName)) {
grid.markDirty();
}
if (renderRectCombo("Roof Top", grid.roofTopRectName)) {
grid.markDirty();
}
if (renderRectCombo("Roof Side", grid.roofSideRectName)) {
grid.markDirty();
}
ImGui::Unindent();
}