Fixed material for Lot geometry and CellGrid is re-created now
This commit is contained in:
@@ -235,6 +235,13 @@ struct LotComponent {
|
||||
// Template name if this lot was created from a template
|
||||
std::string templateName;
|
||||
|
||||
// Procedural material for lot base (optional - falls back to District, then Town)
|
||||
flecs::entity proceduralMaterialEntity;
|
||||
std::string proceduralMaterialEntityId; // For serialization
|
||||
|
||||
// Texture rectangle name from ProceduralTexture for UV mapping
|
||||
std::string textureRectName;
|
||||
|
||||
// Dirty flag
|
||||
bool dirty = true;
|
||||
|
||||
@@ -289,6 +296,13 @@ struct TownComponent {
|
||||
// Material name (created from color rects)
|
||||
std::string materialName;
|
||||
|
||||
// Procedural material for town (used by districts and lots)
|
||||
flecs::entity proceduralMaterialEntity;
|
||||
std::string proceduralMaterialEntityId; // For serialization
|
||||
|
||||
// Texture rectangle name from ProceduralTexture for UV mapping
|
||||
std::string textureRectName;
|
||||
|
||||
// Dirty flag
|
||||
bool dirty = true;
|
||||
bool materialDirty = true;
|
||||
|
||||
@@ -29,6 +29,7 @@ CellGridSystem::CellGridSystem(flecs::world& world, Ogre::SceneManager* sceneMgr
|
||||
, m_cellGridQuery(world.query<CellGridComponent>())
|
||||
, m_townQuery(world.query<TownComponent>())
|
||||
, m_districtQuery(world.query<DistrictComponent>())
|
||||
, m_lotQuery(world.query<LotComponent>())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -81,6 +82,14 @@ void CellGridSystem::update()
|
||||
std::to_string(districtCount) + " districts, " +
|
||||
std::to_string(dirtyDistrictCount) + " dirty");
|
||||
}
|
||||
|
||||
// Process dirty lots (for base geometry)
|
||||
m_lotQuery.each([&](flecs::entity entity, LotComponent& lot) {
|
||||
if (lot.dirty) {
|
||||
buildLotBase(entity, lot);
|
||||
lot.dirty = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CellGridSystem::buildCellGrid(flecs::entity entity, CellGridComponent& grid)
|
||||
@@ -89,7 +98,7 @@ void CellGridSystem::buildCellGrid(flecs::entity entity, CellGridComponent& grid
|
||||
" cells=" + std::to_string(grid.cells.size()));
|
||||
|
||||
// Destroy existing meshes
|
||||
destroyCellGridMeshes(grid);
|
||||
destroyCellGridMeshes(entity);
|
||||
|
||||
// Find parent town for material
|
||||
std::string materialName = "Ogre/StandardFloor"; // default
|
||||
@@ -474,7 +483,51 @@ Ogre::MeshPtr CellGridSystem::convertToMesh(const std::string& name, Procedural:
|
||||
|
||||
void CellGridSystem::destroyCellGridMeshes(CellGridComponent& grid)
|
||||
{
|
||||
// Cleanup handled by entity destruction, but we track mesh names
|
||||
// This is called from buildCellGrid which uses the entity ID as key in m_entityMeshes
|
||||
// The actual cleanup needs to happen there
|
||||
}
|
||||
|
||||
void CellGridSystem::destroyCellGridMeshes(flecs::entity entity)
|
||||
{
|
||||
auto it = m_entityMeshes.find(entity.id());
|
||||
if (it != m_entityMeshes.end()) {
|
||||
// Destroy all entities first
|
||||
for (auto* ent : it->second.entities) {
|
||||
if (ent) {
|
||||
try {
|
||||
m_sceneMgr->destroyEntity(ent);
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
it->second.entities.clear();
|
||||
|
||||
// Destroy meshes
|
||||
auto& meshMgr = Ogre::MeshManager::getSingleton();
|
||||
|
||||
auto removeMesh = [&](const std::string& name) {
|
||||
if (!name.empty()) {
|
||||
try {
|
||||
meshMgr.remove(name);
|
||||
} catch (...) {}
|
||||
}
|
||||
};
|
||||
|
||||
removeMesh(it->second.floorMesh);
|
||||
removeMesh(it->second.ceilingMesh);
|
||||
removeMesh(it->second.extWallMesh);
|
||||
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);
|
||||
}
|
||||
|
||||
m_entityMeshes.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void CellGridSystem::updateTownMaterial(flecs::entity townEntity)
|
||||
@@ -680,3 +733,300 @@ void CellGridSystem::buildDistrictPlaza(flecs::entity entity, DistrictComponent&
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Failed to create plaza mesh: " + e.getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CellGridSystem::buildLotBase(flecs::entity entity, LotComponent& lot)
|
||||
{
|
||||
// Cleanup old mesh
|
||||
auto it = m_lotBaseMeshes.find(entity.id());
|
||||
if (it != m_lotBaseMeshes.end()) {
|
||||
if (it->second.entity) {
|
||||
try { m_sceneMgr->destroyEntity(it->second.entity); } catch (...) {}
|
||||
}
|
||||
if (!it->second.meshName.empty()) {
|
||||
try { Ogre::MeshManager::getSingleton().remove(it->second.meshName); } catch (...) {}
|
||||
}
|
||||
m_lotBaseMeshes.erase(it);
|
||||
}
|
||||
|
||||
// Validate transform
|
||||
if (!entity.has<TransformComponent>()) {
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Lot has no TransformComponent");
|
||||
return;
|
||||
}
|
||||
auto& transform = entity.get_mut<TransformComponent>();
|
||||
if (!transform.node) {
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Lot has no SceneNode");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find parent district and town for radius and material
|
||||
flecs::entity districtEntity = flecs::entity::null();
|
||||
flecs::entity townEntity = flecs::entity::null();
|
||||
float districtRadius = 50.0f;
|
||||
std::string materialName = "Ogre/StandardFloor";
|
||||
|
||||
// Material hierarchy: Lot -> District -> Town
|
||||
// 1. Check Lot's own material first
|
||||
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;
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Using Lot's own material: " + materialName);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Walk up hierarchy for District and Town, and fallback materials
|
||||
flecs::entity parent = entity.parent();
|
||||
while (parent.is_alive()) {
|
||||
if (parent.has<DistrictComponent>()) {
|
||||
districtEntity = parent;
|
||||
auto& district = parent.get<DistrictComponent>();
|
||||
districtRadius = district.radius;
|
||||
// Use District material if Lot doesn't have one
|
||||
if (materialName == "Ogre/StandardFloor" &&
|
||||
district.proceduralMaterialEntity.is_alive() &&
|
||||
district.proceduralMaterialEntity.has<ProceduralMaterialComponent>()) {
|
||||
const auto& mat = district.proceduralMaterialEntity.get<ProceduralMaterialComponent>();
|
||||
if (mat.created && !mat.materialName.empty()) {
|
||||
materialName = mat.materialName;
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Using District material: " + materialName);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parent.has<TownComponent>()) {
|
||||
townEntity = parent;
|
||||
// Check Town's ProceduralMaterial first, then generated material
|
||||
if (materialName == "Ogre/StandardFloor") {
|
||||
auto& town = parent.get<TownComponent>();
|
||||
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;
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Using Town ProceduralMaterial: " + materialName);
|
||||
}
|
||||
}
|
||||
// Fallback to Town's generated material
|
||||
if (materialName == "Ogre/StandardFloor" && !town.materialName.empty()) {
|
||||
materialName = town.materialName;
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Using Town generated material: " + materialName);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
parent = parent.parent();
|
||||
}
|
||||
|
||||
if (!districtEntity.is_alive()) {
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Lot has no parent District");
|
||||
return;
|
||||
}
|
||||
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Material selection for lot " + std::to_string(entity.id()) +
|
||||
" final materialName='" + materialName + "'");
|
||||
|
||||
// Position the Lot's SceneNode at the correct location around the district
|
||||
Ogre::Quaternion lotRotation(Ogre::Degree(lot.angle), Ogre::Vector3::UNIT_Y);
|
||||
Ogre::Vector3 lotOffset = lotRotation * (Ogre::Vector3::UNIT_Z * districtRadius);
|
||||
lotOffset += Ogre::Vector3(lot.offsetX, 0, lot.offsetZ);
|
||||
|
||||
// Set the Lot's position (CellGrid children will inherit this)
|
||||
transform.node->setPosition(lotOffset);
|
||||
transform.node->setOrientation(lotRotation);
|
||||
|
||||
// Update the TransformComponent to match
|
||||
transform.position = lotOffset;
|
||||
transform.rotation = lotRotation;
|
||||
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"CellGrid: Building lot base for entity " + std::to_string(entity.id()) +
|
||||
" at position (" + std::to_string(lotOffset.x) + ", " +
|
||||
std::to_string(lotOffset.y) + ", " + std::to_string(lotOffset.z) +
|
||||
") with material: " + materialName);
|
||||
|
||||
// Build lot base geometry (box foundation)
|
||||
Procedural::TriangleBuffer tb;
|
||||
|
||||
const float cellSize = 4.0f;
|
||||
const float baseHeight = 4.0f;
|
||||
float width = cellSize * lot.width;
|
||||
float depth = cellSize * lot.depth;
|
||||
|
||||
// Create box for lot base
|
||||
Procedural::BoxGenerator()
|
||||
.setSizeX(width)
|
||||
.setSizeY(baseHeight)
|
||||
.setSizeZ(depth)
|
||||
.setNumSegY(8)
|
||||
.setNumSegX(8)
|
||||
.setNumSegZ(8)
|
||||
.setEnableNormals(true)
|
||||
.setPosition(Ogre::Vector3(0.0f, -baseHeight / 2.0f + lot.elevation - 0.01f, 0.0f))
|
||||
.addToTriangleBuffer(tb);
|
||||
|
||||
// Determine which texture rect to use (Lot -> District -> Town)
|
||||
std::string textureRectToUse;
|
||||
flecs::entity materialEntityToUse;
|
||||
|
||||
// If Lot has its own material, use its texture rect
|
||||
if (lot.proceduralMaterialEntity.is_alive()) {
|
||||
textureRectToUse = lot.textureRectName;
|
||||
materialEntityToUse = lot.proceduralMaterialEntity;
|
||||
if (!textureRectToUse.empty()) {
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Using Lot's own texture rect: " + textureRectToUse);
|
||||
}
|
||||
} else {
|
||||
// Lot uses District/Town material - look up hierarchy for texture rect
|
||||
flecs::entity parent = entity.parent();
|
||||
while (parent.is_alive()) {
|
||||
if (parent.has<DistrictComponent>()) {
|
||||
auto& district = parent.get<DistrictComponent>();
|
||||
if (!district.textureRectName.empty()) {
|
||||
textureRectToUse = district.textureRectName;
|
||||
materialEntityToUse = district.proceduralMaterialEntity;
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Using District texture rect: " + textureRectToUse);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (parent.has<TownComponent>()) {
|
||||
auto& town = parent.get<TownComponent>();
|
||||
if (!town.textureRectName.empty()) {
|
||||
textureRectToUse = town.textureRectName;
|
||||
materialEntityToUse = town.proceduralMaterialEntity;
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Using Town texture rect: " + textureRectToUse);
|
||||
}
|
||||
break;
|
||||
}
|
||||
parent = parent.parent();
|
||||
}
|
||||
}
|
||||
|
||||
// Apply UV mapping
|
||||
if (!textureRectToUse.empty()) {
|
||||
// Find texture entity from the material that was selected
|
||||
flecs::entity textureEntity = flecs::entity::null();
|
||||
if (materialEntityToUse.is_alive() &&
|
||||
materialEntityToUse.has<ProceduralMaterialComponent>()) {
|
||||
const auto& mat = materialEntityToUse.get<ProceduralMaterialComponent>();
|
||||
if (mat.diffuseTextureEntity.is_alive()) {
|
||||
textureEntity = mat.diffuseTextureEntity;
|
||||
}
|
||||
}
|
||||
// Fallback to District/Town texture
|
||||
if (!textureEntity.is_alive()) {
|
||||
flecs::entity parent = entity.parent();
|
||||
while (parent.is_alive()) {
|
||||
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.diffuseTextureEntity.is_alive()) {
|
||||
textureEntity = mat.diffuseTextureEntity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parent.has<TownComponent>()) {
|
||||
auto& town = parent.get<TownComponent>();
|
||||
if (town.proceduralMaterialEntity.is_alive() &&
|
||||
town.proceduralMaterialEntity.has<ProceduralMaterialComponent>()) {
|
||||
const auto& mat = town.proceduralMaterialEntity.get<ProceduralMaterialComponent>();
|
||||
if (mat.diffuseTextureEntity.is_alive()) {
|
||||
textureEntity = mat.diffuseTextureEntity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
parent = parent.parent();
|
||||
}
|
||||
}
|
||||
|
||||
if (textureEntity.is_alive() && textureEntity.has<ProceduralTextureComponent>()) {
|
||||
const auto& texture = textureEntity.get<ProceduralTextureComponent>();
|
||||
const TextureRectInfo* rect = texture.getNamedRect(textureRectToUse);
|
||||
if (rect) {
|
||||
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;
|
||||
}
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Applied UV mapping for rect '" +
|
||||
textureRectToUse + "'");
|
||||
} else {
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: WARNING - Rect not found: " + textureRectToUse);
|
||||
}
|
||||
} else {
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: WARNING - No texture entity for rect: " + textureRectToUse);
|
||||
}
|
||||
} else {
|
||||
// Default UV mapping for texture atlas (same as original town.cpp)
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Using default UV mapping (no texture rect set)");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Create mesh and entity
|
||||
std::string meshName = "LotBase_" + std::to_string(entity.id()) + "_" + std::to_string(m_meshCount++);
|
||||
try {
|
||||
if (tb.getVertices().size() < 3) {
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: ERROR - Not enough vertices for lot base mesh");
|
||||
return;
|
||||
}
|
||||
|
||||
Ogre::MeshPtr mesh = tb.transformToMesh(meshName);
|
||||
if (mesh.isNull()) {
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: ERROR - transformToMesh returned null for lot base");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!materialName.empty() && mesh->getNumSubMeshes() > 0) {
|
||||
mesh->getSubMesh(0)->setMaterialName(materialName);
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Set lot base material to: " + materialName);
|
||||
} else {
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: WARNING - Could not set material. materialName='" +
|
||||
materialName + "' subMeshes=" + std::to_string(mesh->getNumSubMeshes()));
|
||||
}
|
||||
|
||||
Ogre::Entity* lotBaseEntity = m_sceneMgr->createEntity(meshName);
|
||||
if (!lotBaseEntity) {
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: ERROR - createEntity returned null for lot base");
|
||||
return;
|
||||
}
|
||||
|
||||
// Attach to the Lot's SceneNode directly (position already set above)
|
||||
transform.node->attachObject(lotBaseEntity);
|
||||
|
||||
// Debug: Check actual material
|
||||
if (lotBaseEntity->getNumSubEntities() > 0) {
|
||||
std::string actualMat = lotBaseEntity->getSubEntity(0)->getMaterialName();
|
||||
Ogre::LogManager::getSingleton().logMessage("CellGrid: Lot base actual material: " + actualMat);
|
||||
}
|
||||
|
||||
LotBaseData data;
|
||||
data.meshName = meshName;
|
||||
data.entity = lotBaseEntity;
|
||||
m_lotBaseMeshes[entity.id()] = data;
|
||||
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"CellGrid: Successfully created lot base mesh for entity " +
|
||||
std::to_string(entity.id()) + " with " +
|
||||
std::to_string(tb.getVertices().size()) + " vertices");
|
||||
|
||||
} catch (const Ogre::Exception& e) {
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"CellGrid: Failed to create lot base mesh: " + e.getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ private:
|
||||
flecs::query<struct CellGridComponent> m_cellGridQuery;
|
||||
flecs::query<struct TownComponent> m_townQuery;
|
||||
flecs::query<struct DistrictComponent> m_districtQuery;
|
||||
flecs::query<struct LotComponent> m_lotQuery;
|
||||
|
||||
bool m_initialized = false;
|
||||
int m_meshCount = 0;
|
||||
@@ -52,6 +53,9 @@ private:
|
||||
// Build plaza for district
|
||||
void buildDistrictPlaza(flecs::entity entity, struct DistrictComponent& district);
|
||||
|
||||
// Build lot base geometry
|
||||
void buildLotBase(flecs::entity entity, struct LotComponent& lot);
|
||||
|
||||
// Convert triangle buffer to mesh
|
||||
Ogre::MeshPtr convertToMesh(const std::string& name, Procedural::TriangleBuffer& tb, const std::string& materialName);
|
||||
|
||||
@@ -60,6 +64,7 @@ private:
|
||||
|
||||
// Destroy existing mesh
|
||||
void destroyCellGridMeshes(struct CellGridComponent& grid);
|
||||
void destroyCellGridMeshes(flecs::entity entity);
|
||||
|
||||
// Mesh storage per entity
|
||||
struct MeshData {
|
||||
@@ -81,4 +86,11 @@ private:
|
||||
Ogre::Entity* entity = nullptr;
|
||||
};
|
||||
std::unordered_map<uint64_t, PlazaData> m_plazaMeshes;
|
||||
|
||||
// Lot base mesh storage per lot entity
|
||||
struct LotBaseData {
|
||||
std::string meshName;
|
||||
Ogre::Entity* entity = nullptr;
|
||||
};
|
||||
std::unordered_map<uint64_t, LotBaseData> m_lotBaseMeshes;
|
||||
};
|
||||
|
||||
@@ -1305,6 +1305,8 @@ nlohmann::json SceneSerializer::serializeTown(flecs::entity entity)
|
||||
|
||||
json["townName"] = town.townName;
|
||||
json["materialName"] = town.materialName;
|
||||
json["proceduralMaterialEntityId"] = town.proceduralMaterialEntityId;
|
||||
json["textureRectName"] = town.textureRectName;
|
||||
|
||||
// Serialize color rects
|
||||
nlohmann::json colorRectsJson = nlohmann::json::object();
|
||||
@@ -1355,6 +1357,8 @@ nlohmann::json SceneSerializer::serializeLot(flecs::entity entity)
|
||||
json["offsetX"] = lot.offsetX;
|
||||
json["offsetZ"] = lot.offsetZ;
|
||||
json["templateName"] = lot.templateName;
|
||||
json["proceduralMaterialEntityId"] = lot.proceduralMaterialEntityId;
|
||||
json["textureRectName"] = lot.textureRectName;
|
||||
|
||||
return json;
|
||||
}
|
||||
@@ -1471,6 +1475,21 @@ void SceneSerializer::deserializeTown(flecs::entity entity, const nlohmann::json
|
||||
|
||||
town.townName = json.value("townName", "New Town");
|
||||
town.materialName = json.value("materialName", "");
|
||||
town.proceduralMaterialEntityId = json.value("proceduralMaterialEntityId", "");
|
||||
town.textureRectName = json.value("textureRectName", "");
|
||||
|
||||
// Resolve material entity reference
|
||||
if (!town.proceduralMaterialEntityId.empty()) {
|
||||
try {
|
||||
uint64_t matId = std::stoull(town.proceduralMaterialEntityId);
|
||||
flecs::entity matEntity(m_world, matId);
|
||||
if (matEntity.is_alive() && matEntity.has<ProceduralMaterialComponent>()) {
|
||||
town.proceduralMaterialEntity = matEntity;
|
||||
}
|
||||
} catch (...) {
|
||||
// Invalid ID format, ignore
|
||||
}
|
||||
}
|
||||
|
||||
// Deserialize color rects
|
||||
if (json.contains("colorRects") && json["colorRects"].is_object()) {
|
||||
@@ -1543,6 +1562,21 @@ void SceneSerializer::deserializeLot(flecs::entity entity, const nlohmann::json&
|
||||
lot.offsetX = json.value("offsetX", 0.0f);
|
||||
lot.offsetZ = json.value("offsetZ", 0.0f);
|
||||
lot.templateName = json.value("templateName", "");
|
||||
lot.proceduralMaterialEntityId = json.value("proceduralMaterialEntityId", "");
|
||||
lot.textureRectName = json.value("textureRectName", "");
|
||||
|
||||
// Resolve material entity reference
|
||||
if (!lot.proceduralMaterialEntityId.empty()) {
|
||||
try {
|
||||
uint64_t matId = std::stoull(lot.proceduralMaterialEntityId);
|
||||
flecs::entity matEntity(m_world, matId);
|
||||
if (matEntity.is_alive() && matEntity.has<ProceduralMaterialComponent>()) {
|
||||
lot.proceduralMaterialEntity = matEntity;
|
||||
}
|
||||
} catch (...) {
|
||||
// Invalid ID format, ignore
|
||||
}
|
||||
}
|
||||
|
||||
lot.dirty = true;
|
||||
entity.set<LotComponent>(lot);
|
||||
|
||||
@@ -27,55 +27,54 @@ bool DistrictEditor::renderComponent(flecs::entity entity, DistrictComponent& di
|
||||
" for entity " + std::to_string(entity.id()));
|
||||
}
|
||||
|
||||
// Plaza material and texture settings
|
||||
if (district.isPlaza) {
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Plaza Material:");
|
||||
// District material and texture settings
|
||||
ImGui::Separator();
|
||||
ImGui::Text("District Material:");
|
||||
|
||||
// Material selector
|
||||
flecs::world world = entity.world();
|
||||
std::vector<flecs::entity> materialEntities;
|
||||
std::vector<std::string> materialNames;
|
||||
|
||||
int currentMatIndex = -1;
|
||||
int noneMatIndex = 0;
|
||||
|
||||
materialEntities.push_back(flecs::entity::null());
|
||||
materialNames.push_back("Use Town Material");
|
||||
|
||||
world.query<ProceduralMaterialComponent>().each([&](flecs::entity e, ProceduralMaterialComponent& mat) {
|
||||
materialEntities.push_back(e);
|
||||
std::string name = mat.materialName.empty() ? "Material " + std::to_string(e.id()) : mat.materialName;
|
||||
materialNames.push_back(name);
|
||||
|
||||
// Material selector
|
||||
flecs::world world = entity.world();
|
||||
std::vector<flecs::entity> materialEntities;
|
||||
std::vector<std::string> materialNames;
|
||||
|
||||
int currentMatIndex = -1;
|
||||
int noneMatIndex = 0;
|
||||
|
||||
materialEntities.push_back(flecs::entity::null());
|
||||
materialNames.push_back("None");
|
||||
|
||||
world.query<ProceduralMaterialComponent>().each([&](flecs::entity e, ProceduralMaterialComponent& mat) {
|
||||
materialEntities.push_back(e);
|
||||
std::string name = mat.materialName.empty() ? "Material " + std::to_string(e.id()) : mat.materialName;
|
||||
materialNames.push_back(name);
|
||||
|
||||
if (district.proceduralMaterialEntity == e) {
|
||||
currentMatIndex = (int)materialEntities.size() - 1;
|
||||
}
|
||||
});
|
||||
|
||||
if (currentMatIndex == -1) currentMatIndex = noneMatIndex;
|
||||
|
||||
std::string matComboItems;
|
||||
for (size_t i = 0; i < materialNames.size(); ++i) {
|
||||
if (i > 0) matComboItems += '\0';
|
||||
matComboItems += materialNames[i];
|
||||
if (district.proceduralMaterialEntity == e) {
|
||||
currentMatIndex = (int)materialEntities.size() - 1;
|
||||
}
|
||||
matComboItems += '\0';
|
||||
|
||||
int newMatIndex = currentMatIndex;
|
||||
if (ImGui::Combo("Material", &newMatIndex, matComboItems.c_str())) {
|
||||
if (newMatIndex == noneMatIndex) {
|
||||
district.proceduralMaterialEntity = flecs::entity::null();
|
||||
district.proceduralMaterialEntityId.clear();
|
||||
} else {
|
||||
district.proceduralMaterialEntity = materialEntities[newMatIndex];
|
||||
district.proceduralMaterialEntityId = std::to_string(materialEntities[newMatIndex].id());
|
||||
}
|
||||
modified = true;
|
||||
});
|
||||
|
||||
if (currentMatIndex == -1) currentMatIndex = noneMatIndex;
|
||||
|
||||
std::string matComboItems;
|
||||
for (size_t i = 0; i < materialNames.size(); ++i) {
|
||||
if (i > 0) matComboItems += '\0';
|
||||
matComboItems += materialNames[i];
|
||||
}
|
||||
matComboItems += '\0';
|
||||
|
||||
int newMatIndex = currentMatIndex;
|
||||
if (ImGui::Combo("Material", &newMatIndex, matComboItems.c_str())) {
|
||||
if (newMatIndex == noneMatIndex) {
|
||||
district.proceduralMaterialEntity = flecs::entity::null();
|
||||
district.proceduralMaterialEntityId.clear();
|
||||
} else {
|
||||
district.proceduralMaterialEntity = materialEntities[newMatIndex];
|
||||
district.proceduralMaterialEntityId = std::to_string(materialEntities[newMatIndex].id());
|
||||
}
|
||||
|
||||
// Texture rectangle selector
|
||||
if (district.proceduralMaterialEntity.is_alive()) {
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// Texture rectangle selector
|
||||
if (district.proceduralMaterialEntity.is_alive()) {
|
||||
// Find the texture entity from the material
|
||||
flecs::entity textureEntity = flecs::entity::null();
|
||||
if (district.proceduralMaterialEntity.has<ProceduralMaterialComponent>()) {
|
||||
@@ -129,6 +128,11 @@ bool DistrictEditor::renderComponent(flecs::entity entity, DistrictComponent& di
|
||||
ImGui::TextDisabled("Material has no associated ProceduralTexture");
|
||||
}
|
||||
}
|
||||
|
||||
// Plaza settings
|
||||
if (district.isPlaza) {
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Plaza uses District Material");
|
||||
}
|
||||
|
||||
// Count lots
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "LotEditor.hpp"
|
||||
#include "../components/CellGrid.hpp"
|
||||
#include "../components/ProceduralTexture.hpp"
|
||||
#include "../components/ProceduralMaterial.hpp"
|
||||
#include <imgui.h>
|
||||
|
||||
bool LotEditor::renderComponent(flecs::entity entity, LotComponent& lot)
|
||||
@@ -39,6 +41,95 @@ bool LotEditor::renderComponent(flecs::entity entity, LotComponent& lot)
|
||||
modified = true;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Lot Material:");
|
||||
|
||||
// Material selector
|
||||
flecs::world world = entity.world();
|
||||
std::vector<flecs::entity> materialEntities;
|
||||
std::vector<std::string> materialNames;
|
||||
materialEntities.push_back(flecs::entity::null());
|
||||
materialNames.push_back("Use District/Town");
|
||||
|
||||
int currentMatIndex = -1;
|
||||
world.query<ProceduralMaterialComponent>().each([&](flecs::entity e, ProceduralMaterialComponent& mat) {
|
||||
materialEntities.push_back(e);
|
||||
std::string name = mat.materialName.empty() ? "Material " + std::to_string(e.id()) : mat.materialName;
|
||||
materialNames.push_back(name);
|
||||
if (lot.proceduralMaterialEntity == e) {
|
||||
currentMatIndex = (int)materialEntities.size() - 1;
|
||||
}
|
||||
});
|
||||
|
||||
if (currentMatIndex == -1) currentMatIndex = 0;
|
||||
|
||||
std::string comboItems;
|
||||
for (size_t i = 0; i < materialNames.size(); ++i) {
|
||||
if (i > 0) comboItems += '\0';
|
||||
comboItems += materialNames[i];
|
||||
}
|
||||
comboItems += '\0';
|
||||
|
||||
int newIndex = currentMatIndex;
|
||||
if (ImGui::Combo("Material", &newIndex, comboItems.c_str())) {
|
||||
if (newIndex == 0) {
|
||||
lot.proceduralMaterialEntity = flecs::entity::null();
|
||||
lot.proceduralMaterialEntityId.clear();
|
||||
} else {
|
||||
lot.proceduralMaterialEntity = materialEntities[newIndex];
|
||||
lot.proceduralMaterialEntityId = std::to_string(materialEntities[newIndex].id());
|
||||
}
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// Texture rectangle selector
|
||||
if (lot.proceduralMaterialEntity.is_alive()) {
|
||||
flecs::entity textureEntity = flecs::entity::null();
|
||||
if (lot.proceduralMaterialEntity.has<ProceduralMaterialComponent>()) {
|
||||
const auto& mat = lot.proceduralMaterialEntity.get<ProceduralMaterialComponent>();
|
||||
if (mat.diffuseTextureEntity.is_alive()) {
|
||||
textureEntity = mat.diffuseTextureEntity;
|
||||
}
|
||||
}
|
||||
|
||||
if (textureEntity.is_alive() && textureEntity.has<ProceduralTextureComponent>()) {
|
||||
const auto& texture = textureEntity.get<ProceduralTextureComponent>();
|
||||
const auto& namedRects = texture.getAllNamedRects();
|
||||
|
||||
if (!namedRects.empty()) {
|
||||
std::vector<std::string> rectNames;
|
||||
rectNames.push_back("None (use full texture)");
|
||||
|
||||
int currentRectIndex = -1;
|
||||
for (const auto& pair : namedRects) {
|
||||
rectNames.push_back(pair.first);
|
||||
if (lot.textureRectName == pair.first) {
|
||||
currentRectIndex = (int)rectNames.size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentRectIndex == -1) currentRectIndex = 0;
|
||||
|
||||
std::string rectComboItems;
|
||||
for (size_t i = 0; i < rectNames.size(); ++i) {
|
||||
if (i > 0) rectComboItems += '\0';
|
||||
rectComboItems += rectNames[i];
|
||||
}
|
||||
rectComboItems += '\0';
|
||||
|
||||
int newRectIndex = currentRectIndex;
|
||||
if (ImGui::Combo("Texture Rectangle", &newRectIndex, rectComboItems.c_str())) {
|
||||
if (newRectIndex == 0) {
|
||||
lot.textureRectName.clear();
|
||||
} else {
|
||||
lot.textureRectName = rectNames[newRectIndex];
|
||||
}
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Statistics
|
||||
int cellCount = 0;
|
||||
int roomCount = 0;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "TownEditor.hpp"
|
||||
#include "../components/CellGrid.hpp"
|
||||
#include "../components/ProceduralMaterial.hpp"
|
||||
#include "../components/ProceduralTexture.hpp"
|
||||
#include <imgui.h>
|
||||
|
||||
bool TownEditor::renderComponent(flecs::entity entity, TownComponent& town)
|
||||
@@ -25,6 +27,96 @@ bool TownEditor::renderComponent(flecs::entity entity, TownComponent& town)
|
||||
});
|
||||
ImGui::Text("Districts: %d", districtCount);
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Town Material:");
|
||||
|
||||
// Material selector
|
||||
flecs::world world = entity.world();
|
||||
std::vector<flecs::entity> materialEntities;
|
||||
std::vector<std::string> materialNames;
|
||||
materialEntities.push_back(flecs::entity::null());
|
||||
materialNames.push_back("Use Generated (Color Rects)");
|
||||
|
||||
int currentMatIndex = -1;
|
||||
world.query<ProceduralMaterialComponent>().each([&](flecs::entity e, ProceduralMaterialComponent& mat) {
|
||||
materialEntities.push_back(e);
|
||||
std::string name = mat.materialName.empty() ? "Material " + std::to_string(e.id()) : mat.materialName;
|
||||
materialNames.push_back(name);
|
||||
if (town.proceduralMaterialEntity == e) {
|
||||
currentMatIndex = (int)materialEntities.size() - 1;
|
||||
}
|
||||
});
|
||||
|
||||
if (currentMatIndex == -1) currentMatIndex = 0;
|
||||
|
||||
std::string comboItems;
|
||||
for (size_t i = 0; i < materialNames.size(); ++i) {
|
||||
if (i > 0) comboItems += '\0';
|
||||
comboItems += materialNames[i];
|
||||
}
|
||||
comboItems += '\0';
|
||||
|
||||
int newIndex = currentMatIndex;
|
||||
if (ImGui::Combo("Material", &newIndex, comboItems.c_str())) {
|
||||
if (newIndex == 0) {
|
||||
town.proceduralMaterialEntity = flecs::entity::null();
|
||||
town.proceduralMaterialEntityId.clear();
|
||||
} else {
|
||||
town.proceduralMaterialEntity = materialEntities[newIndex];
|
||||
town.proceduralMaterialEntityId = std::to_string(materialEntities[newIndex].id());
|
||||
}
|
||||
modified = true;
|
||||
town.markMaterialDirty();
|
||||
}
|
||||
|
||||
// Texture rectangle selector
|
||||
if (town.proceduralMaterialEntity.is_alive()) {
|
||||
flecs::entity textureEntity = flecs::entity::null();
|
||||
if (town.proceduralMaterialEntity.has<ProceduralMaterialComponent>()) {
|
||||
const auto& mat = town.proceduralMaterialEntity.get<ProceduralMaterialComponent>();
|
||||
if (mat.diffuseTextureEntity.is_alive()) {
|
||||
textureEntity = mat.diffuseTextureEntity;
|
||||
}
|
||||
}
|
||||
|
||||
if (textureEntity.is_alive() && textureEntity.has<ProceduralTextureComponent>()) {
|
||||
const auto& texture = textureEntity.get<ProceduralTextureComponent>();
|
||||
const auto& namedRects = texture.getAllNamedRects();
|
||||
|
||||
if (!namedRects.empty()) {
|
||||
std::vector<std::string> rectNames;
|
||||
rectNames.push_back("None (use full texture)");
|
||||
|
||||
int currentRectIndex = -1;
|
||||
for (const auto& pair : namedRects) {
|
||||
rectNames.push_back(pair.first);
|
||||
if (town.textureRectName == pair.first) {
|
||||
currentRectIndex = (int)rectNames.size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentRectIndex == -1) currentRectIndex = 0;
|
||||
|
||||
std::string rectComboItems;
|
||||
for (size_t i = 0; i < rectNames.size(); ++i) {
|
||||
if (i > 0) rectComboItems += '\0';
|
||||
rectComboItems += rectNames[i];
|
||||
}
|
||||
rectComboItems += '\0';
|
||||
|
||||
int newRectIndex = currentRectIndex;
|
||||
if (ImGui::Combo("Texture Rectangle", &newRectIndex, rectComboItems.c_str())) {
|
||||
if (newRectIndex == 0) {
|
||||
town.textureRectName.clear();
|
||||
} else {
|
||||
town.textureRectName = rectNames[newRectIndex];
|
||||
}
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Color rects
|
||||
if (ImGui::CollapsingHeader("Color Rects")) {
|
||||
for (auto& [name, rect] : town.colorRects) {
|
||||
|
||||
Reference in New Issue
Block a user