Now nude meshes work as intended

This commit is contained in:
2026-05-11 00:30:47 +03:00
parent ce888bc5bb
commit 42f6a218fb
@@ -231,13 +231,21 @@ Ogre::String CharacterSlotSystem::resolveMesh(
const Ogre::String &slot, const SlotSelection &sel,
int outfitLevel)
{
if (!sel.explicitMesh.empty())
if (!sel.explicitMesh.empty()) {
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] resolveMesh(" + age + "," + sex +
"," + slot + ") -> explicit: " + sel.explicitMesh);
return sel.explicitMesh;
}
if (!s_catalogLoaded || !s_bodyParts.contains(age) ||
!s_bodyParts[age].contains(sex) ||
!s_bodyParts[age][sex].contains(slot))
!s_bodyParts[age][sex].contains(slot)) {
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] resolveMesh(" + age + "," + sex +
"," + slot + ") -> catalog miss");
return "";
}
const auto &slotEntries = s_bodyParts[age][sex][slot];
@@ -245,37 +253,55 @@ Ogre::String CharacterSlotSystem::resolveMesh(
if (outfitLevel >= 2 && sel.layer2Mesh != "none" &&
!sel.layer2Mesh.empty()) {
for (const auto &entry : slotEntries) {
if (entry.value("mesh", "") == sel.layer2Mesh)
if (entry.value("mesh", "") == sel.layer2Mesh) {
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] resolveMesh(" + age + "," + sex +
"," + slot + ") -> layer2: " + sel.layer2Mesh);
return sel.layer2Mesh;
}
}
}
if (outfitLevel >= 1 && sel.layer1Mesh != "none" &&
!sel.layer1Mesh.empty()) {
for (const auto &entry : slotEntries) {
if (entry.value("mesh", "") == sel.layer1Mesh)
if (entry.value("mesh", "") == sel.layer1Mesh) {
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] resolveMesh(" + age + "," + sex +
"," + slot + ") -> layer1: " + sel.layer1Mesh);
return sel.layer1Mesh;
}
}
}
/* Fallback to layer 0 (nude base) — prefer canonical base mesh */
Ogre::String canonicalBase = sex + "_" + slot + ".mesh";
Ogre::String firstLayer0;
/* Fallback to layer 0 (nude base) — prefer shortest name
* (base mesh name is shortest, combined meshes add suffixes) */
Ogre::String bestLayer0;
for (const auto &entry : slotEntries) {
if (entry.value("layer", 0) == 0) {
Ogre::String mesh = entry["mesh"].get<Ogre::String>();
if (mesh == canonicalBase)
return mesh;
if (firstLayer0.empty())
firstLayer0 = mesh;
if (bestLayer0.empty() || mesh.length() < bestLayer0.length())
bestLayer0 = mesh;
}
}
if (!firstLayer0.empty())
return firstLayer0;
if (!bestLayer0.empty()) {
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] resolveMesh(" + age + "," + sex +
"," + slot + ") -> layer0base: " + bestLayer0);
return bestLayer0;
}
/* Last resort: first available entry */
if (!slotEntries.empty())
return slotEntries[0]["mesh"].get<Ogre::String>();
if (!slotEntries.empty()) {
Ogre::String mesh = slotEntries[0]["mesh"].get<Ogre::String>();
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] resolveMesh(" + age + "," + sex +
"," + slot + ") -> lastResort: " + mesh);
return mesh;
}
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] resolveMesh(" + age + "," + sex +
"," + slot + ") -> empty fallback");
return "";
}
@@ -291,10 +317,20 @@ void CharacterSlotSystem::update()
total++;
if (cs.dirty) {
dirtyCount++;
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] update: entity " +
Ogre::StringConverter::toString(e.id()) +
" is dirty, rebuilding");
buildCharacter(e, cs);
cs.dirty = false;
}
});
if (dirtyCount > 0) {
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] update: total=" +
Ogre::StringConverter::toString(total) + " dirty=" +
Ogre::StringConverter::toString(dirtyCount));
}
}
static const nlohmann::json *findCatalogEntry(
@@ -341,6 +377,13 @@ static void ensureMeshPoseAnimation(const Ogre::String &meshName)
void CharacterSlotSystem::buildCharacter(flecs::entity e,
CharacterSlotsComponent &cs)
{
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] buildCharacter: age=" + cs.age +
" sex=" + cs.sex + " outfitLevel=" +
Ogre::StringConverter::toString(cs.outfitLevel) +
" slots=" + Ogre::StringConverter::toString(
(size_t)cs.slotSelections.size()));
destroyCharacterParts(e);
/* Migrate old slots map to slotSelections if needed */
@@ -361,12 +404,18 @@ void CharacterSlotSystem::buildCharacter(flecs::entity e,
}
}
if (!e.has<TransformComponent>())
if (!e.has<TransformComponent>()) {
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] buildCharacter: no TransformComponent");
return;
}
auto &transform = e.get_mut<TransformComponent>();
if (!transform.node)
if (!transform.node) {
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] buildCharacter: no transform node");
return;
}
/* Determine master slot (face preferred, else first non-empty) */
Ogre::String masterSlot;
@@ -390,6 +439,9 @@ void CharacterSlotSystem::buildCharacter(flecs::entity e,
}
if (masterSlot.empty()) {
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] buildCharacter: masterSlot EMPTY — "
"no valid mesh for any slot");
return;
}
@@ -397,8 +449,16 @@ void CharacterSlotSystem::buildCharacter(flecs::entity e,
cs.slotSelections[masterSlot],
cs.outfitLevel);
if (masterMesh.empty())
if (masterMesh.empty()) {
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] buildCharacter: masterMesh empty for " +
masterSlot);
return;
}
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] buildCharacter: masterSlot=" + masterSlot +
" masterMesh=" + masterMesh);
/* Pre-create pose animation on mesh so entity knows about it */
ensureMeshPoseAnimation(masterMesh);
@@ -412,6 +472,12 @@ void CharacterSlotSystem::buildCharacter(flecs::entity e,
m_entities[e.id()].parts[masterSlot] = masterEnt;
cs.masterEntity = masterEnt;
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] buildCharacter: created master entity " +
masterMesh + " (submeshes=" +
Ogre::StringConverter::toString(meshPtr->getNumSubMeshes()) +
")");
/* Setup pose animation for shape keys */
const nlohmann::json *entry = findCatalogEntry(
cs.age, cs.sex, masterSlot, masterMesh);
@@ -422,7 +488,7 @@ void CharacterSlotSystem::buildCharacter(flecs::entity e,
e.get_mut<AnimationTreeComponent>().dirty = true;
} catch (const Ogre::Exception &ex) {
Ogre::LogManager::getSingleton().logMessage(
"CharacterSlotSystem: Failed to load master mesh '" +
"[CharacterSlotSystem] buildCharacter: FAILED to load master mesh '" +
masterMesh + "': " + ex.getDescription());
return;
}
@@ -453,11 +519,17 @@ void CharacterSlotSystem::buildCharacter(flecs::entity e,
applyShapeKeys(e, partEnt, entry);
} catch (const Ogre::Exception &ex) {
Ogre::LogManager::getSingleton().logMessage(
"CharacterSlotSystem: Failed to load part '" +
"[CharacterSlotSystem] buildCharacter: FAILED to load part '" +
slot + "' mesh '" + mesh +
"': " + ex.getDescription());
}
}
Ogre::LogManager::getSingleton().logMessage(
"[CharacterSlotSystem] buildCharacter: DONE for entity " +
Ogre::StringConverter::toString(e.id()) +
" parts=" + Ogre::StringConverter::toString(
(size_t)m_entities[e.id()].parts.size()));
}
void CharacterSlotSystem::applyShapeKeys(flecs::entity e,