From 9f2f0be4a360d892e19fc3afd72a0686c6a130f1 Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Tue, 7 Apr 2026 11:21:01 +0300 Subject: [PATCH] Adjusted frame geometry --- .../editScene/systems/CellGridSystem.cpp | 288 ++++++++++++++++-- 1 file changed, 258 insertions(+), 30 deletions(-) diff --git a/src/features/editScene/systems/CellGridSystem.cpp b/src/features/editScene/systems/CellGridSystem.cpp index 6c6459b..e47f34c 100644 --- a/src/features/editScene/systems/CellGridSystem.cpp +++ b/src/features/editScene/systems/CellGridSystem.cpp @@ -1715,6 +1715,73 @@ void CellGridSystem::buildRoofs(flecs::entity lotEntity, if (!lotEntity.has()) return; + // Get material for roofs (prefer ProceduralMaterial from Lot/District/Town) + std::string materialName; + flecs::entity materialEntity = flecs::entity::null(); + + // Look for ProceduralMaterial in parent hierarchy + flecs::entity parent = lotEntity.parent(); + while (parent.is_alive()) { + if (parent.has()) { + auto &lot = parent.get(); + if (lot.proceduralMaterialEntity.is_alive() && + lot.proceduralMaterialEntity + .has()) { + const auto &mat = + lot.proceduralMaterialEntity.get< + ProceduralMaterialComponent>(); + if (mat.created && !mat.materialName.empty()) { + materialName = mat.materialName; + materialEntity = + lot.proceduralMaterialEntity; + break; + } + } + } + if (parent.has()) { + auto &district = parent.get(); + if (district.proceduralMaterialEntity.is_alive() && + district.proceduralMaterialEntity + .has()) { + const auto &mat = + district.proceduralMaterialEntity.get< + ProceduralMaterialComponent>(); + if (mat.created && !mat.materialName.empty()) { + materialName = mat.materialName; + materialEntity = + district.proceduralMaterialEntity; + break; + } + } + } + if (parent.has()) { + auto &town = parent.get(); + if (town.proceduralMaterialEntity.is_alive() && + town.proceduralMaterialEntity + .has()) { + 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 + if (!town.materialName.empty()) { + materialName = town.materialName; + break; + } + } + parent = parent.parent(); + } + + // Get roof rect name for UV mapping + std::string roofRectName = + grid.extWallRectName; // Default to exterior wall color + // Get roof components from lot's children lotEntity.children([&](flecs::entity child) { if (child.has()) { @@ -1728,38 +1795,187 @@ void CellGridSystem::buildRoofs(flecs::entity lotEntity, float depth = roof.sizeZ * grid.cellSize; float height = roof.maxHeight - roof.baseHeight; + // EXTEND constant for roof edges (like old code) + const float EXTEND = 0.2f; + switch (roof.type) { case RoofComponent::Flat: { - Procedural::BoxGenerator box; - box.setSizeX(width); - box.setSizeY(roof.baseHeight); - box.setSizeZ(depth); - box.setPosition(Ogre::Vector3( - origin.x + width / 2.0f - - grid.cellSize / 2.0f, - origin.y + roof.baseHeight / 2.0f, - origin.z + depth / 2.0f - - grid.cellSize / 2.0f)); - box.addToTriangleBuffer(roofTb); + // Main roof box + float baseY = origin.y + roof.baseHeight / 2.0f; + Procedural::BoxGenerator() + .setSizeX(width) + .setSizeY(roof.baseHeight) + .setSizeZ(depth) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + origin.x, baseY, origin.z)) + .addToTriangleBuffer(roofTb); + + // Edge trim pieces (Z+ edge) + float trimBaseY = + origin.y + roof.baseHeight / 2.0f; + Procedural::BoxGenerator() + .setSizeX(width) + .setSizeY(roof.baseHeight + + EXTEND * 2.0f) + .setSizeZ(EXTEND) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + origin.x, trimBaseY + EXTEND, + origin.z + depth / 2.0f + + EXTEND / 2.0f)) + .addToTriangleBuffer(roofTb); + + // Z- edge + Procedural::BoxGenerator() + .setSizeX(width) + .setSizeY(roof.baseHeight + + EXTEND * 2.0f) + .setSizeZ(EXTEND) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + origin.x, trimBaseY + EXTEND, + origin.z - depth / 2.0f - + EXTEND / 2.0f)) + .addToTriangleBuffer(roofTb); + + // X+ edge + Procedural::BoxGenerator() + .setSizeX(EXTEND) + .setSizeY(roof.baseHeight + + EXTEND * 2.0f) + .setSizeZ(depth + EXTEND * 2.0f) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + origin.x + width / 2.0f + + EXTEND / 2.0f, + trimBaseY + EXTEND, origin.z)) + .addToTriangleBuffer(roofTb); + + // X- edge + Procedural::BoxGenerator() + .setSizeX(EXTEND) + .setSizeY(roof.baseHeight + + EXTEND * 2.0f) + .setSizeZ(depth + EXTEND * 2.0f) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + origin.x - width / 2.0f - + EXTEND / 2.0f, + trimBaseY + EXTEND, origin.z)) + .addToTriangleBuffer(roofTb); + break; + } + case RoofComponent::Normal: { + // Gable roof along X axis (ridge runs along Z) + // Two angled boxes forming a roof + float q = width / 2.0f; + float m = 1.0f; + float d = Ogre::Math::Sqrt(2.0f) * (q + m); + float baseY = origin.y; + + // First angled box + Procedural::BoxGenerator() + .setSizeX(d) + .setSizeY(roof.baseHeight / 2.0f) + .setSizeZ(depth) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setOrientation(Ogre::Quaternion( + Ogre::Degree(45), + Ogre::Vector3::NEGATIVE_UNIT_Z)) + .setPosition(Ogre::Vector3( + origin.x - width / 2.0f + q + + q / 2.0f + m / 2.0f, + baseY + roof.baseHeight + + q / 2.0f - m / 2.0f, + origin.z)) + .addToTriangleBuffer(roofTb); + + // Second angled box + Procedural::BoxGenerator() + .setSizeX(d) + .setSizeY(roof.baseHeight / 2.0f) + .setSizeZ(depth) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setOrientation(Ogre::Quaternion( + Ogre::Degree(-45), + Ogre::Vector3::NEGATIVE_UNIT_Z)) + .setPosition(Ogre::Vector3( + origin.x - width / 2.0f + + q / 2.0f - m / 2.0f, + baseY + roof.baseHeight + + q / 2.0f - m / 2.0f, + origin.z)) + .addToTriangleBuffer(roofTb); break; } - case RoofComponent::Normal: case RoofComponent::Normal2: { - // Gable roof using two rotated boxes or a prism - // Simplified as a box for now - Procedural::BoxGenerator box; - box.setSizeX(width); - box.setSizeY(roof.baseHeight + height / 2.0f); - box.setSizeZ(depth); - box.setPosition(Ogre::Vector3( - origin.x + width / 2.0f - - grid.cellSize / 2.0f, - origin.y + (roof.baseHeight + - height / 2.0f) / - 2.0f, - origin.z + depth / 2.0f - - grid.cellSize / 2.0f)); - box.addToTriangleBuffer(roofTb); + // Gable roof along Z axis (ridge runs along X) + float q = depth / 2.0f; + float m = 1.0f; + float d = Ogre::Math::Sqrt(2.0f) * (q + m); + float baseY = origin.y; + + // First angled box + Procedural::BoxGenerator() + .setSizeX(width) + .setSizeY(roof.baseHeight / 2.0f) + .setSizeZ(d) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setOrientation(Ogre::Quaternion( + Ogre::Degree(45), + Ogre::Vector3::NEGATIVE_UNIT_X)) + .setPosition(Ogre::Vector3( + origin.x, + baseY + roof.baseHeight + + q / 2.0f - m / 2.0f, + origin.z - depth / 2.0f + + q / 2.0f - m / 2.0f)) + .addToTriangleBuffer(roofTb); + + // Second angled box + Procedural::BoxGenerator() + .setSizeX(width) + .setSizeY(roof.baseHeight / 2.0f) + .setSizeZ(d) + .setNumSegX(1) + .setNumSegY(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setOrientation(Ogre::Quaternion( + Ogre::Degree(-45), + Ogre::Vector3::NEGATIVE_UNIT_X)) + .setPosition(Ogre::Vector3( + origin.x, + baseY + roof.baseHeight + + q / 2.0f - m / 2.0f, + origin.z - depth / 2.0f + q + + q / 2.0f + m / 2.0f)) + .addToTriangleBuffer(roofTb); break; } case RoofComponent::Cone: { @@ -2846,7 +3062,7 @@ void CellGridSystem::createDoorFrameMeshes(const CellGridComponent &grid, // Create external door frame mesh Procedural::TriangleBuffer extFrameTb; - float stepDepth = frameDepth * 1.3f; + float stepDepth = frameDepth * 1.2f; // Left frame with step Procedural::BoxGenerator() @@ -2893,16 +3109,28 @@ void CellGridSystem::createDoorFrameMeshes(const CellGridComponent &grid, float stepWidth = doorWidth + frameThickness * 4; Procedural::BoxGenerator() .setSizeX(stepWidth) - .setSizeY(frameThickness) + .setSizeY(frameThickness * 2.0f) .setSizeZ(stepDepth) .setNumSegX(2) .setNumSegY(1) .setNumSegZ(1) .setPosition( - Ogre::Vector3(0, frameThickness / 2 + 0.1f, + Ogre::Vector3(0, frameThickness, (stepDepth - frameDepth) / 2 + 0.05f)) .setEnableNormals(true) .addToTriangleBuffer(extFrameTb); + Procedural::BoxGenerator() + .setSizeX(stepWidth) + .setSizeY(frameThickness) + .setSizeZ(stepDepth) + .setNumSegX(2) + .setNumSegY(1) + .setNumSegZ(1) + .setPosition(Ogre::Vector3(0, frameThickness * 0.5f, + (stepDepth - frameDepth) / 2 + + 0.05f + stepDepth)) + .setEnableNormals(true) + .addToTriangleBuffer(extFrameTb); applyUVMappingToBuffer(extFrameTb, grid.extDoorFrameRectName, materialEntity);