diff --git a/skee/BodyMorphInterface.cpp b/skee/BodyMorphInterface.cpp index 6569bff..cf42250 100644 --- a/skee/BodyMorphInterface.cpp +++ b/skee/BodyMorphInterface.cpp @@ -38,7 +38,8 @@ extern bool g_parallelMorphing; extern UInt16 g_bodyMorphMode; extern bool g_enableBodyGen; extern bool g_enableBodyMorph; -extern bool g_deferredBodyMorph; + +void ProcessTaskInterface_AddTask(TaskDelegate * cmd); UInt32 BodyMorphInterface::GetVersion() { @@ -72,15 +73,15 @@ void BodyMorphInterface::Revert() actorMorphs.m_data.clear(); } -void BodyMorphInterface::SetMorph(TESObjectREFR * actor, BSFixedString morphName, BSFixedString morphKey, float relative) +void BodyMorphInterface::SetMorph(TESObjectREFR * actor, SKEEFixedString morphName, SKEEFixedString morphKey, float relative) { UInt64 handle = g_overrideInterface.GetHandle(actor, TESObjectREFR::kTypeID); SimpleLocker locker(&actorMorphs.m_lock); - actorMorphs.m_data[handle][morphName][morphKey] = relative; + actorMorphs.m_data[handle][g_stringTable.GetString(morphName)][g_stringTable.GetString(morphKey)] = relative; } -float BodyMorphInterface::GetMorph(TESObjectREFR * actor, BSFixedString morphName, BSFixedString morphKey) +float BodyMorphInterface::GetMorph(TESObjectREFR * actor, SKEEFixedString morphName, SKEEFixedString morphKey) { UInt64 handle = g_overrideInterface.GetHandle(actor, TESObjectREFR::kTypeID); @@ -88,10 +89,10 @@ float BodyMorphInterface::GetMorph(TESObjectREFR * actor, BSFixedString morphNam auto & it = actorMorphs.m_data.find(handle); if(it != actorMorphs.m_data.end()) { - auto & mit = it->second.find(morphName); + auto & mit = it->second.find(g_stringTable.GetString(morphName)); if (mit != it->second.end()) { - auto & kit = mit->second.find(morphKey); + auto & kit = mit->second.find(g_stringTable.GetString(morphKey)); if (kit != mit->second.end()) { return kit->second; @@ -102,7 +103,7 @@ float BodyMorphInterface::GetMorph(TESObjectREFR * actor, BSFixedString morphNam return 0.0; } -void BodyMorphInterface::ClearMorph(TESObjectREFR * actor, BSFixedString morphName, BSFixedString morphKey) +void BodyMorphInterface::ClearMorph(TESObjectREFR * actor, SKEEFixedString morphName, SKEEFixedString morphKey) { UInt64 handle = g_overrideInterface.GetHandle(actor, TESObjectREFR::kTypeID); @@ -110,10 +111,10 @@ void BodyMorphInterface::ClearMorph(TESObjectREFR * actor, BSFixedString morphNa auto & it = actorMorphs.m_data.find(handle); if (it != actorMorphs.m_data.end()) { - auto & mit = it->second.find(morphName); + auto & mit = it->second.find(g_stringTable.GetString(morphName)); if (mit != it->second.end()) { - auto & kit = mit->second.find(morphKey); + auto & kit = mit->second.find(g_stringTable.GetString(morphKey)); if (kit != mit->second.end()) { mit->second.erase(kit); @@ -122,7 +123,7 @@ void BodyMorphInterface::ClearMorph(TESObjectREFR * actor, BSFixedString morphNa } } -bool BodyMorphInterface::HasBodyMorph(TESObjectREFR * actor, BSFixedString morphName, BSFixedString morphKey) +bool BodyMorphInterface::HasBodyMorph(TESObjectREFR * actor, SKEEFixedString morphName, SKEEFixedString morphKey) { UInt64 handle = g_overrideInterface.GetHandle(actor, TESObjectREFR::kTypeID); @@ -130,10 +131,10 @@ bool BodyMorphInterface::HasBodyMorph(TESObjectREFR * actor, BSFixedString morph auto & it = actorMorphs.m_data.find(handle); if (it != actorMorphs.m_data.end()) { - auto & kit = it->second.find(morphName); + auto & kit = it->second.find(g_stringTable.GetString(morphName)); if (kit != it->second.end()) { - auto & mit = kit->second.find(morphKey); + auto & mit = kit->second.find(g_stringTable.GetString(morphKey)); if(mit != kit->second.end()) return true; } @@ -142,7 +143,7 @@ bool BodyMorphInterface::HasBodyMorph(TESObjectREFR * actor, BSFixedString morph return false; } -float BodyMorphInterface::GetBodyMorphs(TESObjectREFR * actor, BSFixedString morphName) +float BodyMorphInterface::GetBodyMorphs(TESObjectREFR * actor, SKEEFixedString morphName) { UInt64 handle = g_overrideInterface.GetHandle(actor, TESObjectREFR::kTypeID); @@ -150,7 +151,7 @@ float BodyMorphInterface::GetBodyMorphs(TESObjectREFR * actor, BSFixedString mor auto & it = actorMorphs.m_data.find(handle); if (it != actorMorphs.m_data.end()) { - auto & mit = it->second.find(morphName); + auto & mit = it->second.find(g_stringTable.GetString(morphName)); if (mit != it->second.end()) { float morphSum = 0; @@ -178,7 +179,7 @@ float BodyMorphInterface::GetBodyMorphs(TESObjectREFR * actor, BSFixedString mor return 0.0; } -bool BodyMorphInterface::HasBodyMorphKey(TESObjectREFR * actor, BSFixedString morphKey) +bool BodyMorphInterface::HasBodyMorphKey(TESObjectREFR * actor, SKEEFixedString morphKey) { UInt64 handle = g_overrideInterface.GetHandle(actor, TESObjectREFR::kTypeID); @@ -188,7 +189,7 @@ bool BodyMorphInterface::HasBodyMorphKey(TESObjectREFR * actor, BSFixedString mo { for (auto & mit : it->second) { - auto & kit = mit.second.find(morphKey); + auto & kit = mit.second.find(g_stringTable.GetString(morphKey)); if (kit != mit.second.end()) { return true; @@ -199,7 +200,7 @@ bool BodyMorphInterface::HasBodyMorphKey(TESObjectREFR * actor, BSFixedString mo return false; } -void BodyMorphInterface::ClearBodyMorphKeys(TESObjectREFR * actor, BSFixedString morphKey) +void BodyMorphInterface::ClearBodyMorphKeys(TESObjectREFR * actor, SKEEFixedString morphKey) { UInt64 handle = g_overrideInterface.GetHandle(actor, TESObjectREFR::kTypeID); @@ -209,7 +210,7 @@ void BodyMorphInterface::ClearBodyMorphKeys(TESObjectREFR * actor, BSFixedString { for (auto & mit : it->second) { - auto & kit = mit.second.find(morphKey); + auto & kit = mit.second.find(g_stringTable.GetString(morphKey)); if (kit != mit.second.end()) { mit.second.erase(kit); @@ -218,7 +219,7 @@ void BodyMorphInterface::ClearBodyMorphKeys(TESObjectREFR * actor, BSFixedString } } -bool BodyMorphInterface::HasBodyMorphName(TESObjectREFR * actor, BSFixedString morphName) +bool BodyMorphInterface::HasBodyMorphName(TESObjectREFR * actor, SKEEFixedString morphName) { UInt64 handle = g_overrideInterface.GetHandle(actor, TESObjectREFR::kTypeID); @@ -226,7 +227,7 @@ bool BodyMorphInterface::HasBodyMorphName(TESObjectREFR * actor, BSFixedString m auto & it = actorMorphs.m_data.find(handle); if (it != actorMorphs.m_data.end()) { - auto & kit = it->second.find(morphName); + auto & kit = it->second.find(g_stringTable.GetString(morphName)); if (kit != it->second.end()) { return true; @@ -236,7 +237,7 @@ bool BodyMorphInterface::HasBodyMorphName(TESObjectREFR * actor, BSFixedString m return false; } -void BodyMorphInterface::ClearBodyMorphNames(TESObjectREFR * actor, BSFixedString morphName) +void BodyMorphInterface::ClearBodyMorphNames(TESObjectREFR * actor, SKEEFixedString morphName) { UInt64 handle = g_overrideInterface.GetHandle(actor, TESObjectREFR::kTypeID); @@ -244,7 +245,7 @@ void BodyMorphInterface::ClearBodyMorphNames(TESObjectREFR * actor, BSFixedStrin auto & it = actorMorphs.m_data.find(handle); if (it != actorMorphs.m_data.end()) { - auto & mit = it->second.find(morphName); + auto & mit = it->second.find(g_stringTable.GetString(morphName)); if (mit != it->second.end()) { mit->second.clear(); @@ -468,9 +469,9 @@ extern const _UpdateReferenceNode UpdateReferenceNode = (_UpdateReferenceNode)0x #include #include -void MorphFileCache::ApplyMorph(TESObjectREFR * refr, NiAVObject * rootNode, bool isAttaching, const std::pair & bodyMorph, std::mutex * mutex, bool deferred) +void MorphFileCache::ApplyMorph(TESObjectREFR * refr, NiAVObject * rootNode, bool isAttaching, const std::pair & bodyMorph, std::mutex * mutex, bool deferred) { - BSFixedString nodeName = bodyMorph.first.data; + BSFixedString nodeName = bodyMorph.first.c_str(); BSGeometry * geometry = rootNode->GetAsBSGeometry(); NiGeometry * legacyGeometry = rootNode->GetAsNiGeometry(); NiAVObject * bodyNode = geometry ? geometry : legacyGeometry ? legacyGeometry : rootNode->GetObjectByName(&nodeName.data); @@ -618,13 +619,20 @@ void MorphFileCache::ApplyMorph(TESObjectREFR * refr, NiAVObject * rootNode, boo // Applies all morphs for this shape bodyMorph.second.ApplyMorphs(refr, vertexMorpher, bodyMorph.second.HasUV() ? uvMorpher : nullptr); + // Propagate the data to the other partitions + for (UInt32 p = 1; p < newSkinPartition->m_uiPartitions; ++p) + { + auto & pPartition = newSkinPartition->m_pkPartitions[p]; + memcpy(pPartition.shapeData->m_RawVertexData, partition.shapeData->m_RawVertexData, newSkinPartition->vertexCount * vertexSize); + } + skinInstance->m_spSkinPartition = newSkinPartition; newSkinPartition->DecRef(); // DeepCopy started refcount at 1 if (mutex) mutex->lock(); auto updateTask = new NIOVTaskUpdateSkinPartition(newSkinPartition); - if (g_task && deferred) + if (deferred) { g_task->AddTask(updateTask); } @@ -682,8 +690,8 @@ void MorphCache::ApplyMorphs(TESObjectREFR * refr, NiAVObject * rootNode, bool i VisitObjects(rootNode, [&](NiAVObject* object) { NiStringExtraData * stringData = ni_cast(object->GetExtraData("BODYTRI"), NiStringExtraData); if (stringData) { - BSFixedString filePath = CreateTRIPath(stringData->m_pString); - CacheFile(filePath.data); + SKEEFixedString filePath = CreateTRIPath(stringData->m_pString); + CacheFile(filePath.c_str()); auto & it = m_data.find(filePath); if (it != m_data.end()) { fileCache = &it->second; @@ -808,22 +816,22 @@ void MorphCache::UpdateMorphs(TESObjectREFR * refr, bool deferUpdate) #endif } -BSFixedString MorphCache::CreateTRIPath(const char * relativePath) +SKEEFixedString MorphCache::CreateTRIPath(const char * relativePath) { if(relativePath == "") - return BSFixedString(""); + return SKEEFixedString(""); std::string targetPath = "meshes\\"; targetPath += std::string(relativePath); std::transform(targetPath.begin(), targetPath.end(), targetPath.begin(), ::tolower); - return BSFixedString(targetPath.c_str()); + return SKEEFixedString(targetPath.c_str()); } void MorphCache::Shrink() { while (totalMemory > memoryLimit && m_data.size() > 0) { - auto & it = std::min_element(m_data.begin(), m_data.end(), [](std::pair a, std::pair b) + auto & it = std::min_element(m_data.begin(), m_data.end(), [](std::pair a, std::pair b) { return (a.second.accessed < b.second.accessed); }); @@ -839,7 +847,7 @@ void MorphCache::Shrink() bool MorphCache::CacheFile(const char * relativePath) { - BSFixedString filePath(relativePath); + SKEEFixedString filePath(relativePath); if(relativePath == "") return false; @@ -851,10 +859,10 @@ bool MorphCache::CacheFile(const char * relativePath) } #ifdef _DEBUG - _DMESSAGE("%s - Parsing: %s", __FUNCTION__, filePath.data); + _DMESSAGE("%s - Parsing: %s", __FUNCTION__, filePath.c_str()); #endif - BSResourceNiBinaryStream binaryStream(filePath.data); + BSResourceNiBinaryStream binaryStream(filePath.c_str()); if(binaryStream.IsValid()) { TriShapeMap trishapeMap; @@ -917,7 +925,7 @@ bool MorphCache::CacheFile(const char * relativePath) _DMESSAGE("%s - Reading Morph %s at (%08X)", __FUNCTION__, morphName.data, binaryStream.GetOffset()); #endif if (tsize == 0) { - _WARNING("%s - %s - Read empty name morph at (%08X)", __FUNCTION__, filePath.data, binaryStream.GetOffset()); + _WARNING("%s - %s - Read empty name morph at (%08X)", __FUNCTION__, filePath.c_str(), binaryStream.GetOffset()); } if (!packed) { @@ -936,10 +944,10 @@ bool MorphCache::CacheFile(const char * relativePath) } if (vertexNum == 0) { - _WARNING("%s - %s - Read morph %s on %s with no vertices at (%08X)", __FUNCTION__, filePath.data, morphName.data, trishapeName.data, binaryStream.GetOffset()); + _WARNING("%s - %s - Read morph %s on %s with no vertices at (%08X)", __FUNCTION__, filePath.c_str(), morphName.data, trishapeName.data, binaryStream.GetOffset()); } if (multiplier == 0.0f) { - _WARNING("%s - %s - Read morph %s on %s with zero multiplier at (%08X)", __FUNCTION__, filePath.data, morphName.data, trishapeName.data, binaryStream.GetOffset()); + _WARNING("%s - %s - Read morph %s on %s with zero multiplier at (%08X)", __FUNCTION__, filePath.c_str(), morphName.data, trishapeName.data, binaryStream.GetOffset()); } #ifdef _DEBUG @@ -947,7 +955,7 @@ bool MorphCache::CacheFile(const char * relativePath) #endif if (vertexNum > (std::numeric_limits::max)()) { - _ERROR("%s - %s - Too many vertices for %s on %s read: %d at (%08X)", __FUNCTION__, filePath.data, morphName.data, vertexNum, trishapeName.data, binaryStream.GetOffset()); + _ERROR("%s - %s - Too many vertices for %s on %s read: %d at (%08X)", __FUNCTION__, filePath.c_str(), morphName.data, vertexNum, trishapeName.data, binaryStream.GetOffset()); return false; } @@ -1050,7 +1058,7 @@ bool MorphCache::CacheFile(const char * relativePath) _DMESSAGE("%s - Reading UV Morph %s at (%08X)", __FUNCTION__, morphName.data, binaryStream.GetOffset()); #endif if (tsize == 0) { - _WARNING("%s - %s - Read empty name morph at (%08X)", __FUNCTION__, filePath.data, binaryStream.GetOffset()); + _WARNING("%s - %s - Read empty name morph at (%08X)", __FUNCTION__, filePath.c_str(), binaryStream.GetOffset()); } UInt32 vertexNum = 0; @@ -1060,10 +1068,10 @@ bool MorphCache::CacheFile(const char * relativePath) trishapeMap.memoryUsage += binaryStream.Read((char *)&vertexNum, sizeof(UInt16)); if (vertexNum == 0) { - _WARNING("%s - %s - Read morph %s on %s with no vertices at (%08X)", __FUNCTION__, filePath.data, morphName.data, trishapeName.data, binaryStream.GetOffset()); + _WARNING("%s - %s - Read morph %s on %s with no vertices at (%08X)", __FUNCTION__, filePath.c_str(), morphName.data, trishapeName.data, binaryStream.GetOffset()); } if (multiplier == 0.0f) { - _WARNING("%s - %s - Read morph %s on %s with zero multiplier at (%08X)", __FUNCTION__, filePath.data, morphName.data, trishapeName.data, binaryStream.GetOffset()); + _WARNING("%s - %s - Read morph %s on %s with zero multiplier at (%08X)", __FUNCTION__, filePath.c_str(), morphName.data, trishapeName.data, binaryStream.GetOffset()); } #ifdef _DEBUG @@ -1071,7 +1079,7 @@ bool MorphCache::CacheFile(const char * relativePath) #endif if (vertexNum > (std::numeric_limits::max)()) { - _ERROR("%s - %s - Too many vertices for %s on %s read: %d at (%08X)", __FUNCTION__, filePath.data, morphName.data, vertexNum, trishapeName.data, binaryStream.GetOffset()); + _ERROR("%s - %s - Too many vertices for %s on %s read: %d at (%08X)", __FUNCTION__, filePath.c_str(), morphName.data, vertexNum, trishapeName.data, binaryStream.GetOffset()); return false; } @@ -1191,8 +1199,9 @@ void NIOVTaskUpdateSkinPartition::Run() UInt32 vertexCount = m_partition->vertexCount; auto deviceContext = g_renderManager->context; + deviceContext->UpdateSubresource(partition.shapeData->m_VertexBuffer, 0, nullptr, partition.shapeData->m_RawVertexData, vertexCount * vertexSize, 0); - for (UInt32 p = 0; p < m_partition->m_uiPartitions; ++p) + for (UInt32 p = 1; p < m_partition->m_uiPartitions; ++p) { auto & pPartition = m_partition->m_pkPartitions[p]; deviceContext->UpdateSubresource(pPartition.shapeData->m_VertexBuffer, 0, nullptr, pPartition.shapeData->m_RawVertexData, vertexCount * vertexSize, 0); @@ -1200,7 +1209,7 @@ void NIOVTaskUpdateSkinPartition::Run() } } -void BodyMorphInterface::VisitMorphs(TESObjectREFR * actor, std::function * map)> functor) +void BodyMorphInterface::VisitMorphs(TESObjectREFR * actor, std::function * map)> functor) { UInt64 handle = g_overrideInterface.GetHandle(actor, TESObjectREFR::kTypeID); auto & it = actorMorphs.m_data.find(handle); @@ -1208,23 +1217,23 @@ void BodyMorphInterface::VisitMorphs(TESObjectREFR * actor, std::functionsecond) { - functor(morph.first, &morph.second); + functor(*morph.first, &morph.second); } } } -void BodyMorphInterface::VisitKeys(TESObjectREFR * actor, BSFixedString name, std::function functor) +void BodyMorphInterface::VisitKeys(TESObjectREFR * actor, SKEEFixedString name, std::function functor) { UInt64 handle = g_overrideInterface.GetHandle(actor, TESObjectREFR::kTypeID); auto & it = actorMorphs.m_data.find(handle); if (it != actorMorphs.m_data.end()) { - auto & mit = it->second.find(name); + auto & mit = it->second.find(g_stringTable.GetString(name)); if (mit != it->second.end()) { for (auto & morph : mit->second) { - functor(morph.first, morph.second); + functor(*morph.first, morph.second); } } } @@ -1247,7 +1256,7 @@ void BodyMorphInterface::UpdateModelWeight(TESObjectREFR * refr, bool immediate) } } -bool BodyMorphInterface::ReadBodyMorphTemplates(BSFixedString filePath) +bool BodyMorphInterface::ReadBodyMorphTemplates(SKEEFixedString filePath) { BSResourceNiBinaryStream file(filePath.c_str()); if (!file.IsValid()) { @@ -1411,7 +1420,7 @@ void BodyMorphInterface::GetFilteredNPCList(std::vector activeNPCs[], U } } -bool BodyMorphInterface::ReadBodyMorphs(BSFixedString filePath) +bool BodyMorphInterface::ReadBodyMorphs(SKEEFixedString filePath) { BSResourceNiBinaryStream file(filePath.c_str()); if (!file.IsValid()) { @@ -1623,8 +1632,6 @@ bool BodyMorphInterface::ReadBodyMorphs(BSFixedString filePath) } bodyGenData[0][npc] = dataTemplates; - - } for (auto & npc : activeNPCs[1]) @@ -1656,7 +1663,7 @@ bool BodyMorphInterface::ReadBodyMorphs(BSFixedString filePath) return true; } -UInt32 BodyGenMorphSelector::Evaluate(std::function eval) +UInt32 BodyGenMorphSelector::Evaluate(std::function eval) { if (size() > 0) { std::random_device rd; @@ -1675,7 +1682,7 @@ UInt32 BodyGenMorphSelector::Evaluate(std::function return 0; } -UInt32 BodyGenMorphs::Evaluate(std::function eval) +UInt32 BodyGenMorphs::Evaluate(std::function eval) { UInt32 total = 0; for (auto value : *this) { @@ -1688,7 +1695,7 @@ UInt32 BodyGenMorphs::Evaluate(std::function eval) return total; } -UInt32 BodyGenTemplate::Evaluate(std::function eval) +UInt32 BodyGenTemplate::Evaluate(std::function eval) { if (size() > 0) { std::random_device rd; @@ -1702,7 +1709,7 @@ UInt32 BodyGenTemplate::Evaluate(std::function eval) return 0; } -UInt32 BodyTemplateList::Evaluate(std::function eval) +UInt32 BodyTemplateList::Evaluate(std::function eval) { if (size() > 0) { std::random_device rd; @@ -1716,7 +1723,7 @@ UInt32 BodyTemplateList::Evaluate(std::function eval return 0; } -UInt32 BodyGenDataTemplates::Evaluate(std::function eval) +UInt32 BodyGenDataTemplates::Evaluate(std::function eval) { UInt32 total = 0; for (auto & tempList : *this) @@ -1742,7 +1749,7 @@ UInt32 BodyMorphInterface::EvaluateBodyMorphs(TESObjectREFR * actor) // Found a matching template if (morphSet != bodyGenData[gender].end()) { auto & templates = morphSet->second; - UInt32 ret = templates->Evaluate([&](const BSFixedString & morphName, float value) + UInt32 ret = templates->Evaluate([&](const SKEEFixedString & morphName, float value) { SetMorph(actor, morphName, "RSMBodyGen", value); }); @@ -1755,14 +1762,14 @@ UInt32 BodyMorphInterface::EvaluateBodyMorphs(TESObjectREFR * actor) return 0; } -void BodyMorphInterface::VisitStrings(std::function functor) +void BodyMorphInterface::VisitStrings(std::function functor) { SimpleLocker locker(&actorMorphs.m_lock); for (auto & i1 : actorMorphs.m_data) { for (auto & i2 : i1.second) { - functor(i2.first); + functor(*i2.first); for (auto & i3 : i2.second) { - functor(i3.first); + functor(*i3.first); } } } @@ -1784,18 +1791,16 @@ void BodyMorph::Save(SKSESerializationInterface * intfc, UInt32 kVersion) { intfc->OpenRecord('MRPV', kVersion); - UInt8 morphLength = strlen(m_name.data); - intfc->WriteRecordData(&morphLength, sizeof(morphLength)); - intfc->WriteRecordData(m_name.data, morphLength); + g_stringTable.WriteString(intfc, m_name); + intfc->WriteRecordData(&m_value, sizeof(m_value)); } -bool BodyMorph::Load(SKSESerializationInterface * intfc, UInt32 kVersion) +bool BodyMorph::Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable) { UInt32 type, length, version; bool error = false; - m_name = ""; m_value = 0.0; if(intfc->GetNextRecordInfo(&type, &version, &length)) @@ -1804,23 +1809,30 @@ bool BodyMorph::Load(SKSESerializationInterface * intfc, UInt32 kVersion) { case 'MRPV': { - char * stringName = NULL; - UInt8 stringLength; - if (!intfc->ReadRecordData(&stringLength, sizeof(stringLength))) + if (kVersion >= BodyMorphInterface::kSerializationVersion3) { - _ERROR("%s - Error loading body morph name length", __FUNCTION__); - error = true; - return error; + m_name = StringTable::ReadString(intfc, stringTable); } + else if (kVersion >= BodyMorphInterface::kSerializationVersion2) + { + char * stringName = NULL; + UInt8 stringLength; + if (!intfc->ReadRecordData(&stringLength, sizeof(stringLength))) + { + _ERROR("%s - Error loading body morph name length", __FUNCTION__); + error = true; + return error; + } - stringName = new char[stringLength + 1]; - if(!intfc->ReadRecordData(stringName, stringLength)) { - _ERROR("%s - Error loading body morph name", __FUNCTION__); - error = true; - return error; + stringName = new char[stringLength + 1]; + if (!intfc->ReadRecordData(stringName, stringLength)) { + _ERROR("%s - Error loading body morph name", __FUNCTION__); + error = true; + return error; + } + stringName[stringLength] = 0; + m_name = g_stringTable.GetString(stringName); } - stringName[stringLength] = 0; - m_name = BSFixedString(stringName); if (!intfc->ReadRecordData(&m_value, sizeof(m_value))) { _ERROR("%s - Error loading body morph value", __FUNCTION__); @@ -1857,7 +1869,7 @@ void BodyMorphSet::Save(SKSESerializationInterface * intfc, UInt32 kVersion) const_cast((*it)).Save(intfc, kVersion); } -bool BodyMorphSet::Load(SKSESerializationInterface * intfc, UInt32 kVersion) +bool BodyMorphSet::Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable) { UInt32 type, length, version; bool error = false; @@ -1880,9 +1892,9 @@ bool BodyMorphSet::Load(SKSESerializationInterface * intfc, UInt32 kVersion) for (UInt32 i = 0; i < numMorphs; i++) { BodyMorph value; - if (!value.Load(intfc, version)) + if (!value.Load(intfc, version, stringTable)) { - if(value.m_name == BSFixedString("")) + if(*value.m_name == SKEEFixedString("")) continue; #ifdef _DEBUG @@ -1947,20 +1959,20 @@ void BodyMorphData::Save(SKSESerializationInterface * intfc, UInt32 kVersion) for (auto & morph : *this) { - g_stringTable.WriteString(intfc, morph.first, kVersion); + g_stringTable.WriteString(intfc, morph.first); UInt32 numKeys = morph.second.size(); intfc->WriteRecordData(&numKeys, sizeof(numKeys)); for (auto & keys : morph.second) { - g_stringTable.WriteString(intfc, keys.first, kVersion); + g_stringTable.WriteString(intfc, keys.first); intfc->WriteRecordData(&keys.second, sizeof(keys.second)); } } } -bool BodyMorphData::Load(SKSESerializationInterface * intfc, UInt32 kVersion) +bool BodyMorphData::Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable) { UInt32 type, length, version; bool error = false; @@ -1981,7 +1993,7 @@ bool BodyMorphData::Load(SKSESerializationInterface * intfc, UInt32 kVersion) for (UInt32 i = 0; i < numMorphs; i++) { - BSFixedString morphName = g_stringTable.ReadString(intfc, kVersion); + auto morphName = StringTable::ReadString(intfc, stringTable); UInt32 numKeys = 0; if (!intfc->ReadRecordData(&numKeys, sizeof(numKeys))) @@ -1991,10 +2003,10 @@ bool BodyMorphData::Load(SKSESerializationInterface * intfc, UInt32 kVersion) return error; } - std::unordered_map pairs; + std::unordered_map pairs; for (UInt32 i = 0; i < numKeys; i++) { - BSFixedString keyName = g_stringTable.ReadString(intfc, kVersion); + auto keyName = StringTable::ReadString(intfc, stringTable); float value = 0; if (!intfc->ReadRecordData(&value, sizeof(value))) { @@ -2004,11 +2016,11 @@ bool BodyMorphData::Load(SKSESerializationInterface * intfc, UInt32 kVersion) } // If the keys were mapped by mod name, skip them if they arent in load order - std::string strKey(keyName.data); - BSFixedString ext(strKey.substr(strKey.find_last_of(".") + 1).c_str()); - if (ext == BSFixedString("esp") || ext == BSFixedString("esm")) + std::string strKey(keyName->c_str()); + SKEEFixedString ext(strKey.substr(strKey.find_last_of(".") + 1).c_str()); + if (ext == SKEEFixedString("esp") || ext == SKEEFixedString("esm")) { - if (!DataHandler::GetSingleton()->LookupModByName(keyName.data)) + if (!DataHandler::GetSingleton()->LookupModByName(keyName->c_str())) continue; } @@ -2032,7 +2044,7 @@ bool BodyMorphData::Load(SKSESerializationInterface * intfc, UInt32 kVersion) return error; } -bool ActorMorphs::Load(SKSESerializationInterface * intfc, UInt32 kVersion) +bool ActorMorphs::Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable) { bool error = false; @@ -2048,20 +2060,20 @@ bool ActorMorphs::Load(SKSESerializationInterface * intfc, UInt32 kVersion) BodyMorphSet morphSet; BodyMorphData morphMap; - if (kVersion == BodyMorphInterface::kSerializationVersion1) + if (kVersion >= BodyMorphInterface::kSerializationVersion2) { - if (morphSet.Load(intfc, kVersion)) + if (morphMap.Load(intfc, kVersion, stringTable)) { - _ERROR("%s - Error loading MorphSet", __FUNCTION__); + _ERROR("%s - Error loading MorphMap", __FUNCTION__); error = true; return error; } } - else if (kVersion == BodyMorphInterface::kSerializationVersion2) + else if (kVersion >= BodyMorphInterface::kSerializationVersion1) { - if (morphMap.Load(intfc, kVersion)) + if (morphSet.Load(intfc, kVersion, stringTable)) { - _ERROR("%s - Error loading MorphMap", __FUNCTION__); + _ERROR("%s - Error loading MorphSet", __FUNCTION__); error = true; return error; } @@ -2071,7 +2083,7 @@ bool ActorMorphs::Load(SKSESerializationInterface * intfc, UInt32 kVersion) { for (auto & morph : morphSet) { - morphMap[morph.m_name]["RSMLegacy"] = morph.m_value; + morphMap[morph.m_name][g_stringTable.GetString("RSMLegacy")] = morph.m_value; } } @@ -2107,7 +2119,7 @@ void BodyMorphInterface::Save(SKSESerializationInterface * intfc, UInt32 kVersio actorMorphs.Save(intfc, kVersion); } -bool BodyMorphInterface::Load(SKSESerializationInterface * intfc, UInt32 kVersion) +bool BodyMorphInterface::Load(SKSESerializationInterface * intfc, UInt32 kVersion, const std::unordered_map & stringTable) { - return actorMorphs.Load(intfc, kVersion); + return actorMorphs.Load(intfc, kVersion, stringTable); } \ No newline at end of file diff --git a/skee/BodyMorphInterface.h b/skee/BodyMorphInterface.h index c9d8585..da3f7c4 100644 --- a/skee/BodyMorphInterface.h +++ b/skee/BodyMorphInterface.h @@ -8,6 +8,8 @@ #include "skse64/NiTypes.h" #include "skse64/NiGeometry.h" +#include "StringTable.h" + #ifdef min #undef min #endif @@ -44,14 +46,14 @@ class NiSkinPartition; class BodyMorph { public: - bool operator<(const BodyMorph & rhs) const { return m_name < rhs.m_name; } - bool operator==(const BodyMorph & rhs) const { return m_name == rhs.m_name; } + bool operator<(const BodyMorph & rhs) const { return *m_name < *rhs.m_name; } + bool operator==(const BodyMorph & rhs) const { return *m_name == *rhs.m_name; } - BSFixedString m_name; + StringTableItem m_name; float m_value; void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - bool Load(SKSESerializationInterface * intfc, UInt32 kVersion); + bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable); }; class BodyMorphSet : public std::set @@ -59,14 +61,14 @@ class BodyMorphSet : public std::set public: // Serialization void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - bool Load(SKSESerializationInterface * intfc, UInt32 kVersion); + bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable); }; -class BodyMorphData : public std::unordered_map> +class BodyMorphData : public std::unordered_map> { public: void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - bool Load(SKSESerializationInterface * intfc, UInt32 kVersion); + bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable); }; class ActorMorphs : public SafeDataHolder> @@ -77,7 +79,7 @@ class ActorMorphs : public SafeDataHolder TriShapePackedUVDataPtr; -class BodyMorphMap : public std::unordered_map> +class BodyMorphMap : public std::unordered_map> { friend class MorphCache; public: @@ -173,7 +175,7 @@ class BodyMorphMap : public std::unordered_map +class TriShapeMap : public std::unordered_map { public: TriShapeMap() @@ -190,14 +192,14 @@ class MorphFileCache friend class BodyMorphInterface; public: void ApplyMorphs(TESObjectREFR * refr, NiAVObject * rootNode, bool erase = false, bool defer = false); - void ApplyMorph(TESObjectREFR * refr, NiAVObject * rootNode, bool erase, const std::pair & bodyMorph, std::mutex * mtx = nullptr, bool deferred = true); + void ApplyMorph(TESObjectREFR * refr, NiAVObject * rootNode, bool erase, const std::pair & bodyMorph, std::mutex * mtx = nullptr, bool deferred = true); private: TriShapeMap vertexMap; std::time_t accessed; }; -class MorphCache : public SafeDataHolder> +class MorphCache : public SafeDataHolder> { friend class BodyMorphInterface; @@ -208,9 +210,9 @@ class MorphCache : public SafeDataHolder FileMap; + typedef std::unordered_map FileMap; - BSFixedString CreateTRIPath(const char * relativePath); + SKEEFixedString CreateTRIPath(const char * relativePath); bool CacheFile(const char * modelPath); void ApplyMorphs(TESObjectREFR * refr, NiAVObject * rootNode, bool attaching = false, bool deferUpdate = false); @@ -250,7 +252,7 @@ class NIOVTaskUpdateSkinPartition : public TaskDelegate class BodyGenMorphData { public: - BSFixedString name; + SKEEFixedString name; float lower; float upper; }; @@ -258,35 +260,35 @@ class BodyGenMorphData class BodyGenMorphSelector : public std::vector { public: - UInt32 Evaluate(std::function eval); + UInt32 Evaluate(std::function eval); }; class BodyGenMorphs : public std::vector { public: - UInt32 Evaluate(std::function eval); + UInt32 Evaluate(std::function eval); }; class BodyGenTemplate : public std::vector { public: - UInt32 Evaluate(std::function eval); + UInt32 Evaluate(std::function eval); }; typedef std::shared_ptr BodyGenTemplatePtr; -typedef std::unordered_map BodyGenTemplates; +typedef std::unordered_map BodyGenTemplates; class BodyTemplateList : public std::vector { public: - UInt32 Evaluate(std::function eval); + UInt32 Evaluate(std::function eval); }; class BodyGenDataTemplates : public std::vector { public: - UInt32 Evaluate(std::function eval); + UInt32 Evaluate(std::function eval); }; typedef std::shared_ptr BodyGenDataTemplatesPtr; @@ -300,26 +302,27 @@ class BodyMorphInterface : public IPluginInterface kCurrentPluginVersion = 3, kSerializationVersion1 = 1, kSerializationVersion2 = 2, - kSerializationVersion = kSerializationVersion2 + kSerializationVersion3 = 3, + kSerializationVersion = kSerializationVersion3 }; virtual UInt32 GetVersion(); // Serialization virtual void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - virtual bool Load(SKSESerializationInterface * intfc, UInt32 kVersion); + virtual bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable); virtual void Revert(); void LoadMods(); - virtual void SetMorph(TESObjectREFR * actor, BSFixedString morphName, BSFixedString morphKey, float relative); - virtual float GetMorph(TESObjectREFR * actor, BSFixedString morphName, BSFixedString morphKey); - virtual void ClearMorph(TESObjectREFR * actor, BSFixedString morphName, BSFixedString morphKey); + virtual void SetMorph(TESObjectREFR * actor, SKEEFixedString morphName, SKEEFixedString morphKey, float relative); + virtual float GetMorph(TESObjectREFR * actor, SKEEFixedString morphName, SKEEFixedString morphKey); + virtual void ClearMorph(TESObjectREFR * actor, SKEEFixedString morphName, SKEEFixedString morphKey); - virtual float GetBodyMorphs(TESObjectREFR * actor, BSFixedString morphName); - virtual void ClearBodyMorphNames(TESObjectREFR * actor, BSFixedString morphName); + virtual float GetBodyMorphs(TESObjectREFR * actor, SKEEFixedString morphName); + virtual void ClearBodyMorphNames(TESObjectREFR * actor, SKEEFixedString morphName); - virtual void VisitMorphs(TESObjectREFR * actor, std::function *)> functor); - virtual void VisitKeys(TESObjectREFR * actor, BSFixedString name, std::function functor); + virtual void VisitMorphs(TESObjectREFR * actor, std::function *)> functor); + virtual void VisitKeys(TESObjectREFR * actor, SKEEFixedString name, std::function functor); virtual void ClearMorphs(TESObjectREFR * actor); @@ -331,15 +334,15 @@ class BodyMorphInterface : public IPluginInterface virtual void SetCacheLimit(UInt32 limit); virtual bool HasMorphs(TESObjectREFR * actor); - virtual bool ReadBodyMorphs(BSFixedString filePath); - virtual bool ReadBodyMorphTemplates(BSFixedString filePath); + virtual bool ReadBodyMorphs(SKEEFixedString filePath); + virtual bool ReadBodyMorphTemplates(SKEEFixedString filePath); virtual UInt32 EvaluateBodyMorphs(TESObjectREFR * actor); - virtual bool HasBodyMorph(TESObjectREFR * actor, BSFixedString morphName, BSFixedString morphKey); - virtual bool HasBodyMorphName(TESObjectREFR * actor, BSFixedString morphName); - virtual bool HasBodyMorphKey(TESObjectREFR * actor, BSFixedString morphKey); - virtual void ClearBodyMorphKeys(TESObjectREFR * actor, BSFixedString morphKey); - virtual void VisitStrings(std::function functor); + virtual bool HasBodyMorph(TESObjectREFR * actor, SKEEFixedString morphName, SKEEFixedString morphKey); + virtual bool HasBodyMorphName(TESObjectREFR * actor, SKEEFixedString morphName); + virtual bool HasBodyMorphKey(TESObjectREFR * actor, SKEEFixedString morphKey); + virtual void ClearBodyMorphKeys(TESObjectREFR * actor, SKEEFixedString morphKey); + virtual void VisitStrings(std::function functor); virtual void VisitActors(std::function functor); protected: diff --git a/skee/CDXBrush.cpp b/skee/CDXBrush.cpp index 4ade9cf..4b48dc3 100644 --- a/skee/CDXBrush.cpp +++ b/skee/CDXBrush.cpp @@ -307,7 +307,7 @@ bool CDXInflateBrush::UpdateStroke(CDXPickInfo & pickInfo, CDXEditableMesh * mes for (auto i : hitVertex) { normal += mesh->CalculateVertexNormal(i.first); } - D3DXVec3Normalize(&normal, &normal); + XMVector3Normalize(&normal, &normal); for (auto i : hitVertex) { CDXInflateStroke::InflateInfo strokeInfo; diff --git a/skee/CDXCamera.cpp b/skee/CDXCamera.cpp index 35a29e1..2475758 100644 --- a/skee/CDXCamera.cpp +++ b/skee/CDXCamera.cpp @@ -5,26 +5,28 @@ CDXArcBall::CDXArcBall() { Reset(); - m_vDownPt = D3DXVECTOR3( 0, 0, 0 ); - m_vCurrentPt = D3DXVECTOR3( 0, 0, 0 ); + m_vDownPt = DirectX::XMVectorZero(); + m_vCurrentPt = DirectX::XMVectorZero(); m_Offset.x = m_Offset.y = 0; } //-------------------------------------------------------------------------------------- void CDXArcBall::Reset() { - D3DXQuaternionIdentity( &m_qDown ); - D3DXQuaternionIdentity( &m_qNow ); - D3DXMatrixIdentity( &m_mRotation ); - D3DXMatrixIdentity( &m_mTranslation ); - D3DXMatrixIdentity( &m_mTranslationDelta ); + m_qDown = DirectX::XMQuaternionIdentity(); + m_qNow = DirectX::XMQuaternionIdentity(); + + m_mRotation = DirectX::XMMatrixIdentity(); + m_mTranslation = DirectX::XMMatrixIdentity(); + m_mTranslationDelta = DirectX::XMMatrixIdentity(); + m_bDrag = FALSE; m_fRadiusTranslation = 1.0f; m_fRadius = 1.0f; } //-------------------------------------------------------------------------------------- -D3DXVECTOR3 CDXArcBall::ScreenToVector( float fScreenPtX, float fScreenPtY ) +CDXVec CDXArcBall::ScreenToVector( float fScreenPtX, float fScreenPtY ) { // Scale to screen FLOAT x = -( fScreenPtX - m_Offset.x - m_nWidth / 2 ) / ( m_fRadius * m_nWidth / 2 ); @@ -43,17 +45,15 @@ D3DXVECTOR3 CDXArcBall::ScreenToVector( float fScreenPtX, float fScreenPtY ) z = sqrtf( 1.0f - mag ); // Return vector - return D3DXVECTOR3( x, y, z ); + return DirectX::XMVectorSet(x, y, z, 1); } //-------------------------------------------------------------------------------------- -D3DXQUATERNION CDXArcBall::QuatFromBallPoints( const D3DXVECTOR3& vFrom, const D3DXVECTOR3& vTo ) +CDXVec CDXArcBall::QuatFromBallPoints( const CDXVec& vFrom, const CDXVec& vTo ) { - D3DXVECTOR3 vPart; - float fDot = D3DXVec3Dot( &vFrom, &vTo ); - D3DXVec3Cross( &vPart, &vFrom, &vTo ); - - return D3DXQUATERNION( vPart.x, vPart.y, vPart.z, fDot ); + float fDot = DirectX::XMVectorGetX(DirectX::XMVector3Dot(vFrom, vTo)); + CDXVec vPart = DirectX::XMVector3Cross(vFrom, vTo); + return DirectX::XMVectorSet(DirectX::XMVectorGetX(vPart), DirectX::XMVectorGetY(vPart), DirectX::XMVectorGetZ(vPart), fDot ); } //-------------------------------------------------------------------------------------- @@ -78,7 +78,7 @@ void CDXArcBall::OnRotate( int nX, int nY ) if( m_bDrag ) { m_vCurrentPt = ScreenToVector( ( float )nX, ( float )nY ); - m_qNow = m_qDown * QuatFromBallPoints( m_vDownPt, m_vCurrentPt ); + m_qNow = DirectX::XMVectorMultiply(m_qDown, QuatFromBallPoints( m_vDownPt, m_vCurrentPt )); } } @@ -101,7 +101,7 @@ VOID CDXModelViewerCamera::SetProjParams( FLOAT fFOV, FLOAT fAspect, FLOAT fNear m_fNearPlane = fNearPlane; m_fFarPlane = fFarPlane; - D3DXMatrixPerspectiveFovLH( &m_mProj, fFOV, fAspect, fNearPlane, fFarPlane ); + m_mProj = DirectX::XMMatrixPerspectiveFovLH(fFOV, fAspect, fNearPlane, fFarPlane ); } /* void CDXModelViewerCamera::UpdateMouseDelta(int currentX, int currentY) @@ -131,16 +131,10 @@ void CDXModelViewerCamera::UpdateMouseDelta(int currentX, int currentY) //-------------------------------------------------------------------------------------- // Clamps pV to lie inside m_vMinBoundary & m_vMaxBoundary //-------------------------------------------------------------------------------------- -void CDXModelViewerCamera::ConstrainToBoundary( D3DXVECTOR3* pV ) +void CDXModelViewerCamera::ConstrainToBoundary( CDXVec* pV ) { // Constrain vector to a bounding box - pV->x = __max( pV->x, m_vMinBoundary.x ); - pV->y = __max( pV->y, m_vMinBoundary.y ); - pV->z = __max( pV->z, m_vMinBoundary.z ); - - pV->x = __min( pV->x, m_vMaxBoundary.x ); - pV->y = __min( pV->y, m_vMaxBoundary.y ); - pV->z = __min( pV->z, m_vMaxBoundary.z ); + *pV = DirectX::XMVectorClamp(*pV, m_vMinBoundary, m_vMaxBoundary); } //-------------------------------------------------------------------------------------- @@ -148,14 +142,14 @@ void CDXModelViewerCamera::ConstrainToBoundary( D3DXVECTOR3* pV ) //-------------------------------------------------------------------------------------- CDXModelViewerCamera::CDXModelViewerCamera() { - D3DXVECTOR3 vEyePt = D3DXVECTOR3( 0.0f, 0.0f, 0.0f ); - D3DXVECTOR3 vLookatPt = D3DXVECTOR3( 0.0f, 0.0f, 1.0f ); + CDXVec vEyePt = DirectX::XMVectorZero(); + CDXVec vLookatPt = DirectX::XMVectorSet(0, 0, 1.0f, 0); // Setup the view matrix SetViewParams( &vEyePt, &vLookatPt ); // Setup the projection matrix - SetProjParams( D3DX_PI / 4, 1.0f, 1.0f, 1000.0f ); + SetProjParams( DirectX::XM_PI / 4, 1.0f, 1.0f, 1000.0f ); //m_ptLastMousePosition.x = 0; //m_ptLastMousePosition.y = 0; @@ -164,9 +158,9 @@ CDXModelViewerCamera::CDXModelViewerCamera() m_fCameraPitchAngle = 0.0f; //SetRect( &m_rcDrag, LONG_MIN, LONG_MIN, LONG_MAX, LONG_MAX ); - m_vVelocity = D3DXVECTOR3( 0, 0, 0 ); + m_vVelocity = DirectX::XMVectorZero(); //m_bMovementDrag = false; - //m_vVelocityDrag = D3DXVECTOR3( 0, 0, 0 ); + //m_vVelocityDrag = CDXVec( 0, 0, 0 ); //m_fDragTimer = 0.0f; //m_fTotalDragTimeToZero = 0.25; //m_vRotVelocity = D3DXVECTOR2( 0, 0 ); @@ -178,14 +172,14 @@ CDXModelViewerCamera::CDXModelViewerCamera() //m_fFramesToSmoothMouseData = 2.0f; m_bClipToBoundary = false; - m_vMinBoundary = D3DXVECTOR3( -1, -1, -1 ); - m_vMaxBoundary = D3DXVECTOR3( 1, 1, 1 ); - - D3DXMatrixIdentity( &m_mWorld ); - D3DXMatrixIdentity( &m_mModelRot ); - D3DXMatrixIdentity( &m_mModelLastRot ); - D3DXMatrixIdentity( &m_mCameraRotLast ); - m_vModelCenter = D3DXVECTOR3( 0, 0, 0 ); + m_vMinBoundary = DirectX::XMVectorSet(-1, -1, -1, 0); + m_vMaxBoundary = DirectX::XMVectorSet(1, 1, 1, 0); + + m_mWorld = DirectX::XMMatrixIdentity(); + m_mModelRot = DirectX::XMMatrixIdentity(); + m_mModelLastRot = DirectX::XMMatrixIdentity(); + m_mCameraRotLast = DirectX::XMMatrixIdentity(); + m_vModelCenter = DirectX::XMVectorZero(); m_fRadius = 5.0f; m_fDefaultRadius = 5.0f; m_fMinRadius = 1.0f; @@ -205,20 +199,21 @@ void CDXModelViewerCamera::OnRotate(int currentX, int currentY) void CDXModelViewerCamera::OnMoveBegin(int currentX, int currentY) { m_vDragLook = m_vLookAt; - m_vDragStart = D3DXVECTOR2(currentX, currentY); + m_vDragStart = DirectX::XMVectorSet(currentX, currentY, 0, 0); m_vDragPos = m_vDragStart; } void CDXModelViewerCamera::OnMove(int x, int y) { - m_vDragPos = D3DXVECTOR2(x, y); + m_vDragPos = DirectX::XMVectorSet(x, y, 0, 0); m_vLookAt = m_vDragLook; - - D3DXVECTOR2 panDelta = (m_vDragPos - m_vDragStart) * m_fPanMultiplier; - m_vLookAt.x += -panDelta.x; - m_vLookAt.z += panDelta.y; + auto multiplier = DirectX::XMVectorMultiply(DirectX::XMVectorReplicate(m_fPanMultiplier), DirectX::XMVectorSet(-1, 1, 1, 1)); + + auto panDelta = DirectX::XMVectorMultiply(DirectX::XMVectorSubtract(m_vDragPos, m_vDragStart), multiplier); + + m_vLookAt = DirectX::XMVectorAdd(m_vLookAt, panDelta); m_bDragSinceLastUpdate = true; Update(); @@ -227,8 +222,8 @@ void CDXModelViewerCamera::OnMove(int x, int y) void CDXModelViewerCamera::OnMoveEnd() { m_vDragLook = m_vLookAt; - m_vDragStart = D3DXVECTOR2(0, 0); - m_vDragPos = D3DXVECTOR2(0, 0); + m_vDragStart = DirectX::XMVectorZero(); + m_vDragPos = DirectX::XMVectorZero(); } void CDXModelViewerCamera::Update() @@ -240,28 +235,29 @@ void CDXModelViewerCamera::Update() m_bDragSinceLastUpdate = false; // Simple euler method to calculate position delta - //D3DXVECTOR3 vPosDelta = m_vVelocity; + //CDXVec vPosDelta = m_vVelocity; // Change the radius from the camera to the model based on wheel scrolling m_fRadius = __min( m_fMaxRadius, m_fRadius ); m_fRadius = __max( m_fMinRadius, m_fRadius ); // Get the inverse of the arcball's rotation matrix - D3DXMATRIX mCameraRot; - D3DXMatrixInverse( &mCameraRot, NULL, m_ViewArcBall.GetRotationMatrix() ); + + auto det = DirectX::XMMatrixDeterminant(m_ViewArcBall.GetRotationMatrix()); + auto mCameraRot = DirectX::XMMatrixInverse(&det, m_ViewArcBall.GetRotationMatrix()); //D3DXMATRIX mCameraRot; //D3DXMatrixRotationYawPitchRoll(&mCameraRot, m_fCameraYawAngle, m_fCameraPitchAngle, 0); // Transform vectors based on camera's rotation matrix - D3DXVECTOR3 vWorldUp, vWorldAhead; - D3DXVECTOR3 vLocalUp = D3DXVECTOR3( 0, 1, 0 ); - D3DXVECTOR3 vLocalAhead = D3DXVECTOR3( 0, 0, 1 ); - D3DXVec3TransformCoord( &vWorldUp, &vLocalUp, &mCameraRot ); - D3DXVec3TransformCoord( &vWorldAhead, &vLocalAhead, &mCameraRot ); + CDXVec vWorldUp, vWorldAhead; + CDXVec vLocalUp = DirectX::XMVectorSet( 0, 1, 0, 0 ); + CDXVec vLocalAhead = DirectX::XMVectorSet(0, 0, 1, 0 ); + vWorldUp = XMVector3TransformCoord(vLocalUp, mCameraRot ); + vWorldAhead = XMVector3TransformCoord(vLocalAhead, mCameraRot ); // Transform the position delta by the camera's rotation - /*D3DXVECTOR3 vPosDeltaWorld; + /*CDXVec vPosDeltaWorld; D3DXVec3TransformCoord( &vPosDeltaWorld, &vPosDelta, &mCameraRot ); // Move the lookAt position @@ -274,23 +270,23 @@ void CDXModelViewerCamera::Update() ConstrainToBoundary(&m_vEye);*/ // Update the eye point based on a radius away from the lookAt position - m_vEye = m_vLookAt - vWorldAhead * m_fRadius; + m_vEye = DirectX::XMVectorScale(DirectX::XMVectorSubtract(m_vLookAt, vWorldAhead), m_fRadius); //m_vLookAt = m_vEye + vWorldAhead * m_fRadius; // Update the view matrix - D3DXMatrixLookAtLH( &m_mView, &m_vEye, &m_vLookAt, &vWorldUp ); + m_mView = DirectX::XMMatrixLookAtLH( m_vEye, m_vLookAt, vWorldUp ); + + auto mInvViewDet = DirectX::XMMatrixDeterminant(m_mView); + auto mInvView = DirectX::XMMatrixInverse(&mInvViewDet, m_mView); - D3DXMATRIX mInvView; - D3DXMatrixInverse( &mInvView, NULL, &m_mView ); - mInvView._41 = mInvView._42 = mInvView._43 = 0; + mInvView.r[3] = DirectX::XMVectorZero(); - D3DXMATRIX mModelLastRotInv; - D3DXMatrixInverse( &mModelLastRotInv, NULL, &m_mModelLastRot ); + auto mModelLastRotInvDet = DirectX::XMMatrixDeterminant(m_mModelLastRot); + auto mModelLastRotInv = DirectX::XMMatrixInverse(&mModelLastRotInvDet, m_mModelLastRot); // Accumulate the delta of the arcball's rotation in view space. // Note that per-frame delta rotations could be problematic over long periods of time. - D3DXMATRIX mModelRot; - mModelRot = *m_WorldArcBall.GetRotationMatrix(); + auto mModelRot = m_WorldArcBall.GetRotationMatrix(); m_mModelRot *= m_mView * mModelLastRotInv * mModelRot * mInvView; m_mCameraRotLast = mCameraRot; @@ -298,28 +294,16 @@ void CDXModelViewerCamera::Update() // Since we're accumulating delta rotations, we need to orthonormalize // the matrix to prevent eventual matrix skew - D3DXVECTOR3* pXBasis = ( D3DXVECTOR3* )&m_mModelRot._11; - D3DXVECTOR3* pYBasis = ( D3DXVECTOR3* )&m_mModelRot._21; - D3DXVECTOR3* pZBasis = ( D3DXVECTOR3* )&m_mModelRot._31; - D3DXVec3Normalize( pXBasis, pXBasis ); - D3DXVec3Cross( pYBasis, pZBasis, pXBasis ); - D3DXVec3Normalize( pYBasis, pYBasis ); - D3DXVec3Cross( pZBasis, pXBasis, pYBasis ); - - // Translate the rotation matrix to the same position as the lookAt position - //m_mModelRot._41 = m_vLookAt.x; - //m_mModelRot._42 = m_vLookAt.y; - //m_mModelRot._43 = m_vLookAt.z; - - m_mModelRot._41 = m_vModelCenter.x; - m_mModelRot._42 = m_vModelCenter.y; - m_mModelRot._43 = m_vModelCenter.z; + m_mModelRot.r[0] = DirectX::XMVector3Normalize(m_mModelRot.r[0]); + m_mModelRot.r[1] = DirectX::XMVector3Cross(m_mModelRot.r[2], m_mModelRot.r[0]); + m_mModelRot.r[1] = DirectX::XMVector3Normalize(m_mModelRot.r[1]); + m_mModelRot.r[2] = DirectX::XMVector3Cross(m_mModelRot.r[0], m_mModelRot.r[1]); + + m_mModelRot.r[3] = m_vModelCenter; // Translate world matrix so its at the center of the model - D3DXMATRIX mTrans; //D3DXMatrixTranslation( &mTrans, -m_vModelCenter.x, -m_vModelCenter.y, -m_vModelCenter.z ); - D3DXMatrixScaling(&mTrans, -1.0, 1.0, 1.0); - m_mWorld = mTrans * m_mModelRot; + m_mWorld = DirectX::XMMatrixScaling(-1.0, 1.0, 1.0) * m_mModelRot; } //-------------------------------------------------------------------------------------- @@ -329,10 +313,10 @@ VOID CDXModelViewerCamera::Reset() { SetViewParams( &m_vDefaultEye, &m_vDefaultLookAt ); - D3DXMatrixIdentity( &m_mWorld ); - D3DXMatrixIdentity( &m_mModelRot ); - D3DXMatrixIdentity( &m_mModelLastRot ); - D3DXMatrixIdentity( &m_mCameraRotLast ); + m_mWorld = DirectX::XMMatrixIdentity(); + m_mModelRot = DirectX::XMMatrixIdentity(); + m_mModelLastRot = DirectX::XMMatrixIdentity(); + m_mCameraRotLast = DirectX::XMMatrixIdentity(); m_fRadius = m_fDefaultRadius; m_WorldArcBall.Reset(); @@ -343,7 +327,7 @@ VOID CDXModelViewerCamera::Reset() //-------------------------------------------------------------------------------------- // Override for setting the view parameters //-------------------------------------------------------------------------------------- -void CDXModelViewerCamera::SetViewParams( D3DXVECTOR3* pvEyePt, D3DXVECTOR3* pvLookatPt ) +void CDXModelViewerCamera::SetViewParams( CDXVec* pvEyePt, CDXVec* pvLookatPt ) { if( NULL == pvEyePt || NULL == pvLookatPt ) return; @@ -352,36 +336,33 @@ void CDXModelViewerCamera::SetViewParams( D3DXVECTOR3* pvEyePt, D3DXVECTOR3* pvL m_vDefaultLookAt = m_vLookAt = *pvLookatPt; // Calc the view matrix - D3DXVECTOR3 vUp( 0,1,0 ); - D3DXMatrixLookAtLH( &m_mView, pvEyePt, pvLookatPt, &vUp ); + CDXVec vUp = DirectX::XMVectorSet( 0,1,0,1 ); + m_mView = DirectX::XMMatrixLookAtLH( *pvEyePt, *pvLookatPt, vUp ); - D3DXMATRIX mInvView; - D3DXMatrixInverse( &mInvView, NULL, &m_mView ); + auto m_mViewDet = DirectX::XMMatrixDeterminant(m_mView); + auto mInvView = DirectX::XMMatrixInverse(&m_mViewDet, m_mView); // The axis basis vectors and camera position are stored inside the // position matrix in the 4 rows of the camera's world matrix. // To figure out the yaw/pitch of the camera, we just need the Z basis vector - D3DXVECTOR3* pZBasis = ( D3DXVECTOR3* )&mInvView._31; + CDXVec* pZBasis = ( CDXVec* )&mInvView.r[3]; - m_fCameraYawAngle = atan2f( pZBasis->x, pZBasis->z ); - float fLen = sqrtf( pZBasis->z * pZBasis->z + pZBasis->x * pZBasis->x ); - m_fCameraPitchAngle = -atan2f( pZBasis->y, fLen ); + m_fCameraYawAngle = atan2f(DirectX::XMVectorGetX(*pZBasis), DirectX::XMVectorGetZ(*pZBasis)); + float fLen = sqrtf(DirectX::XMVectorGetZ(*pZBasis) * DirectX::XMVectorGetZ(*pZBasis) + DirectX::XMVectorGetX(*pZBasis) * DirectX::XMVectorGetX(*pZBasis)); + m_fCameraPitchAngle = -atan2f(DirectX::XMVectorGetY(*pZBasis), fLen ); // Propogate changes to the member arcball - D3DXQUATERNION quat; - D3DXMATRIXA16 mRotation; - D3DXMatrixLookAtLH( &mRotation, pvEyePt, pvLookatPt, &vUp ); - D3DXMatrixRotationYawPitchRoll(&mRotation, 0, -D3DX_PI/2, 0); - D3DXQuaternionRotationMatrix( &quat, &mRotation ); + auto mRotation = DirectX::XMMatrixLookAtLH(*pvEyePt, *pvLookatPt, vUp); + mRotation = DirectX::XMMatrixRotationRollPitchYaw(-DirectX::XM_PI / 2, 0, 0); + + auto quat = DirectX::XMQuaternionRotationMatrix(mRotation); m_ViewArcBall.SetQuatNow( quat ); // Set the radius according to the distance - D3DXVECTOR3 vEyeToPoint; - D3DXVec3Subtract( &vEyeToPoint, pvLookatPt, pvEyePt ); - SetRadius( D3DXVec3Length( &vEyeToPoint ) ); + CDXVec vEyeToPoint = DirectX::XMVectorSubtract(*pvLookatPt, *pvEyePt); + SetRadius( DirectX::XMVectorGetX(DirectX::XMVector3Length(vEyeToPoint))); // View information changed. FrameMove should be called. m_bDragSinceLastUpdate = true; } - #endif \ No newline at end of file diff --git a/skee/CDXCamera.h b/skee/CDXCamera.h index 5dbe211..4dd4df5 100644 --- a/skee/CDXCamera.h +++ b/skee/CDXCamera.h @@ -4,6 +4,8 @@ #ifdef FIXME +#include "CDXTypes.h" + class CDXArcBall { public: @@ -12,7 +14,7 @@ class CDXArcBall // Functions to change behavior void Reset(); void SetTranslationRadius( float fRadiusTranslation ) { m_fRadiusTranslation = fRadiusTranslation; } - void SetWindow( int nWidth, int nHeight, float fRadius = 0.9f ) { m_nWidth = nWidth; m_nHeight = nHeight; m_fRadius = fRadius; m_vCenter = D3DXVECTOR2(m_nWidth/2.0f,m_nHeight/2.0f); } + void SetWindow( int nWidth, int nHeight, float fRadius = 0.9f ) { m_nWidth = nWidth; m_nHeight = nHeight; m_fRadius = fRadius; m_vCenter = CDXVec2(m_nWidth/2.0f,m_nHeight/2.0f); } void SetOffset( int nX, int nY ) { m_Offset.x = nX; m_Offset.y = nY; } // Call these from client and use GetRotationMatrix() to read new rotation matrix @@ -24,37 +26,37 @@ class CDXArcBall int GetHeight() const { return m_nHeight; } // Functions to get/set state - const D3DXMATRIX* GetRotationMatrix() { return D3DXMatrixRotationQuaternion(&m_mRotation, &m_qNow); }; - const D3DXMATRIX* GetTranslationMatrix() const { return &m_mTranslation; } - const D3DXMATRIX* GetTranslationDeltaMatrix() const { return &m_mTranslationDelta; } + const CDXMatrix GetRotationMatrix() { return DirectX::XMMatrixRotationQuaternion(m_qNow); }; + const CDXMatrix & GetTranslationMatrix() const { return m_mTranslation; } + const CDXMatrix & GetTranslationDeltaMatrix() const { return m_mTranslationDelta; } bool IsBeingDragged() const { return m_bDrag; } - D3DXQUATERNION GetQuatNow() const { return m_qNow; } - void SetQuatNow( D3DXQUATERNION q ) { m_qNow = q; } + CDXVec & GetQuatNow() { return m_qNow; } + void SetQuatNow( CDXVec q ) { m_qNow = q; } - static D3DXQUATERNION WINAPI QuatFromBallPoints( const D3DXVECTOR3& vFrom, const D3DXVECTOR3& vTo ); + static CDXVec WINAPI QuatFromBallPoints( const CDXVec& vFrom, const CDXVec& vTo ); protected: - D3DXMATRIXA16 m_mRotation; // Matrix for arc ball's orientation - D3DXMATRIXA16 m_mTranslation; // Matrix for arc ball's position - D3DXMATRIXA16 m_mTranslationDelta; // Matrix for arc ball's position + CDXMatrix m_mRotation; // Matrix for arc ball's orientation + CDXMatrix m_mTranslation; // Matrix for arc ball's position + CDXMatrix m_mTranslationDelta; // Matrix for arc ball's position POINT m_Offset; // window offset, or upper-left corner of window int m_nWidth; // arc ball's window width int m_nHeight; // arc ball's window height - D3DXVECTOR2 m_vCenter; // center of arc ball + CDXVec2 m_vCenter; // center of arc ball float m_fRadius; // arc ball's radius in screen coords float m_fRadiusTranslation; // arc ball's radius for translating the target - D3DXQUATERNION m_qDown; // Quaternion before button down - D3DXQUATERNION m_qNow; // Composite quaternion for current drag + CDXVec m_qDown; // Quaternion before button down + CDXVec m_qNow; // Composite quaternion for current drag bool m_bDrag; // Whether user is dragging arc ball POINT m_ptLastMouse; // position of last mouse point - D3DXVECTOR3 m_vDownPt; // starting point of rotation arc - D3DXVECTOR3 m_vCurrentPt; // current point of rotation arc + CDXVec m_vDownPt; // starting point of rotation arc + CDXVec m_vCurrentPt; // current point of rotation arc - D3DXVECTOR3 ScreenToVector( float fScreenPtX, float fScreenPtY ); + CDXVec ScreenToVector( float fScreenPtX, float fScreenPtY ); }; class CDXModelViewerCamera @@ -62,22 +64,22 @@ class CDXModelViewerCamera public: CDXModelViewerCamera(); - const D3DXMATRIX* GetViewMatrix() const { return &m_mView; } - const D3DXMATRIX* GetProjMatrix() const { return &m_mProj; } - const D3DXVECTOR3* GetEyePt() const { return &m_vEye; } - const D3DXVECTOR3* GetLookAtPt() const { return &m_vLookAt; } + const CDXMatrix& GetViewMatrix() const { return m_mView; } + const CDXMatrix& GetProjMatrix() const { return m_mProj; } + const CDXVec& GetEyePt() const { return m_vEye; } + const CDXVec& GetLookAtPt() const { return m_vLookAt; } void Update(); void Reset(); - void SetViewParams( D3DXVECTOR3* pvEyePt, D3DXVECTOR3* pvLookatPt ); + void SetViewParams( CDXVec* pvEyePt, CDXVec* pvLookatPt ); void SetWindow( int nWidth, int nHeight, float fArcballRadius=0.9f ) { m_WorldArcBall.SetWindow( nWidth, nHeight, fArcballRadius ); m_ViewArcBall.SetWindow( nWidth, nHeight, fArcballRadius ); } void SetRadius( float fDefaultRadius=5.0f, float fMinRadius=1.0f, float fMaxRadius=FLT_MAX ) { m_fDefaultRadius = m_fRadius = fDefaultRadius; m_fMinRadius = fMinRadius; m_fMaxRadius = fMaxRadius; m_bDragSinceLastUpdate = true; } - void SetModelCenter( D3DXVECTOR3 vModelCenter ) { m_vModelCenter = vModelCenter; } - void SetViewQuat( D3DXQUATERNION q ) { m_ViewArcBall.SetQuatNow( q ); m_bDragSinceLastUpdate = true; } - void SetWorldQuat( D3DXQUATERNION q ) { m_WorldArcBall.SetQuatNow( q ); m_bDragSinceLastUpdate = true; } + void SetModelCenter( CDXVec vModelCenter ) { m_vModelCenter = vModelCenter; } + void SetViewQuat( CDXVec q ) { m_ViewArcBall.SetQuatNow( q ); m_bDragSinceLastUpdate = true; } + void SetWorldQuat( CDXVec q ) { m_WorldArcBall.SetQuatNow( q ); m_bDragSinceLastUpdate = true; } void SetProjParams( FLOAT fFOV, FLOAT fAspect, FLOAT fNearPlane, FLOAT fFarPlane ); //void UpdateMouseDelta(int currentX, int currentY); - void ConstrainToBoundary( D3DXVECTOR3* pV ); + void ConstrainToBoundary( CDXVec* pV ); float GetRadius() const { return m_fRadius; } int GetWidth() const { return m_WorldArcBall.GetWidth(); } @@ -94,37 +96,37 @@ class CDXModelViewerCamera void SetPanSpeed(float speed) { m_fPanMultiplier = speed; }; // Functions to get state - const D3DXMATRIX* GetWorldMatrix() const { return &m_mWorld; } - void SetWorldMatrix( D3DXMATRIX &mWorld ) { m_mWorld = mWorld; m_bDragSinceLastUpdate = true; } + const CDXMatrix* GetWorldMatrix() const { return &m_mWorld; } + void SetWorldMatrix( CDXMatrix &mWorld ) { m_mWorld = mWorld; m_bDragSinceLastUpdate = true; } protected: - D3DXMATRIX m_mView; // View matrix - D3DXMATRIX m_mProj; // Projection matrix + CDXMatrix m_mView; // View matrix + CDXMatrix m_mProj; // Projection matrix - //D3DXVECTOR3 m_vKeyboardDirection; // Direction vector of keyboard input + //CDXVec3 m_vKeyboardDirection; // Direction vector of keyboard input //POINT m_ptLastMousePosition; // Last absolute position of mouse cursor - //D3DXVECTOR2 m_vMouseDelta; // Mouse relative delta smoothed over a few frames + //CDXVec2 m_vMouseDelta; // Mouse relative delta smoothed over a few frames //float m_fFramesToSmoothMouseData; // Number of frames to smooth mouse data over - D3DXVECTOR3 m_vDefaultEye; // Default camera eye position - D3DXVECTOR3 m_vDefaultLookAt; // Default LookAt position - D3DXVECTOR3 m_vEye; // Camera eye position - D3DXVECTOR3 m_vLookAt; // LookAt position + CDXVec m_vDefaultEye; // Default camera eye position + CDXVec m_vDefaultLookAt; // Default LookAt position + CDXVec m_vEye; // Camera eye position + CDXVec m_vLookAt; // LookAt position float m_fCameraYawAngle; // Yaw angle of camera float m_fCameraPitchAngle; // Pitch angle of camera float m_fPanMultiplier; - D3DXVECTOR3 m_vDragLook; - D3DXVECTOR2 m_vDragStart; - D3DXVECTOR2 m_vDragPos; + CDXVec m_vDragLook; + CDXVec m_vDragStart; + CDXVec m_vDragPos; //RECT m_rcDrag; // Rectangle within which a drag can be initiated. - D3DXVECTOR3 m_vVelocity; // Velocity of camera + CDXVec m_vVelocity; // Velocity of camera //bool m_bMovementDrag; // If true, then camera movement will slow to a stop otherwise movement is instant - //D3DXVECTOR3 m_vVelocityDrag; // Velocity drag force + //CDXVec3 m_vVelocityDrag; // Velocity drag force //float m_fDragTimer; // Countdown timer to apply drag //float m_fTotalDragTimeToZero; // Time it takes for velocity to go from full to 0 - //D3DXVECTOR2 m_vRotVelocity; // Velocity of camera + //CDXVec2 m_vRotVelocity; // Velocity of camera float m_fFOV; // Field of view float m_fAspect; // Aspect ratio @@ -135,15 +137,15 @@ class CDXModelViewerCamera //float m_fMoveScaler; // Scaler for movement bool m_bClipToBoundary; // If true, then the camera will be clipped to the boundary - D3DXVECTOR3 m_vMinBoundary; // Min point in clip boundary - D3DXVECTOR3 m_vMaxBoundary; // Max point in clip boundary + CDXVec m_vMinBoundary; // Min point in clip boundary + CDXVec m_vMaxBoundary; // Max point in clip boundary CDXArcBall m_WorldArcBall; CDXArcBall m_ViewArcBall; - D3DXVECTOR3 m_vModelCenter; - D3DXMATRIX m_mModelLastRot; // Last arcball rotation matrix for model - D3DXMATRIX m_mModelRot; // Rotation matrix of model - D3DXMATRIX m_mWorld; // World matrix of model + CDXVec m_vModelCenter; + CDXMatrix m_mModelLastRot; // Last arcball rotation matrix for model + CDXMatrix m_mModelRot; // Rotation matrix of model + CDXMatrix m_mWorld; // World matrix of model float m_fRadius; // Distance from the camera to model float m_fDefaultRadius; // Distance from the camera to model @@ -151,9 +153,8 @@ class CDXModelViewerCamera float m_fMaxRadius; // Max radius bool m_bDragSinceLastUpdate; // True if mouse drag has happened since last time FrameMove is called. - D3DXMATRIX m_mCameraRotLast; + CDXMatrix m_mCameraRotLast; }; #endif - #endif \ No newline at end of file diff --git a/skee/CDXEditableMesh.cpp b/skee/CDXEditableMesh.cpp index 98c04b7..40d2a6d 100644 --- a/skee/CDXEditableMesh.cpp +++ b/skee/CDXEditableMesh.cpp @@ -65,32 +65,32 @@ void CDXEditableMesh::VisitAdjacencies(CDXMeshIndex i, std::functionSetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); + //pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); CDXMesh::Render(pDevice, shader); // Render again but in wireframe if (m_wireframe) { - pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); + //pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); - if (m_material) { + /*if (m_material) { m_material->SetWireframe(true); CDXMesh::Render(pDevice, shader); m_material->SetWireframe(false); - } + }*/ } } -CDXVec3 CDXEditableMesh::CalculateVertexNormal(CDXMeshIndex i) +CDXVec CDXEditableMesh::CalculateVertexNormal(CDXMeshIndex i) { CDXMeshVert* pVertices = NULL; CDXMeshIndex* pIndices = NULL; - D3DXVECTOR3 vNormal(0, 0, 0); - LPDIRECT3DVERTEXBUFFER9 pVB = GetVertexBuffer(); - pVB->Lock(0, 0, (void**)&pVertices, 0); + CDXVec vNormal = DirectX::XMVectorZero(); + ID3D11Buffer * pVB = GetVertexBuffer(); + //pVB->Lock(0, 0, (void**)&pVertices, 0); if (!pVertices) return vNormal; @@ -101,15 +101,17 @@ CDXVec3 CDXEditableMesh::CalculateVertexNormal(CDXMeshIndex i) CDXMeshVert * v2 = &pVertices[tri.v2]; CDXMeshVert * v3 = &pVertices[tri.v3]; - D3DXVECTOR3 e1 = *(D3DXVECTOR3*)v2 - *(D3DXVECTOR3*)v1; - D3DXVECTOR3 e2 = *(D3DXVECTOR3*)v3 - *(D3DXVECTOR3*)v2; - D3DXVec3Cross(&vNormal, &e1, &e2); - D3DXVec3Normalize(&vNormal, &vNormal); + auto e1 = DirectX::XMVectorSubtract(DirectX::XMLoadFloat3(&v2->Position), DirectX::XMLoadFloat3(&v1->Position)); + auto e2 = DirectX::XMVectorSubtract(DirectX::XMLoadFloat3(&v3->Position), DirectX::XMLoadFloat3(&v2->Position)); + + auto faceNormal = DirectX::XMVector3Cross(e1, e2); + faceNormal = DirectX::XMVector3Normalize(faceNormal); + DirectX::XMVectorAdd(vNormal, faceNormal); } - D3DXVec3Normalize(&vNormal, &vNormal); + DirectX::XMVector3Normalize(vNormal); } - pVB->Unlock(); + //pVB->Unlock(); return vNormal; } @@ -121,5 +123,4 @@ bool CDXEditableMesh::IsEdgeVertex(CDXMeshIndex i) const return false; } - #endif \ No newline at end of file diff --git a/skee/CDXEditableMesh.h b/skee/CDXEditableMesh.h index a06b5e1..ae7d15d 100644 --- a/skee/CDXEditableMesh.h +++ b/skee/CDXEditableMesh.h @@ -1,10 +1,10 @@ -#ifdef FIXME - #ifndef __CDXEDITABLEMESH__ #define __CDXEDITABLEMESH__ #pragma once +#ifdef FIXME + #include "CDXMesh.h" #include @@ -26,8 +26,8 @@ typedef std::map CDXAdjacencyMap; typedef std::unordered_map CDXEdgeMap; typedef std::unordered_set CDXVertexEdgeList; -#define COLOR_UNSELECTED D3DCOLOR_RGBA(255, 255, 255, 255) -#define COLOR_SELECTED D3DCOLOR_RGBA(0, 0, 255, 255) +#define COLOR_UNSELECTED CDXColor(255, 255, 255, 255) +#define COLOR_SELECTED CDXColor(0, 0, 255, 255) class CDXEditableMesh : public CDXMesh { @@ -35,7 +35,7 @@ class CDXEditableMesh : public CDXMesh CDXEditableMesh(); ~CDXEditableMesh(); - virtual void Render(LPDIRECT3DDEVICE9 pDevice, CDXShader * shader); + virtual void Render(ID3D11Device * pDevice, CDXShader * shader); virtual bool IsEditable(); virtual bool IsLocked(); @@ -46,7 +46,7 @@ class CDXEditableMesh : public CDXMesh void VisitAdjacencies(CDXMeshIndex i, std::function functor); bool IsEdgeVertex(CDXMeshIndex i) const; - CDXVec3 CalculateVertexNormal(CDXMeshIndex i); + CDXVec CalculateVertexNormal(CDXMeshIndex i); protected: CDXAdjacencyMap m_adjacency; @@ -56,5 +56,4 @@ class CDXEditableMesh : public CDXMesh }; #endif - -#endif \ No newline at end of file +#endif diff --git a/skee/CDXEditableScene.cpp b/skee/CDXEditableScene.cpp index af45ebd..93933d1 100644 --- a/skee/CDXEditableScene.cpp +++ b/skee/CDXEditableScene.cpp @@ -29,7 +29,7 @@ void CDXEditableScene::ReleaseBrushes() m_brushes.clear(); } -void CDXEditableScene::Setup(LPDIRECT3DDEVICE9 pDevice) +void CDXEditableScene::Setup(ID3D11Device * pDevice) { CreateBrushes(); CDXScene::Setup(pDevice); diff --git a/skee/CDXEditableScene.h b/skee/CDXEditableScene.h index 199736a..e70831e 100644 --- a/skee/CDXEditableScene.h +++ b/skee/CDXEditableScene.h @@ -14,7 +14,7 @@ class CDXEditableScene : public CDXScene public: CDXEditableScene(); - virtual void Setup(LPDIRECT3DDEVICE9 pDevice); + virtual void Setup(ID3D11Device * pDevice); virtual void Release(); virtual void CreateBrushes(); diff --git a/skee/CDXMaterial.cpp b/skee/CDXMaterial.cpp index 3b3d2c1..f8813db 100644 --- a/skee/CDXMaterial.cpp +++ b/skee/CDXMaterial.cpp @@ -5,9 +5,9 @@ CDXMaterial::CDXMaterial() { m_diffuseTexture = NULL; - m_specularColor = D3DXVECTOR3(1.0f, 1.0f, 1.0f); - m_diffuseColor = D3DXVECTOR3(1.0f, 1.0f, 1.0f); - m_ambientColor = D3DXVECTOR3(1.0f, 1.0f, 1.0f); + m_specularColor = DirectX::XMFLOAT3(1.0f, 1.0f, 1.0f); + m_diffuseColor = DirectX::XMFLOAT3(1.0f, 1.0f, 1.0f); + m_ambientColor = DirectX::XMFLOAT3(1.0f, 1.0f, 1.0f); m_alphaThreshold = 0; m_shaderFlags1 = 0; m_shaderFlags2 = 0; @@ -40,28 +40,28 @@ void CDXMaterial::SetDiffuseTexture(LPDIRECT3DBASETEXTURE9 texture) m_diffuseTexture->AddRef(); } -void CDXMaterial::SetDiffuseColor(D3DXVECTOR3 color) +void CDXMaterial::SetDiffuseColor(DirectX::XMFLOAT3 color) { #ifdef CDX_MUTEX std::lock_guard guard(m_mutex); #endif m_diffuseColor = color; } -void CDXMaterial::SetSpecularColor(D3DXVECTOR3 color) +void CDXMaterial::SetSpecularColor(DirectX::XMFLOAT3 color) { #ifdef CDX_MUTEX std::lock_guard guard(m_mutex); #endif m_specularColor = color; } -void CDXMaterial::SetAmbientColor(D3DXVECTOR3 color) +void CDXMaterial::SetAmbientColor(DirectX::XMFLOAT3 color) { #ifdef CDX_MUTEX std::lock_guard guard(m_mutex); #endif m_ambientColor = color; } -void CDXMaterial::SetWireframeColor(D3DXVECTOR3 color) +void CDXMaterial::SetWireframeColor(DirectX::XMFLOAT3 color) { #ifdef CDX_MUTEX std::lock_guard guard(m_mutex); @@ -69,28 +69,28 @@ void CDXMaterial::SetWireframeColor(D3DXVECTOR3 color) m_wireframeColor = color; } -D3DXVECTOR3 & CDXMaterial::GetDiffuseColor() +DirectX::XMFLOAT3 & CDXMaterial::GetDiffuseColor() { #ifdef CDX_MUTEX std::lock_guard guard(m_mutex); #endif return m_diffuseColor; } -D3DXVECTOR3 & CDXMaterial::GetSpecularColor() +DirectX::XMFLOAT3 & CDXMaterial::GetSpecularColor() { #ifdef CDX_MUTEX std::lock_guard guard(m_mutex); #endif return m_specularColor; } -D3DXVECTOR3 & CDXMaterial::GetAmbientColor() +DirectX::XMFLOAT3 & CDXMaterial::GetAmbientColor() { #ifdef CDX_MUTEX std::lock_guard guard(m_mutex); #endif return m_ambientColor; } -D3DXVECTOR3 & CDXMaterial::GetWireframeColor() +DirectX::XMFLOAT3 & CDXMaterial::GetWireframeColor() { #ifdef CDX_MUTEX std::lock_guard guard(m_mutex); diff --git a/skee/CDXMaterial.h b/skee/CDXMaterial.h index 7d66f44..215920f 100644 --- a/skee/CDXMaterial.h +++ b/skee/CDXMaterial.h @@ -70,15 +70,15 @@ class CDXMaterial void SetDiffuseTexture(LPDIRECT3DBASETEXTURE9 texture); LPDIRECT3DBASETEXTURE9 GetDiffuseTexture() const { return m_diffuseTexture; } - void SetDiffuseColor(D3DXVECTOR3 color); - void SetSpecularColor(D3DXVECTOR3 color); - void SetAmbientColor(D3DXVECTOR3 color); - void SetWireframeColor(D3DXVECTOR3 color); + void SetDiffuseColor(DirectX::XMFLOAT3 color); + void SetSpecularColor(DirectX::XMFLOAT3 color); + void SetAmbientColor(DirectX::XMFLOAT3 color); + void SetWireframeColor(DirectX::XMFLOAT3 color); - D3DXVECTOR3 & GetDiffuseColor(); - D3DXVECTOR3 & GetSpecularColor(); - D3DXVECTOR3 & GetAmbientColor(); - D3DXVECTOR3 & GetWireframeColor(); + DirectX::XMFLOAT3 & GetDiffuseColor(); + DirectX::XMFLOAT3 & GetSpecularColor(); + DirectX::XMFLOAT3 & GetAmbientColor(); + DirectX::XMFLOAT3 & GetWireframeColor(); UInt32 GetShaderFlags1() const { return m_shaderFlags1; } UInt32 GetShaderFlags2() const { return m_shaderFlags2; } @@ -154,10 +154,10 @@ class CDXMaterial protected: LPDIRECT3DBASETEXTURE9 m_diffuseTexture; - D3DXVECTOR3 m_specularColor; - D3DXVECTOR3 m_diffuseColor; - D3DXVECTOR3 m_ambientColor; - D3DXVECTOR3 m_wireframeColor; + DirectX::XMFLOAT3 m_specularColor; + DirectX::XMFLOAT3 m_diffuseColor; + DirectX::XMFLOAT3 m_ambientColor; + DirectX::XMFLOAT3 m_wireframeColor; DeclareFlags(UInt16); UInt32 m_shaderFlags1; UInt32 m_shaderFlags2; diff --git a/skee/CDXMesh.cpp b/skee/CDXMesh.cpp index a266909..3df3c18 100644 --- a/skee/CDXMesh.cpp +++ b/skee/CDXMesh.cpp @@ -13,10 +13,10 @@ CDXMesh::CDXMesh() m_indexBuffer = NULL; m_primitiveCount = 0; m_visible = true; - m_material = NULL; - m_primitiveType = D3DPT_TRIANGLELIST; + //m_material = NULL; + //m_primitiveType = D3DPT_TRIANGLELIST; - D3DXMatrixIdentity(&m_transform); + m_transform = DirectX::XMMatrixIdentity(); } CDXMesh::~CDXMesh() @@ -37,11 +37,11 @@ void CDXMesh::Release() m_indexBuffer->Release(); m_indexBuffer = NULL; } - if(m_material) { + /*if(m_material) { m_material->Release(); delete m_material; m_material = NULL; - } + }*/ } void CDXMesh::SetMaterial(CDXMaterial * material) @@ -49,7 +49,7 @@ void CDXMesh::SetMaterial(CDXMaterial * material) #ifdef CDX_MUTEX std::lock_guard guard(m_mutex); #endif - m_material = material; + //m_material = material; } CDXMaterial * CDXMesh::GetMaterial() @@ -57,7 +57,7 @@ CDXMaterial * CDXMesh::GetMaterial() #ifdef CDX_MUTEX std::lock_guard guard(m_mutex); #endif - return m_material; + return nullptr;//m_material; } void CDXMesh::SetVisible(bool visible) @@ -75,14 +75,14 @@ bool CDXMesh::IsVisible() return m_visible; } -LPDIRECT3DVERTEXBUFFER9 CDXMesh::GetVertexBuffer() +ID3D11Buffer * CDXMesh::GetVertexBuffer() { #ifdef CDX_MUTEX std::lock_guard guard(m_mutex); #endif return m_vertexBuffer; } -LPDIRECT3DINDEXBUFFER9 CDXMesh::GetIndexBuffer() +ID3D11Buffer * CDXMesh::GetIndexBuffer() { #ifdef CDX_MUTEX std::lock_guard guard(m_mutex); @@ -99,29 +99,29 @@ UInt32 CDXMesh::GetVertexCount() return m_vertCount; } -D3DXVECTOR3 CalculateFaceNormal(UInt32 f, CDXMeshIndex * faces, CDXMeshVert * vertices) +DirectX::XMVECTOR CalculateFaceNormal(UInt32 f, CDXMeshIndex * faces, CDXMeshVert * vertices) { - D3DXVECTOR3 vNormal(0, 0, 0); + DirectX::XMVECTOR vNormal; CDXMeshVert * v1 = &vertices[faces[f]]; CDXMeshVert * v2 = &vertices[faces[f + 1]]; CDXMeshVert * v3 = &vertices[faces[f + 2]]; - D3DXVECTOR3 e1 = *(D3DXVECTOR3*)v2 - *(D3DXVECTOR3*)v1; - D3DXVECTOR3 e2 = *(D3DXVECTOR3*)v3 - *(D3DXVECTOR3*)v2; + auto e1 = DirectX::XMVectorSubtract(DirectX::XMLoadFloat3(&v2->Position), DirectX::XMLoadFloat3(&v1->Position)); + auto e2 = DirectX::XMVectorSubtract(DirectX::XMLoadFloat3(&v3->Position), DirectX::XMLoadFloat3(&v2->Position)); - D3DXVec3Cross(&vNormal, &e1, &e2); - D3DXVec3Normalize(&vNormal, &vNormal); + vNormal = DirectX::XMVector3Cross(e1, e2); + vNormal = DirectX::XMVector3Normalize(vNormal); return vNormal; } -bool IntersectSphere(float radius, float & dist, CDXVec3 & center, CDXVec3 & rayOrigin, CDXVec3 & rayDir) +bool IntersectSphere(float radius, float & dist, CDXVec & center, CDXVec & rayOrigin, CDXVec & rayDir) { //FLOAT t0 = -1, t1 = -1; // solutions for t if the ray intersects - CDXVec3 L = center - rayOrigin; - float tca = D3DXVec3Dot(&L, &rayDir); + CDXVec L = DirectX::XMVectorSubtract(center, rayOrigin); + float tca = DirectX::XMVector3Dot(L, rayDir).m128_f32[0]; if (tca < 0) return false; - float d2 = D3DXVec3Dot(&L, &L) - tca * tca; + float d2 = DirectX::XMVector3Dot(L, L).m128_f32[0] - tca * tca; if (d2 > radius) return false; float thc = sqrt(radius - d2); //t0 = tca - thc; @@ -130,24 +130,23 @@ bool IntersectSphere(float radius, float & dist, CDXVec3 & center, CDXVec3 & ray return true; } -bool IntersectTriangle( const CDXVec3& orig, const CDXVec3& dir, CDXVec3& v0, CDXVec3& v1, CDXVec3& v2, float* t, float* u, float* v ) +bool IntersectTriangle( const CDXVec& orig, const CDXVec& dir, CDXVec& v0, CDXVec& v1, CDXVec& v2, float* t, float* u, float* v ) { // Find vectors for two edges sharing vert0 - CDXVec3 edge1 = v1 - v0; - CDXVec3 edge2 = v2 - v0; + CDXVec edge1 = DirectX::XMVectorSubtract(v1, v0); + CDXVec edge2 = DirectX::XMVectorSubtract(v2, v0); // Begin calculating determinant - also used to calculate U parameter - CDXVec3 pvec; - D3DXVec3Cross(&pvec, &dir, &edge2); + CDXVec pvec = DirectX::XMVector3Cross(dir, edge2); // If determinant is near zero, ray lies in plane of triangle - float det = D3DXVec3Dot(&edge1, &pvec); + float det = DirectX::XMVector3Dot(edge1, pvec).m128_f32[0]; - CDXVec3 tvec; + CDXVec tvec; if(det > 0) { - tvec = orig - v0; + tvec = DirectX::XMVectorSubtract(orig, v0); } else { - tvec = v0 - orig; + tvec = DirectX::XMVectorSubtract(v0, orig); det = -det; } @@ -155,21 +154,20 @@ bool IntersectTriangle( const CDXVec3& orig, const CDXVec3& dir, CDXVec3& v0, CD return false; // Calculate U parameter and test bounds - *u = D3DXVec3Dot(&tvec, &pvec); + *u = DirectX::XMVector3Dot(tvec, pvec).m128_f32[0]; if(*u < 0.0f || *u > det) return false; // Prepare to test V parameter - CDXVec3 qvec; - D3DXVec3Cross(&qvec, &tvec, &edge1); + CDXVec qvec = DirectX::XMVector3Cross(tvec, edge1); // Calculate V parameter and test bounds - *v = D3DXVec3Dot(&dir, &qvec); + *v = DirectX::XMVector3Dot(dir, qvec).m128_f32[0]; if(*v < 0.0f || *u + *v > det) return false; // Calculate t, scale parameters, ray intersects triangle - *t = D3DXVec3Dot(&edge2, &qvec); + *t = DirectX::XMVector3Dot(edge2, qvec).m128_f32[0]; float fInvDet = 1.0f / det; *t *= fInvDet; *u *= fInvDet; @@ -190,28 +188,27 @@ bool CDXMesh::Pick(CDXRayInfo & rayInfo, CDXPickInfo & pickInfo) return false; float hitDist = FLT_MAX; - CDXVec3 hitNormal(0, 0, 0); + CDXVec hitNormal = DirectX::XMVectorZero(); // Edges = Face * 3 for(UInt32 e = 0; e < m_primitiveCount * 3; e += 3) { - CDXVec3 v0 = pVertices[pIndices[e + 0]].Position; - CDXVec3 v1 = pVertices[pIndices[e + 1]].Position; - CDXVec3 v2 = pVertices[pIndices[e + 2]].Position; + CDXVec v0 = DirectX::XMLoadFloat3(&pVertices[pIndices[e + 0]].Position); + CDXVec v1 = DirectX::XMLoadFloat3(&pVertices[pIndices[e + 1]].Position); + CDXVec v2 = DirectX::XMLoadFloat3(&pVertices[pIndices[e + 2]].Position); // Calculate the norm of the face - CDXVec3 fNormal(0,0,0); - CDXVec3 f1 = v1 - v0; - CDXVec3 f2 = v2 - v1; - D3DXVec3Cross(&fNormal, &f1, &f2); - D3DXVec3Normalize(&fNormal, &fNormal); + CDXVec fNormal = DirectX::XMVectorZero(); + CDXVec f1 = DirectX::XMVectorSubtract(v1, v0); + CDXVec f2 = DirectX::XMVectorSubtract(v2, v1); + fNormal = DirectX::XMVector3Cross(f1, f2); + fNormal = DirectX::XMVector3Normalize(fNormal); // Normalize the direction, just in case - CDXVec3 vDir = rayInfo.direction; - D3DXVec3Normalize(&vDir, &vDir); + CDXVec vDir = DirectX::XMVector3Normalize(rayInfo.direction); // Skip faces that are in the same direction as the ray - if(D3DXVec3Dot(&vDir, &fNormal) >= 0) + if(DirectX::XMVector3Dot(vDir, fNormal).m128_f32[0] >= 0) continue; // Skip face that doesn't intersect with the ray @@ -231,14 +228,15 @@ bool CDXMesh::Pick(CDXRayInfo & rayInfo, CDXPickInfo & pickInfo) pickInfo.dist = hitDist; if (hitDist != FLT_MAX) { - CDXVec3 vHit = rayInfo.origin + rayInfo.direction * hitDist; + CDXVec hitVec = DirectX::XMVectorReplicate(hitDist); + CDXVec vHit = DirectX::XMVectorMultiplyAdd(rayInfo.direction, hitVec, rayInfo.origin); pickInfo.origin = vHit; pickInfo.normal = hitNormal; pickInfo.isHit = true; } else { - pickInfo.origin = CDXVec3(0, 0, 0); - pickInfo.normal = CDXVec3(0, 0, 0); + pickInfo.origin = DirectX::XMVectorZero(); + pickInfo.normal = DirectX::XMVectorZero(); pickInfo.isHit = false; } @@ -253,8 +251,8 @@ CDXMeshVert* CDXMesh::LockVertices() if (!m_vertexBuffer) return NULL; - if (FAILED(m_vertexBuffer->Lock(0, 0, (void**)&pVertices, D3DLOCK_DISCARD))) - return NULL; + /*if (FAILED(m_vertexBuffer->Lock(0, 0, (void**)&pVertices, D3DLOCK_DISCARD))) + return NULL;*/ return pVertices; } @@ -265,27 +263,27 @@ CDXMeshIndex* CDXMesh::LockIndices() if (!m_indexBuffer) return NULL; - if (FAILED(m_indexBuffer->Lock(0, 0, (void**)&pIndices, D3DLOCK_DISCARD))) - return NULL; + /*if (FAILED(m_indexBuffer->Lock(0, 0, (void**)&pIndices, D3DLOCK_DISCARD))) + return NULL;*/ return pIndices; } void CDXMesh::UnlockVertices() { - m_vertexBuffer->Unlock(); + //m_vertexBuffer->Unlock(); } void CDXMesh::UnlockIndices() { - m_indexBuffer->Unlock(); + //m_indexBuffer->Unlock(); } -void CDXMesh::Render(LPDIRECT3DDEVICE9 pDevice, CDXShader * shader) +void CDXMesh::Render(ID3D11Device * pDevice, CDXShader * shader) { #ifdef CDX_MUTEX std::lock_guard guard(m_mutex); #endif - ID3DXEffect * effect = shader->GetEffect(); + /*ID3DXEffect * effect = shader->GetEffect(); effect->SetMatrix(shader->m_hTransform, &m_transform); if (m_material && m_material->IsWireframe()) { @@ -310,17 +308,16 @@ void CDXMesh::Render(LPDIRECT3DDEVICE9 pDevice, CDXShader * shader) effect->EndPass(); } - effect->End(); + effect->End();*/ } -void CDXMesh::Pass(LPDIRECT3DDEVICE9 pDevice, UInt32 iPass, CDXShader * shader) +void CDXMesh::Pass(ID3D11Device * pDevice, UInt32 iPass, CDXShader * shader) { if (!m_vertexBuffer || !m_indexBuffer) return; - pDevice->SetStreamSource(0, m_vertexBuffer, 0, sizeof(CDXMeshVert)); - pDevice->SetIndices(m_indexBuffer); - pDevice->DrawIndexedPrimitive((D3DPRIMITIVETYPE)m_primitiveType, 0, 0, m_vertCount, 0, m_primitiveCount); + //pDevice->SetStreamSource(0, m_vertexBuffer, 0, sizeof(CDXMeshVert)); + //pDevice->SetIndices(m_indexBuffer); + //pDevice->DrawIndexedPrimitive((D3DPRIMITIVETYPE)m_primitiveType, 0, 0, m_vertCount, 0, m_primitiveCount); } - #endif \ No newline at end of file diff --git a/skee/CDXMesh.h b/skee/CDXMesh.h index 9d2c49f..bdd3c5c 100644 --- a/skee/CDXMesh.h +++ b/skee/CDXMesh.h @@ -1,108 +1,19 @@ #pragma once -#ifdef FIXME - #ifdef CDX_MUTEX #include #endif -#include - -typedef D3DXMATRIX CDXMatrix; -typedef unsigned short CDXMeshIndex; -typedef D3DXVECTOR3 CDXVec3; -typedef D3DXVECTOR2 CDXVec2; -typedef D3DCOLOR CDXColor; - -typedef std::set CDXMeshIndexSet; -typedef std::map CDXHitIndexMap; - -struct CDXMeshEdge -{ - CDXMeshIndex p1; - CDXMeshIndex p2; - - CDXMeshEdge(CDXMeshIndex _p1, CDXMeshIndex _p2) - { - p1 = _p1; - p2 = _p2; - } -}; - -struct CDXMeshFace -{ - CDXMeshIndex v1; - CDXMeshIndex v2; - CDXMeshIndex v3; - - CDXMeshFace(CDXMeshIndex _v1, CDXMeshIndex _v2, CDXMeshIndex _v3) - { - v1 = _v1; - v2 = _v2; - v3 = _v3; - } -}; - -namespace std { - template<> struct hash < CDXMeshEdge > - { - std::size_t operator() (const CDXMeshEdge & t) const - { - return ((t.p2 << 16) | (t.p1 & 0xFFFF)); - } - - }; - - template <> struct equal_to < CDXMeshEdge > - { - bool operator() (const CDXMeshEdge& t1, const CDXMeshEdge& t2) const - { - return ((t1.p1 == t2.p1) && (t1.p2 == t2.p2)); - } - }; - - template <> struct hash < CDXMeshFace > { - std::size_t operator() (const CDXMeshFace& t) const { - char* d = (char*)&t; - size_t len = sizeof(CDXMeshFace); - size_t hash, i; - for (hash = i = 0; i < len; ++i) - { - hash += d[i]; - hash += (hash << 10); - hash ^= (hash >> 6); - } - hash += (hash << 3); - hash ^= (hash >> 11); - hash += (hash << 15); - return hash; - } - }; - template <> struct equal_to < CDXMeshFace > - { - bool operator() (const CDXMeshFace& t1, const CDXMeshFace& t2) const - { - return ((t1.v1 == t2.v1) && (t1.v2 == t2.v2) && (t1.v3 == t2.v3)); - } - }; -} +#include "CDXTypes.h" -struct CDXMeshVert -{ - CDXVec3 Position; - CDXVec3 Normal; - CDXVec2 Tex; - CDXColor Color; -}; - -const static D3DVERTEXELEMENT9 VertexDecl[6] = +/*const static D3DVERTEXELEMENT9 VertexDecl[6] = { { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 }, { 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, { 0, 32, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 }, D3DDECL_END() -}; +};*/ class CDXMaterial; class CDXPicker; @@ -117,8 +28,8 @@ class CDXMesh CDXMesh(); ~CDXMesh(); - virtual void Render(LPDIRECT3DDEVICE9 pDevice, CDXShader * shader); - virtual void Pass(LPDIRECT3DDEVICE9 pDevice, UInt32 iPass, CDXShader * shader); + virtual void Render(ID3D11Device * pDevice, CDXShader * shader); + virtual void Pass(ID3D11Device * pDevice, UInt32 iPass, CDXShader * shader); virtual void Release(); virtual bool Pick(CDXRayInfo & rayInfo, CDXPickInfo & pickInfo); virtual bool IsEditable() { return false; } @@ -136,20 +47,20 @@ class CDXMesh void UnlockVertices(); void UnlockIndices(); - LPDIRECT3DVERTEXBUFFER9 GetVertexBuffer(); - LPDIRECT3DINDEXBUFFER9 GetIndexBuffer(); + ID3D11Buffer * GetVertexBuffer(); + ID3D11Buffer * GetIndexBuffer(); UInt32 GetPrimitiveCount(); UInt32 GetVertexCount(); protected: bool m_visible; - LPDIRECT3DVERTEXBUFFER9 m_vertexBuffer; + ID3D11Buffer * m_vertexBuffer; UInt32 m_vertCount; - LPDIRECT3DINDEXBUFFER9 m_indexBuffer; + ID3D11Buffer * m_indexBuffer; UInt32 m_primitiveCount; - UInt8 m_primitiveType; - CDXMaterial * m_material; + //UInt8 m_primitiveType; + //CDXMaterial * m_material; CDXMatrix m_transform; #ifdef CDX_MUTEX @@ -157,8 +68,6 @@ class CDXMesh #endif }; -D3DXVECTOR3 CalculateFaceNormal(UInt32 f, CDXMeshIndex * faces, CDXMeshVert * vertices); -bool IntersectSphere(float radius, float & dist, CDXVec3 & center, CDXVec3 & rayOrigin, CDXVec3 & rayDir); -bool IntersectTriangle(const CDXVec3& orig, const CDXVec3& dir, CDXVec3& v0, CDXVec3& v1, CDXVec3& v2, float* t, float* u, float* v); - -#endif \ No newline at end of file +DirectX::XMVECTOR CalculateFaceNormal(UInt32 f, CDXMeshIndex * faces, CDXMeshVert * vertices); +bool IntersectSphere(float radius, float & dist, CDXVec & center, CDXVec & rayOrigin, CDXVec & rayDir); +bool IntersectTriangle(const CDXVec& orig, const CDXVec& dir, CDXVec& v0, CDXVec& v1, CDXVec& v2, float* t, float* u, float* v); diff --git a/skee/CDXNifMesh.cpp b/skee/CDXNifMesh.cpp index 7633e3a..1358557 100644 --- a/skee/CDXNifMesh.cpp +++ b/skee/CDXNifMesh.cpp @@ -46,12 +46,12 @@ bool CDXNifMesh::IsMorphable() return m_morphable; } -CDXNifMesh * CDXNifMesh::Create(LPDIRECT3DDEVICE9 pDevice, NiGeometry * geometry) +CDXNifMesh * CDXNifMesh::Create(ID3D11Device * pDevice, NiGeometry * geometry) { UInt32 vertCount = 0; UInt32 triangleCount = 0; - LPDIRECT3DVERTEXBUFFER9 vertexBuffer = NULL; - LPDIRECT3DINDEXBUFFER9 indexBuffer = NULL; + ID3D11Buffer * vertexBuffer = NULL; + ID3D11Buffer * indexBuffer = NULL; LPDIRECT3DBASETEXTURE9 diffuseTexture = NULL; UInt16 alphaFlags = 0; UInt8 alphaThreshold = 0; @@ -139,10 +139,10 @@ CDXNifMesh * CDXNifMesh::Create(LPDIRECT3DDEVICE9 pDevice, NiGeometry * geometry for (UInt32 i = 0; i < vertCount; i++) { NiPoint3 xformed = localTransform * geometryData->m_pkVertex[i]; NiPoint2 uv = geometryData->m_pkTexture[i]; - pVertices[i].Position = *(D3DXVECTOR3*)&xformed; - D3DXVECTOR3 vNormal(0, 0, 0); + pVertices[i].Position = *(DirectX::XMFLOAT3*)&xformed; + DirectX::XMFLOAT3 vNormal(0, 0, 0); pVertices[i].Normal = vNormal; - pVertices[i].Tex = *(D3DXVECTOR2*)&uv; + pVertices[i].Tex = *(DirectX::XMFLOAT2*)&uv; pVertices[i].Color = COLOR_UNSELECTED; // Build adjacency table @@ -209,11 +209,11 @@ CDXNifMesh * CDXNifMesh::Create(LPDIRECT3DDEVICE9 pDevice, NiGeometry * geometry if (nifMesh->m_morphable) { for (UInt32 i = 0; i < vertCount; i++) { // Setup normals - D3DXVECTOR3 vNormal(0, 0, 0); + DirectX::XMFLOAT3 vNormal(0, 0, 0); if (!geometryData->m_pkNormal) vNormal = nifMesh->CalculateVertexNormal(i); else - vNormal = *(D3DXVECTOR3*)&geometryData->m_pkNormal[i]; + vNormal = *(DirectX::XMFLOAT3*)&geometryData->m_pkNormal[i]; pVertices[i].Normal = vNormal; } @@ -223,10 +223,10 @@ CDXNifMesh * CDXNifMesh::Create(LPDIRECT3DDEVICE9 pDevice, NiGeometry * geometry CDXMaterial * material = new CDXMaterial; material->SetDiffuseTexture(diffuseTexture); - material->SetSpecularColor(D3DXVECTOR3(1.0f, 1.0f, 1.0f)); - material->SetAmbientColor(D3DXVECTOR3(0.2f, 0.2f, 0.2f)); - material->SetDiffuseColor(D3DXVECTOR3(1.0f, 1.0f, 1.0f)); - material->SetWireframeColor(D3DXVECTOR3(1.0f, 1.0f, 1.0f)); + material->SetSpecularColor(DirectX::XMFLOAT3(1.0f, 1.0f, 1.0f)); + material->SetAmbientColor(DirectX::XMFLOAT3(0.2f, 0.2f, 0.2f)); + material->SetDiffuseColor(DirectX::XMFLOAT3(1.0f, 1.0f, 1.0f)); + material->SetWireframeColor(DirectX::XMFLOAT3(1.0f, 1.0f, 1.0f)); material->SetShaderFlags1(shaderFlags1); material->SetShaderFlags2(shaderFlags2); if (alphaFlags != 0) { @@ -245,7 +245,7 @@ CDXNifMesh * CDXNifMesh::Create(LPDIRECT3DDEVICE9 pDevice, NiGeometry * geometry return nifMesh; } -void CDXNifMesh::Pass(LPDIRECT3DDEVICE9 pDevice, UInt32 iPass, CDXShader * shader) +void CDXNifMesh::Pass(ID3D11Device * pDevice, UInt32 iPass, CDXShader * shader) { ID3DXEffect * effect = shader->GetEffect(); if (m_material) { diff --git a/skee/CDXNifMesh.h b/skee/CDXNifMesh.h index 9af3d3d..614f454 100644 --- a/skee/CDXNifMesh.h +++ b/skee/CDXNifMesh.h @@ -20,9 +20,9 @@ class CDXNifMesh : public CDXEditableMesh CDXNifMesh(); ~CDXNifMesh(); - static CDXNifMesh * Create(LPDIRECT3DDEVICE9 pDevice, NiGeometry * geometry); + static CDXNifMesh * Create(ID3D11Device * pDevice, NiGeometry * geometry); - virtual void Pass(LPDIRECT3DDEVICE9 pDevice, UInt32 iPass, CDXShader * shader); + virtual void Pass(ID3D11Device * pDevice, UInt32 iPass, CDXShader * shader); NiGeometry * GetNifGeometry(); bool IsMorphable(); diff --git a/skee/CDXNifScene.cpp b/skee/CDXNifScene.cpp index a7c468e..b6f376d 100644 --- a/skee/CDXNifScene.cpp +++ b/skee/CDXNifScene.cpp @@ -35,7 +35,7 @@ void CDXNifScene::CreateBrushes() m_brushes.push_back(new CDXNifMoveBrush); } -void CDXNifScene::Setup(LPDIRECT3DDEVICE9 pDevice) +void CDXNifScene::Setup(ID3D11Device * pDevice) { if (m_textureGroup) Release(); diff --git a/skee/CDXNifScene.h b/skee/CDXNifScene.h index 56443c8..b4a6c84 100644 --- a/skee/CDXNifScene.h +++ b/skee/CDXNifScene.h @@ -17,7 +17,7 @@ class CDXNifScene : public CDXEditableScene public: CDXNifScene(); - virtual void Setup(LPDIRECT3DDEVICE9 pDevice); + virtual void Setup(ID3D11Device * pDevice); virtual void Release(); virtual void CreateBrushes(); diff --git a/skee/CDXPicker.h b/skee/CDXPicker.h index 78f11ce..494f9d7 100644 --- a/skee/CDXPicker.h +++ b/skee/CDXPicker.h @@ -1,8 +1,8 @@ -#ifdef FIXME - #ifndef __CDXPICKER__ #define __CDXPICKER__ +#ifdef FIXME + #pragma once #include "CDXMesh.h" @@ -10,9 +10,9 @@ class CDXRayInfo { public: - CDXVec3 origin; - CDXVec3 direction; - CDXVec3 point; + CDXVec origin; + CDXVec direction; + CDXVec point; CDXRayInfo() { @@ -21,17 +21,17 @@ class CDXRayInfo void Clear() { - origin = CDXVec3(0, 0, 0); - direction = CDXVec3(0, 0, 0); - point = CDXVec3(0, 0, 0); + origin = DirectX::XMVectorZero(); + direction = DirectX::XMVectorZero(); + point = DirectX::XMVectorZero(); } }; class CDXPickInfo { public: - CDXVec3 origin; - CDXVec3 normal; + CDXVec origin; + CDXVec normal; CDXRayInfo ray; float dist; bool isHit; @@ -44,8 +44,8 @@ class CDXPickInfo void Clear() { dist = FLT_MAX; - origin = CDXVec3(0, 0, 0); - normal = CDXVec3(0, 0, 0); + origin = DirectX::XMVectorZero(); + normal = DirectX::XMVectorZero(); isHit = false; } }; @@ -58,5 +58,4 @@ class CDXPicker }; #endif - #endif \ No newline at end of file diff --git a/skee/CDXResetMask.cpp b/skee/CDXResetMask.cpp index 4400b9a..c1d88d6 100644 --- a/skee/CDXResetMask.cpp +++ b/skee/CDXResetMask.cpp @@ -7,7 +7,7 @@ CDXResetMask::CDXResetMask(CDXMesh * mesh) m_mesh = mesh; CDXMeshVert* pVertices = NULL; - LPDIRECT3DVERTEXBUFFER9 pVB = m_mesh->GetVertexBuffer(); + ID3D11Buffer * pVB = m_mesh->GetVertexBuffer(); pVB->Lock(0, 0, (void**)&pVertices, 0); for (CDXMeshIndex i = 0; i < m_mesh->GetVertexCount(); i++) { @@ -38,7 +38,7 @@ void CDXResetMask::Redo() CDXMeshVert* pVertices = NULL; CDXMeshIndex* pIndices = NULL; - LPDIRECT3DVERTEXBUFFER9 pVB = m_mesh->GetVertexBuffer(); + ID3D11Buffer * pVB = m_mesh->GetVertexBuffer(); pVB->Lock(0, 0, (void**)&pVertices, 0); @@ -54,7 +54,7 @@ void CDXResetMask::Undo() CDXMeshVert* pVertices = NULL; CDXMeshIndex* pIndices = NULL; - LPDIRECT3DVERTEXBUFFER9 pVB = m_mesh->GetVertexBuffer(); + ID3D11Buffer * pVB = m_mesh->GetVertexBuffer(); pVB->Lock(0, 0, (void**)&pVertices, 0); diff --git a/skee/CDXScene.cpp b/skee/CDXScene.cpp index 323b651..ce896f5 100644 --- a/skee/CDXScene.cpp +++ b/skee/CDXScene.cpp @@ -52,7 +52,7 @@ void CDXScene::Release() m_meshes.clear(); } -void CDXScene::Setup(LPDIRECT3DDEVICE9 pDevice) +void CDXScene::Setup(ID3D11Device * pDevice) { pDevice->CreateVertexDeclaration(VertexDecl, &m_pMeshDecl); @@ -66,7 +66,7 @@ void CDXScene::Setup(LPDIRECT3DDEVICE9 pDevice) g_Camera.Update(); } -void CDXScene::Begin(LPDIRECT3DDEVICE9 pDevice) +void CDXScene::Begin(ID3D11Device * pDevice) { if(!m_pStateBlock) pDevice->CreateStateBlock(D3DSBT_ALL,&m_pStateBlock); @@ -74,12 +74,12 @@ void CDXScene::Begin(LPDIRECT3DDEVICE9 pDevice) m_pStateBlock->Capture(); } -void CDXScene::End(LPDIRECT3DDEVICE9 pDevice) +void CDXScene::End(ID3D11Device * pDevice) { m_pStateBlock->Apply(); } -void CDXScene::Render(LPDIRECT3DDEVICE9 pDevice) +void CDXScene::Render(ID3D11Device * pDevice) { CDXMatrix16 mWorld; CDXMatrix16 mView; @@ -217,7 +217,7 @@ bool CDXScene::Pick(int x, int y, CDXPicker & picker) const CDXMatrix matWorld = *g_Camera.GetWorldMatrix(); CDXMatrix mWorldView = matWorld * matView; CDXMatrix m; - D3DXMatrixInverse( &m, NULL, &mWorldView ); + DirectX::XMFLOAT4X4Inverse( &m, NULL, &mWorldView ); // Transform the screen space pick ray into 3D space rayInfo.direction.x = v.x * m._11 + v.y * m._21 + v.z * m._31; diff --git a/skee/CDXScene.h b/skee/CDXScene.h index 998fb82..dbc12b1 100644 --- a/skee/CDXScene.h +++ b/skee/CDXScene.h @@ -16,8 +16,8 @@ class CDXBrush; class CDXMaskAddBrush; class CDXInflateBrush; -typedef D3DXMATRIXA16 CDXMatrix16; -typedef D3DXMATRIX CDXMatrix; +typedef DirectX::XMFLOAT4X4A16 CDXMatrix16; +typedef DirectX::XMFLOAT4X4 CDXMatrix; typedef std::vector CDXMeshList; class CDXScene @@ -26,13 +26,13 @@ class CDXScene CDXScene(); ~CDXScene(); - virtual void Setup(LPDIRECT3DDEVICE9 pDevice); + virtual void Setup(ID3D11Device * pDevice); virtual void Release(); - virtual void Render(LPDIRECT3DDEVICE9 pDevice); + virtual void Render(ID3D11Device * pDevice); - virtual void Begin(LPDIRECT3DDEVICE9 pDevice); - virtual void End(LPDIRECT3DDEVICE9 pDevice); + virtual void Begin(ID3D11Device * pDevice); + virtual void End(ID3D11Device * pDevice); UInt32 GetNumMeshes() { return m_meshes.size(); } CDXMesh * GetNthMesh(UInt32 i) { return m_meshes.at(i); } diff --git a/skee/CDXShader.cpp b/skee/CDXShader.cpp index 2304b1c..2228712 100644 --- a/skee/CDXShader.cpp +++ b/skee/CDXShader.cpp @@ -29,7 +29,7 @@ void CDXShader::Release() } } -void CDXShader::CreateEffect(LPDIRECT3DDEVICE9 pDevice) +void CDXShader::CreateEffect(ID3D11Device * pDevice) { /*const char* g_strBuffer = "float3 g_vMaterialAmbient : Ambient = float3( 0.2f, 0.2f, 0.2f ); // Material's ambient color\r\n" "float3 g_vMaterialDiffuse : Diffuse = float3( 1.0f, 1.0f, 1.0f ); // Material's diffuse color\r\n" diff --git a/skee/CDXShader.h b/skee/CDXShader.h index 9405e5b..14f3e9c 100644 --- a/skee/CDXShader.h +++ b/skee/CDXShader.h @@ -11,7 +11,7 @@ class CDXShader { public: CDXShader(); - void CreateEffect(LPDIRECT3DDEVICE9 pDevice); + void CreateEffect(ID3D11Device * pDevice); void Release(); D3DXHANDLE m_hAmbient; diff --git a/skee/CDXTypes.h b/skee/CDXTypes.h new file mode 100644 index 0000000..48c6b97 --- /dev/null +++ b/skee/CDXTypes.h @@ -0,0 +1,94 @@ +#pragma once + +#include +#include +#include + +typedef DirectX::XMMATRIX CDXMatrix; +typedef unsigned short CDXMeshIndex; +typedef DirectX::XMVECTOR CDXVec; +typedef DirectX::XMFLOAT4 CDXVec4; +typedef DirectX::XMFLOAT3 CDXVec3; +typedef DirectX::XMFLOAT2 CDXVec2; +typedef DirectX::XMFLOAT3 CDXColor; + +typedef std::set CDXMeshIndexSet; +typedef std::map CDXHitIndexMap; + +struct CDXMeshEdge +{ + CDXMeshIndex p1; + CDXMeshIndex p2; + + CDXMeshEdge(CDXMeshIndex _p1, CDXMeshIndex _p2) + { + p1 = _p1; + p2 = _p2; + } +}; + +struct CDXMeshFace +{ + CDXMeshIndex v1; + CDXMeshIndex v2; + CDXMeshIndex v3; + + CDXMeshFace(CDXMeshIndex _v1, CDXMeshIndex _v2, CDXMeshIndex _v3) + { + v1 = _v1; + v2 = _v2; + v3 = _v3; + } +}; + +namespace std { + template<> struct hash < CDXMeshEdge > + { + std::size_t operator() (const CDXMeshEdge & t) const + { + return ((t.p2 << 16) | (t.p1 & 0xFFFF)); + } + + }; + + template <> struct equal_to < CDXMeshEdge > + { + bool operator() (const CDXMeshEdge& t1, const CDXMeshEdge& t2) const + { + return ((t1.p1 == t2.p1) && (t1.p2 == t2.p2)); + } + }; + + template <> struct hash < CDXMeshFace > { + std::size_t operator() (const CDXMeshFace& t) const { + char* d = (char*)&t; + size_t len = sizeof(CDXMeshFace); + size_t hash, i; + for (hash = i = 0; i < len; ++i) + { + hash += d[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; + } + }; + template <> struct equal_to < CDXMeshFace > + { + bool operator() (const CDXMeshFace& t1, const CDXMeshFace& t2) const + { + return ((t1.v1 == t2.v1) && (t1.v2 == t2.v2) && (t1.v3 == t2.v3)); + } + }; +} + +struct CDXMeshVert +{ + CDXVec3 Position; + CDXVec3 Normal; + CDXVec2 Tex; + CDXColor Color; +}; \ No newline at end of file diff --git a/skee/FaceMorphInterface.cpp b/skee/FaceMorphInterface.cpp new file mode 100644 index 0000000..7015068 --- /dev/null +++ b/skee/FaceMorphInterface.cpp @@ -0,0 +1,3247 @@ +#include "common/IFileStream.h" +#include "skse64_common/skse_version.h" + +#include "skse64/PluginAPI.h" + +#include "skse64/GameAPI.h" +#include "skse64/GameData.h" +#include "skse64/GameObjects.h" +#include "skse64/GameMenus.h" +#include "skse64/GameRTTI.h" +#include "skse64/GameResources.h" +#include "skse64/GameStreams.h" + +#include "skse64/NiNodes.h" +#include "skse64/NiGeometry.h" +#include "skse64/NiMaterial.h" +#include "skse64/NiProperties.h" +#include "skse64/NiExtraData.h" +#include "skse64/NiSerialization.h" + +#include "skse64/ScaleformMovie.h" + +#include "skse64/HashUtil.h" + +#include "FaceMorphInterface.h" +#include "PartHandler.h" +#include "ScaleformFunctions.h" +#include "NifUtils.h" +#include "SKEEHooks.h" + +#include "StringTable.h" + +#include "OverrideVariant.h" +#include "OverrideInterface.h" +#include "NiTransformInterface.h" +#include "BodyMorphInterface.h" +#include "OverlayInterface.h" + +extern OverrideInterface g_overrideInterface; +extern NiTransformInterface g_transformInterface; +extern BodyMorphInterface g_bodyMorphInterface; +extern OverlayInterface g_overlayInterface; +extern StringTable g_stringTable; + +#include +#include + +extern float g_sliderMultiplier; +extern float g_sliderInterval; +extern PartSet g_partSet; +extern FaceMorphInterface g_morphInterface; +extern std::string g_raceTemplate; +extern bool g_extendedMorphs; + +extern SKSEMessagingInterface * g_messaging; +extern PluginHandle g_pluginHandle; + +void RaceMap::Revert() +{ + clear(); +} + +void FaceMorphInterface::Revert() +{ + m_valueMap.clear(); + m_sculptStorage.clear(); +} + +void FaceMorphInterface::RevertInternals() +{ + m_internalMap.clear(); +} + +UInt32 FaceMorphInterface::GetVersion() +{ + return kSerializationVersion; +} + +bool SliderSet::for_each_slider(std::function func) +{ + // Iterate the list of SliderSet + for (auto rit = begin(); rit != end(); ++rit) + { + // Iterate the SliderMap + for (auto it = (*rit)->begin(); it != (*rit)->end(); ++it) + { + if (func(it->second)) + return true; + } + } + + return false; +} + +SliderSetPtr RaceMap::GetSliderSet(TESRace * race) +{ + RaceMap::iterator it = find(race); + if(it != end()) + return it->second; + + return NULL; +} + +SInt32 ReadTRIVertexCount(const char * triPath) +{ + if(triPath[0] == 0) { + return -1; + } + + char filePath[MAX_PATH]; + memset(filePath, 0, MAX_PATH); + sprintf_s(filePath, MAX_PATH, "Meshes\\%s", triPath); + BSFixedString newPath(filePath); + + // Cached file already exists, load it + BSResourceNiBinaryStream file(newPath.data); + if (!file.IsValid()) { + return -1; + } + + char header[0x08]; + file.Read(header, 0x08); + if(strncmp(header, "FRTRI003", 8) != 0) + return -1; + + UInt32 vertexNum = 0; + file.Read(&vertexNum, sizeof(vertexNum)); + return vertexNum; +} + + +bool TRIFile::Load(const char * triPath) +{ + if (triPath[0] == 0) { + return -1; + } + + char filePath[MAX_PATH]; + memset(filePath, 0, MAX_PATH); + sprintf_s(filePath, MAX_PATH, "Meshes\\%s", triPath); + BSFixedString newPath(filePath); + + BSResourceNiBinaryStream file(newPath.data); + if (!file.IsValid()) { + return false; + } + + char header[0x08]; + file.Read(header, 0x08); + if (strncmp(header, "FRTRI003", 8) != 0) + return false; + + file.Read(&vertexCount, sizeof(vertexCount)); + + UInt32 polytris = 0, polyquads = 0, unk2 = 0, unk3 = 0, + uvverts = 0, flags = 0, numMorphs = 0, numMods = 0, + modVerts = 0, unk7 = 0, unk8 = 0, unk9 = 0, unk10 = 0; + + file.Read(&polytris, sizeof(polytris)); + file.Read(&polyquads, sizeof(polyquads)); + file.Read(&unk2, sizeof(unk2)); + file.Read(&unk3, sizeof(unk3)); + file.Read(&uvverts, sizeof(uvverts)); + file.Read(&flags, sizeof(flags)); + file.Read(&numMorphs, sizeof(numMorphs)); + file.Read(&numMods, sizeof(numMods)); + file.Read(&modVerts, sizeof(modVerts)); + file.Read(&unk7, sizeof(unk7)); + file.Read(&unk8, sizeof(unk8)); + file.Read(&unk9, sizeof(unk9)); + file.Read(&unk10, sizeof(unk10)); + + // Skip reference verts + file.Seek(vertexCount * 3 * sizeof(float)); + + // Skip polytris + file.Seek(polytris * 3 * sizeof(UInt32)); + + // Skip UV + if (uvverts > 0) + file.Seek(uvverts * 2 * sizeof(float)); + + // Skip text coords + file.Seek(polytris * 3 * sizeof(UInt32)); + + for (UInt32 i = 0; i < numMorphs; i++) + { + UInt32 strLen = 0; + file.Read(&strLen, sizeof(strLen)); + + char * name = new char[strLen+1]; + for (UInt32 l = 0; l < strLen; l++) + { + file.Read(&name[l], sizeof(char)); + } + name[strLen] = 0; + + float mult = 0.0f; + file.Read(&mult, sizeof(mult)); + + Morph morph; + morph.name = name; + morph.multiplier = mult; + + for (UInt32 v = 0; v < vertexCount; v++) + { + Morph::Vertex vert; + file.Read(&vert, sizeof(vert)); + morph.vertices.push_back(vert); + } + + morphs.insert(std::make_pair(morph.name, morph)); + } + + return true; +} + +bool TRIFile::Apply(BSGeometry * geometry, SKEEFixedString morphName, float relative) +{ + BSFaceGenBaseMorphExtraData * extraData = (BSFaceGenBaseMorphExtraData *)geometry->GetExtraData("FOD"); + if (!extraData) + return false; + + // Found morph doesn't match the cached morph + if (extraData->vertexCount != vertexCount) + return false; + + // Morph name wasn't found + auto & morph = morphs.find(morphName); + if (morph == morphs.end()) + return false; + + // What? + if (extraData->vertexCount != morph->second.vertices.size()) + return false; + + UInt32 size = morph->second.vertices.size(); + for (UInt32 i = 0; i < size; i++) + { + auto & vert = morph->second.vertices.at(i); + extraData->vertexData[i].x += (float)((double)vert.x * (double)morph->second.multiplier * (double)relative); + extraData->vertexData[i].y += (float)((double)vert.y * (double)morph->second.multiplier * (double)relative); + extraData->vertexData[i].z += (float)((double)vert.z * (double)morph->second.multiplier * (double)relative); + } + + UpdateModelFace(geometry); + return true; +} + +class RacePartVisitor +{ +public: + virtual bool Accept(BGSHeadPart * headPart) = 0; +}; + +void VisitRaceParts(TESRace * race, UInt32 gender, RacePartVisitor & visitor) +{ + TESRace::CharGenData * chargenData = race->chargenData[gender]; + if(chargenData) { + tArray * headParts = chargenData->headParts; + if(headParts) { + for(UInt32 i = 0; i < headParts->count; i++) { + BGSHeadPart* headPart = NULL; + if(headParts->GetNthItem(i, headPart)) { + if(visitor.Accept(headPart)) + break; + } + } + } + } +} + +class RacePartByType : public RacePartVisitor +{ +public: + RacePartByType::RacePartByType(UInt32 partType) : m_type(partType), m_headPart(NULL) {} + virtual bool Accept(BGSHeadPart * headPart) + { + if(headPart->type == m_type) { + m_headPart = headPart; + return true; + } + + return false; + } + + UInt32 m_type; + BGSHeadPart * m_headPart; +}; + +class RacePartDefaultGen : public RacePartVisitor +{ +public: + RacePartDefaultGen::RacePartDefaultGen(TESRace * sourceRace, TESRace * targetRace, std::vector * parts, UInt32 gender) : m_sourceRace(sourceRace), m_targetRace(targetRace), m_gender(gender), m_partList(parts), m_acceptDefault(false) {} + + virtual bool Accept(BGSHeadPart * headPart) + { + BSFixedString sourceMorphPath(headPart->chargenMorph.GetModelName()); + if(sourceMorphPath == BSFixedString("")) + return false; + + auto it = std::find(m_partList->begin(), m_partList->end(), sourceMorphPath); + if (it != m_partList->end()) { // Found part tri file + if(headPart->type == BGSHeadPart::kTypeFace) + m_acceptDefault = true; + } else { + RacePartByType racePart(headPart->type); + VisitRaceParts(m_targetRace, m_gender, racePart); + + BGSHeadPart * targetPart = racePart.m_headPart; + if(targetPart) { + BSFixedString targetMorphPath(targetPart->chargenMorph.GetModelName()); + if(targetMorphPath == BSFixedString("")) { + _ERROR("%s - Could not bind default morphs for %s on %s[%d] using %s. No valid morph path.", __FUNCTION__, headPart->partName.data, m_sourceRace->editorId.data, m_gender, m_targetRace->editorId.data, sourceMorphPath.data); + return false; + } + + TRIModelData sourceData, targetData; + g_morphInterface.GetModelTri(sourceMorphPath, sourceData); + g_morphInterface.GetModelTri(targetMorphPath, targetData); + + if (sourceData.vertexCount == targetData.vertexCount && sourceData.vertexCount > 0 && targetData.vertexCount > 0) { + // Bind additional morphs here, the source and target morphs are identical + _DMESSAGE("%s - Binding default morphs for %s (%s) on %s[%d] using %s. (%s - %d | %s - %d)", __FUNCTION__, headPart->partName.data, sourceMorphPath.data, m_sourceRace->editorId.data, m_gender, m_targetRace->editorId.data, sourceMorphPath.data, sourceData.vertexCount, targetMorphPath.data, targetData.vertexCount); + auto titer = g_morphInterface.m_morphMap.find(targetMorphPath); + if(titer != g_morphInterface.m_morphMap.end()) + g_morphInterface.m_morphMap.emplace(sourceMorphPath, titer->second); + if(headPart->type == BGSHeadPart::kTypeFace) + m_acceptDefault = true; + } else if(sourceData.vertexCount == 0 || targetData.vertexCount == 0) { + _ERROR("%s - Could not bind default morphs for %s on %s[%d] using %s. Invalid vertex count (%s - %d | %s - %d).", __FUNCTION__, headPart->partName.data, m_sourceRace->editorId.data, m_gender, m_targetRace->editorId.data, sourceMorphPath.data, sourceData.vertexCount, targetMorphPath.data, targetData.vertexCount); + } else { + _ERROR("%s - Could not bind default morphs for %s on %s[%d] using %s. Vertex mismatch (%s - %d | %s - %d).", __FUNCTION__, headPart->partName.data, m_sourceRace->editorId.data, m_gender, m_targetRace->editorId.data, sourceMorphPath.data, sourceData.vertexCount, targetMorphPath.data, targetData.vertexCount); + } + } + } + + return false; + } + + UInt32 m_gender; + TESRace * m_sourceRace; + TESRace * m_targetRace; + std::vector * m_partList; + bool m_acceptDefault; +}; + +class RacePartFiles : public RacePartVisitor +{ +public: + RacePartFiles::RacePartFiles(std::vector * parts) : m_parts(parts) {} + virtual bool Accept(BGSHeadPart * headPart) + { + if (headPart->chargenMorph.name.data != BSFixedString("").data) + m_parts->push_back(headPart->chargenMorph.name); + if (headPart->raceMorph.name.data != BSFixedString("").data) + m_parts->push_back(headPart->raceMorph.name); + + return false; + } + + std::vector * m_parts; +}; + +bool RaceMap::CreateDefaultMap(TESRace * race) +{ + TESRace * templateRace = GetRaceByName(g_raceTemplate); + RaceMap::iterator rit = find(templateRace); + if(rit != end()) { // Found NordRace slider maps + + std::vector templateFiles; + for (UInt32 gender = 0; gender <= 1; gender++) { + RacePartFiles parts(&templateFiles); + VisitRaceParts(templateRace, gender, parts); + } + + bool acceptDefault = false; + for(UInt32 gender = 0; gender <= 1; gender++) { // Iterate genders + RacePartDefaultGen defaultGen(race, templateRace, &templateFiles, gender); + VisitRaceParts(race, gender, defaultGen); + acceptDefault = defaultGen.m_acceptDefault; + } + + if(acceptDefault) { + SliderSetPtr sliderMaps = rit->second; + if(sliderMaps->size() > 0) { + UInt32 addedMaps = 0; + for(auto smit = sliderMaps->begin(); smit != sliderMaps->end(); ++smit) { + if(AddSliderMap(race, *smit)) + addedMaps++; + } + if(addedMaps > 0) { + _DMESSAGE("%s - Added default slider maps for %s from %s", __FUNCTION__, race->editorId.data, rit->first->editorId.data); + return true; + } + } + } + } + + return false; +} + +bool RaceMap::AddSliderMap(TESRace * race, SliderMapPtr sliderMap) +{ + RaceMap::iterator it = find(race); + if(it != end()) { + //std::pair ret; + auto ret = it->second->insert(sliderMap); + return ret.second; + } else { + SliderSetPtr sliderMaps = std::make_shared(); + sliderMaps->insert(sliderMap); + emplace(race, sliderMaps); + return true; + } + + return false; +} + +SliderInternalPtr FaceMorphInterface::GetSliderByIndex(TESRace * race, UInt32 index) +{ + RaceSliders::iterator it = m_internalMap.find(race); + if(it != m_internalMap.end()) { + if(index < it->second.size()) + return it->second.at(index); + } + return NULL; +} + +float ValueMap::GetMorphValueByName(TESNPC* npc, SKEEFixedString name) +{ + ValueMap::iterator it = find(npc); + if(it != end()) { + return it->second.GetValue(name); + } + + return 0.0; +} + +void ValueMap::SetMorphValue(TESNPC* npc, SKEEFixedString name, float value) +{ + ValueMap::iterator it = find(npc); + if(it != end()) { + it->second.SetValue(name, value); + } else { + ValueSet newSet; + newSet.emplace(g_stringTable.GetString(name), value); + emplace(npc, newSet); + } +} + +void ValueSet::SetValue(SKEEFixedString name, float value) +{ + ValueSet::iterator val = find(g_stringTable.GetString(name)); + if (val != end()) + val->second = value; + else + emplace(g_stringTable.GetString(name), value); +} + +void ValueSet::ClearValue(SKEEFixedString name) +{ + ValueSet::iterator val = find(g_stringTable.GetString(name)); + if(val != end()) + erase(val); +} + +float ValueSet::GetValue(SKEEFixedString name) +{ + ValueSet::iterator val = find(g_stringTable.GetString(name)); + if(val != end()) + return val->second; + + return 0.0; +} + +ValueSet * ValueMap::GetValueSet(TESNPC* npc) +{ + ValueMap::iterator it = find(npc); + if(it != end()) { + return &it->second; + } + + return NULL; +} + +void ValueMap::EraseNPC(TESNPC * npc) +{ + auto it = find(npc); + if (it != end()) + erase(it); +} + +float FaceMorphInterface::GetMorphValueByName(TESNPC* npc, SKEEFixedString name) +{ + return m_valueMap.GetMorphValueByName(npc, name); +} + +void FaceMorphInterface::SetMorphValue(TESNPC* npc, SKEEFixedString name, float value) +{ + return m_valueMap.SetMorphValue(npc, name, value); +} + +void MorphMap::Revert() +{ + clear(); +} + +class ExtendedMorphCache : public MorphMap::Visitor +{ +public: + virtual bool Accept(SKEEFixedString morphName) + { + g_morphInterface.GetExtendedModelTri(morphName); + return false; + } +}; + +void FaceMorphInterface::LoadMods() +{ + DataHandler * dataHandler = DataHandler::GetSingleton(); + if (dataHandler) + { + ForEachMod([&](ModInfo * modInfo) + { + std::string fixedPath = "Meshes\\"; + fixedPath.append(SLIDER_MOD_DIRECTORY); + std::string modPath = modInfo->name; + modPath.append("\\"); + + ReadRaces(fixedPath, modPath, "races.ini"); + if (g_extendedMorphs) + ReadMorphs(fixedPath, modPath, "morphs.ini"); + + ReadPartReplacements(fixedPath, modPath, "replacements.ini"); + }); + + if (g_extendedMorphs) { + BGSHeadPart * part = NULL; + for (UInt32 i = 0; i < dataHandler->headParts.count; i++) + { + if (dataHandler->headParts.GetNthItem(i, part)) { + if (CacheHeadPartModel(part)) { + + BSFixedString key = part->chargenMorph.GetModelName(); + + // Cache all of the extended morphs + ExtendedMorphCache extendedCache; + VisitMorphMap(key, extendedCache); + } + } + } + } + + // Create default slider maps + TESRace * race = NULL; + for (UInt32 i = 0; i < dataHandler->races.count; i++) + { + if (dataHandler->races.GetNthItem(i, race)) { + +#ifdef FIXME + if (g_allowAllMorphs) { + for (UInt32 i = 0; i <= 1; i++) { + if (race->chargenData[i]) { + for (UInt32 t = 0; t < FacePresetList::kNumPresets; t++) { + const char * gameSetting = FacePresetList::GetSingleton()->presets[t].data->gameSettingName; + race->chargenData[i]->presetFlags[t][0] = 0xFFFFFFFF; + race->chargenData[i]->presetFlags[t][1] = 0xFFFFFFFF; + race->chargenData[i]->totalPresets[t] = GetGameSettingInt(gameSetting); + } + } + } + } +#endif + if ((race->data.raceFlags & TESRace::kRace_FaceGenHead) == TESRace::kRace_FaceGenHead) + m_raceMap.CreateDefaultMap(race); + } + } + } +} + +bool FaceMorphInterface::CacheHeadPartModel(BGSHeadPart * headPart, bool cacheTRI) +{ + BSFixedString modelPath = headPart->chargenMorph.GetModelName(); + if (modelPath == BSFixedString("")) + return false; + + ModelMap::iterator it = m_modelMap.find(modelPath); + if (it == m_modelMap.end()) { + TRIModelData data; + + data.morphModel = &headPart->chargenMorph; + if (!cacheTRI) { + data.vertexCount = ReadTRIVertexCount(modelPath.data); + } + else { + data.triFile = std::make_shared(); + data.triFile->Load(modelPath.data); + data.vertexCount = data.triFile->vertexCount; + } + + m_modelMap.emplace(modelPath, data); + } + else if (cacheTRI && !it->second.triFile) { + it->second.triFile = std::make_shared(); + it->second.triFile->Load(modelPath.data); + } + + return true; +} + +bool FaceMorphInterface::GetModelTri(SKEEFixedString filePath, TRIModelData & modelData) +{ + ModelMap::iterator it = m_modelMap.find(filePath); + if (it != m_modelMap.end()) { + modelData = it->second; + return true; + } + + return false; +} + +TRIModelData & FaceMorphInterface::GetExtendedModelTri(SKEEFixedString morphName, bool cacheTRI) +{ + std::string filePath(SLIDER_DIRECTORY); + filePath.append(morphName.c_str()); + SKEEFixedString morphFile(filePath.c_str()); + ModelMap::iterator it = m_modelMap.find(morphFile); + if(it == m_modelMap.end()) { + void* memory = Heap_Allocate(sizeof(TESModelTri)); + memset(memory, 0, sizeof(TESModelTri)); + ((uintptr_t*)memory)[0] = TESModelTri_vtbl.GetUIntPtr(); + TESModelTri* xData = (TESModelTri*)memory; + xData->SetModelName(morphFile.c_str()); + + TRIModelData data; + data.morphModel = xData; + if (!cacheTRI) { + data.vertexCount = ReadTRIVertexCount(morphFile.c_str()); + } + else { + data.triFile = std::make_shared(); + data.triFile->Load(morphFile.c_str()); + data.vertexCount = data.triFile->vertexCount; + } + + auto ret = m_modelMap.emplace(morphFile, data); + return ret.first->second; + } + else if(cacheTRI && !it->second.triFile) { + it->second.triFile = std::make_shared(); + it->second.triFile->Load(morphFile.c_str()); + } + + return it->second; +} + +PresetDataPtr FaceMorphInterface::GetPreset(TESNPC* npc) +{ + auto it = m_mappedPreset.find(npc); + if (it != m_mappedPreset.end()) + return it->second; + + return NULL; +} + +void FaceMorphInterface::AssignPreset(TESNPC * npc, PresetDataPtr presetData) +{ + ErasePreset(npc); + m_mappedPreset.emplace(npc, presetData); +} + +bool FaceMorphInterface::ErasePreset(TESNPC * npc) +{ + auto it = m_mappedPreset.find(npc); + if (it != m_mappedPreset.end()) { + m_mappedPreset.erase(it); + return true; + } + + return false; +} + +void FaceMorphInterface::ClearPresets() +{ + m_mappedPreset.clear(); +} + +void FaceMorphInterface::ApplyPreset(TESNPC * npc, BSFaceGenNiNode * faceNode, BGSHeadPart * headPart) +{ + PresetDataPtr presetData = GetPreset(npc); + if (presetData && faceNode && headPart) { + NiAVObject * object = faceNode->GetObjectByName(&headPart->partName.data); + if (object) { + BSGeometry * geometry = object->GetAsBSGeometry(); + if (geometry) { + BSShaderProperty * shaderProperty = niptr_cast(geometry->m_spEffectState); + if (shaderProperty) { + if (ni_is_type(shaderProperty->GetRTTI(), BSLightingShaderProperty)) { + BSLightingShaderProperty * lightingShader = (BSLightingShaderProperty *)shaderProperty; + BSLightingShaderMaterial * material = (BSLightingShaderMaterial *)shaderProperty->material; + if (headPart->type == BGSHeadPart::kTypeFace) { + for (auto & texture : presetData->faceTextures) + material->textureSet->SetTexturePath(texture.index, texture.name.AsBSFixedString().c_str()); + material->textureSet->SetTexturePath(6, presetData->tintTexture.data); + material->ReleaseTextures(); + CALL_MEMBER_FN(lightingShader, InvalidateTextures)(0); + CALL_MEMBER_FN(lightingShader, InitializeShader)(geometry); + } + else if (material->GetShaderType() == BSLightingShaderMaterial::kShaderType_HairTint) { + BSLightingShaderMaterialHairTint * tintedMaterial = (BSLightingShaderMaterialHairTint *)material; // I don't know what this * 2.0 bullshit is. + tintedMaterial->tintColor.r = (((presetData->hairColor >> 16) & 0xFF) / 255.0) * 2.0; + tintedMaterial->tintColor.g = (((presetData->hairColor >> 8) & 0xFF) / 255.0) * 2.0; + tintedMaterial->tintColor.b = ((presetData->hairColor & 0xFF) / 255.0) * 2.0; + } + } + } + } + } + } +} + +class ValidRaceFinder : public BGSListForm::Visitor +{ +public: + ValidRaceFinder::ValidRaceFinder(TESRace * race) : m_race(race) { } + virtual bool Accept(TESForm * form) + { + if (m_race == form) + return true; + + return false; + }; + TESRace * m_race; +}; + +void FaceMorphInterface::ApplyPresetData(Actor * actor, PresetDataPtr presetData, bool setSkinColor, ApplyTypes applyType) +{ + PlayerCharacter * player = (*g_thePlayer); + TESNPC * npc = DYNAMIC_CAST(actor->baseForm, TESForm, TESNPC); + TESRace * race = npc->race.race; + + // Wipe the HeadPart list and replace it with the default race list + UInt8 gender = CALL_MEMBER_FN(npc, GetSex)(); + TESRace::CharGenData * chargenData = race->chargenData[gender]; + if (chargenData) { + BGSHeadPart ** headParts = npc->headparts; + tArray * headPartList = race->chargenData[gender]->headParts; + if (headParts && headPartList) { + Heap_Free(headParts); + npc->numHeadParts = headPartList->count; + headParts = (BGSHeadPart **)Heap_Allocate(npc->numHeadParts * sizeof(BGSHeadPart*)); + for (UInt32 i = 0; i < headPartList->count; i++) + headPartList->GetNthItem(i, headParts[i]); + npc->headparts = headParts; + } + } + + // Replace the old parts with the new parts if they are the right sex + for (auto & part : presetData->headParts) { + if ((gender == 0 && (part->partFlags & BGSHeadPart::kFlagMale) == BGSHeadPart::kFlagMale) || + (gender == 1 && (part->partFlags & BGSHeadPart::kFlagFemale) == BGSHeadPart::kFlagFemale)) + { + ValidRaceFinder partFinder(race); + if (part->validRaces) { + if (part->validRaces->Visit(partFinder)) + CALL_MEMBER_FN(npc, ChangeHeadPart)(part); + } + } + } + + npc->weight = presetData->weight; + + if (!npc->faceMorph) + npc->faceMorph = (TESNPC::FaceMorphs*)Heap_Allocate(sizeof(TESNPC::FaceMorphs)); + + UInt32 i = 0; + for (auto value : presetData->presets) { + npc->faceMorph->presets[i] = value; + i++; + } + + i = 0; + for (auto value : presetData->morphs) { + npc->faceMorph->option[i] = value; + i++; + } + + for (auto & tint : presetData->tints) { + float alpha = (tint.color >> 24) / 255.0; + TintMask * tintMask = NULL; + if (player == actor && player->tintMasks.GetNthItem(tint.index, tintMask)) { + tintMask->color.red = (tint.color >> 16) & 0xFF; + tintMask->color.green = (tint.color >> 8) & 0xFF; + tintMask->color.blue = tint.color & 0xFF; + tintMask->alpha = alpha; + if (tintMask->alpha > 0) + tintMask->texture->str = tint.name; + } + + if (tint.index == 0 && setSkinColor) + { + float alpha = (tint.color >> 24) / 255.0; + TintMask tintMask; + tintMask.color.red = (tint.color >> 16) & 0xFF; + tintMask.color.green = (tint.color >> 8) & 0xFF; + tintMask.color.blue = tint.color & 0xFF; + tintMask.alpha = alpha; + tintMask.tintType = TintMask::kMaskType_SkinTone; + + NiColorA colorResult; + CALL_MEMBER_FN(npc, SetSkinFromTint)(&colorResult, &tintMask, 1, 0); + } + } + + g_morphInterface.EraseSculptData(npc); + if (presetData->sculptData) { + if (presetData->sculptData->size() > 0) { + g_morphInterface.SetSculptTarget(npc, presetData->sculptData); + } + } + + g_morphInterface.EraseMorphData(npc); + for (auto & morph : presetData->customMorphs) + g_morphInterface.SetMorphValue(npc, morph.name, morph.value); + + g_overrideInterface.RemoveAllReferenceNodeOverrides(actor); + g_overlayInterface.RevertOverlays(actor, true); + + if ((applyType & kPresetApplyOverrides) == kPresetApplyOverrides) + { + for (auto & nodes : presetData->overrideData) { + for (auto & value : nodes.second) { + g_overrideInterface.AddNodeOverride(actor, gender == 1 ? true : false, nodes.first, value); + } + } + } + + if ((applyType & kPresetApplySkinOverrides) == kPresetApplySkinOverrides) + { + for (UInt32 i = 0; i <= 1; i++) { + for (auto & slot : presetData->skinData[i]) { + for (auto & value : slot.second) { + g_overrideInterface.AddSkinOverride(actor, gender == 1 ? true : false, i == 1 ? true : false, slot.first, value); + } + } + } + } + + g_transformInterface.RemoveAllReferenceTransforms(actor); + + if ((applyType & kPresetApplyTransforms) == kPresetApplyTransforms) + { + for (UInt32 i = 0; i <= 1; i++) { + for (auto & xForms : presetData->transformData[i]) { + for (auto & key : xForms.second) { + for (auto & value : key.second) { + g_transformInterface.AddNodeTransform(actor, i == 1 ? true : false, gender == 1 ? true : false, xForms.first, key.first, value); + } + } + } + } + } + g_transformInterface.UpdateNodeAllTransforms(actor); + + + g_bodyMorphInterface.ClearMorphs(actor); + + if ((applyType & kPresetApplyBodyMorphs) == kPresetApplyBodyMorphs) + { + for (auto & morph : presetData->bodyMorphData) { + for (auto & keys : morph.second) + g_bodyMorphInterface.SetMorph(actor, morph.first, keys.first, keys.second); + } + } + + g_bodyMorphInterface.UpdateModelWeight(actor); +} + +bool MorphMap::Visit(SKEEFixedString key, Visitor & visitor) +{ + MorphMap::iterator it = find(key); + if(it != end()) + { +#ifdef _DEBUG_MORPHAPPLICATOR + _DMESSAGE("%s - Applying %d additional morphs to %s", __FUNCTION__, it->second.size(), key.data); + gLog.Indent(); +#endif + for(auto iter = it->second.begin(); iter != it->second.end(); ++iter) + { +#ifdef _DEBUG_MORPHAPPLICATOR + _DMESSAGE("%s - Visting %s", __FUNCTION__, (*iter).data); +#endif + if(visitor.Accept(*iter)) + break; + } +#ifdef _DEBUG_MORPHAPPLICATOR + gLog.Outdent(); +#endif + return true; + } +#ifdef _DEBUG_MORPHAPPLICATOR + else { + _DMESSAGE("%s - No additional morphs for %s", __FUNCTION__, key.data); + } +#endif + + return false; +} + + + +bool FaceMorphInterface::VisitMorphMap(SKEEFixedString key, MorphMap::Visitor & visitor) +{ + //key = toLower(key); + return m_morphMap.Visit(key, visitor); +} + +void MorphMap::AddMorph(SKEEFixedString key, SKEEFixedString value) +{ + //key = toLower(key); + MorphMap::iterator it = find(key); + if(it != end()) { + if (std::find(it->second.begin(), it->second.end(), value) == it->second.end()) + it->second.push_back(value); + } else { + MorphSet firstSet; + firstSet.push_back(value); + emplace(key, firstSet); + } +} + +void FaceMorphInterface::ReadMorphs(std::string fixedPath, std::string modName, std::string fileName) +{ + std::string fullPath = fixedPath + modName + fileName; + BSResourceNiBinaryStream file(fullPath.c_str()); + if (!file.IsValid()) { + return; + } + + UInt32 lineCount = 0; + std::string str = ""; + while(BSReadLine(&file, &str)) + { + lineCount++; + str = std::trim(str); + if(str.length() == 0) + continue; + if(str.at(0) == '#') + continue; + + std::vector side = explode(str, '='); + if(side.size() < 2) { + _ERROR("ReadMorphs Error - Line (%d) loading a morph from %s has no left-hand side.", lineCount, fullPath.c_str()); + continue; + } + + std::string lSide = std::trim(side[0]); + std::string rSide = std::trim(side[1]); + + if(_strnicmp(lSide.c_str(), "extension", 9) != 0) { + _ERROR("ReadMorphs Error - Line (%d) loading a morph from %s invalid left-hand side.", lineCount, fullPath.c_str()); + continue; + } + + std::vector params = explode(rSide, ','); + if(params.size() < 2) { + _ERROR("ReadMorphs Error - Line (%d) slider %s from %s has less than 2 parameters.", lineCount, lSide.c_str(), fullPath.c_str()); + continue; + } + + // Trim all parameters + for(UInt32 i = 0; i < params.size(); i++) + params[i] = std::trim(params[i]); + + std::string key = params[0]; + for(UInt32 i = 1; i < params.size(); i++) { +#ifdef _DEBUG_DATAREADER + _DMESSAGE("ReadMorphs Info - Line (%d) added %s morph to %s from %s.", lineCount, params[i].c_str(), key.c_str(), fullPath.c_str()); +#endif + m_morphMap.AddMorph(SKEEFixedString(key.c_str()), SKEEFixedString(params[i].c_str())); + } + } +} + +void FaceMorphInterface::ReadRaces(std::string fixedPath, std::string modPath, std::string fileName) +{ + std::string fullPath = fixedPath + modPath + fileName; + BSResourceNiBinaryStream file(fullPath.c_str()); + if (!file.IsValid()) { + return; + } + + std::map fileMap; + + UInt32 lineCount = 0; + std::string str = ""; + while(BSReadLine(&file, &str)) + { + lineCount++; + str = std::trim(str); + if(str.length() == 0) + continue; + if(str.at(0) == '#') + continue; + + std::vector side = explode(str, '='); + if(side.size() < 2) { + _ERROR("ReadRaces Error - Line (%d) loading a race from %s has insufficient parameters.", lineCount, fullPath.c_str()); + continue; + } + + std::string lSide = std::trim(side[0]); + std::string rSide = std::trim(side[1]); + + std::vector files = explode(rSide, ','); + for(UInt32 i = 0; i < files.size(); i++) + files[i] = std::trim(files[i]); + + for(UInt32 i = 0; i < files.size(); i++) + { + std::string pathOverride = modPath; + if(files[i].at(0) == ':') { + pathOverride = ""; + files[i].erase(0, 1); + } + + SliderMapPtr sliderMap = NULL; + std::map::iterator it = fileMap.find(files[i]); + if(it != fileMap.end()) { + sliderMap = it->second; + } else { + sliderMap = ReadSliders(fixedPath, pathOverride, files[i]); + if(sliderMap) { + fileMap.emplace(files[i], sliderMap); + } else { + _ERROR("ReadRaces Error - Line (%d) failed to load slider map for %s from %s.", lineCount, lSide.c_str(), fullPath.c_str()); + } + } + + if(sliderMap) { +#ifdef _DEBUG_DATAREADER + _DMESSAGE("ReadRaces Info - Line (%d) Loaded %s for Race %s from %s.", lineCount, files[i].c_str(), lSide.c_str(), fullPath.c_str()); +#endif + + TESRace * race = GetRaceByName(lSide); + if(race) + m_raceMap.AddSliderMap(race, sliderMap); + } + } + } +} + +void SliderMap::AddSlider(SKEEFixedString key, UInt8 gender, SliderInternal & sliderInternal) +{ + SliderMap::iterator it = find(key); + if(it != end()) { + it->second->slider[gender] = std::make_shared(); + it->second->slider[gender]->copy(&sliderInternal); + } else { + SliderGenderPtr sliderGender = std::make_shared(); + sliderGender->slider[gender] = std::make_shared(); + sliderGender->slider[gender]->copy(&sliderInternal); + emplace(key, sliderGender); + } +} + +SliderMapPtr FaceMorphInterface::ReadSliders(std::string fixedPath, std::string modPath, std::string fileName) +{ + SliderMapPtr sliderMap = NULL; + std::string fullPath = fixedPath + modPath + fileName; + BSResourceNiBinaryStream file(fullPath.c_str()); + if (!file.IsValid()) { + return NULL; + } + + sliderMap = std::make_shared(); + + UInt8 gender = 0; + UInt32 lineCount = 0; + std::string str = ""; + while(BSReadLine(&file, &str)) + { + lineCount++; + str = std::trim(str); + if(str.length() == 0) + continue; + if(str.at(0) == '#') + continue; + + if(str.at(0) == '[') + { + str.erase(0, 1); + if(_strnicmp(str.c_str(), "Male", 4) == 0) + gender = 0; + if(_strnicmp(str.c_str(), "Female", 6) == 0) + gender = 1; + continue; + } + + std::vector side = explode(str, '='); + if(side.size() < 2) { + _ERROR("ReadSliders Error - Line (%d) slider from %s has no left-hand side.", lineCount, fullPath.c_str()); + continue; + } + + std::string lSide = std::trim(side[0]); + std::string rSide = std::trim(side[1]); + + std::vector params = explode(rSide, ','); + if(params.size() < 3) { + _ERROR("ReadSliders Error - Line (%d) slider %s from %s has less than 3 parameters.", lineCount, lSide.c_str(), fullPath.c_str()); + continue; + } + + // Trim all parameters + for(UInt32 i = 0; i < params.size(); i++) + params[i] = std::trim(params[i]); + + SKEEFixedString sliderName = SKEEFixedString(lSide.c_str()); + SliderInternal sliderInternal; + sliderInternal.name = sliderName; + sliderInternal.category = atoi(params.at(0).c_str()); + if(sliderInternal.category == -1) + sliderInternal.category = SliderInternal::kCategoryExtra; + switch(sliderInternal.category) + { + case SliderInternal::kCategoryExpressions: + case SliderInternal::kCategoryExtra: + case SliderInternal::kCategoryBody: + case SliderInternal::kCategoryHead: + case SliderInternal::kCategoryFace: + case SliderInternal::kCategoryEyes: + case SliderInternal::kCategoryBrow: + case SliderInternal::kCategoryMouth: + case SliderInternal::kCategoryHair: + break; + default: + _ERROR("ReadSliders Error - Line (%d) loading slider %s from %s has invalid category (%d).", lineCount, lSide.c_str(), fullPath.c_str(), sliderInternal.category); + continue; + break; + } + if(_strnicmp(params[1].c_str(), "Slider", 6) == 0) { + sliderInternal.type = SliderInternal::kTypeSlider; + } else if(_strnicmp(params[1].c_str(), "Preset", 6) == 0) { + sliderInternal.type = SliderInternal::kTypePreset; + } else if(_strnicmp(params[1].c_str(), "HeadPart", 8) == 0) { + sliderInternal.type = SliderInternal::kTypeHeadPart; + } else { + _ERROR("ReadSliders Error - Line (%d) loading slider %s from %s has invalid slider type (%s).", lineCount, lSide.c_str(), fullPath.c_str(), params[1].c_str()); + continue; + } + switch(sliderInternal.type) + { + case SliderInternal::kTypeSlider: + { + // Additional morphs are disabled + if (!g_extendedMorphs) + continue; + + if(params.size() < 4) { + _ERROR("ReadSliders Error - Line (%d) slider %s from %s has less than 4 parameters.", lineCount, lSide.c_str(), fullPath.c_str()); + continue; + } + + sliderInternal.lowerBound = SKEEFixedString(params[2].c_str()); + sliderInternal.upperBound = SKEEFixedString(params[3].c_str()); + + if(sliderInternal.lowerBound == SKEEFixedString("None")) + sliderInternal.lowerBound = SKEEFixedString(""); + if(sliderInternal.upperBound == SKEEFixedString("None")) + sliderInternal.upperBound = SKEEFixedString(""); + } + break; + case SliderInternal::kTypePreset: + { + // Additional morphs are disabled + if (!g_extendedMorphs) + continue; + + if (params.size() < 4) { + _ERROR("ReadSliders Error - Line (%d) slider %s from %s has less than 4 parameters.", lineCount, lSide.c_str(), fullPath.c_str()); + continue; + } + + sliderInternal.lowerBound = SKEEFixedString(params[2].c_str()); + UInt32 presetCount = atoi(params[3].c_str()); + if (presetCount > 255) { + presetCount = 255; + _WARNING("ReadSliders Warning - Line (%d) loading slider %s from %s has exceeded a preset count of %d.", lineCount, lSide.c_str(), fullPath.c_str(), presetCount); + } + sliderInternal.presetCount = presetCount; + + } + break; + case SliderInternal::kTypeHeadPart: + { + sliderInternal.presetCount = atoi(params[2].c_str()); + } + break; + } +#ifdef _DEBUG_DATAREADER + _DMESSAGE("ReadSliders Info - Line (%d) Added Slider (%s, %d, %d, %d, %s, %s) to Gender %d %s from %s.", lineCount, sliderInternal.name.data, sliderInternal.category, sliderInternal.type, sliderInternal.presetCount, sliderInternal.lowerBound.data, sliderInternal.upperBound.data, gender, lSide.c_str(), fullPath.c_str()); +#endif + sliderMap->AddSlider(sliderName, gender, sliderInternal); + } + + return sliderMap; +} + +#ifdef _DEBUG_DATADUMP +void SliderMap::DumpMap() +{ + for(SliderMap::iterator it = begin(); it != end(); ++it) + { + SliderGender * gender = it->second; + if(gender->slider[0]) + _DMESSAGE("Slider - Name: %s Gender: Male Type: %d Cat: %d LowerBound: %s UpperBound: %s PresetCount: %d", it->first.data, gender->slider[0]->type, gender->slider[0]->category, gender->slider[0]->lowerBound.data, gender->slider[0]->upperBound.data, gender->slider[0]->presetCount); + if(gender->slider[1]) + _DMESSAGE("Slider - Name: %s Gender: Female Type: %d Cat: %d LowerBound: %s UpperBound: %s PresetCount: %d", it->first.data, gender->slider[1]->type, gender->slider[1]->category, gender->slider[1]->lowerBound.data, gender->slider[1]->upperBound.data, gender->slider[1]->presetCount); + } +} + +void RaceMap::DumpMap() +{ + for(RaceMap::iterator it = begin(); it != end(); ++it) + { + _DMESSAGE("Race: %08X - %s", it->first->formID, it->first->editorId.data); + gLog.Indent(); + for(SliderSet::iterator sit = it->second.begin(); sit != it->second.end(); ++sit) + { + _DMESSAGE("Map: %08X", (*sit)); + (*sit)->DumpMap(); + } + gLog.Outdent(); + } +} + +void MorphMap::DumpMap() +{ + for(MorphMap::iterator it = begin(); it != end(); ++it) + { + _DMESSAGE("Morph: %s", it->first.data); + DumpVisitor visitor; + gLog.Indent(); + Visit(it->first, visitor); + gLog.Outdent(); + } +} + +void MorphHandler::DumpAll() +{ + m_raceMap.DumpMap(); + m_morphMap.DumpMap(); +} +#endif + +SliderInternalPtr FaceMorphInterface::GetSlider(TESRace * race, UInt8 gender, SKEEFixedString name) +{ + SliderSetPtr sliderMaps = m_raceMap.GetSliderSet(race); + if(sliderMaps) + { + SliderInternalPtr sliderInternal; + sliderMaps->for_each_slider([&](SliderGenderPtr genders) { + sliderInternal = genders->slider[gender]; + if (sliderInternal && sliderInternal->name == name) + return true; + return false; + }); + if (sliderInternal) + return sliderInternal; + } + + return NULL; +} + + +bool sortFixedStrings(SliderInternalPtr s1, SliderInternalPtr s2) +{ + return s1->name < s2->name; +} + +SliderList * FaceMorphInterface::CreateSliderList(TESRace * race, UInt8 gender) +{ + // Clear the old map before building the new + RaceSliders::iterator it = m_internalMap.find(race); + if(it != m_internalMap.end()) { + it->second.clear(); + } + + SliderSetPtr sliderMaps = m_raceMap.GetSliderSet(race); + if(sliderMaps) + { + sliderMaps->for_each_slider([&](SliderGenderPtr genders) { + SliderInternalPtr sliderInternal = genders->slider[gender]; + if (sliderInternal) + AddSlider(race, sliderInternal); + return false; + }); + } + + // Return the list + it = m_internalMap.find(race); + if(it != m_internalMap.end()) { + std::sort(it->second.begin(), it->second.end(), sortFixedStrings); + it->second.resize(it->second.size()); + return &it->second; + } + + return NULL; +} + +void FaceMorphInterface::AddSlider(TESRace * race, SliderInternalPtr & slider) +{ + RaceSliders::iterator it = m_internalMap.find(race); + if(it != m_internalMap.end()) { + it->second.push_back(slider); + } else { + SliderList newList; + newList.push_back(slider); + m_internalMap.emplace(race, newList); + } +} + +void FaceMorphInterface::ApplyMorph(TESNPC * npc, BGSHeadPart * headPart, BSFaceGenNiNode * faceNode) +{ + char buffer[MAX_PATH]; + + auto sculptTarget = GetSculptTarget(npc, false); + if (sculptTarget) { + if (headPart) { + + NiAVObject * object = faceNode->GetObjectByName(&headPart->partName.data); + if (object) { + BSGeometry * geometry = object->GetAsBSGeometry(); + if (geometry) { + auto sculptHost = sculptTarget->GetSculptHost(SculptData::GetHostByPart(headPart), false); + if (sculptHost) { + BSFaceGenBaseMorphExtraData * extraData = (BSFaceGenBaseMorphExtraData *)geometry->GetExtraData("FOD"); + if (extraData) { + for (auto data : *sculptHost) + extraData->vertexData[data.first] += data.second; + } + } + } + } + } + } + + if (!g_extendedMorphs) + return; + + ValueSet * valueSet = m_valueMap.GetValueSet(npc); + if(!valueSet) + return; + + UInt8 gender = CALL_MEMBER_FN(npc, GetSex)(); + + for(auto it = valueSet->begin(); it != valueSet->end(); ++it) + { + SliderInternalPtr slider = GetSlider(npc->race.race, gender, *it->first); + if(slider) { + if(slider->type == SliderInternal::kTypePreset) { + if(it->second != 0) { // There should be no zero morph for presets + memset(buffer, 0, MAX_PATH); + sprintf_s(buffer, MAX_PATH, "%s%d", slider->lowerBound.c_str(), (UInt32)it->second); + BSFixedString morphName(buffer); +#ifdef _DEBUG_MORPH + _DMESSAGE("Applying Single Preset %s value %f", morphName.data, it->value); +#endif + CALL_MEMBER_FN(FaceGen::GetSingleton(), ApplyMorph)(faceNode, headPart, &morphName, 1.0); + } + } else { + BSFixedString morphName = slider->lowerBound; + if(it->second < 0) + morphName = slider->lowerBound; + if(it->second > 0) + morphName = slider->upperBound; + + float relative = abs(it->second); + if(relative > 1.0) { + UInt32 count = (UInt32)relative; + float difference = relative - count; + for(UInt32 i = 0; i < count; i++) + CALL_MEMBER_FN(FaceGen::GetSingleton(), ApplyMorph)(faceNode, headPart, &morphName, 1.0); + relative = difference; + } +#ifdef _DEBUG_MORPH + _DMESSAGE("Applying Single Slider %s value %f", morphName.data, it->value); +#endif + CALL_MEMBER_FN(FaceGen::GetSingleton(), ApplyMorph)(faceNode, headPart, &morphName, relative); + } + } + } +} + +void FaceMorphInterface::ApplyMorphs(TESNPC * npc, BSFaceGenNiNode * faceNode) +{ + char buffer[MAX_PATH]; + + + auto sculptTarget = GetSculptTarget(npc, false); + if (sculptTarget) { + VisitObjects(faceNode, [&](NiAVObject* object) + { + if (BSGeometry * geometry = object->GetAsBSGeometry()) { + std::string headPartName = object->m_name; + BGSHeadPart * headPart = GetHeadPartByName(headPartName); + if (headPart) { + auto sculptHost = sculptTarget->GetSculptHost(SculptData::GetHostByPart(headPart), false); + if (sculptHost) { + BSFaceGenBaseMorphExtraData * extraData = (BSFaceGenBaseMorphExtraData *)geometry->GetExtraData("FOD"); + if (extraData) { + for (auto data : *sculptHost) + extraData->vertexData[data.first] += data.second; + } + } + } + } + + return false; + }); + } + + if (!g_extendedMorphs) + return; + + ValueSet * valueSet = m_valueMap.GetValueSet(npc); + if(!valueSet) + return; + + UInt8 gender = CALL_MEMBER_FN(npc, GetSex)(); + + + for(auto it = valueSet->begin(); it != valueSet->end(); ++it) + { + SliderInternalPtr slider = GetSlider(npc->race.race, gender, *it->first); + if(slider) { + if(slider->type == SliderInternal::kTypePreset) { + if(it->second != 0) { // There should be no zero morph for presets + memset(buffer, 0, MAX_PATH); + sprintf_s(buffer, MAX_PATH, "%s%d", slider->lowerBound.c_str(), (UInt32)it->second); + BSFixedString morphName(buffer); +#ifdef _DEBUG_MORPH + _DMESSAGE("Applying Full Preset %s value %f", morphName.data, it->value); +#endif + SetMorph(npc, faceNode, morphName.data, 1.0); + } + } else { + auto morphName = slider->lowerBound; + if(it->second < 0.0) + morphName = slider->lowerBound; + if(it->second > 0.0) + morphName = slider->upperBound; + + float relative = abs(it->second); + if(relative > 1.0) { + UInt32 count = (UInt32)relative; + float difference = relative - count; + for(UInt32 i = 0; i < count; i++) + SetMorph(npc, faceNode, morphName, 1.0); + relative = difference; + } + +#ifdef _DEBUG_MORPH + _DMESSAGE("Applying Full Slider %s value %f", morphName.data, it->value); +#endif + SetMorph(npc, faceNode, morphName, relative); + } + } + } +} + +void FaceMorphInterface::SetMorph(TESNPC * npc, BSFaceGenNiNode * faceNode, const char * name, float relative) +{ +#ifdef _DEBUG_MORPH + _DMESSAGE("Applying Morph %s", name); +#endif + BSFixedString morphName(name); + FaceGenApplyMorph(FaceGen::GetSingleton(), faceNode, npc, &morphName, relative); +} + +SInt32 FaceMorphInterface::LoadSliders(tArray * sliderArray, RaceMenuSlider * slider) +{ + PlayerCharacter * player = (*g_thePlayer); + TESNPC * npc = DYNAMIC_CAST(player->baseForm, TESForm, TESNPC); + + UInt32 sliderId = sliderArray->count; + UInt32 morphIndex = SLIDER_OFFSET; + + currentList = CreateSliderList(player->race, CALL_MEMBER_FN(npc, GetSex)()); + if(!currentList) + return sliderId; + + ValueSet * valueSet = m_valueMap.GetValueSet(npc); + + // Clean up invalid morphs + if(valueSet) { + ValueSet::iterator it = valueSet->begin(); + while (it != valueSet->end()) { + bool foundMorph = false; + for(auto mit = currentList->begin(); mit != currentList->end(); ++mit) { + SliderInternalPtr slider = (*mit); + if(slider->name == *it->first) { + foundMorph = true; + break; + } + } + + if (!foundMorph) { + _DMESSAGE("Erasing %s", it->first->c_str()); + valueSet->erase(it++); + } + else + it++; + } + } + + UInt32 i = 0; + for(auto it = currentList->begin(); it != currentList->end(); ++it) + { + SliderInternalPtr slider = (*it); + std::string sliderName = "$"; + sliderName.append(slider->name.c_str()); + + float value = valueSet ? valueSet->GetValue(slider->name) : 0.0; + + UInt32 sliderIndex = morphIndex + i; + float lowerBound = slider->lowerBound == BSFixedString("") ? 0.0 : -1.0; + float upperBound = slider->upperBound == BSFixedString("") ? 0.0 : 1.0; + float interval = g_sliderInterval; + float lowerMultiplier = g_sliderMultiplier; + float upperMultiplier = g_sliderMultiplier; + + if(slider->type == SliderInternal::kTypePreset) { + lowerBound = 0.0; + interval = 1; + lowerMultiplier = 1.0; + upperMultiplier = 1.0; + upperBound = (float)slider->presetCount; + } else if(slider->type == SliderInternal::kTypeHeadPart) { + lowerBound = 0.0; + interval = 1; + lowerMultiplier = 1.0; + upperMultiplier = 1.0; + HeadPartList * headPartList = g_partSet.GetPartList(slider->presetCount); + BGSHeadPart * headPart = npc->GetHeadPartByType(slider->presetCount); + SInt32 partIndex = -1; + if(headPart && headPartList) + partIndex = g_partSet.GetPartIndex(headPartList, headPart); + if(partIndex != -1) + value = (float)(partIndex + 1); + if(headPartList) + upperBound = (float)headPartList->size(); + else + upperBound = 0; + } + + BSFixedString morphName(sliderName.c_str()); + +#ifdef _DEBUG_SLIDER + _DMESSAGE("Adding slider: %s Morph: %s Value: %f SliderID: %d Index: %d", morphName.data, slider->name.data, value, sliderId, sliderIndex); +#endif + + RaceMenuSlider newSlider(slider->category, morphName.c_str(), "ChangeDoubleMorph", sliderId++, sliderIndex, 2, 1, lowerBound * lowerMultiplier, upperBound * upperMultiplier, value, interval, 0); + AddRaceMenuSlider(sliderArray, &newSlider); + i++; + } + + return sliderId; +} + +void FaceMorphInterface::Save(SKSESerializationInterface * intfc, UInt32 kVersion) +{ + PlayerCharacter * player = (*g_thePlayer); + TESNPC * npc = DYNAMIC_CAST(player->baseForm, TESForm, TESNPC); + + auto sculptData = m_sculptStorage.GetSculptTarget(npc, false); + if (sculptData) { + if (sculptData->size() > 0) { + intfc->OpenRecord('SCDT', kVersion); + + UInt16 numValidParts = 0; + for (auto part : *sculptData) { + if (part.first->length() != 0 && part.second->size() > 0) { + numValidParts++; + } + } + + intfc->WriteRecordData(&numValidParts, sizeof(numValidParts)); + if (numValidParts > 0) { + for (auto part : *sculptData) { + UInt16 diffCount = part.second->size(); + if (diffCount > 0) { + intfc->OpenRecord('SCPT', kVersion); + g_stringTable.WriteString(intfc, part.first); + intfc->WriteRecordData(&diffCount, sizeof(diffCount)); + + for (auto diff : *part.second) { + intfc->WriteRecordData(&diff.first, sizeof(diff.first)); + intfc->WriteRecordData(&diff.second, sizeof(NiPoint3)); + } + } + } + } + } + } + + ValueSet * valueSet = m_valueMap.GetValueSet(npc); + if (valueSet) { + // Count only non-zero morphs + UInt32 numMorphs = 0; + for (auto it = valueSet->begin(); it != valueSet->end(); ++it) { + if (it->second != 0.0) + numMorphs++; + } + + if (numMorphs > 0) { + intfc->OpenRecord('MRST', kVersion); + intfc->WriteRecordData(&numMorphs, sizeof(numMorphs)); + for (auto it = valueSet->begin(); it != valueSet->end(); ++it) { + if (it->second != 0.0) { + intfc->OpenRecord('MRPH', kVersion); + g_stringTable.WriteString(intfc, it->first); + intfc->WriteRecordData(&it->second, sizeof(it->second)); + } + } + } + } +} + +bool FaceMorphInterface::LoadMorphData(SKSESerializationInterface * intfc, UInt32 kVersion, const std::unordered_map & stringTable) +{ + UInt32 type; + UInt32 length; + + bool error = false; + + PlayerCharacter * player = (*g_thePlayer); + TESNPC * playerBase = DYNAMIC_CAST(player->baseForm, TESForm, TESNPC); + + UInt32 numMorphs = 0; + if (!intfc->ReadRecordData(&numMorphs, sizeof(numMorphs))) + { + _MESSAGE("Error loading morph count"); + error = true; + return true; + } + + for (UInt32 i = 0; i < numMorphs; i++) + { + StringTableItem sculptName; + UInt32 index = 0; + float value = 0.0; + + if (intfc->GetNextRecordInfo(&type, &kVersion, &length)) + { + switch (type) + { + case 'MRPH': + { + + if (kVersion >= BodyMorphInterface::kSerializationVersion3) + { + sculptName = StringTable::ReadString(intfc, stringTable); + if (!sculptName) + { + _MESSAGE("Error loading sculpt name"); + error = true; + return true; + } + } + else if (kVersion >= BodyMorphInterface::kSerializationVersion2) + { + char * name = NULL; + UInt16 nameLength = 0; + if (!intfc->ReadRecordData(&nameLength, sizeof(nameLength))) { + _MESSAGE("Error loading morph name length"); + error = true; + return true; + } + + name = new char[nameLength + 1]; + if (!intfc->ReadRecordData(name, nameLength)) { + _MESSAGE("Error loading morph name"); + error = true; + return true; + } + name[nameLength] = 0; + sculptName = g_stringTable.GetString(name); + } + + if (!intfc->ReadRecordData(&value, sizeof(value))) { + _MESSAGE("Error loading morph value"); + error = true; + return true; + } + } + break; + default: + _MESSAGE("unhandled type %08X", type); + error = true; + break; + } + } + + if (value != 0.0) { + _MESSAGE("Loaded Morph: %s - Value: %f", sculptName->c_str(), value); + m_valueMap.SetMorphValue(playerBase, *sculptName, value); + } + } + + return error; +} + +bool FaceMorphInterface::LoadSculptData(SKSESerializationInterface * intfc, UInt32 kVersion, const std::unordered_map & stringTable) +{ + UInt32 type; + UInt32 length; + bool error = false; + + PlayerCharacter * player = (*g_thePlayer); + TESNPC * playerBase = DYNAMIC_CAST(player->baseForm, TESForm, TESNPC); + + UInt16 numParts = 0; + if (!intfc->ReadRecordData(&numParts, sizeof(numParts))) + { + _MESSAGE("Error loading sculpt part count"); + error = true; + return true; + } + + for (UInt32 i = 0; i < numParts; i++) + { + if (intfc->GetNextRecordInfo(&type, &kVersion, &length)) + { + switch (type) + { + case 'SCPT': + { + StringTableItem sculptName; + if (kVersion >= BodyMorphInterface::kSerializationVersion3) + { + sculptName = StringTable::ReadString(intfc, stringTable); + if (!sculptName) + { + _MESSAGE("Error loading sculpt name"); + error = true; + return true; + } + } + else if (kVersion >= BodyMorphInterface::kSerializationVersion2) + { + UInt16 nameLength = 0; + if (!intfc->ReadRecordData(&nameLength, sizeof(nameLength))) { + _MESSAGE("Error loading sculpt part name length"); + error = true; + return true; + } + + char * name = new char[nameLength + 1]; + if (!intfc->ReadRecordData(name, nameLength)) { + _MESSAGE("Error loading sculpt part name"); + error = true; + return true; + } + name[nameLength] = 0; + sculptName = g_stringTable.GetString(name); + } + + UInt16 totalDifferences = 0; + if (!intfc->ReadRecordData(&totalDifferences, sizeof(totalDifferences))) { + _MESSAGE("Error loading sculpt difference count"); + error = true; + return true; + } + + SculptDataPtr sculptTarget; + if (totalDifferences > 0) + sculptTarget = m_sculptStorage.GetSculptTarget(playerBase, true); + MappedSculptDataPtr sculptHost; + if (sculptTarget && totalDifferences > 0) + sculptHost = sculptTarget->GetSculptHost(*sculptName, true); + + UInt16 index = 0; + NiPoint3 value; + for (UInt16 i = 0; i < totalDifferences; i++) + { + if (!intfc->ReadRecordData(&index, sizeof(index))) { + _MESSAGE("Error loading sculpt index"); + error = true; + return true; + } + + if (!intfc->ReadRecordData(&value, sizeof(value))) { + _MESSAGE("Error loading sculpt index"); + error = true; + return true; + } + + if (sculptTarget && sculptHost) + sculptHost->force_insert(std::make_pair(index, value)); + } + } + break; + default: + _MESSAGE("unhandled type %08X", type); + error = true; + break; + } + } + } + + return error; +} + +struct PresetHeader +{ + enum + { + kSignature = MACRO_SWAP32('SKSE'), // endian-swapping so the order matches + kVersion = 3, + + kVersion_Invalid = 0 + }; + + UInt32 signature; + UInt32 formatVersion; + UInt32 skseVersion; + UInt32 runtimeVersion; +}; + +#include + +bool FaceMorphInterface::SaveJsonPreset(const char * filePath) +{ + Json::StyledWriter writer; + Json::Value root; + + IFileStream currentFile; + IFileStream::MakeAllDirs(filePath); + if (!currentFile.Create(filePath)) + { + _ERROR("%s: couldn't create preset file (%s) Error (%d)", __FUNCTION__, filePath, GetLastError()); + return true; + } + + Json::Value versionInfo; + versionInfo["signature"] = PresetHeader::kSignature; + versionInfo["formatVersion"] = PresetHeader::kVersion; + versionInfo["skseVersion"] = PACKED_SKSE_VERSION; + versionInfo["runtimeVersion"] = RUNTIME_VERSION_1_4_2; + + + PlayerCharacter * player = (*g_thePlayer); + TESNPC * npc = DYNAMIC_CAST(player->baseForm, TESForm, TESNPC); + DataHandler * dataHandler = DataHandler::GetSingleton(); + + bool isFemale = false; + if (npc) + isFemale = CALL_MEMBER_FN(npc, GetSex)() == 1; + + std::map modListLegacy; + std::set modList; + std::map partList; + + UInt32 numHeadParts = 0; + BGSHeadPart ** headParts = NULL; + if (CALL_MEMBER_FN(npc, HasOverlays)()) { + numHeadParts = GetNumActorBaseOverlays(npc); + headParts = GetActorBaseOverlays(npc); + } + else { + numHeadParts = npc->numHeadParts; + headParts = npc->headparts; + } + + // Acquire only vanilla dependencies + for (UInt32 i = 0; i < numHeadParts; i++) + { + BGSHeadPart * headPart = headParts[i]; + if (headPart && !headPart->IsExtraPart()) { + ModInfo * modInfo = GetModInfoByFormID(headPart->formID, false); + if (modInfo) { + modListLegacy.emplace(modInfo->modIndex, modInfo->name); + } + + partList.emplace(i, headPart); + } + } + + // Acquire all mod dependencies + for (UInt32 i = 0; i < numHeadParts; i++) + { + BGSHeadPart * headPart = headParts[i]; + if (headPart && !headPart->IsExtraPart()) { + ModInfo * modInfo = GetModInfoByFormID(headPart->formID, true); + if (modInfo) { + modList.emplace(modInfo->name); + } + } + } + + std::map> tintList; + for (UInt32 i = 0; i < player->tintMasks.count; i++) + { + TintMask * tintMask = NULL; + if (player->tintMasks.GetNthItem(i, tintMask)) + { + UInt32 tintColor = ((UInt32)(tintMask->alpha * 255.0) << 24) | tintMask->color.red << 16 | tintMask->color.green << 8 | tintMask->color.blue; + if (tintMask->texture) + tintList.emplace(i, std::make_pair(tintColor, tintMask->texture->str.data)); + } + } + + Json::Value modInfo; + for (auto mIt = modListLegacy.begin(); mIt != modListLegacy.end(); ++mIt) + { + Json::Value mod; + mod["index"] = mIt->first; + mod["name"] = mIt->second; + modInfo.append(mod); + } + + Json::Value modNames; + for (auto mIt = modList.begin(); mIt != modList.end(); ++mIt) + { + modNames.append(*mIt); + } + + Json::Value headPartInfo; + for (auto pIt = partList.begin(); pIt != partList.end(); ++pIt) + { + Json::Value partInfo; + partInfo["type"] = pIt->first; + partInfo["formId"] = (Json::UInt)pIt->second->formID; + partInfo["formIdentifier"] = GetFormIdentifier(pIt->second); + headPartInfo.append(partInfo); + } + + Json::Value tintInfo; + for (auto tmIt = tintList.begin(); tmIt != tintList.end(); ++tmIt) + { + Json::Value tint; + tint["index"] = tmIt->first; + tint["color"] = (Json::UInt)tmIt->second.first; + tint["texture"] = tmIt->second.second; + tintInfo.append(tint); + } + + Json::Value morphInfo; + if (npc->faceMorph) + { + for (UInt8 p = 0; p < TESNPC::FaceMorphs::kNumPresets; p++) { + Json::Value morphValue = (Json::UInt)npc->faceMorph->presets[p]; + morphInfo["presets"].append(morphValue); + } + + for (UInt8 o = 0; o < TESNPC::FaceMorphs::kNumOptions; o++) { + Json::Value morphValue = npc->faceMorph->option[o]; + morphInfo["morphs"].append(morphValue); + } + } + + Json::Value customMorphInfo; + ValueSet * valueSet = m_valueMap.GetValueSet(npc); + if (valueSet) + { + for (auto it = valueSet->begin(); it != valueSet->end(); ++it) + { + if (it->second != 0.0) { + Json::Value morphValue; + morphValue["name"] = it->first->c_str(); + morphValue["value"] = it->second; + customMorphInfo.append(morphValue); + } + } + } + + + Json::Value sculptData; + auto sculptTarget = GetSculptTarget(npc, false); + if (sculptTarget) { + for (UInt32 i = 0; i < numHeadParts; i++) // Acquire all unique parts + { + BGSHeadPart * headPart = headParts[i]; + if (headPart) { + BSFixedString morphPath = SculptData::GetHostByPart(headPart); + auto sculptHost = sculptTarget->GetSculptHost(morphPath, false); + if (sculptHost) { + Json::Value hostData; + hostData["host"] = morphPath.data; + + TRIModelData data; + GetModelTri(morphPath, data); + + hostData["vertices"] = (Json::UInt)data.vertexCount; + + for (auto morph : *sculptHost) { + Json::Value value; + value.append(morph.first); + value.append((Json::Int)(morph.second.x * VERTEX_MULTIPLIER)); + value.append((Json::Int)(morph.second.y * VERTEX_MULTIPLIER)); + value.append((Json::Int)(morph.second.z * VERTEX_MULTIPLIER)); + hostData["data"].append(value); + } + + sculptData.append(hostData); + } + } + } + } + + Json::Value textureInfo; + BGSHeadPart * facePart = npc->GetCurrentHeadPartByType(BGSHeadPart::kTypeFace); + if (facePart) { + BGSTextureSet * textureSet = GetTextureSetForPart(npc, facePart); + if (textureSet) { + for (UInt8 i = 0; i < BSShaderTextureSet::kNumTextures; i++) { + const char * texturePath = textureSet->textureSet.GetTexturePath(i); + if (texturePath != NULL) { + Json::Value textureValue; + textureValue["index"] = i; + textureValue["texture"] = texturePath; + textureInfo.append(textureValue); + } + } + } + } + + // Collect override data + PresetData::OverrideData overrideData; + g_overrideInterface.VisitNodes(player, [&overrideData](SKEEFixedString node, OverrideVariant & value) + { + overrideData[node].push_back(value); + }); + + // Collect skin data + PresetData::SkinData skinData[2]; + for (UInt32 i = 0; i <= 1; i++) { + g_overrideInterface.VisitSkin(player, isFemale, i == 1, [&i, &skinData](UInt32 slotMask, OverrideVariant & value) + { + skinData[i][slotMask].push_back(value); + return false; + }); + } + + // Collect transform data + PresetData::TransformData transformData[2]; + for (UInt32 i = 0; i <= 1; i++) { + g_transformInterface.VisitNodes(player, i == 1, isFemale, [&i, &transformData](SKEEFixedString node, OverrideRegistration * keys) + { + keys->Visit([&i, &node, &transformData](const StringTableItem & key, OverrideSet * set) + { + if (*key == SKEEFixedString("internal")) + return false; + + set->Visit([&i, &node, &transformData, &key](OverrideVariant * value) + { + transformData[i][node][*key].push_back(*value); + return false; + }); + return false; + }); + + return false; + }); + } + + // Collect body morph data + PresetData::BodyMorphData bodyMorphData; + g_bodyMorphInterface.VisitMorphs(player, [&](SKEEFixedString name, std::unordered_map * map) + { + for (auto & it : *map) + { + bodyMorphData[name][*it.first] = it.second; + } + }); + + for (UInt32 i = 0; i <= 1; i++) { + for (auto & data : transformData[i]) { + Json::Value transform; + transform["firstPerson"] = (bool)(i == 1); + transform["node"] = data.first.c_str(); + + for (auto & key : data.second) { + Json::Value transformKey; + transformKey["name"] = key.first.c_str(); + + for (auto & value : key.second) { + Json::Value jvalue; + jvalue["key"] = value.key; + jvalue["type"] = value.type; + jvalue["index"] = value.index; + switch (value.type) { + case OverrideVariant::kType_Bool: + jvalue["data"] = value.data.b; + break; + case OverrideVariant::kType_Int: + jvalue["data"] = value.data.i; + break; + case OverrideVariant::kType_Float: + jvalue["data"] = value.data.f; + break; + case OverrideVariant::kType_String: + jvalue["data"] = value.str ? value.str->c_str() : ""; + break; + } + transformKey["values"].append(jvalue); + } + transform["keys"].append(transformKey); + } + root["transforms"].append(transform); + } + } + for (auto & data : overrideData) { + Json::Value ovr; + ovr["node"] = data.first.c_str(); + + for (auto & value : data.second) { + Json::Value jvalue; + jvalue["key"] = value.key; + jvalue["type"] = value.type; + jvalue["index"] = value.index; + switch (value.type) { + case OverrideVariant::kType_Bool: + jvalue["data"] = value.data.b; + break; + case OverrideVariant::kType_Int: + jvalue["data"] = value.data.i; + break; + case OverrideVariant::kType_Float: + jvalue["data"] = value.data.f; + break; + case OverrideVariant::kType_String: + jvalue["data"] = value.str ? value.str->c_str() : ""; + break; + } + ovr["values"].append(jvalue); + } + root["overrides"].append(ovr); + } + + for (UInt32 i = 0; i <= 1; i++) { + for (auto & data : skinData[i]) { + Json::Value slot; + slot["firstPerson"] = (bool)(i == 1); + slot["slotMask"] = (Json::UInt)data.first; + + for (auto & value : data.second) { + Json::Value jvalue; + jvalue["key"] = value.key; + jvalue["type"] = value.type; + jvalue["index"] = value.index; + switch (value.type) { + case OverrideVariant::kType_Bool: + jvalue["data"] = value.data.b; + break; + case OverrideVariant::kType_Int: + jvalue["data"] = value.data.i; + break; + case OverrideVariant::kType_Float: + jvalue["data"] = value.data.f; + break; + case OverrideVariant::kType_String: + jvalue["data"] = value.str ? value.str->c_str() : ""; + break; + } + slot["values"].append(jvalue); + } + + root["skinOverrides"].append(slot); + } + } + + for (auto & data : bodyMorphData) { + Json::Value bm; + bm["name"] = data.first.c_str(); + for (auto & keys : data.second) + { + Json::Value jvalue; + jvalue["key"] = keys.first.c_str(); + jvalue["value"] = keys.second; + bm["keys"].append(jvalue); + } + root["bodyMorphs"].append(bm); + } + + root["version"] = versionInfo; + root["mods"] = modInfo; + root["modNames"] = modNames; + root["headParts"] = headPartInfo; + root["actor"]["weight"] = npc->weight; + + if (npc->headData) { + auto hairColor = npc->headData->hairColor; + if (hairColor) + root["actor"]["hairColor"] = (Json::UInt)(hairColor->color.red << 16 | hairColor->color.green << 8 | hairColor->color.blue); + } + + root["tintInfo"] = tintInfo; + root["faceTextures"] = textureInfo; + root["morphs"]["default"] = morphInfo; + root["morphs"]["custom"] = customMorphInfo; + root["morphs"]["sculptDivisor"] = VERTEX_MULTIPLIER; + root["morphs"]["sculpt"] = sculptData; + + std::string data = writer.write(root); + currentFile.WriteBuf(data.c_str(), data.length()); + currentFile.Close(); + return false; +} + +bool FaceMorphInterface::SaveBinaryPreset(const char * filePath) +{ + IFileStream currentFile; + IFileStream::MakeAllDirs(filePath); + + _DMESSAGE("creating preset"); + if(!currentFile.Create(filePath)) + { + _ERROR("%s: couldn't create preset file (%s) Error (%d)", __FUNCTION__, filePath, GetLastError()); + return true; + } + + try + { + PresetHeader fileHeader; + fileHeader.signature = PresetHeader::kSignature; + fileHeader.formatVersion = PresetHeader::kVersion; + fileHeader.skseVersion = PACKED_SKSE_VERSION; + fileHeader.runtimeVersion = RUNTIME_VERSION_1_4_2; + + currentFile.Skip(sizeof(fileHeader)); + + PlayerCharacter * player = (*g_thePlayer); + TESNPC * npc = DYNAMIC_CAST(player->baseForm, TESForm, TESNPC); + DataHandler * dataHandler = DataHandler::GetSingleton(); + + typedef std::map ModMap; + typedef std::pair ModPair; + + typedef std::map PartMap; + typedef std::pair PartPair; + + typedef std::pair TintCouple; + typedef std::map TintMap; + typedef std::pair TintPair; + + ModMap modList; + PartMap partList; + + UInt32 numHeadParts = 0; + BGSHeadPart ** headParts = NULL; + if (CALL_MEMBER_FN(npc, HasOverlays)()) { + numHeadParts = GetNumActorBaseOverlays(npc); + headParts = GetActorBaseOverlays(npc); + } else { + numHeadParts = npc->numHeadParts; + headParts = npc->headparts; + } + for (UInt32 i = 0; i < numHeadParts; i++) // Acquire all unique parts + { + BGSHeadPart * headPart = headParts[i]; + if(headPart && !headPart->IsExtraPart()) { + ModInfo* modInfo = GetModInfoByFormID(headPart->formID); + if(modInfo) { + modList.emplace(modInfo->modIndex, modInfo->name); + partList.emplace(i, headPart); + } + } + } + + TintMap tintList; + for(UInt32 i = 0; i < player->tintMasks.count; i++) + { + TintMask * tintMask = NULL; + if(player->tintMasks.GetNthItem(i, tintMask)) + { + UInt32 tintColor = ((UInt32)(tintMask->alpha * 255.0) << 24) | tintMask->color.red << 16 | tintMask->color.green << 8 | tintMask->color.blue; + tintList.emplace(i, TintCouple(tintColor, tintMask->texture->str.data)); + } + } + + UInt8 modCount = modList.size(); + UInt8 partCount = partList.size(); + UInt8 tintCount = tintList.size(); + + currentFile.Write8(modCount); + for(auto mIt = modList.begin(); mIt != modList.end(); ++mIt) + { + currentFile.Write8(mIt->first); + + UInt16 strLen = strlen(mIt->second); + currentFile.Write16(strLen); + currentFile.WriteBuf(mIt->second, strLen); + } + + currentFile.Write8(partCount); + for(auto pIt = partList.begin(); pIt != partList.end(); ++pIt) + { + currentFile.Write8(pIt->first); + currentFile.Write32(pIt->second->formID); + } + + currentFile.WriteFloat(npc->weight); + + if (npc->faceMorph) + { + currentFile.Write8(TESNPC::FaceMorphs::kNumPresets); + for (UInt8 p = 0; p < TESNPC::FaceMorphs::kNumPresets; p++) + currentFile.Write8(npc->faceMorph->presets[p]); + + currentFile.Write8(TESNPC::FaceMorphs::kNumOptions); + for (UInt8 o = 0; o < TESNPC::FaceMorphs::kNumOptions; o++) + currentFile.WriteFloat(npc->faceMorph->option[o]); + } + else { + currentFile.Write8(0); + currentFile.Write8(0); + } + + UInt32 hairColor = 0; + if(npc->headData && npc->headData->hairColor) { + hairColor = npc->headData->hairColor->color.red << 16 | npc->headData->hairColor->color.green << 8 | npc->headData->hairColor->color.blue; + } + currentFile.Write32(hairColor); + + currentFile.Write8(tintCount); + for(auto tmIt = tintList.begin(); tmIt != tintList.end(); ++tmIt) + { + currentFile.Write8(tmIt->first); + currentFile.Write32(tmIt->second.first); + + UInt16 strLen = strlen(tmIt->second.second); + currentFile.Write16(strLen); + currentFile.WriteBuf(tmIt->second.second, strLen); + } + + SInt64 offset = currentFile.GetOffset(); + currentFile.Skip(sizeof(UInt8)); + UInt8 totalMorphs = 0; + + ValueSet * valueSet = m_valueMap.GetValueSet(npc); + if(valueSet) + { + for(auto it = valueSet->begin(); it != valueSet->end(); ++it) + { + if(it->second != 0.0) { + UInt16 strLen = strlen(it->first->c_str()); + currentFile.Write16(strLen); + currentFile.WriteBuf(it->first->c_str(), strLen); + + currentFile.WriteFloat(it->second); + totalMorphs++; + } + } + } + + SInt64 jumpBack = currentFile.GetOffset(); + currentFile.SetOffset(offset); + currentFile.Write8(totalMorphs); + + currentFile.SetOffset(jumpBack); + offset = currentFile.GetOffset(); + currentFile.Skip(sizeof(UInt8)); + UInt8 totalTextures = 0; + BGSHeadPart * facePart = npc->GetCurrentHeadPartByType(BGSHeadPart::kTypeFace); + if (facePart) { + BGSTextureSet * textureSet = GetTextureSetForPart(npc, facePart); + if (textureSet) { + for (UInt8 i = 0; i < BSShaderTextureSet::kNumTextures; i++) { + const char * texturePath = textureSet->textureSet.GetTexturePath(i); + if (texturePath != NULL) { + UInt16 strLen = strlen(texturePath); + currentFile.Write8(i); + currentFile.Write16(strLen); + currentFile.WriteBuf(texturePath, strLen); + totalTextures++; + } + } + } + } + currentFile.SetOffset(offset); + currentFile.Write8(totalTextures); + + // write header + currentFile.SetOffset(0); + currentFile.WriteBuf(&fileHeader, sizeof(fileHeader)); + + } + catch(...) + { + _ERROR("SavePreset: exception during save"); + } + + currentFile.Close(); + return false; +} + +PresetData::PresetData() +{ + weight = 0; + hairColor = 0; +} + +bool FaceMorphInterface::LoadJsonPreset(const char * filePath, PresetDataPtr presetData) +{ + bool loadError = false; + BSResourceNiBinaryStream file(filePath); + if (!file.IsValid()) { + _ERROR("%s: File %s failed to open.", __FUNCTION__, filePath); + loadError = true; + return loadError; + } + + std::string in; + BSReadAll(&file, &in); + + Json::Features features; + features.all(); + + Json::Value root; + Json::Reader reader(features); + + bool parseSuccess = reader.parse(in, root); + if (!parseSuccess) { + _ERROR("%s: Error occured parsing json for %s.", __FUNCTION__, filePath); + loadError = true; + return loadError; + } + + Json::Value defaultValue; + Json::Value version = root["version"]; + if (version.empty()) { + _ERROR("%s: No version header.", __FUNCTION__); + loadError = true; + return loadError; + } + + UInt32 signature = version["signature"].asUInt(); + if (signature != PresetHeader::kSignature) + { + _ERROR("%s: invalid file signature (found %08X expected %08X)", __FUNCTION__, signature, PresetHeader::kSignature); + loadError = true; + return loadError; + } + + UInt32 formatVersion = version["formatVersion"].asUInt(); + if (formatVersion <= PresetHeader::kVersion_Invalid) + { + _ERROR("%s: version invalid (%08X)", __FUNCTION__, formatVersion); + loadError = true; + return loadError; + } + + Json::Value mods = root["mods"]; + Json::Value modNames = root["modNames"]; + if (mods.empty() && modNames.empty()) { + _ERROR("%s: No mods header.", __FUNCTION__); + loadError = true; + return loadError; + } + + DataHandler * dataHandler = DataHandler::GetSingleton(); + + std::map modList; + if (mods.type() == Json::arrayValue) { + for (auto & mod : mods) { + UInt32 modIndex = mod["index"].asUInt(); + std::string modName = mod["name"].asString(); + + modList.emplace(modIndex, modName); + presetData->modList.push_back(modName); + } + } + + if (root.isMember("modNames") && modNames.type() == Json::arrayValue) { + presetData->modList.clear(); + for (auto & mod : modNames) { + presetData->modList.push_back(mod.asString()); + } + } + + Json::Value headParts = root["headParts"]; + if (!headParts.empty() && headParts.type() == Json::arrayValue) { + for (auto & part : headParts) { + if (part.isMember("formIdentifier")) { + TESForm * headPartForm = GetFormFromIdentifier(part["formIdentifier"].asString()); + if (headPartForm) { + BGSHeadPart * headPart = DYNAMIC_CAST(headPartForm, TESForm, BGSHeadPart); + if (headPart) { + presetData->headParts.push_back(headPart); + } + } + } + else if (part.isMember("formId")) { + UInt8 partType = part["type"].asUInt(); + UInt32 formId = part["formId"].asUInt(); + + UInt32 modIndex = formId >> 24; + auto it = modList.find(modIndex); + if (it != modList.end()) { + UInt8 gameIndex = dataHandler->GetLoadedModIndex(it->second.c_str()); + if (gameIndex != 255) { + formId = (formId & 0x00FFFFFF) | (gameIndex << 24); + TESForm * headPartForm = LookupFormByID(formId); + if (headPartForm) { + BGSHeadPart * headPart = DYNAMIC_CAST(headPartForm, TESForm, BGSHeadPart); + if (headPart) { + presetData->headParts.push_back(headPart); + } + } + else { + _WARNING("Could not resolve part %08X", formId); + } + } + else { + _WARNING("Could not load part type %d from %s; mod not found.", partType, it->second.c_str()); + } + } + } + } + } + + Json::Value actor = root["actor"]; + if (!actor.empty() && actor.type() == Json::objectValue) { + presetData->weight = actor["weight"].asFloat(); + presetData->hairColor = actor["hairColor"].asUInt(); + } + + + Json::Value tintInfo = root["tintInfo"]; + if (!tintInfo.empty() && tintInfo.type() == Json::arrayValue) { + for (auto & tint : tintInfo) { + PresetData::Tint tintData; + tintData.color = tint["color"].asUInt(); + tintData.index = tint["index"].asUInt(); + tintData.name = tint["texture"].asString().c_str(); + presetData->tints.push_back(tintData); + } + } + + Json::Value faceTextures = root["faceTextures"]; + if (!faceTextures.empty() && faceTextures.type() == Json::arrayValue) { + for (auto & faceTexture : faceTextures) { + PresetData::Texture texture; + texture.index = faceTexture["index"].asUInt(); + texture.name = faceTexture["texture"].asString().c_str(); + presetData->faceTextures.push_back(texture); + } + } + + Json::Value morphs = root["morphs"]; + if (!morphs.empty()) { + Json::Value defaultMorphs = morphs["default"]; + if (!defaultMorphs.empty()) { + Json::Value presets = defaultMorphs["presets"]; + for (auto & preset : presets) { + UInt32 presetValue = preset.asUInt(); + if (presetValue == 255) + presetValue = -1; + + presetData->presets.push_back(presetValue); + } + + Json::Value morphs = defaultMorphs["morphs"]; + for (auto & morph : morphs) { + presetData->morphs.push_back(morph.asFloat()); + } + } + Json::Value customMorphs = morphs["custom"]; + if (!customMorphs.empty()) { + for (auto & customMorph : customMorphs) { + PresetData::Morph morph; + morph.name = customMorph["name"].asString().c_str(); + morph.value = customMorph["value"].asFloat(); + presetData->customMorphs.push_back(morph); + } + } + + SInt32 multiplier = -1; + + Json::Value sculptMult = morphs["sculptDivisor"]; + if (!sculptMult.empty()) + multiplier = sculptMult.asInt(); + + Json::Value sculptData = morphs["sculpt"]; + if (!sculptData.empty()) { + presetData->sculptData = std::make_shared(); + for (auto & hostFile : sculptData) { + SKEEFixedString host = hostFile["host"].asString().c_str(); + Json::Value data = hostFile["data"]; + + auto sculptedData = std::make_shared(); + for (auto & morphData : data) { + UInt16 index = morphData[0].asUInt(); + NiPoint3 pt; + + if (multiplier > 0) { + pt.x = (float)morphData[1].asInt() / (float)multiplier; + pt.y = (float)morphData[2].asInt() / (float)multiplier; + pt.z = (float)morphData[3].asInt() / (float)multiplier; + } else { + pt.x = morphData[1].asFloat(); + pt.y = morphData[2].asFloat(); + pt.z = morphData[3].asFloat(); + } + + sculptedData->force_insert(std::make_pair(index, pt)); + } + + presetData->sculptData->emplace(g_stringTable.GetString(host), sculptedData); + } + } + } + + Json::Value transforms = root["transforms"]; + if (!transforms.empty()) { + for (auto & xForm : transforms) { + bool isFirstPerson = xForm["firstPerson"].asBool(); + BSFixedString nodeName = xForm["node"].asString().c_str(); + + Json::Value keys = xForm["keys"]; + for (auto & key : keys) { + BSFixedString keyName = key["name"].asString().c_str(); + + Json::Value values = key["values"]; + for (auto & jvalue : values) { + OverrideVariant value; + value.key = jvalue["key"].asUInt(); + value.type = jvalue["type"].asInt(); + value.index = jvalue["index"].asInt(); + switch (value.type) { + case OverrideVariant::kType_Bool: + value.data.b = jvalue["data"].asBool(); + break; + case OverrideVariant::kType_Int: + value.data.i = jvalue["data"].asInt(); + break; + case OverrideVariant::kType_Float: + value.data.f = jvalue["data"].asFloat(); + break; + case OverrideVariant::kType_String: + value.str = g_stringTable.GetString(jvalue["data"].asString().c_str()); + break; + } + + presetData->transformData[isFirstPerson ? 1 : 0][nodeName][keyName].push_back(value); + } + } + } + } + + Json::Value overrides = root["overrides"]; + if (!overrides.empty()) { + for (auto & ovr : overrides) { + BSFixedString node = ovr["node"].asString().c_str(); + Json::Value values = ovr["values"]; + for (auto & jvalue : values) { + OverrideVariant value; + value.key = jvalue["key"].asUInt(); + value.type = jvalue["type"].asInt(); + value.index = jvalue["index"].asInt(); + switch (value.type) { + case OverrideVariant::kType_Bool: + value.data.b = jvalue["data"].asBool(); + break; + case OverrideVariant::kType_Int: + value.data.i = jvalue["data"].asInt(); + break; + case OverrideVariant::kType_Float: + value.data.f = jvalue["data"].asFloat(); + break; + case OverrideVariant::kType_String: + value.str = g_stringTable.GetString(jvalue["data"].asString().c_str()); + break; + } + presetData->overrideData[node].push_back(value); + } + } + } + + Json::Value skinOverrides = root["skinOverrides"]; + if (!skinOverrides.empty()) { + for (auto & skinData : skinOverrides) { + bool isFirstPerson = skinData["firstPerson"].asBool(); + UInt32 slotMask = skinData["slotMask"].asUInt(); + + Json::Value values = skinData["values"]; + for (auto & jvalue : values) { + OverrideVariant value; + value.key = jvalue["key"].asUInt(); + value.type = jvalue["type"].asInt(); + value.index = jvalue["index"].asInt(); + switch (value.type) { + case OverrideVariant::kType_Bool: + value.data.b = jvalue["data"].asBool(); + break; + case OverrideVariant::kType_Int: + value.data.i = jvalue["data"].asInt(); + break; + case OverrideVariant::kType_Float: + value.data.f = jvalue["data"].asFloat(); + break; + case OverrideVariant::kType_String: + value.str = g_stringTable.GetString(jvalue["data"].asString().c_str()); + break; + } + + presetData->skinData[isFirstPerson ? 1 : 0][slotMask].push_back(value); + } + } + } + + Json::Value bodyMorphs = root["bodyMorphs"]; + if (!bodyMorphs.empty()) { + for (auto & bm : bodyMorphs) { + BSFixedString name = bm["name"].asString().c_str(); + + // Legacy version + Json::Value keyless = bm["value"]; + if (!keyless.empty()) + { + float value = bm["value"].asFloat(); + presetData->bodyMorphData[name]["RSMLegacy"] = value; + } + + // New version + Json::Value values = bm["keys"]; + if (!values.empty()) + { + for (auto & jvalue : values) { + SKEEFixedString key = jvalue["key"].asString().c_str(); + float value = jvalue["value"].asFloat(); + + // If the keys were mapped by mod name, skip them if they arent in load order + std::string strKey(key); + SKEEFixedString ext(strKey.substr(strKey.find_last_of(".") + 1).c_str()); + if (ext == SKEEFixedString("esp") || ext == SKEEFixedString("esm")) + { + if (!dataHandler->LookupLoadedModByName(key.c_str())) + continue; + } + else if (ext == SKEEFixedString("esl")) + { + if (!dataHandler->LookupLoadedLightModByName(key.c_str())) + continue; + } + + presetData->bodyMorphData[name][key] = value; + } + } + } + } + + return loadError; +} + +bool FaceMorphInterface::LoadBinaryPreset(const char * filePath, PresetDataPtr presetData) +{ + bool loadError = false; + BSResourceNiBinaryStream file(filePath); + if (!file.IsValid()) { + _ERROR("%s: File %s failed to open.", __FUNCTION__, filePath); + loadError = true; + return loadError; + } + + try + { + PresetHeader header; + file.Read(&header, sizeof(header)); + + if(header.signature != PresetHeader::kSignature) + { + _ERROR("%s: invalid file signature (found %08X expected %08X)", __FUNCTION__, header.signature, PresetHeader::kSignature); + loadError = true; + goto done; + } + + if(header.formatVersion <= PresetHeader::kVersion_Invalid) + { + _ERROR("%s: version invalid (%08X)", __FUNCTION__, header.formatVersion); + loadError = true; + goto done; + } + + if(header.formatVersion < 2) + { + _ERROR("%s: version too old (found %08X current %08X)", __FUNCTION__, header.formatVersion, PresetHeader::kVersion); + goto done; + } + + DataHandler * dataHandler = DataHandler::GetSingleton(); + + typedef std::map ModMap; + typedef std::pair ModPair; + + ModMap modList; + UInt8 modCount = 0; + file.Read(&modCount, sizeof(modCount)); + + char textBuffer[MAX_PATH]; + for(UInt8 i = 0; i < modCount; i++) + { + UInt8 modIndex; + file.Read(&modIndex, sizeof(modIndex)); + + UInt16 strLen = 0; + file.Read(&strLen, sizeof(strLen)); + + memset(textBuffer, 0, MAX_PATH); + file.Read(textBuffer, strLen); + + std::string modName(textBuffer); + + modList.emplace(modIndex, modName); + presetData->modList.push_back(modName); + } + + UInt8 partCount = 0; + file.Read(&partCount, sizeof(partCount)); + + for(UInt8 i = 0; i < partCount; i++) + { + UInt8 partType = 0; + file.Read(&partType, sizeof(partType)); + + UInt32 formId = 0; + file.Read(&formId, sizeof(formId)); + + UInt8 modIndex = formId >> 24; + auto it = modList.find(modIndex); + if(it != modList.end()) { + UInt8 gameIndex = dataHandler->GetLoadedModIndex(it->second.c_str()); + if(gameIndex != 255) { + formId = (formId & 0x00FFFFFF) | (gameIndex << 24); + TESForm * headPartForm = LookupFormByID(formId); + if(headPartForm) { + BGSHeadPart * headPart = DYNAMIC_CAST(headPartForm, TESForm, BGSHeadPart); + if(headPart) { + presetData->headParts.push_back(headPart); + } + } else { + _WARNING("Could not resolve part %08X", formId); + } + } else { + _WARNING("Could not load part type %d from %s; mod not found.", partType, it->second.c_str()); + } + } + } + + float weight = 0.0; + file.Read(&weight, sizeof(weight)); + + presetData->weight = weight; + + UInt8 presetCount = 0; + file.Read(&presetCount, sizeof(presetCount)); + + for(UInt8 i = 0; i < presetCount; i++) + { + SInt32 preset = 0; + file.Read(&preset, sizeof(UInt8)); + + if(preset == 255) + preset = -1; + + presetData->presets.push_back(preset); + } + + UInt8 optionCount = 0; + file.Read(&optionCount, sizeof(optionCount)); + + for(UInt8 i = 0; i < optionCount; i++) + { + float option = 0.0; + file.Read(&option, sizeof(option)); + + presetData->morphs.push_back(option); + } + + UInt32 hairColor = 0; + file.Read(&hairColor, sizeof(hairColor)); + + presetData->hairColor = hairColor; + + UInt8 tintCount = 0; + file.Read(&tintCount, sizeof(tintCount)); + + for(UInt8 i = 0; i < tintCount; i++) + { + UInt8 tintIndex = 0; + file.Read(&tintIndex, sizeof(tintIndex)); + + UInt32 tintColor = 0; + file.Read(&tintColor, sizeof(tintColor)); + + UInt16 strLen = 0; + file.Read(&strLen, sizeof(strLen)); + + memset(textBuffer, 0, MAX_PATH); + file.Read(textBuffer, strLen); + + BSFixedString tintPath = textBuffer; + + PresetData::Tint tint; + tint.color = tintColor; + tint.index = tintIndex; + tint.name = tintPath; + presetData->tints.push_back(tint); + } + + UInt8 morphCount = 0; + file.Read(&morphCount, sizeof(morphCount)); + + for(UInt8 i = 0; i < morphCount; i++) + { + UInt16 strLen = 0; + file.Read(&strLen, sizeof(strLen)); + + memset(textBuffer, 0, MAX_PATH); + file.Read(textBuffer, strLen); + + float morphValue = 0.0; + file.Read(&morphValue, sizeof(morphValue)); + + PresetData::Morph morph; + morph.name = textBuffer; + morph.value = morphValue; + + presetData->customMorphs.push_back(morph); + } + + if(header.formatVersion >= 3) + { + UInt8 textureCount = 0; + file.Read(&textureCount, sizeof(textureCount)); + + for(UInt8 i = 0; i < textureCount; i++) + { + UInt8 textureIndex = 0; + file.Read(&textureIndex, sizeof(textureIndex)); + + UInt16 strLen = 0; + file.Read(&strLen, sizeof(strLen)); + + memset(textBuffer, 0, MAX_PATH); + file.Read(textBuffer, strLen); + + BSFixedString texturePath(textBuffer); + + PresetData::Texture texture; + texture.index = textureIndex; + texture.name = texturePath; + + presetData->faceTextures.push_back(texture); + } + } + } + catch(...) + { + _ERROR("%s: exception during load", __FUNCTION__); + loadError = true; + } + +done: + return loadError; +} + +SKEEFixedString SculptData::GetHostByPart(BGSHeadPart * headPart) +{ + const char * morphPath = headPart->chargenMorph.GetModelName(); + if (morphPath != NULL && morphPath[0] != NULL) + return morphPath; + + morphPath = headPart->morph.GetModelName(); + if (morphPath != NULL && morphPath[0] != NULL) + return morphPath; + + morphPath = headPart->raceMorph.GetModelName(); + if (morphPath != NULL && morphPath[0] != NULL) + return morphPath; + + return SKEEFixedString(""); +} + +MappedSculptDataPtr SculptData::GetSculptHost(SKEEFixedString host, bool create) +{ + auto stringItem = g_stringTable.GetString(host); + auto it = find(stringItem); + if (it != end()) + return it->second; + else if (create) { + auto data = std::make_shared(); + emplace(stringItem, data); + return data; + } + + return nullptr; +} + +SculptDataPtr SculptStorage::GetSculptTarget(TESNPC * target, bool create) +{ + auto it = find(target); + if (it != end()) + return it->second; + else if (create) { + auto data = std::make_shared(); + emplace(target, data); + return data; + } + + return nullptr; +} + +void SculptStorage::SetSculptTarget(TESNPC * target, SculptDataPtr sculptData) +{ + auto it = find(target); + if (it != end()) + it->second = sculptData; + else { + emplace(target, sculptData); + } +} + +void SculptStorage::EraseNPC(TESNPC * npc) +{ + auto sculptTarget = find(npc); + if (sculptTarget != end()) { + erase(sculptTarget); + } +} + +SKSETaskApplyMorphs::SKSETaskApplyMorphs(Actor * actor) +{ + m_formId = actor->formID; +} + +void SKSETaskApplyMorphs::Run() +{ + if (!m_formId) + return; + + TESForm * form = LookupFormByID(m_formId); + Actor * actor = DYNAMIC_CAST(form, TESForm, Actor); + if (!actor) + return; + + TESNPC * actorBase = DYNAMIC_CAST(actor->baseForm, TESForm, TESNPC); + if (!actorBase) + return; + + BSFaceGenNiNode * faceNode = actor->GetFaceGenNiNode(); + if (faceNode) { + g_morphInterface.ApplyMorphs(actorBase, faceNode); + UpdateModelFace(faceNode); + } +} + +SKSETaskImportHead::SKSETaskImportHead(Actor * actor, BSFixedString nifPath) : m_nifPath(nifPath) +{ + m_formId = actor->formID; +} +/* +class TESNPC_Extn : public TESNPC +{ +public: + MEMBER_FN_PREFIX(TESNPC_Extn); + DEFINE_MEMBER_FN(UpdateHead, void, 0x560E00, NiGeometry * geometry); +}; + +class BSFaceGenNiNode_Extn : public BSFaceGenNiNode +{ +public: + MEMBER_FN_PREFIX(BSFaceGenNiNode_Extn); + DEFINE_MEMBER_FN(UpdateSkin, void, 0x5A83C0, NiAVObject * root, NiAVObject * geometry, UInt32 unk1); +}; + +class UnknownClass1 +{ +public: + static UnknownClass1 * GetSingleton(void) + { + return *((UnknownClass1 **)0x1BA7680); + } + + MEMBER_FN_PREFIX(UnknownClass1); + DEFINE_MEMBER_FN(Invalidate, void, 0xC78D80, NiAVObject * object); + DEFINE_MEMBER_FN(Invalidate2, void, 0xC77EB0, NiAVObject * object, char unk1, char unk2); +}; + +typedef UInt32(*_UpdateModelGeometryData)(NiAVObject*, UInt8 * update); +const _UpdateModelGeometryData UpdateModelGeometryData = (_UpdateModelGeometryData)0x005A9B30; + +typedef void (*_InvalidateGeometryData)(NiAVObject* object, NiAVObject* root); +const _InvalidateGeometryData InvalidateGeometryData = (_InvalidateGeometryData)0x00C6F3B0; +*/ + +void SKSETaskImportHead::Run() +{ + if (!m_formId) + return; + + TESForm * form = LookupFormByID(m_formId); + Actor * actor = DYNAMIC_CAST(form, TESForm, Actor); + if (!actor) + return; + + NiNode * root = actor->GetNiRootNode(0); + BSFaceGenNiNode * faceNode = actor->GetFaceGenNiNode(); + TESNPC * actorBase = DYNAMIC_CAST(actor->baseForm, TESForm, TESNPC); + if (!actorBase || !faceNode || !root) + return; + + BSFaceGenAnimationData * animationData = actor->GetFaceGenAnimationData(); + if (animationData) { + FaceGen::GetSingleton()->isReset = 0; + for (UInt32 t = BSFaceGenAnimationData::kKeyframeType_Expression; t <= BSFaceGenAnimationData::kKeyframeType_Phoneme; t++) + { + BSFaceGenKeyframeMultiple * keyframe = &animationData->keyFrames[t]; + for (UInt32 i = 0; i < keyframe->count; i++) + keyframe->values[i] = 0.0; + keyframe->isUpdated = 0; + } + UpdateModelFace(faceNode); + } + + UInt32 numParts = actorBase->numHeadParts; + BGSHeadPart ** headParts = actorBase->headparts; + if (CALL_MEMBER_FN(actorBase, HasOverlays)()) { + numParts = GetNumActorBaseOverlays(actorBase); + headParts = GetActorBaseOverlays(actorBase); + } + + std::unordered_map> typeMap; + for (UInt32 i = 0; i < numParts; i++) + { + BGSHeadPart * headPart = headParts[i]; + for (UInt32 p = 0; p < faceNode->m_children.m_size; p++) + { + NiAVObject * object = faceNode->m_children.m_data[p]; + if (object && BSFixedString(object->m_name) == headPart->partName) { + BSGeometry * geometry = object->GetAsBSGeometry(); + if (geometry) { + BSGeometry * otherGeometry = NULL; + geometry->IncRef(); + typeMap.emplace(SculptData::GetHostByPart(headPart), std::make_tuple(headPart, geometry, otherGeometry)); + break; + } + } + } + } + + UInt8 niStreamMemory[0x5B4]; + memset(niStreamMemory, 0, 0x5B4); + NiStream * niStream = (NiStream *)niStreamMemory; + CALL_MEMBER_FN(niStream, ctor)(); + + + BSResourceNiBinaryStream binaryStream(m_nifPath.data); + if (binaryStream.IsValid()) { + + NiNode * rootNode = NULL; + niStream->LoadStream(&binaryStream); + if (niStream->m_rootObjects.m_data) + { + if (niStream->m_rootObjects.m_data[0]) // Get the root node + rootNode = niStream->m_rootObjects.m_data[0]->GetAsNiNode(); + if (rootNode) + { + VisitObjects(rootNode, [&](NiAVObject* trishape) + { + NiNode * parent = trishape->m_parent; + if (parent && BSFixedString(parent->m_name) == BSFixedString("BSFaceGenNiNodeSkinned")) { + BSGeometry * geometry = trishape->GetAsBSGeometry(); + if (geometry) { + std::string name(trishape->m_name); + BGSHeadPart * part = GetHeadPartByName(name); + if (part) { + auto it = typeMap.find(SculptData::GetHostByPart(part)); + if (it != typeMap.end()) { + geometry->IncRef(); + std::get<2>(it->second) = geometry; + } + } + } + } + return false; + }); + + } + } + + CALL_MEMBER_FN(niStream, dtor)(); + } + + for (auto obj : typeMap) { + BGSHeadPart * part = std::get<0>(obj.second); + BSGeometry * source = std::get<1>(obj.second); + BSGeometry * target = std::get<2>(obj.second); + if (!part || !source || !target) + continue; + +#ifdef FIXME_GEOMETRY + NiGeometryData * sourceData = niptr_cast(source->m_spModelData); + NiGeometryData * targetData = niptr_cast(target->m_spModelData); + if (!sourceData || !targetData) + continue; + + if (sourceData->m_usVertices == targetData->m_usVertices) { + auto sculptHost = g_morphHandler.GetSculptTarget(actorBase); + if (sculptHost) { + auto sculptData = sculptHost->GetSculptHost(obj.first); + if (sculptData) { + BSFaceGenBaseMorphExtraData * extraData = (BSFaceGenBaseMorphExtraData *)source->GetExtraData("FOD"); + if (extraData) { + for (UInt32 i = 0; i < sourceData->m_usVertices; i++) { + NiPoint3 diff = targetData->m_pkVertex[i] - sourceData->m_pkVertex[i]; + + if (abs(diff.x) > 0.001 || abs(diff.y) > 0.001 || abs(diff.z) > 0.001) { + extraData->vertexData[i] += diff; + sculptData->force_insert(std::make_pair(i, diff)); + } + } + } + } + } + } +#endif + + if (source) + source->DecRef(); + if (target) + target->DecRef(); + } + + UpdateModelFace(faceNode); + + if (animationData) { + animationData->overrideFlag = 0; + CALL_MEMBER_FN(animationData, Reset)(1.0, 1, 1, 0, 0); + FaceGen::GetSingleton()->isReset = 1; + UpdateModelFace(faceNode); + } +} \ No newline at end of file diff --git a/skee/FaceMorphInterface.h b/skee/FaceMorphInterface.h new file mode 100644 index 0000000..fc25c82 --- /dev/null +++ b/skee/FaceMorphInterface.h @@ -0,0 +1,466 @@ +#pragma once + +#include "skse64/GameTypes.h" +#include "StringTable.h" + +#ifdef _DEBUG +//#define _DEBUG_HOOK +//#define _DEBUG_MORPHAPPLICATOR +//#define _DEBUG_DATADUMP +//#define _DEBUG_DATAREADER +//#define _DEBUG_MORPH +#endif + +class BSFaceGenNiNode; +class TESNPC; +class SliderArray; +class RaceMenuSlider; +struct SKSESerializationInterface; +class TESRace; +class BGSHeadPart; +class TESForm; +class TESModelTri; + +#define SLIDER_OFFSET 200 +#define SLIDER_CATEGORY_EXTRA 512 +#define SLIDER_CATEGORY_EXPRESSIONS 1024 + +#define SLIDER_MOD_DIRECTORY "actors\\character\\FaceGenMorphs\\" +#define SLIDER_DIRECTORY "actors\\character\\FaceGenMorphs\\morphs\\" + +#define MORPH_CACHE_TEMPLATE "%08X.tri" +#define MORPH_CACHE_DIR "cache\\" +#define MORPH_CACHE_PATH "actors\\character\\FaceGenMorphs\\morphs\\cache\\" + +#include +#include +#include +#include +#include + +#include "IHashType.h" +#include "OverrideVariant.h" +#include "IPluginInterface.h" + +#include "skse64/GameResources.h" +#include "skse64/GameThreads.h" + +class SliderInternal +{ +public: + SliderInternal::SliderInternal() + { + category = -1; + name = ""; + lowerBound = ""; + upperBound = ""; + type = 0; + presetCount = 0; + } + + void SliderInternal::copy(SliderInternal * slider) + { + category = slider->category; + name = slider->name; + lowerBound = slider->lowerBound; + upperBound = slider->upperBound; + type = slider->type; + presetCount = slider->presetCount; + } + + enum + { + kCategoryExpressions = SLIDER_CATEGORY_EXPRESSIONS, + kCategoryExtra = SLIDER_CATEGORY_EXTRA, + kCategoryBody = 4, + kCategoryHead = 8, + kCategoryFace = 16, + kCategoryEyes = 32, + kCategoryBrow = 64, + kCategoryMouth = 128, + kCategoryHair = 256 + }; + + enum + { + kTypeSlider = 0, + kTypePreset = 1, + kTypeHeadPart = 2 + }; + + SInt32 category; + SKEEFixedString name; + SKEEFixedString lowerBound; + SKEEFixedString upperBound; + UInt8 type; + UInt8 presetCount; +}; + +typedef std::shared_ptr SliderInternalPtr; + +class SliderGender +{ +public: + SliderGender::SliderGender() + { + slider[0] = NULL; + slider[1] = NULL; + } + + SliderInternalPtr slider[2]; +}; +typedef std::shared_ptr SliderGenderPtr; +typedef std::vector SliderList; +typedef std::unordered_map RaceSliders; +typedef std::vector MorphSet; + +class MorphMap : public std::map +{ +public: + class Visitor + { + public: + virtual bool Accept(SKEEFixedString morphName) { return false; }; + }; + + void AddMorph(SKEEFixedString key, SKEEFixedString value); + bool Visit(SKEEFixedString key, Visitor & visitor); + void Revert(); + +#ifdef _DEBUG_DATADUMP + void DumpMap(); + class DumpVisitor : public Visitor + { + public: + virtual bool Accept(BSFixedString morphName) + { + _MESSAGE("Extra Morph: %s", morphName.data); + return false; + }; + }; +#endif +}; + +class SliderMap : public std::unordered_map +{ +public: + SliderMap::SliderMap() : std::unordered_map(){ } + + void AddSlider(SKEEFixedString key, UInt8 gender, SliderInternal & slider); + +#ifdef _DEBUG_DATADUMP + void DumpMap(); +#endif +}; + +typedef std::shared_ptr SliderMapPtr; + + +class SliderSet : public std::set +{ +public: + bool for_each_slider(std::function func); +}; + +typedef std::shared_ptr SliderSetPtr; + +class RaceMap : public std::unordered_map +{ +public: + SliderSetPtr GetSliderSet(TESRace * race); + bool AddSliderMap(TESRace * race, SliderMapPtr sliderMap); + bool CreateDefaultMap(TESRace * race); + + void Revert(); + +#ifdef _DEBUG_DATADUMP + void DumpMap(); +#endif +}; + +class ValueSet : public std::unordered_map +{ +public: + void SetValue(SKEEFixedString name, float value); + void ClearValue(SKEEFixedString name); + float GetValue(SKEEFixedString name); +}; + +class ValueMap : public std::unordered_map +{ +public: + ValueSet * GetValueSet(TESNPC* npc); + void EraseNPC(TESNPC * npc); + + float GetMorphValueByName(TESNPC* npc, SKEEFixedString name); + void SetMorphValue(TESNPC* npc, SKEEFixedString name, float value); +}; + +#define VERTEX_THRESHOLD 0.00001 +#define VERTEX_MULTIPLIER 10000 + +class MappedSculptData : public std::unordered_map +{ +public: + void force_insert(value_type const & v) + { + if (abs(v.second.x) < VERTEX_THRESHOLD && abs(v.second.y) < VERTEX_THRESHOLD && abs(v.second.z) < VERTEX_THRESHOLD) + return; + + auto res = insert(v); + if (!res.second) + (*res.first).second = v.second; + } + + void add(value_type const & v) + { + auto res = insert(v); + if (!res.second) + (*res.first).second += v.second; + + if (abs((*res.first).second.x) < VERTEX_THRESHOLD && abs((*res.first).second.y) < VERTEX_THRESHOLD && abs((*res.first).second.z) < VERTEX_THRESHOLD) + erase(res.first); + } +}; +typedef std::shared_ptr MappedSculptDataPtr; + +class SculptData : public std::unordered_map +{ +public: + MappedSculptDataPtr GetSculptHost(SKEEFixedString, bool create = true); + + static SKEEFixedString GetHostByPart(BGSHeadPart * headPart); +}; +typedef std::shared_ptr SculptDataPtr; + +class SculptStorage : public std::unordered_map < TESNPC*, SculptDataPtr > +{ +public: + void SetSculptTarget(TESNPC * npc, SculptDataPtr sculptData); + SculptDataPtr GetSculptTarget(TESNPC* npc, bool create = true); + void EraseNPC(TESNPC * npc); +}; + + +class PresetData +{ +public: + PresetData(); + + struct Tint + { + UInt32 index; + UInt32 color; + SKEEFixedString name; + }; + + struct Morph + { + float value; + SKEEFixedString name; + }; + + struct Texture + { + UInt8 index; + SKEEFixedString name; + }; + + float weight; + UInt32 hairColor; + std::vector modList; + std::vector headParts; + std::vector presets; + std::vector morphs; + std::vector tints; + std::vector customMorphs; + std::vector faceTextures; + BSFixedString tintTexture; + typedef std::map> OverrideData; + OverrideData overrideData; + typedef std::map> SkinData; + SkinData skinData[2]; + typedef std::map>> TransformData; + TransformData transformData[2]; + SculptDataPtr sculptData; + typedef std::unordered_map> BodyMorphData; + BodyMorphData bodyMorphData; +}; +typedef std::shared_ptr PresetDataPtr; + +class TRIFile +{ +public: + TRIFile() + { + vertexCount = -1; + } + + bool Load(const char * triPath); + bool Apply(BSGeometry * geometry, SKEEFixedString morph, float relative); + + struct Morph + { + SKEEFixedString name; + float multiplier; + + struct Vertex + { + SInt16 x, y, z; + }; + + std::vector vertices; + }; + + SInt32 vertexCount; + std::unordered_map morphs; +}; + +class TRIModelData +{ +public: + TRIModelData() + { + vertexCount = -1; + morphModel = NULL; + } + SInt32 vertexCount; + std::shared_ptr triFile; + TESModelTri * morphModel; +}; + +typedef std::unordered_map ModelMap; +typedef std::unordered_map PresetMap; + +class FaceMorphInterface : public IPluginInterface +{ +public: + enum + { + kCurrentPluginVersion = 1, + kSerializationVersion1 = 1, + kSerializationVersion2 = 2, + kSerializationVersion = kSerializationVersion2 + }; + virtual UInt32 GetVersion(); + + virtual bool Load(SKSESerializationInterface * intfc, UInt32 version) { return false; } // Unused due to separate dblock name for morph and sculpt + virtual void Save(SKSESerializationInterface * intfc, UInt32 kVersion); + virtual void Revert(); + void RevertInternals(); + + bool LoadMorphData(SKSESerializationInterface * intfc, UInt32 version, const StringIdMap & stringTable); + bool LoadSculptData(SKSESerializationInterface * intfc, UInt32 version, const StringIdMap & stringTable); + + void LoadMods(); + + virtual float GetMorphValueByName(TESNPC* npc, SKEEFixedString name); + virtual void SetMorphValue(TESNPC* npc, SKEEFixedString name, float value); + + void SetMorph(TESNPC * npc, BSFaceGenNiNode * faceNode, const char * name, float relative); + + void ApplyMorph(TESNPC * npc, BGSHeadPart * headPart, BSFaceGenNiNode * faceNode); + void ApplyMorphs(TESNPC * npc, BSFaceGenNiNode * faceNode); + + SInt32 LoadSliders(tArray * sliderArray, RaceMenuSlider * slider); + + void ReadMorphs(std::string fixedPath, std::string modName, std::string fileName); + void ReadRaces(std::string fixedPath, std::string modName, std::string fileName); + SliderMapPtr ReadSliders(std::string fixedPath, std::string modName, std::string fileName); + + SliderInternalPtr GetSlider(TESRace * race, UInt8 gender, SKEEFixedString name); + SliderInternalPtr GetSliderByIndex(TESRace * race, UInt32 index); + + SliderList * CreateSliderList(TESRace * race, UInt8 gender); + void AddSlider(TESRace * race, SliderInternalPtr & slider); + + bool VisitMorphMap(SKEEFixedString key, MorphMap::Visitor & visitor); + + bool CacheHeadPartModel(BGSHeadPart * headPart, bool cacheTRI = false); + bool GetModelTri(SKEEFixedString filePath, TRIModelData & modelData); + TRIModelData & GetExtendedModelTri(SKEEFixedString morphName, bool cacheTRI = false); + + inline SculptDataPtr GetSculptTarget(TESNPC * npc, bool create = true) + { + return m_sculptStorage.GetSculptTarget(npc, create); + } + inline void SetSculptTarget(TESNPC * npc, const SculptDataPtr & data) + { + return m_sculptStorage.SetSculptTarget(npc, data); + } + inline void EraseSculptData(TESNPC * npc) + { + m_sculptStorage.EraseNPC(npc); + } + inline void EraseMorphData(TESNPC * npc) + { + m_valueMap.EraseNPC(npc); + } + + PresetDataPtr GetPreset(TESNPC* npc); + void AssignPreset(TESNPC * npc, PresetDataPtr presetData); + void ApplyPreset(TESNPC * npc, BSFaceGenNiNode * faceNode, BGSHeadPart * headPart); + bool ErasePreset(TESNPC * npc); + void ClearPresets(); + + bool SaveBinaryPreset(const char * filePath); + //bool LoadPreset(const char * filePath, GFxMovieView * movieView, GFxValue * rootObject); + bool LoadBinaryPreset(const char * filePath, PresetDataPtr presetData); + + enum ApplyTypes + { + kPresetApplyFace = (0 << 0), + kPresetApplyOverrides = (1 << 0), + kPresetApplyBodyMorphs = (1 << 1), + kPresetApplyTransforms = (1 << 2), + kPresetApplySkinOverrides = (1 << 3), + kPresetApplyAll = kPresetApplyFace | kPresetApplyOverrides | kPresetApplyBodyMorphs | kPresetApplyTransforms | kPresetApplySkinOverrides + }; + + void ApplyPresetData(Actor * actor, PresetDataPtr presetData, bool setSkinColor = false, ApplyTypes applyType = kPresetApplyAll); + + bool SaveJsonPreset(const char * filePath); + bool LoadJsonPreset(const char * filePath, PresetDataPtr presetData); + +protected: + SliderList * currentList; + RaceSliders m_internalMap; + RaceMap m_raceMap; + MorphMap m_morphMap; + ValueMap m_valueMap; + ModelMap m_modelMap; + PresetMap m_mappedPreset; + + SculptStorage m_sculptStorage; + + friend class RacePartDefaultGen; + +#ifdef _DEBUG_DATADUMP + void DumpAll(); +#endif +}; + +class SKSETaskImportHead : public TaskDelegate +{ +public: + virtual void Run(); + virtual void Dispose() { delete this; } + + SKSETaskImportHead(Actor * actor, BSFixedString nifPath); + +private: + UInt32 m_formId; + BSFixedString m_nifPath; +}; + +class SKSETaskApplyMorphs : public TaskDelegate +{ +public: + virtual void Run(); + virtual void Dispose() { delete this; } + + SKSETaskApplyMorphs(Actor * actor); + +private: + UInt32 m_formId; +}; \ No newline at end of file diff --git a/skee/NiTransformInterface.cpp b/skee/NiTransformInterface.cpp index 28e19f9..3971c5e 100644 --- a/skee/NiTransformInterface.cpp +++ b/skee/NiTransformInterface.cpp @@ -39,14 +39,14 @@ void NodeTransformKeys::Save(SKSESerializationInterface * intfc, UInt32 kVersion intfc->OpenRecord('NOTM', kVersion); // Key - WriteKey(intfc, it->first, kVersion); + WriteKey(intfc, it->first, kVersion); // Value it->second.Save(intfc, kVersion); } } -bool NodeTransformKeys::Load(SKSESerializationInterface * intfc, UInt32 kVersion) +bool NodeTransformKeys::Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable) { UInt32 type, length, version; bool error = false; @@ -68,8 +68,8 @@ bool NodeTransformKeys::Load(SKSESerializationInterface * intfc, UInt32 kVersion { case 'NOTM': { - BSFixedString key; - if (ReadKey(intfc, key, kVersion)) { + StringTableItem key; + if (ReadKey(intfc, key, kVersion, stringTable)) { _ERROR("%s - Error loading node entry key", __FUNCTION__); error = true; return error; @@ -79,11 +79,11 @@ bool NodeTransformKeys::Load(SKSESerializationInterface * intfc, UInt32 kVersion bool loadError = false; NodeTransformKeys::iterator iter = this->find(key); // Find existing first if (iter != this->end()) { - error = iter->second.Load(intfc, version); + error = iter->second.Load(intfc, version, stringTable); } else { // No existing, create - OverrideRegistration set; - error = set.Load(intfc, version); + OverrideRegistration set; + error = set.Load(intfc, version, stringTable); emplace(key, set); } if (loadError) @@ -125,7 +125,7 @@ void NodeTransformRegistrationMapHolder::Save(SKSESerializationInterface* intfc, } } -bool NodeTransformRegistrationMapHolder::Load(SKSESerializationInterface* intfc, UInt32 kVersion, UInt64 * outHandle) +bool NodeTransformRegistrationMapHolder::Load(SKSESerializationInterface* intfc, UInt32 kVersion, UInt64 * outHandle, const StringIdMap & stringTable) { bool error = false; @@ -139,7 +139,7 @@ bool NodeTransformRegistrationMapHolder::Load(SKSESerializationInterface* intfc, } MultiRegistration,2> reg; - if (reg.Load(intfc, kVersion)) + if (reg.Load(intfc, kVersion, stringTable)) { _ERROR("%s - Error loading transform gender registrations", __FUNCTION__); error = true; @@ -199,19 +199,18 @@ class NIOVTaskUpdateReference : public TaskDelegate NiTransformInterface * m_interface; }; -void NiTransformInterface::VisitStrings(std::function functor) +void NiTransformInterface::VisitStrings(std::function functor) { for (auto & i1 : transformData.m_data) { for (UInt8 gender = 0; gender <= 1; gender++) { for (UInt8 fp = 0; fp <= 1; fp++) { for (auto & i2 : i1.second[gender][fp]) { - functor(i2.first); + functor(*i2.first); for (auto & i3 : i2.second) { - functor(i3.first); + functor(*i3.first); for (auto & i4 : i3.second) { if (i4.type == OverrideVariant::kType_String) { - BSFixedString str(i4.data.str); - functor(str); + functor(*i4.str); } } } @@ -225,10 +224,10 @@ void NiTransformInterface::Save(SKSESerializationInterface * intfc, UInt32 kVers { transformData.Save(intfc, kVersion); } -bool NiTransformInterface::Load(SKSESerializationInterface* intfc, UInt32 kVersion) +bool NiTransformInterface::Load(SKSESerializationInterface* intfc, UInt32 kVersion, const StringIdMap & stringTable) { UInt64 handle = 0; - if (!transformData.Load(intfc, kVersion, &handle)) + if (!transformData.Load(intfc, kVersion, &handle, stringTable)) { RemoveInvalidTransforms(handle); RemoveNamedTransforms(handle, "internal"); @@ -246,18 +245,18 @@ bool NiTransformInterface::Load(SKSESerializationInterface* intfc, UInt32 kVersi return false; } -bool NiTransformInterface::AddNodeTransform(TESObjectREFR * refr, bool firstPerson, bool isFemale, BSFixedString node, BSFixedString name, OverrideVariant & value) +bool NiTransformInterface::AddNodeTransform(TESObjectREFR * refr, bool firstPerson, bool isFemale, SKEEFixedString node, SKEEFixedString name, OverrideVariant & value) { SimpleLocker lock(&transformData.m_lock); UInt64 handle = g_overrideInterface.GetHandle(refr, refr->formType); - transformData.m_data[handle][isFemale ? 1 : 0][firstPerson ? 1 : 0][node][name].erase(value); - transformData.m_data[handle][isFemale ? 1 : 0][firstPerson ? 1 : 0][node][name].insert(value); + transformData.m_data[handle][isFemale ? 1 : 0][firstPerson ? 1 : 0][g_stringTable.GetString(node)][g_stringTable.GetString(name)].erase(value); + transformData.m_data[handle][isFemale ? 1 : 0][firstPerson ? 1 : 0][g_stringTable.GetString(node)][g_stringTable.GetString(name)].insert(value); return true; } -bool NiTransformInterface::RemoveNodeTransform(TESObjectREFR * refr, bool firstPerson, bool isFemale, BSFixedString node, BSFixedString name) +bool NiTransformInterface::RemoveNodeTransform(TESObjectREFR * refr, bool firstPerson, bool isFemale, SKEEFixedString node, SKEEFixedString name) { SimpleLocker lock(&transformData.m_lock); @@ -268,10 +267,10 @@ bool NiTransformInterface::RemoveNodeTransform(TESObjectREFR * refr, bool firstP auto & it = transformData.m_data.find(handle); if (it != transformData.m_data.end()) { - auto & ait = it->second[gender][fp].find(node); + auto & ait = it->second[gender][fp].find(g_stringTable.GetString(node)); if (ait != it->second[gender][fp].end()) { - auto & oit = ait->second.find(name); + auto & oit = ait->second.find(g_stringTable.GetString(name)); if (oit != ait->second.end()) { ait->second.erase(oit); @@ -296,9 +295,9 @@ void NiTransformInterface::RemoveInvalidTransforms(UInt64 handle) { for (auto it = ait.second.begin(); it != ait.second.end();) { - std::string strKey(it->first.data); - BSFixedString ext(strKey.substr(strKey.find_last_of(".") + 1).c_str()); - if (ext == BSFixedString("esp") || ext == BSFixedString("esm")) + std::string strKey(*it->first); + SKEEFixedString ext(strKey.substr(strKey.find_last_of(".") + 1).c_str()); + if (ext == SKEEFixedString("esp") || ext == SKEEFixedString("esm") || ext == SKEEFixedString("esl")) { it = ait.second.erase(it); } @@ -311,7 +310,7 @@ void NiTransformInterface::RemoveInvalidTransforms(UInt64 handle) } } -void NiTransformInterface::RemoveNamedTransforms(UInt64 handle, BSFixedString name) +void NiTransformInterface::RemoveNamedTransforms(UInt64 handle, SKEEFixedString name) { SimpleLocker lock(&transformData.m_lock); @@ -324,7 +323,7 @@ void NiTransformInterface::RemoveNamedTransforms(UInt64 handle, BSFixedString na { for (auto & ait : it->second[gender][fp]) { - auto & oit = ait.second.find(name); + auto & oit = ait.second.find(g_stringTable.GetString(name)); if (oit != ait.second.end()) { ait.second.erase(oit); @@ -358,7 +357,7 @@ void NiTransformInterface::RemoveAllReferenceTransforms(TESObjectREFR * refr) } } -bool NiTransformInterface::RemoveNodeTransformComponent(TESObjectREFR * refr, bool firstPerson, bool isFemale, BSFixedString node, BSFixedString name, UInt16 key, UInt16 index) +bool NiTransformInterface::RemoveNodeTransformComponent(TESObjectREFR * refr, bool firstPerson, bool isFemale, SKEEFixedString node, SKEEFixedString name, UInt16 key, UInt16 index) { SimpleLocker lock(&transformData.m_lock); @@ -368,10 +367,10 @@ bool NiTransformInterface::RemoveNodeTransformComponent(TESObjectREFR * refr, bo auto & it = transformData.m_data.find(handle); if (it != transformData.m_data.end()) { - auto & ait = it->second[gender][fp].find(node); + auto & ait = it->second[gender][fp].find(g_stringTable.GetString(node)); if (ait != it->second[gender][fp].end()) { - auto & oit = ait->second.find(name); + auto & oit = ait->second.find(g_stringTable.GetString(name)); if (oit != ait->second.end()) { OverrideVariant ovr; @@ -390,7 +389,7 @@ bool NiTransformInterface::RemoveNodeTransformComponent(TESObjectREFR * refr, bo return false; } -void NiTransformInterface::VisitNodes(TESObjectREFR * refr, bool firstPerson, bool isFemale, std::function * value)> functor) +void NiTransformInterface::VisitNodes(TESObjectREFR * refr, bool firstPerson, bool isFemale, std::function * value)> functor) { SimpleLocker lock(&transformData.m_lock); @@ -402,13 +401,13 @@ void NiTransformInterface::VisitNodes(TESObjectREFR * refr, bool firstPerson, bo if (it != transformData.m_data.end()) { for (auto node : it->second[gender][fp]) { - if (functor(node.first, &node.second)) + if (functor(*node.first, &node.second)) break; } } } -bool NiTransformInterface::VisitNodeTransforms(TESObjectREFR * refr, bool firstPerson, bool isFemale, BSFixedString node, std::function*)> each_key, std::function finalize) +bool NiTransformInterface::VisitNodeTransforms(TESObjectREFR * refr, bool firstPerson, bool isFemale, BSFixedString node, std::function*)> each_key, std::function finalize) { SimpleLocker lock(&transformData.m_lock); @@ -437,7 +436,7 @@ bool NiTransformInterface::VisitNodeTransforms(TESObjectREFR * refr, bool firstP NiStringsExtraData * extraSkeletons = ni_cast(extraData, NiStringsExtraData); if (extraSkeletons && (extraSkeletons->m_size % 3) == 0) { for (UInt32 i = 0; i < extraSkeletons->m_size; i+= 3) { - BSFixedString extnSkeleton = extraSkeletons->m_data[i+2]; + SKEEFixedString extnSkeleton = extraSkeletons->m_data[i+2]; baseTransform = transformCache.GetBaseTransform(extnSkeleton, node, false); if (baseTransform) return true; @@ -450,7 +449,7 @@ bool NiTransformInterface::VisitNodeTransforms(TESObjectREFR * refr, bool firstP } if (baseTransform) { - auto & nodeIt = it->second[gender][fp].find(node); + auto & nodeIt = it->second[gender][fp].find(g_stringTable.GetString(node)); if (nodeIt != it->second[gender][fp].end()) if (each_key(&nodeIt->second)) ret = true; @@ -465,12 +464,12 @@ bool NiTransformInterface::VisitNodeTransforms(TESObjectREFR * refr, bool firstP return ret; } -void NiTransformInterface::UpdateNodeTransforms(TESObjectREFR * ref, bool firstPerson, bool isFemale, BSFixedString node) +void NiTransformInterface::UpdateNodeTransforms(TESObjectREFR * ref, bool firstPerson, bool isFemale, SKEEFixedString node) { BSFixedString target(""); NiTransform transformResult; VisitNodeTransforms(ref, firstPerson, isFemale, node, - [&](OverrideRegistration* keys) + [&](OverrideRegistration* keys) { for (auto dit = keys->begin(); dit != keys->end(); ++dit) {// Loop Keys NiTransform localTransform; @@ -483,7 +482,7 @@ void NiTransformInterface::UpdateNodeTransforms(TESObjectREFR * ref, bool firstP value.key = OverrideVariant::kParam_NodeDestination; auto & it = dit->second.find(value); if (it != dit->second.end()) { - target = BSFixedString(it->data.str); + target = it->str ? *it->str : ""; } } return false; @@ -510,17 +509,17 @@ void NiTransformInterface::UpdateNodeTransforms(TESObjectREFR * ref, bool firstP }); } -OverrideVariant NiTransformInterface::GetOverrideNodeValue(TESObjectREFR * refr, bool firstPerson, bool isFemale, BSFixedString node, BSFixedString name, UInt16 key, SInt8 index) +OverrideVariant NiTransformInterface::GetOverrideNodeValue(TESObjectREFR * refr, bool firstPerson, bool isFemale, SKEEFixedString node, SKEEFixedString name, UInt16 key, SInt8 index) { OverrideVariant foundValue; VisitNodeTransforms(refr, firstPerson, isFemale, node, - [&](OverrideRegistration* keys) + [&](OverrideRegistration* keys) { - if (name == BSFixedString("")) { + if (name == SKEEFixedString("")) { return true; } else { - auto & it = keys->find(name); + auto & it = keys->find(g_stringTable.GetString(name)); if (it != keys->end()) { OverrideVariant searchValue; searchValue.key = key; @@ -538,15 +537,15 @@ OverrideVariant NiTransformInterface::GetOverrideNodeValue(TESObjectREFR * refr, return foundValue; } -bool NiTransformInterface::GetOverrideNodeTransform(TESObjectREFR * refr, bool firstPerson, bool isFemale, BSFixedString node, BSFixedString name, UInt16 key, NiTransform * result) +bool NiTransformInterface::GetOverrideNodeTransform(TESObjectREFR * refr, bool firstPerson, bool isFemale, SKEEFixedString node, SKEEFixedString name, UInt16 key, NiTransform * result) { return VisitNodeTransforms(refr, firstPerson, isFemale, node, - [&](OverrideRegistration* keys) + [&](OverrideRegistration* keys) { - if (name == BSFixedString("")) { + if (name == SKEEFixedString("")) { return true; } else { - auto it = keys->find(name); + auto it = keys->find(g_stringTable.GetString(name)); if (it != keys->end()) { GetOverrideTransform(&it->second, key, result); return true; @@ -557,7 +556,7 @@ bool NiTransformInterface::GetOverrideNodeTransform(TESObjectREFR * refr, bool f }, [&](NiNode * root, NiAVObject * foundNode, NiTransform * baseTransform) { - if (name == BSFixedString("")) + if (name == SKEEFixedString("")) *result = *baseTransform; }); } @@ -593,13 +592,13 @@ void NiTransformInterface::SetHandleNodeTransforms(UInt64 handle, bool immediate if (root == lastNode) // First and Third are the same, skip continue; - BSFixedString skeleton = GetRootModelPath(refr, i >= 1 ? true : false, gender >= 1 ? true : false); + SKEEFixedString skeleton = GetRootModelPath(refr, i >= 1 ? true : false, gender >= 1 ? true : false); if (root) { NiAutoRefCounter rc(root); // Gather up skeleton extensions - std::vector additionalSkeletons; - std::set modified, changed; + std::vector additionalSkeletons; + std::set modified, changed; VisitObjects(root, [&](NiAVObject * root) { NiExtraData * extraData = root->GetExtraData(BSFixedString("EXTN").data); @@ -608,7 +607,7 @@ void NiTransformInterface::SetHandleNodeTransforms(UInt64 handle, bool immediate NiStringsExtraData * extraSkeletons = ni_cast(extraData, NiStringsExtraData); if (extraSkeletons && (extraSkeletons->m_size % 3) == 0) { for (UInt32 i = 0; i < extraSkeletons->m_size; i += 3) { - BSFixedString extnSkeleton = extraSkeletons->m_data[i+2]; + SKEEFixedString extnSkeleton = extraSkeletons->m_data[i+2]; additionalSkeletons.push_back(extnSkeleton); } } @@ -652,10 +651,10 @@ void NiTransformInterface::SetHandleNodeTransforms(UInt64 handle, bool immediate for (auto & ait = it->second[gender][i].begin(); ait != it->second[gender][i].end(); ++ait) // Loop Nodes { - NiTransform * baseTransform = transformCache.GetBaseTransform(skeleton, ait->first, true); + NiTransform * baseTransform = transformCache.GetBaseTransform(skeleton, *ait->first, true); if (!baseTransform) { // Not found in base skeleton, search additional skeletons for (auto & secondaryPath : additionalSkeletons) { - baseTransform = transformCache.GetBaseTransform(secondaryPath, ait->first, false); + baseTransform = transformCache.GetBaseTransform(secondaryPath, *ait->first, false); if (baseTransform) break; } @@ -688,7 +687,7 @@ void NiTransformInterface::SetHandleNodeTransforms(UInt64 handle, bool immediate value.key = OverrideVariant::kParam_NodeDestination; auto & it = dit->second.find(value); if (it != dit->second.end()) { - target = BSFixedString(it->data.str); + target = BSFixedString(it->str ? it->str->c_str() : ""); } } if (g_scaleMode == 1) @@ -700,7 +699,7 @@ void NiTransformInterface::SetHandleNodeTransforms(UInt64 handle, bool immediate combinedTransform.scale = fScaleValue; } } - BSFixedString nodeName = ait->first; + BSFixedString nodeName = *ait->first; NiAVObject * transformable = root->GetObjectByName(&nodeName.data); if (transformable) { NiAutoRefCounter rc(transformable); @@ -839,7 +838,7 @@ void NiTransformInterface::GetOverrideTransform(OverrideSet * set, UInt16 key, N } -NiTransform * NodeTransformCache::GetBaseTransform(BSFixedString rootModel, BSFixedString nodeName, bool relative) +NiTransform * NodeTransformCache::GetBaseTransform(SKEEFixedString rootModel, SKEEFixedString nodeName, bool relative) { SimpleLocker lock(&m_lock); @@ -854,17 +853,17 @@ NiTransform * NodeTransformCache::GetBaseTransform(BSFixedString rootModel, BSFi } char pathBuffer[MAX_PATH]; - BSFixedString newPath = rootModel; + SKEEFixedString newPath = rootModel; if (relative) { memset(pathBuffer, 0, MAX_PATH); - sprintf_s(pathBuffer, MAX_PATH, "meshes\\%s", rootModel.data); + sprintf_s(pathBuffer, MAX_PATH, "meshes\\%s", rootModel.c_str()); newPath = pathBuffer; } // No skeleton path found, why is this? - BSResourceNiBinaryStream binaryStream(newPath.data); + BSResourceNiBinaryStream binaryStream(newPath.c_str()); if (!binaryStream.IsValid()) { - _ERROR("%s - Failed to acquire skeleton at \"%s\".", __FUNCTION__, newPath.data); + _ERROR("%s - Failed to acquire skeleton at \"%s\".", __FUNCTION__, newPath.c_str()); return NULL; } @@ -889,8 +888,8 @@ NiTransform * NodeTransformCache::GetBaseTransform(BSFixedString rootModel, BSFi if (child->m_name == NULL) return false; - BSFixedString localName(child->m_name); - if (strlen(localName.data) == 0) + SKEEFixedString localName(child->m_name); + if (localName.length() == 0) return false; transformMap.insert_or_assign(localName, child->m_localTransform); @@ -913,7 +912,7 @@ NiTransform * NodeTransformCache::GetBaseTransform(BSFixedString rootModel, BSFi return NULL; } -BSFixedString NiTransformInterface::GetRootModelPath(TESObjectREFR * refr, bool firstPerson, bool isFemale) +SKEEFixedString NiTransformInterface::GetRootModelPath(TESObjectREFR * refr, bool firstPerson, bool isFemale) { TESModel * model = NULL; Character * character = DYNAMIC_CAST(refr, TESObjectREFR, Character); @@ -921,7 +920,7 @@ BSFixedString NiTransformInterface::GetRootModelPath(TESObjectREFR * refr, bool if (firstPerson) { Setting * setting = (*g_gameSettingCollection)->Get("sFirstPersonSkeleton"); if (setting && setting->GetType() == Setting::kType_String) - return BSFixedString(setting->data.s); + return SKEEFixedString(setting->data.s); } TESRace * race = character->race; @@ -938,7 +937,7 @@ BSFixedString NiTransformInterface::GetRootModelPath(TESObjectREFR * refr, bool model = DYNAMIC_CAST(refr->baseForm, TESForm, TESModel); if (model) - return BSFixedString(model->GetModelName()); + return SKEEFixedString(model->GetModelName()); - return BSFixedString(""); + return SKEEFixedString(""); } \ No newline at end of file diff --git a/skee/NiTransformInterface.h b/skee/NiTransformInterface.h index a234f9f..51a57ae 100644 --- a/skee/NiTransformInterface.h +++ b/skee/NiTransformInterface.h @@ -14,11 +14,11 @@ class NiNode; class NiAVObject; class NiTransform; -class NodeTransformKeys : public std::unordered_map> +class NodeTransformKeys : public std::unordered_map> { public: void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - bool Load(SKSESerializationInterface * intfc, UInt32 kVersion); + bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable); }; class NodeTransformRegistrationMapHolder : public SafeDataHolder,2>>> @@ -28,18 +28,18 @@ class NodeTransformRegistrationMapHolder : public SafeDataHolder,2>> RegMap; void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, UInt64 * outHandle); + bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, UInt64 * outHandle, const StringIdMap & stringTable); }; // Node names are hashed here due to some case where the node "NPC" gets overwritten for some unknown reason -class NodeTransformCache : public SafeDataHolder>> +class NodeTransformCache : public SafeDataHolder>> { friend class NiTransformInterface; public: - typedef std::unordered_map NodeMap; - typedef std::unordered_map RegMap; + typedef std::unordered_map NodeMap; + typedef std::unordered_map RegMap; - NiTransform * GetBaseTransform(BSFixedString rootModel, BSFixedString nodeName, bool relative); + NiTransform * GetBaseTransform(SKEEFixedString rootModel, SKEEFixedString nodeName, bool relative); }; class NiTransformInterface : public IPluginInterface @@ -50,35 +50,36 @@ class NiTransformInterface : public IPluginInterface kCurrentPluginVersion = 2, kSerializationVersion1 = 1, kSerializationVersion2 = 2, - kSerializationVersion = kSerializationVersion2 + kSerializationVersion3 = 3, + kSerializationVersion = kSerializationVersion3 }; virtual UInt32 GetVersion(); virtual void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - virtual bool Load(SKSESerializationInterface* intfc, UInt32 kVersion); + virtual bool Load(SKSESerializationInterface* intfc, UInt32 kVersion, const StringIdMap & stringTable); virtual void Revert(); - virtual bool AddNodeTransform(TESObjectREFR * ref, bool firstPerson, bool isFemale, BSFixedString node, BSFixedString name, OverrideVariant & value); - virtual bool RemoveNodeTransformComponent(TESObjectREFR * ref, bool firstPerson, bool isFemale, BSFixedString node, BSFixedString name, UInt16 key, UInt16 index); - virtual bool RemoveNodeTransform(TESObjectREFR * refr, bool firstPerson, bool isFemale, BSFixedString node, BSFixedString name); + virtual bool AddNodeTransform(TESObjectREFR * ref, bool firstPerson, bool isFemale, SKEEFixedString node, SKEEFixedString name, OverrideVariant & value); + virtual bool RemoveNodeTransformComponent(TESObjectREFR * ref, bool firstPerson, bool isFemale, SKEEFixedString node, SKEEFixedString name, UInt16 key, UInt16 index); + virtual bool RemoveNodeTransform(TESObjectREFR * refr, bool firstPerson, bool isFemale, SKEEFixedString node, SKEEFixedString name); virtual void RemoveAllReferenceTransforms(TESObjectREFR * refr); - virtual OverrideVariant GetOverrideNodeValue(TESObjectREFR * refr, bool firstPerson, bool isFemale, BSFixedString node, BSFixedString name, UInt16 key, SInt8 index); - virtual bool GetOverrideNodeTransform(TESObjectREFR * refr, bool firstPerson, bool isFemale, BSFixedString node, BSFixedString name, UInt16 key, NiTransform * result); + virtual OverrideVariant GetOverrideNodeValue(TESObjectREFR * refr, bool firstPerson, bool isFemale, SKEEFixedString node, SKEEFixedString name, UInt16 key, SInt8 index); + virtual bool GetOverrideNodeTransform(TESObjectREFR * refr, bool firstPerson, bool isFemale, SKEEFixedString node, SKEEFixedString name, UInt16 key, NiTransform * result); virtual void GetOverrideTransform(OverrideSet * set, UInt16 key, NiTransform * result); - virtual BSFixedString GetRootModelPath(TESObjectREFR * refr, bool firstPerson, bool isFemale); + virtual SKEEFixedString GetRootModelPath(TESObjectREFR * refr, bool firstPerson, bool isFemale); virtual void UpdateNodeAllTransforms(TESObjectREFR * ref); - virtual void VisitNodes(TESObjectREFR * refr, bool firstPerson, bool isFemale, std::function*)> functor); - virtual bool VisitNodeTransforms(TESObjectREFR * refr, bool firstPerson, bool isFemale, BSFixedString node, std::function*)> each_key, std::function finalize); - virtual void UpdateNodeTransforms(TESObjectREFR * ref, bool firstPerson, bool isFemale, BSFixedString node); + virtual void VisitNodes(TESObjectREFR * refr, bool firstPerson, bool isFemale, std::function*)> functor); + virtual bool VisitNodeTransforms(TESObjectREFR * refr, bool firstPerson, bool isFemale, BSFixedString node, std::function*)> each_key, std::function finalize); + virtual void UpdateNodeTransforms(TESObjectREFR * ref, bool firstPerson, bool isFemale, SKEEFixedString node); - virtual void VisitStrings(std::function functor); + virtual void VisitStrings(std::function functor); void RemoveInvalidTransforms(UInt64 handle); - void RemoveNamedTransforms(UInt64 handle, BSFixedString name); + void RemoveNamedTransforms(UInt64 handle, SKEEFixedString name); void SetHandleNodeTransforms(UInt64 handle, bool immediate = false, bool reset = false); NodeTransformRegistrationMapHolder transformData; diff --git a/skee/NifUtils.cpp b/skee/NifUtils.cpp index 4be285e..e4bb6fa 100644 --- a/skee/NifUtils.cpp +++ b/skee/NifUtils.cpp @@ -19,6 +19,10 @@ #include "skse64/NiTextures.h" #include "skse64/NiExtraData.h" +#include + +#include "DirectXTex/DirectXTex.h" + #ifdef FIXME #include #pragma comment(lib, "d3dx9.lib") @@ -91,6 +95,38 @@ void SaveRenderedDDS(NiRenderedTexture * pkTexture, const char * pcFileName) } #endif +void SaveRenderedDDS(NiRenderedTexture * pkTexture, const char * pcFileName) +{ + if (pkTexture->rendererData) + { + auto texture = pkTexture->rendererData; + if (texture) + { + auto context = g_renderManager->context; + ID3D11Device * device = nullptr; + context->GetDevice(&device); + + DirectX::ScratchImage si; + if (texture->texture) + { + DirectX::CaptureTexture(device, context, texture->texture, si); + } + + auto image = si.GetImage(0, 0, 0); + if (image) + { + size_t len = strlen(pcFileName) + 1; + wchar_t * fileName = new wchar_t[len]; + memset(fileName, 0, sizeof(len) * 2); + size_t converted = 0; + mbstowcs_s(&converted, fileName, len, pcFileName, len * 2); + DirectX::SaveToDDSFile(*image, DirectX::DDS_FLAGS_NONE, fileName); + delete[] fileName; + } + } + } +} + BSGeometry * GetHeadGeometry(Actor * actor, UInt32 partType) { BSFaceGenNiNode * faceNode = actor->GetFaceGenNiNode(); @@ -172,9 +208,7 @@ void ExportTintMaskDDS(Actor * actor, BSFixedString filePath) if (material->GetShaderType() == BSShaderMaterial::kShaderType_FaceGen) { BSLightingShaderMaterialFacegen * maskedMaterial = (BSLightingShaderMaterialFacegen *)material; -#ifdef FIXME SaveRenderedDDS(niptr_cast(maskedMaterial->renderedTexture), filePath.data); -#endif } } } @@ -239,7 +273,7 @@ void SKSETaskExportHead::Run() if (!actorBase || !faceNode) return; - BSFaceGenAnimationData * animationData = actor->GetFaceGenAnimationData(); + /*BSFaceGenAnimationData * animationData = actor->GetFaceGenAnimationData(); if (animationData) { FaceGen::GetSingleton()->isReset = 0; for (UInt32 t = BSFaceGenAnimationData::kKeyframeType_Expression; t <= BSFaceGenAnimationData::kKeyframeType_Phoneme; t++) @@ -250,7 +284,7 @@ void SKSETaskExportHead::Run() keyframe->isUpdated = 0; } UpdateModelFace(faceNode); - } + }*/ IFileStream::MakeAllDirs(m_nifPath.data); @@ -267,13 +301,11 @@ void SKSETaskExportHead::Run() if (!object) continue; - if (BSGeometry * geometry = object->GetAsBSGeometry()) { -#ifdef FIXME_GEOMETRY + if (NiGeometry * geometry = object->GetAsNiGeometry()) { NiGeometryData * geometryData = niptr_cast(geometry->m_spModelData); NiGeometryData * newGeometryData = NULL; if (geometryData) CALL_MEMBER_FN(geometryData, DeepCopy)((NiObject **)&newGeometryData); -#endif NiProperty * trishapeEffect = niptr_cast(geometry->m_spEffectState); NiProperty * newTrishapeEffect = NULL; @@ -288,7 +320,7 @@ void SKSETaskExportHead::Run() NiSkinInstance * skinInstance = niptr_cast(geometry->m_spSkinInstance); NiSkinInstance * newSkinInstance = NULL; if (skinInstance) { - newSkinInstance = skinInstance->Clone(false); + newSkinInstance = skinInstance->Clone(); newSkinInstance->m_pkRootParent = skinnedNode; UInt32 numBones = 0; @@ -338,9 +370,8 @@ void SKSETaskExportHead::Run() } } - BSGeometry * newGeometry = NULL; + NiGeometry * newGeometry = NULL; -#ifdef FIXME_GEOMETRY if (NiTriShape * trishape = geometry->GetAsNiTriShape()) { NiTriShape * newTrishape = NiTriShape::Create(static_cast(newGeometryData)); newGeometryData->DecRef(); @@ -354,20 +385,205 @@ void SKSETaskExportHead::Run() newTrishape->m_spSkinInstance = newSkinInstance; newGeometry = newTrishape; } - else if (NiTriStrips * tristrips = geometry->GetAsNiTriStrips()) { - NiTriStrips * newTristrips = NiTriStrips::Create(static_cast(newGeometryData)); - newGeometryData->DecRef(); - newTristrips->m_localTransform = geometry->m_localTransform; - newTristrips->m_name = geometry->m_name; + else if (NiTriBasedGeom * trigeom = geometry->GetAsNiTriBasedGeom()) { + + NiTriStrips * tristrips = ni_cast(trigeom, NiTriStrips); + if (tristrips) + { + NiTriStrips * newTristrips = NiTriStrips::Create(static_cast(newGeometryData)); + newGeometryData->DecRef(); + newTristrips->m_localTransform = geometry->m_localTransform; + newTristrips->m_name = geometry->m_name; #ifdef FIXME - memcpy(&newTristrips->unk88, &geometry->unk88, 0x1F); + memcpy(&newTristrips->unk88, &geometry->unk88, 0x1F); #endif - newTristrips->m_spEffectState = newTrishapeEffect; - newTristrips->m_spPropertyState = newTrishapeProperty; - newTristrips->m_spSkinInstance = newSkinInstance; - newGeometry = newTristrips; + newTristrips->m_spEffectState = newTrishapeEffect; + newTristrips->m_spPropertyState = newTrishapeProperty; + newTristrips->m_spSkinInstance = newSkinInstance; + newGeometry = newTristrips; + } + } + + if (newGeometry) + { + auto textureData = GetTextureSetForPartByName(actorBase, newGeometry->m_name); + if (textureData.first && textureData.second) { + BSShaderProperty * shaderProperty = niptr_cast(newGeometry->m_spEffectState); + if (shaderProperty) { + if (ni_is_type(shaderProperty->GetRTTI(), BSLightingShaderProperty)) { + BSLightingShaderProperty * lightingShader = static_cast(shaderProperty); + BSLightingShaderMaterial * material = static_cast(shaderProperty->material); + if (material && material->textureSet) { + for (UInt32 i = 0; i < BGSTextureSet::kNumTextures; i++) + material->textureSet->SetTexturePath(i, textureData.first->textureSet.GetTexturePath(i)); + + if (textureData.second->type == BGSHeadPart::kTypeFace) + material->textureSet->SetTexturePath(6, m_ddsPath.data); + } + } + } + + // Save the previous tint mask + BSShaderProperty * originalShaderProperty = niptr_cast(geometry->m_spEffectState); + if (originalShaderProperty) { + if (ni_is_type(originalShaderProperty->GetRTTI(), BSLightingShaderProperty)) { + BSLightingShaderProperty * lightingShader = static_cast(originalShaderProperty); + BSLightingShaderMaterial * material = static_cast(originalShaderProperty->material); + if (material) { + if (material->GetShaderType() == BSShaderMaterial::kShaderType_FaceGen) { + BSLightingShaderMaterialFacegen * maskedMaterial = static_cast(material); + SaveRenderedDDS(niptr_cast(maskedMaterial->renderedTexture), m_ddsPath.data); + } + } + } + } + } + + skinnedNode->AttachChild(newGeometry, true); + } + } + else if (BSGeometry * geometry = object->GetAsBSGeometry()) { + + NiProperty * trishapeEffect = niptr_cast(geometry->m_spEffectState); + NiProperty * newTrishapeEffect = NULL; + if (trishapeEffect) + CALL_MEMBER_FN(trishapeEffect, DeepCopy)((NiObject **)&newTrishapeEffect); + + NiProperty * trishapeProperty = niptr_cast(geometry->m_spPropertyState); + NiProperty * newTrishapeProperty = NULL; + if (trishapeProperty) + CALL_MEMBER_FN(trishapeProperty, DeepCopy)((NiObject **)&newTrishapeProperty); + + NiSkinInstance * skinInstance = niptr_cast(geometry->m_spSkinInstance); + NiSkinInstance * newSkinInstance = NULL; + if (skinInstance) { + newSkinInstance = skinInstance->Clone(); + newSkinInstance->m_pkRootParent = skinnedNode; + + UInt32 numBones = 0; + NiSkinData * skinData = niptr_cast(skinInstance->m_spSkinData); + NiSkinData * newSkinData = NULL; + if (skinData) { + numBones = skinData->m_uiBones; + CALL_MEMBER_FN(skinData, DeepCopy)((NiObject **)&newSkinData); + } + + NiSkinPartition * skinPartition = niptr_cast(skinInstance->m_spSkinPartition); + NiSkinPartition * newSkinPartition = NULL; + if (skinPartition) + CALL_MEMBER_FN(skinPartition, DeepCopy)((NiObject **)&newSkinPartition); + + newSkinInstance->m_spSkinData = newSkinData; + newSkinData->DecRef(); + + newSkinInstance->m_spSkinPartition = newSkinPartition; + newSkinPartition->DecRef(); + + // Remap the bones to new NiNode instances + if (numBones > 0) + { + newSkinInstance->m_ppkBones = (NiAVObject**)Heap_Allocate(numBones * sizeof(NiAVObject*)); + for (UInt32 i = 0; i < numBones; i++) + { + NiAVObject * bone = skinInstance->m_ppkBones[i]; + if (bone) + { + auto it = boneMap.find(bone); + if (it == boneMap.end()) { + NiNode * newBone = NiNode::Create(); + newBone->m_name = bone->m_name; + newBone->m_flags = bone->m_flags; + boneMap.insert(std::make_pair(bone, newBone)); + newSkinInstance->m_ppkBones[i] = newBone; + } + else + newSkinInstance->m_ppkBones[i] = it->second; + } + else + { + newSkinInstance->m_ppkBones[i] = nullptr; + } + } + } + } + + BSGeometry * newGeometry = NULL; + + BSTriShape * trishape = geometry->GetAsBSTriShape(); + if (trishape) + { + BSTriShape * newTrishape = nullptr; + BSDynamicTriShape * dynamicShape = ni_cast(trishape, BSDynamicTriShape); + if (dynamicShape) + { + void* memory = Heap_Allocate(sizeof(BSDynamicTriShape)); + memset(memory, 0, sizeof(BSDynamicTriShape)); + BSDynamicTriShape* xData = (BSDynamicTriShape*)memory; + xData->ctor(); + newTrishape = xData; + if (dynamicShape->diffBlock) + { + xData->diffBlock = (float*)Heap_Allocate(trishape->numVertices * sizeof(float) * 4); + memcpy(xData->diffBlock, dynamicShape->diffBlock, trishape->numVertices * sizeof(float) * 4); + } + + xData->unk168 = dynamicShape->unk168; + xData->unk170 = dynamicShape->unk170; + xData->unk178 = dynamicShape->unk178; + } + else + { + newTrishape = CreateBSTriShape(); + } + + if (newTrishape) + { + newTrishape->m_localTransform = geometry->m_localTransform; + newTrishape->m_name = geometry->m_name; + newTrishape->m_spEffectState = newTrishapeEffect; + if (newTrishapeEffect) + newTrishapeEffect->DecRef(); + + newTrishape->m_spPropertyState = newTrishapeProperty; + if (newTrishapeProperty) + newTrishapeProperty->DecRef(); + + newTrishape->m_spSkinInstance = newSkinInstance; + + newTrishape->geometryData = trishape->geometryData; + if (newTrishape->geometryData) + { + newTrishape->geometryData->refCount++; + } + + newTrishape->m_flags = trishape->m_flags; + + newTrishape->unkE4 = trishape->unkE4; + newTrishape->unkE8 = trishape->unkE8; + newTrishape->unkEC = trishape->unkEC; + newTrishape->unkF0 = trishape->unkF0; + + newTrishape->unk104 = trishape->unk104; + newTrishape->unk108 = trishape->unk108; + newTrishape->unk109 = trishape->unk109; + + newTrishape->unk110 = trishape->unk110; + newTrishape->unk118 = trishape->unk118; + newTrishape->unk140 = trishape->unk140; + newTrishape->unk148 = trishape->unk148; + newTrishape->unk150 = trishape->unk150; + newTrishape->unk151 = trishape->unk151; + newTrishape->unk152 = trishape->unk152; + newTrishape->unk154 = trishape->unk154; + + newTrishape->unk158 = trishape->unk158; + newTrishape->numVertices = trishape->numVertices; + newTrishape->unk15C = trishape->unk15C; + newTrishape->unk15D = trishape->unk15D; + } + + newGeometry = newTrishape; } -#endif if (newGeometry) { @@ -397,9 +613,7 @@ void SKSETaskExportHead::Run() if (material) { if (material->GetShaderType() == BSShaderMaterial::kShaderType_FaceGen) { BSLightingShaderMaterialFacegen * maskedMaterial = static_cast(material); -#ifdef FIXME SaveRenderedDDS(niptr_cast(maskedMaterial->renderedTexture), m_ddsPath.data); -#endif } } } @@ -416,8 +630,9 @@ void SKSETaskExportHead::Run() rootNode->AttachChild(skinnedNode, true); - UInt8 niStreamMemory[0x5B4]; - memset(niStreamMemory, 0, 0x5B4); + static const int MAX_SIZE = sizeof(NiStream) + 0x200; + UInt8 niStreamMemory[MAX_SIZE]; + memset(niStreamMemory, 0, MAX_SIZE); NiStream * niStream = (NiStream *)niStreamMemory; CALL_MEMBER_FN(niStream, ctor)(); CALL_MEMBER_FN(niStream, AddObject)(rootNode); @@ -426,12 +641,12 @@ void SKSETaskExportHead::Run() rootNode->DecRef(); - if (animationData) { + /*if (animationData) { animationData->overrideFlag = 0; CALL_MEMBER_FN(animationData, Reset)(1.0, 1, 1, 0, 0); FaceGen::GetSingleton()->isReset = 1; UpdateModelFace(faceNode); - } + }*/ } bool VisitObjects(NiAVObject * parent, std::function functor) diff --git a/skee/OverlayInterface.cpp b/skee/OverlayInterface.cpp index c1e4b97..d46bb48 100644 --- a/skee/OverlayInterface.cpp +++ b/skee/OverlayInterface.cpp @@ -771,13 +771,13 @@ void OverlayInterface::RevertOverlays(TESObjectREFR * reference, bool resetDiffu for(UInt32 i = 0; i < g_numFaceOverlays; i++) { memset(buff, 0, MAX_PATH); - sprintf_s(buff, MAX_PATH, FEET_NODE, i); + sprintf_s(buff, MAX_PATH, FACE_NODE, i); g_task->AddTask(new SKSETaskRevertFaceOverlay(reference, buff, BGSHeadPart::kTypeFace, BSShaderMaterial::kShaderType_FaceGen, resetDiffuse)); } for(UInt32 i = 0; i < g_numSpellFaceOverlays; i++) { memset(buff, 0, MAX_PATH); - sprintf_s(buff, MAX_PATH, FEET_NODE_SPELL, i); + sprintf_s(buff, MAX_PATH, FACE_NODE_SPELL, i); g_task->AddTask(new SKSETaskRevertFaceOverlay(reference, buff, BGSHeadPart::kTypeFace, BSShaderMaterial::kShaderType_FaceGen, resetDiffuse)); } } diff --git a/skee/OverrideInterface.cpp b/skee/OverrideInterface.cpp index 4a26b70..fe4a19e 100644 --- a/skee/OverrideInterface.cpp +++ b/skee/OverrideInterface.cpp @@ -28,8 +28,8 @@ UInt32 OverrideInterface::GetVersion() void OverrideInterface::AddRawOverride(UInt64 handle, bool isFemale, UInt64 armorHandle, UInt64 addonHandle, BSFixedString nodeName, OverrideVariant & value) { armorData.Lock(); - armorData.m_data[handle][isFemale ? 1 : 0][armorHandle][addonHandle][nodeName].erase(value); - armorData.m_data[handle][isFemale ? 1 : 0][armorHandle][addonHandle][nodeName].insert(value); + armorData.m_data[handle][isFemale ? 1 : 0][armorHandle][addonHandle][g_stringTable.GetString(nodeName)].erase(value); + armorData.m_data[handle][isFemale ? 1 : 0][armorHandle][addonHandle][g_stringTable.GetString(nodeName)].insert(value); armorData.Release(); } @@ -39,16 +39,16 @@ void OverrideInterface::AddOverride(TESObjectREFR * refr, bool isFemale, TESObje UInt64 armorHandle = GetHandle(armor, armor->formType); UInt64 addonHandle = GetHandle(addon, addon->formType); armorData.Lock(); - armorData.m_data[handle][isFemale ? 1 : 0][armorHandle][addonHandle][nodeName].erase(value); - armorData.m_data[handle][isFemale ? 1 : 0][armorHandle][addonHandle][nodeName].insert(value); + armorData.m_data[handle][isFemale ? 1 : 0][armorHandle][addonHandle][g_stringTable.GetString(nodeName)].erase(value); + armorData.m_data[handle][isFemale ? 1 : 0][armorHandle][addonHandle][g_stringTable.GetString(nodeName)].insert(value); armorData.Release(); } void OverrideInterface::AddRawNodeOverride(UInt64 handle, bool isFemale, BSFixedString nodeName, OverrideVariant & value) { nodeData.Lock(); - nodeData.m_data[handle][isFemale ? 1 : 0][nodeName].erase(value); - nodeData.m_data[handle][isFemale ? 1 : 0][nodeName].insert(value); + nodeData.m_data[handle][isFemale ? 1 : 0][g_stringTable.GetString(nodeName)].erase(value); + nodeData.m_data[handle][isFemale ? 1 : 0][g_stringTable.GetString(nodeName)].insert(value); nodeData.Release(); } @@ -56,16 +56,16 @@ void OverrideInterface::AddNodeOverride(TESObjectREFR * refr, bool isFemale, BSF { UInt64 handle = GetHandle(refr, refr->formType); nodeData.Lock(); - nodeData.m_data[handle][isFemale ? 1 : 0][nodeName].erase(value); - nodeData.m_data[handle][isFemale ? 1 : 0][nodeName].insert(value); + nodeData.m_data[handle][isFemale ? 1 : 0][g_stringTable.GetString(nodeName)].erase(value); + nodeData.m_data[handle][isFemale ? 1 : 0][g_stringTable.GetString(nodeName)].insert(value); nodeData.Release(); } void OverrideInterface::AddRawWeaponOverride(UInt64 handle, bool isFemale, bool firstPerson, UInt64 weaponHandle, BSFixedString nodeName, OverrideVariant & value) { weaponData.Lock(); - weaponData.m_data[handle][isFemale ? 1 : 0][firstPerson ? 1 : 0][weaponHandle][nodeName].erase(value); - weaponData.m_data[handle][isFemale ? 1 : 0][firstPerson ? 1 : 0][weaponHandle][nodeName].insert(value); + weaponData.m_data[handle][isFemale ? 1 : 0][firstPerson ? 1 : 0][weaponHandle][g_stringTable.GetString(nodeName)].erase(value); + weaponData.m_data[handle][isFemale ? 1 : 0][firstPerson ? 1 : 0][weaponHandle][g_stringTable.GetString(nodeName)].insert(value); weaponData.Release(); } @@ -74,8 +74,8 @@ void OverrideInterface::AddWeaponOverride(TESObjectREFR * refr, bool isFemale, b UInt64 handle = GetHandle(refr, refr->formType); UInt64 weaponHandle = GetHandle(weapon, weapon->formType); weaponData.Lock(); - weaponData.m_data[handle][isFemale ? 1 : 0][firstPerson ? 1 : 0][weaponHandle][nodeName].erase(value); - weaponData.m_data[handle][isFemale ? 1 : 0][firstPerson ? 1 : 0][weaponHandle][nodeName].insert(value); + weaponData.m_data[handle][isFemale ? 1 : 0][firstPerson ? 1 : 0][weaponHandle][g_stringTable.GetString(nodeName)].erase(value); + weaponData.m_data[handle][isFemale ? 1 : 0][firstPerson ? 1 : 0][weaponHandle][g_stringTable.GetString(nodeName)].insert(value); weaponData.Release(); } @@ -109,7 +109,7 @@ OverrideVariant * OverrideInterface::GetOverride(TESObjectREFR * refr, bool isFe auto & dit = ait->second.find(addonHandle); if(dit != ait->second.end()) { - auto & oit = dit->second.find(nodeName); + auto & oit = dit->second.find(g_stringTable.GetString(nodeName)); if(oit != dit->second.end()) { OverrideVariant ovr; @@ -136,7 +136,7 @@ OverrideVariant * OverrideInterface::GetNodeOverride(TESObjectREFR * refr, bool auto & it = nodeData.m_data.find(handle); if(it != nodeData.m_data.end()) { - auto & oit = it->second[gender].find(nodeName); + auto & oit = it->second[gender].find(g_stringTable.GetString(nodeName)); if(oit != it->second[gender].end()) { OverrideVariant ovr; @@ -165,7 +165,7 @@ OverrideVariant * OverrideInterface::GetWeaponOverride(TESObjectREFR * refr, boo auto & ait = it->second[gender][firstPerson].find(weaponHandle); if (ait != it->second[gender][firstPerson].end()) { - auto & oit = ait->second.find(nodeName); + auto & oit = ait->second.find(g_stringTable.GetString(nodeName)); if (oit != ait->second.end()) { OverrideVariant ovr; @@ -334,7 +334,7 @@ void OverrideInterface::RemoveAllArmorAddonNodeOverrides(TESObjectREFR * refr, b auto & dit = ait->second.find(addonHandle); if(dit != ait->second.end()) { - auto & oit = dit->second.find(nodeName); + auto & oit = dit->second.find(g_stringTable.GetString(nodeName)); if(oit != dit->second.end()) { dit->second.erase(oit); @@ -360,7 +360,7 @@ void OverrideInterface::RemoveArmorAddonOverride(TESObjectREFR * refr, bool isFe auto & dit = ait->second.find(addonHandle); if(dit != ait->second.end()) { - auto & oit = dit->second.find(nodeName); + auto & oit = dit->second.find(g_stringTable.GetString(nodeName)); if(oit != dit->second.end()) { OverrideVariant ovr; @@ -385,7 +385,7 @@ void OverrideInterface::RemoveAllNodeNameOverrides(TESObjectREFR * refr, bool is auto & it = nodeData.m_data.find(handle); if(it != nodeData.m_data.end()) { - auto & oit = it->second[gender].find(nodeName); + auto & oit = it->second[gender].find(g_stringTable.GetString(nodeName)); if(oit != it->second[gender].end()) { it->second[gender].erase(oit); @@ -401,7 +401,7 @@ void OverrideInterface::RemoveNodeOverride(TESObjectREFR * refr, bool isFemale, auto & it = nodeData.m_data.find(handle); if(it != nodeData.m_data.end()) { - auto & oit = it->second[gender].find(nodeName); + auto & oit = it->second[gender].find(g_stringTable.GetString(nodeName)); if(oit != it->second[gender].end()) { OverrideVariant ovr; @@ -447,7 +447,7 @@ void OverrideInterface::RemoveAllWeaponNodeOverrides(TESObjectREFR * refr, bool WeaponRegistration::iterator ait = it->second[gender][fPerson].find(weaponHandle); if(ait != it->second[gender][firstPerson].end()) { - OverrideRegistration::iterator oit = ait->second.find(nodeName); + OverrideRegistration::iterator oit = ait->second.find(g_stringTable.GetString(nodeName)); if(oit != ait->second.end()) { ait->second.erase(oit); @@ -470,7 +470,7 @@ void OverrideInterface::RemoveWeaponOverride(TESObjectREFR * refr, bool isFemale WeaponRegistration::iterator ait = it->second[gender][fPerson].find(weaponHandle); if(ait != it->second[gender][firstPerson].end()) { - OverrideRegistration::iterator oit = ait->second.find(nodeName); + OverrideRegistration::iterator oit = ait->second.find(g_stringTable.GetString(nodeName)); if(oit != ait->second.end()) { OverrideVariant ovr; @@ -797,10 +797,10 @@ void OverrideInterface::SetHandleProperties(UInt64 handle, bool immediate) VisitArmorAddon(actor, armor, addon, [&](bool isFP, NiNode * rootNode, NiAVObject * armorNode) { - dit->second.Visit([&](const BSFixedString & key, OverrideSet * set) + dit->second.Visit([&](const StringTableItem & key, OverrideSet * set) { - BSFixedString nodeName(key.data); - NiAVObject * foundNode = key == BSFixedString("") ? armorNode : armorNode->GetObjectByName(&nodeName.data); + BSFixedString nodeName(key->c_str()); + NiAVObject * foundNode = nodeName == BSFixedString("") ? armorNode : armorNode->GetObjectByName(&nodeName.data); if (foundNode) { set->Visit([&](OverrideVariant * value) { @@ -878,10 +878,10 @@ void OverrideInterface::SetHandleNodeProperties(UInt64 handle, bool immediate) if(root) { root->IncRef(); - nit->second[gender].Visit([&](const BSFixedString & key, OverrideSet * set) + nit->second[gender].Visit([&](const StringTableItem & key, OverrideSet * set) { - BSFixedString nodeName(key.data); - NiAVObject * foundNode = key == BSFixedString("") ? root : root->GetObjectByName(&nodeName.data); + BSFixedString nodeName(key->c_str()); + NiAVObject * foundNode = nodeName == BSFixedString("") ? root : root->GetObjectByName(&nodeName.data); if (foundNode) { set->Visit([&](OverrideVariant * value) { @@ -942,10 +942,10 @@ void OverrideInterface::SetHandleWeaponProperties(UInt64 handle, bool immediate) // Find the Armor node NiAVObject * weaponNode = root->GetObjectByName(&weaponName.data); if (weaponNode) { - ait->second.Visit([&](const BSFixedString & key, OverrideSet * set) + ait->second.Visit([&](const StringTableItem & key, OverrideSet * set) { - BSFixedString nodeName(key.data); - NiAVObject * foundNode = key == BSFixedString("") ? weaponNode : weaponNode->GetObjectByName(&nodeName.data); + BSFixedString nodeName(key->c_str()); + NiAVObject * foundNode = nodeName == BSFixedString("") ? weaponNode : weaponNode->GetObjectByName(&nodeName.data); if (foundNode) { set->Visit([&](OverrideVariant * value) { @@ -1042,7 +1042,7 @@ void OverrideInterface::SetHandleSkinProperties(UInt64 handle, bool immediate) } } -void OverrideInterface::VisitNodes(TESObjectREFR * refr, std::function functor) +void OverrideInterface::VisitNodes(TESObjectREFR * refr, std::function functor) { UInt64 handle = GetHandle(refr, refr->formType); @@ -1058,7 +1058,7 @@ void OverrideInterface::VisitNodes(TESObjectREFR * refr, std::functionsecond[gender]) // Loop Overrides { for (auto prop : ovr.second) { - functor(ovr.first, prop); + functor(*ovr.first, prop); } } } @@ -1160,12 +1160,12 @@ void OverrideInterface::SetHandleArmorAddonProperties(UInt64 handle, UInt64 armo class NodeOverrideApplicator : public GeometryVisitor { public: - NodeOverrideApplicator::NodeOverrideApplicator(OverrideRegistration * overrides, bool immediate) : m_overrides(overrides), m_immediate(immediate) {} + NodeOverrideApplicator::NodeOverrideApplicator(OverrideRegistration * overrides, bool immediate) : m_overrides(overrides), m_immediate(immediate) {} virtual bool Accept(BSGeometry * geometry) { - BSFixedString nodeName(geometry->m_name); - OverrideRegistration::iterator nit = m_overrides->find(nodeName); + SKEEFixedString nodeName(geometry->m_name); + auto nit = m_overrides->find(g_stringTable.GetString(nodeName)); if(nit != m_overrides->end()) { nit->second.Visit([&](OverrideVariant * value) @@ -1177,14 +1177,14 @@ class NodeOverrideApplicator : public GeometryVisitor return false; } - OverrideRegistration * m_overrides; + OverrideRegistration * m_overrides; bool m_immediate; }; class OverrideApplicator : public GeometryVisitor { public: - OverrideApplicator::OverrideApplicator(OverrideRegistration * overrides, bool immediate) : m_overrides(overrides), m_immediate(immediate) {} + OverrideApplicator::OverrideApplicator(OverrideRegistration * overrides, bool immediate) : m_overrides(overrides), m_immediate(immediate) {} virtual bool Accept(BSGeometry * geometry) { @@ -1196,8 +1196,8 @@ class OverrideApplicator : public GeometryVisitor { for(auto & geometry : m_geometryList) { - BSFixedString objectName(m_geometryList.size() == 1 ? "" : geometry->m_name); - OverrideRegistration::iterator nit = m_overrides->find(objectName); + SKEEFixedString objectName(m_geometryList.size() == 1 ? "" : geometry->m_name); + auto nit = m_overrides->find(g_stringTable.GetString(objectName)); if(nit != m_overrides->end()) { nit->second.Visit([&](OverrideVariant* value) @@ -1210,7 +1210,7 @@ class OverrideApplicator : public GeometryVisitor } std::vector m_geometryList; - OverrideRegistration * m_overrides; + OverrideRegistration * m_overrides; bool m_immediate; }; @@ -1488,7 +1488,7 @@ void OverrideVariant::Save(SKSESerializationInterface * intfc, UInt32 kVersion) break; case kType_String: { - g_stringTable.WriteString(intfc, data.str, kVersion); + g_stringTable.WriteString(intfc, str); } break; case kType_Identifier: @@ -1506,7 +1506,7 @@ void OverrideVariant::Save(SKSESerializationInterface * intfc, UInt32 kVersion) #endif } -bool OverrideVariant::Load(SKSESerializationInterface * intfc, UInt32 kVersion) +bool OverrideVariant::Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable) { UInt32 type, length, version; bool error = false; @@ -1578,8 +1578,16 @@ bool OverrideVariant::Load(SKSESerializationInterface * intfc, UInt32 kVersion) break; case kType_String: { - BSFixedString str = g_stringTable.ReadString(intfc, kVersion); - this->data.str = str.data; + if (kVersion >= OverrideInterface::kSerializationVersion3) + { + this->str = g_stringTable.ReadString(intfc, stringTable); + } + else if (kVersion >= OverrideInterface::kSerializationVersion1) + { + SKEEFixedString str; + Serialization::ReadData(intfc, &str); + this->str = g_stringTable.GetString(str); + } } break; case kType_Identifier: @@ -1623,18 +1631,17 @@ bool OverrideVariant::Load(SKSESerializationInterface * intfc, UInt32 kVersion) return error; } -void OverrideInterface::VisitStrings(std::function functor) +void OverrideInterface::VisitStrings(std::function functor) { for (auto & i1 : armorData.m_data){ for (UInt8 gender = 0; gender <= 1; gender++) { for (UInt8 fp = 0; fp <= 1; fp++) { for (auto & i2 : i1.second[gender][fp]) { for (auto & i3 : i2.second) { - functor(i3.first); + functor(*i3.first); for (auto & i4 : i3.second){ if (i4.type == OverrideVariant::kType_String) { - BSFixedString str(i4.data.str); - functor(str); + functor(*i4.str); } } } @@ -1648,11 +1655,10 @@ void OverrideInterface::VisitStrings(std::function functor) for (UInt8 fp = 0; fp <= 1; fp++) { for (auto & i2 : i1.second[gender][fp]) { for (auto & i3 : i2.second) { - functor(i3.first); + functor(*i3.first); for (auto & i4 : i3.second) { if (i4.type == OverrideVariant::kType_String) { - BSFixedString str(i4.data.str); - functor(str); + functor(*i4.str); } } } @@ -1664,11 +1670,10 @@ void OverrideInterface::VisitStrings(std::function functor) for (auto & i1 : nodeData.m_data) { for (UInt8 fp = 0; fp <= 1; fp++) { for (auto & i2 : i1.second[fp]) { - functor(i2.first); + functor(*i2.first); for (auto & i3 : i2.second) { if (i3.type == OverrideVariant::kType_String) { - BSFixedString str(i3.data.str); - functor(str); + functor(*i3.str); } } } @@ -1681,8 +1686,7 @@ void OverrideInterface::VisitStrings(std::function functor) for (auto & i2 : i1.second[gender][fp]) { for (auto & i3 : i2.second) { if (i3.type == OverrideVariant::kType_String) { - BSFixedString str(i3.data.str); - functor(str); + functor(*i3.str); } } } @@ -1707,7 +1711,7 @@ void OverrideSet::Save(SKSESerializationInterface * intfc, UInt32 kVersion) const_cast((*it)).Save(intfc, kVersion); } -bool OverrideSet::Load(SKSESerializationInterface * intfc, UInt32 kVersion) +bool OverrideSet::Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable) { UInt32 type, length, version; bool error = false; @@ -1730,7 +1734,7 @@ bool OverrideSet::Load(SKSESerializationInterface * intfc, UInt32 kVersion) for (UInt32 i = 0; i < numOverrides; i++) { OverrideVariant value; - if (!value.Load(intfc, version)) + if (!value.Load(intfc, version, stringTable)) { if(value.type == OverrideVariant::kType_None) continue; @@ -1739,7 +1743,7 @@ bool OverrideSet::Load(SKSESerializationInterface * intfc, UInt32 kVersion) if (value.type != OverrideVariant::kType_String) _MESSAGE("Loaded override value %d %X", value.key, value.data.u); else - _MESSAGE("Loaded override value %d %s", value.key, value.data.GetStr()->data); + _MESSAGE("Loaded override value %d %s", value.key, value.str->c_str()); #endif this->insert(value); @@ -1768,14 +1772,14 @@ bool OverrideSet::Load(SKSESerializationInterface * intfc, UInt32 kVersion) // OverrideRegistration template<> -bool ReadKey(SKSESerializationInterface * intfc, BSFixedString & key, UInt32 kVersion) +bool ReadKey(SKSESerializationInterface * intfc, StringTableItem & key, UInt32 kVersion, const StringIdMap & stringTable) { - key = g_stringTable.ReadString(intfc, kVersion); + key = StringTable::ReadString(intfc, stringTable); return false; } template<> -bool ReadKey(SKSESerializationInterface * intfc, UInt32 & key, UInt32 kVersion) +bool ReadKey(SKSESerializationInterface * intfc, UInt32 & key, UInt32 kVersion, const StringIdMap & stringTable) { if(!intfc->ReadRecordData(&key, sizeof(key))) { return true; @@ -1785,12 +1789,12 @@ bool ReadKey(SKSESerializationInterface * intfc, UInt32 & key, UInt32 kVersion) } template<> -void WriteKey(SKSESerializationInterface * intfc, const BSFixedString key, UInt32 kVersion) +void WriteKey(SKSESerializationInterface * intfc, const StringTableItem key, UInt32 kVersion) { - g_stringTable.WriteString(intfc, key, kVersion); + g_stringTable.WriteString(intfc, key); #ifdef _DEBUG - _MESSAGE("Saving Key %s", key.data); + _MESSAGE("Saving Key %s", key->c_str()); #endif } @@ -1822,7 +1826,7 @@ void OverrideRegistration::Save(SKSESerializationInterface * intfc, UInt32 kV } template -bool OverrideRegistration::Load(SKSESerializationInterface * intfc, UInt32 kVersion) +bool OverrideRegistration::Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable) { UInt32 type, length, version; bool error = false; @@ -1845,7 +1849,7 @@ bool OverrideRegistration::Load(SKSESerializationInterface * intfc, UInt32 kV case 'NOEN': { T key; - if(ReadKey(intfc, key, kVersion)) { + if(ReadKey(intfc, key, kVersion, stringTable)) { _MESSAGE("%s - Error loading node entry key", __FUNCTION__); error = true; return error; @@ -1855,10 +1859,10 @@ bool OverrideRegistration::Load(SKSESerializationInterface * intfc, UInt32 kV bool loadError = false; auto iter = this->find(key); // Find existing first if(iter != this->end()) { - error = iter->second.Load(intfc, version); + error = iter->second.Load(intfc, version, stringTable); } else { // No existing, create OverrideSet set; - error = set.Load(intfc, version); + error = set.Load(intfc, version, stringTable); emplace(key, set); } if(loadError) @@ -1905,7 +1909,7 @@ void AddonRegistration::Save(SKSESerializationInterface * intfc, UInt32 kVersion } } -bool AddonRegistration::Load(SKSESerializationInterface * intfc, UInt32 kVersion) +bool AddonRegistration::Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable) { UInt32 type, length, version; bool error = false; @@ -1936,8 +1940,8 @@ bool AddonRegistration::Load(SKSESerializationInterface * intfc, UInt32 kVersion return error; } - OverrideRegistration overrideRegistration; - if (overrideRegistration.Load(intfc, version)) + OverrideRegistration overrideRegistration; + if (overrideRegistration.Load(intfc, version, stringTable)) { _MESSAGE("%s - Error loading ArmorAddon override registrations", __FUNCTION__); error = true; @@ -1994,7 +1998,7 @@ void ArmorRegistration::Save(SKSESerializationInterface * intfc, UInt32 kVersion } } -bool ArmorRegistration::Load(SKSESerializationInterface * intfc, UInt32 kVersion) +bool ArmorRegistration::Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable) { UInt32 type, length, version; bool error = false; @@ -2015,7 +2019,7 @@ bool ArmorRegistration::Load(SKSESerializationInterface * intfc, UInt32 kVersion } AddonRegistration addonRegistration; - if (addonRegistration.Load(intfc, version)) + if (addonRegistration.Load(intfc, version, stringTable)) { _MESSAGE("%s - Error loading ArmorAddon registrations", __FUNCTION__); error = true; @@ -2072,7 +2076,7 @@ void WeaponRegistration::Save(SKSESerializationInterface * intfc, UInt32 kVersio } } -bool WeaponRegistration::Load(SKSESerializationInterface * intfc, UInt32 kVersion) +bool WeaponRegistration::Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable) { UInt32 type, length, version; bool error = false; @@ -2103,8 +2107,8 @@ bool WeaponRegistration::Load(SKSESerializationInterface * intfc, UInt32 kVersio return error; } - OverrideRegistration overrideRegistration; - if (overrideRegistration.Load(intfc, version)) + OverrideRegistration overrideRegistration; + if (overrideRegistration.Load(intfc, version, stringTable)) { _MESSAGE("%s - Error loading Weapon override registrations", __FUNCTION__); error = true; @@ -2161,7 +2165,7 @@ void SkinRegistration::Save(SKSESerializationInterface * intfc, UInt32 kVersion) } } -bool SkinRegistration::Load(SKSESerializationInterface * intfc, UInt32 kVersion) +bool SkinRegistration::Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable) { UInt32 type, length, version; bool error = false; @@ -2193,7 +2197,7 @@ bool SkinRegistration::Load(SKSESerializationInterface * intfc, UInt32 kVersion) } OverrideSet overrideSet; - if (overrideSet.Load(intfc, version)) + if (overrideSet.Load(intfc, version, stringTable)) { _MESSAGE("%s - Error loading skin override set", __FUNCTION__); error = true; @@ -2222,7 +2226,7 @@ bool SkinRegistration::Load(SKSESerializationInterface * intfc, UInt32 kVersion) return error; } -bool NodeRegistrationMapHolder::Load(SKSESerializationInterface * intfc, UInt32 kVersion, UInt64 * outHandle) +bool NodeRegistrationMapHolder::Load(SKSESerializationInterface * intfc, UInt32 kVersion, UInt64 * outHandle, const StringIdMap & stringTable) { bool error = false; @@ -2234,8 +2238,8 @@ bool NodeRegistrationMapHolder::Load(SKSESerializationInterface * intfc, UInt32 return error; } - MultiRegistration,2> reg; - if (reg.Load(intfc, kVersion)) + MultiRegistration,2> reg; + if (reg.Load(intfc, kVersion, stringTable)) { _MESSAGE("%s - Error loading override gender registrations", __FUNCTION__); error = true; @@ -2306,7 +2310,7 @@ void ActorRegistrationMapHolder::Save(SKSESerializationInterface* intfc, UInt32 } } -bool ActorRegistrationMapHolder::Load(SKSESerializationInterface* intfc, UInt32 kVersion, UInt64 * outHandle) +bool ActorRegistrationMapHolder::Load(SKSESerializationInterface* intfc, UInt32 kVersion, UInt64 * outHandle, const StringIdMap & stringTable) { bool error = false; @@ -2320,7 +2324,7 @@ bool ActorRegistrationMapHolder::Load(SKSESerializationInterface* intfc, UInt32 } MultiRegistration reg; - if (reg.Load(intfc, kVersion)) + if (reg.Load(intfc, kVersion, stringTable)) { _MESSAGE("%s - Error loading armor gender registrations", __FUNCTION__); error = true; @@ -2380,7 +2384,7 @@ void WeaponRegistrationMapHolder::Save(SKSESerializationInterface* intfc, UInt32 } } -bool WeaponRegistrationMapHolder::Load(SKSESerializationInterface* intfc, UInt32 kVersion, UInt64 * outHandle) +bool WeaponRegistrationMapHolder::Load(SKSESerializationInterface* intfc, UInt32 kVersion, UInt64 * outHandle, const StringIdMap & stringTable) { bool error = false; @@ -2394,7 +2398,7 @@ bool WeaponRegistrationMapHolder::Load(SKSESerializationInterface* intfc, UInt32 } MultiRegistration,2> reg; - if (reg.Load(intfc, kVersion)) + if (reg.Load(intfc, kVersion, stringTable)) { _MESSAGE("%s - Error loading weapon registrations", __FUNCTION__); error = true; @@ -2454,7 +2458,7 @@ void SkinRegistrationMapHolder::Save(SKSESerializationInterface* intfc, UInt32 k } } -bool SkinRegistrationMapHolder::Load(SKSESerializationInterface* intfc, UInt32 kVersion, UInt64 * outHandle) +bool SkinRegistrationMapHolder::Load(SKSESerializationInterface* intfc, UInt32 kVersion, UInt64 * outHandle, const StringIdMap & stringTable) { bool error = false; @@ -2468,7 +2472,7 @@ bool SkinRegistrationMapHolder::Load(SKSESerializationInterface* intfc, UInt32 k } MultiRegistration, 2> reg; - if (reg.Load(intfc, kVersion)) + if (reg.Load(intfc, kVersion, stringTable)) { _MESSAGE("%s - Error loading skin registrations", __FUNCTION__); error = true; @@ -2518,7 +2522,7 @@ void OverrideInterface::Save(SKSESerializationInterface * intfc, UInt32 kVersion skinData.Save(intfc, kVersion); } -bool OverrideInterface::LoadWeaponOverrides(SKSESerializationInterface* intfc, UInt32 kVersion) +bool OverrideInterface::LoadWeaponOverrides(SKSESerializationInterface* intfc, UInt32 kVersion, const StringIdMap & stringTable) { #ifdef _DEBUG _MESSAGE("%s - Loading Weapon Overrides...", __FUNCTION__); @@ -2526,7 +2530,7 @@ bool OverrideInterface::LoadWeaponOverrides(SKSESerializationInterface* intfc, U bool immediate = (g_loadMode == 1) || (g_firstLoad && g_loadMode == 0); UInt64 handle = 0; - if(!weaponData.Load(intfc, kVersion, &handle)) + if(!weaponData.Load(intfc, kVersion, &handle, stringTable)) { if(handle != 0) SetHandleWeaponProperties(handle, immediate); @@ -2535,7 +2539,7 @@ bool OverrideInterface::LoadWeaponOverrides(SKSESerializationInterface* intfc, U return false; } -bool OverrideInterface::LoadOverrides(SKSESerializationInterface* intfc, UInt32 kVersion) +bool OverrideInterface::LoadOverrides(SKSESerializationInterface* intfc, UInt32 kVersion, const StringIdMap & stringTable) { #ifdef _DEBUG _MESSAGE("%s - Loading Overrides...", __FUNCTION__); @@ -2543,7 +2547,7 @@ bool OverrideInterface::LoadOverrides(SKSESerializationInterface* intfc, UInt32 bool immediate = (g_loadMode == 1) || (g_firstLoad && g_loadMode == 0); UInt64 handle = 0; - if(!armorData.Load(intfc, kVersion, &handle)) + if(!armorData.Load(intfc, kVersion, &handle, stringTable)) { if(handle != 0) SetHandleProperties(handle, immediate); @@ -2552,7 +2556,7 @@ bool OverrideInterface::LoadOverrides(SKSESerializationInterface* intfc, UInt32 return false; } -bool OverrideInterface::LoadNodeOverrides(SKSESerializationInterface* intfc, UInt32 kVersion) +bool OverrideInterface::LoadNodeOverrides(SKSESerializationInterface* intfc, UInt32 kVersion, const StringIdMap & stringTable) { #ifdef _DEBUG _MESSAGE("%s - Loading Node Overrides...", __FUNCTION__); @@ -2560,7 +2564,7 @@ bool OverrideInterface::LoadNodeOverrides(SKSESerializationInterface* intfc, UIn bool immediate = (g_loadMode == 1) || (g_firstLoad && g_loadMode == 0); UInt64 handle = 0; - if(!nodeData.Load(intfc, kVersion, &handle)) + if(!nodeData.Load(intfc, kVersion, &handle, stringTable)) { if(handle != 0) SetHandleNodeProperties(handle, immediate); @@ -2569,7 +2573,7 @@ bool OverrideInterface::LoadNodeOverrides(SKSESerializationInterface* intfc, UIn return false; } -bool OverrideInterface::LoadSkinOverrides(SKSESerializationInterface* intfc, UInt32 kVersion) +bool OverrideInterface::LoadSkinOverrides(SKSESerializationInterface* intfc, UInt32 kVersion, const StringIdMap & stringTable) { #ifdef _DEBUG _MESSAGE("%s - Loading Skin Overrides...", __FUNCTION__); @@ -2577,7 +2581,7 @@ bool OverrideInterface::LoadSkinOverrides(SKSESerializationInterface* intfc, UIn bool immediate = (g_loadMode == 1) || (g_firstLoad && g_loadMode == 0); UInt64 handle = 0; - if (!skinData.Load(intfc, kVersion, &handle)) + if (!skinData.Load(intfc, kVersion, &handle, stringTable)) { if (handle != 0) SetHandleSkinProperties(handle, immediate); @@ -2601,15 +2605,15 @@ void OverrideInterface::DumpMap() for(AddonRegistration::iterator dit = ait->second.begin(); dit != ait->second.end(); ++dit) // Loop Addons { _MESSAGE("Addon Handle: %016llX children %d", dit->first, dit->second.size()); - for(OverrideRegistration::iterator nit = dit->second.begin(); nit != dit->second.end(); ++nit) // Loop Overrides + for(auto nit = dit->second.begin(); nit != dit->second.end(); ++nit) // Loop Overrides { - _MESSAGE("Override Node: %s children %d", nit->first.data, nit->second.size()); + _MESSAGE("Override Node: %s children %d", nit->first->c_str(), nit->second.size()); for(OverrideSet::iterator iter = nit->second.begin(); iter != nit->second.end(); ++iter) { switch((*iter).type) { case OverrideVariant::kType_String: - _MESSAGE("Override: Key %d Value %s", (*iter).key, (*iter).data.str); + _MESSAGE("Override: Key %d Value %s", (*iter).key, (*iter).str->c_str()); break; case OverrideVariant::kType_Float: _MESSAGE("Override: Key %d Value %f", (*iter).key, (*iter).data.f); @@ -2629,15 +2633,15 @@ void OverrideInterface::DumpMap() for(UInt8 gender = 0; gender < 2; gender++) { _MESSAGE("Node Handle: %016llX children %d", nit->first, nit->second[gender].size()); - for(OverrideRegistration::iterator oit = nit->second[gender].begin(); oit != nit->second[gender].end(); ++oit) // Loop Overrides + for(auto oit = nit->second[gender].begin(); oit != nit->second[gender].end(); ++oit) // Loop Overrides { - _MESSAGE("Override Node: %s children %d", oit->first.data, oit->second.size()); + _MESSAGE("Override Node: %s children %d", oit->first->c_str(), oit->second.size()); for(OverrideSet::iterator iter = oit->second.begin(); iter != oit->second.end(); ++iter) { switch((*iter).type) { case OverrideVariant::kType_String: - _MESSAGE("Override: Key %d Value %s", (*iter).key, (*iter).data.str); + _MESSAGE("Override: Key %d Value %s", (*iter).key, (*iter).str->c_str()); break; case OverrideVariant::kType_Float: _MESSAGE("Override: Key %d Value %f", (*iter).key, (*iter).data.f); diff --git a/skee/OverrideInterface.h b/skee/OverrideInterface.h index 6ad2e46..7a21aa4 100644 --- a/skee/OverrideInterface.h +++ b/skee/OverrideInterface.h @@ -3,8 +3,11 @@ #include "IPluginInterface.h" #include "IHashType.h" +#include "StringTable.h" + #include "skse64/GameTypes.h" #include "skse64/NiTypes.h" + #include #include #include @@ -25,13 +28,13 @@ class OverrideSet : public std::set public: // Serialization void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - bool Load(SKSESerializationInterface * intfc, UInt32 kVersion); + bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable); virtual void Visit(std::function functor); }; template -bool ReadKey(SKSESerializationInterface * intfc, T & key, UInt32 kVersion); +bool ReadKey(SKSESerializationInterface * intfc, T & key, UInt32 kVersion, const StringIdMap & stringTable); template void WriteKey(SKSESerializationInterface * intfc, const T key, UInt32 kVersion); @@ -42,17 +45,17 @@ class OverrideRegistration : public std::unordered_map public: // Serialization void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - bool Load(SKSESerializationInterface * intfc, UInt32 kVersion); + bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable); virtual void Visit(std::function functor); }; -class AddonRegistration : public std::unordered_map> +class AddonRegistration : public std::unordered_map> { public: // Serialization void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - bool Load(SKSESerializationInterface * intfc, UInt32 kVersion); + bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable); }; class ArmorRegistration : public std::unordered_map @@ -60,15 +63,15 @@ class ArmorRegistration : public std::unordered_map public: // Serialization void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - bool Load(SKSESerializationInterface * intfc, UInt32 kVersion); + bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable); }; -class WeaponRegistration : public std::unordered_map> +class WeaponRegistration : public std::unordered_map> { public: // Serialization void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - bool Load(SKSESerializationInterface * intfc, UInt32 kVersion); + bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable); }; class SkinRegistration : public std::unordered_map @@ -76,7 +79,7 @@ class SkinRegistration : public std::unordered_map public: // Serialization void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - bool Load(SKSESerializationInterface * intfc, UInt32 kVersion); + bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable); }; template @@ -85,7 +88,7 @@ class MultiRegistration public: // Serialization - bool Load(SKSESerializationInterface * intfc, UInt32 kVersion) + bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable) { bool error = false; @@ -108,7 +111,7 @@ class MultiRegistration } T regs; - if (regs.Load(intfc, kVersion)) + if (regs.Load(intfc, kVersion, stringTable)) { _MESSAGE("%s - Error loading multi-registrations (%d/%d)", __FUNCTION__, i + 1, size); error = true; @@ -178,19 +181,19 @@ class ActorRegistrationMapHolder : public SafeDataHolder, 2>>> +class NodeRegistrationMapHolder : public SafeDataHolder, 2>>> { public: - typedef std::unordered_map, 2>> RegMap; + typedef std::unordered_map, 2>> RegMap; // Serialization void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, UInt64 * outHandle); + bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, UInt64 * outHandle, const StringIdMap & stringTable); friend class OverrideInterface; }; @@ -202,7 +205,7 @@ class WeaponRegistrationMapHolder : public SafeDataHolder functor); + virtual void VisitNodes(TESObjectREFR * refr, std::function functor); virtual void VisitSkin(TESObjectREFR * refr, bool isFemale, bool firstPerson, std::function functor); - virtual void VisitStrings(std::function functor); + virtual void VisitStrings(std::function functor); #ifdef _DEBUG void DumpMap(); diff --git a/skee/OverrideVariant.cpp b/skee/OverrideVariant.cpp index b891146..1d7e962 100644 --- a/skee/OverrideVariant.cpp +++ b/skee/OverrideVariant.cpp @@ -56,21 +56,36 @@ template <> void PackValue (OverrideVariant * dst, UInt16 key, UInt8 index dst->SetNone(); //dst->SetBool(key, index, *src); } -template <> void PackValue (OverrideVariant * dst, UInt16 key, UInt8 index, BSFixedString * src) +template <> void PackValue (OverrideVariant * dst, UInt16 key, UInt8 index, SKEEFixedString * src) { switch (key) { case OverrideVariant::kParam_ShaderTexture: - dst->SetString(key, index, src->data); + dst->SetString(key, index, *src); break; case OverrideVariant::kParam_NodeDestination: - dst->SetString(key, index, src->data); + dst->SetString(key, index, *src); break; default: dst->SetNone(); break; } } +template <> void PackValue (OverrideVariant * dst, UInt16 key, UInt8 index, BSFixedString * src) +{ + switch (key) + { + case OverrideVariant::kParam_ShaderTexture: + dst->SetString(key, index, *src); + break; + case OverrideVariant::kParam_NodeDestination: + dst->SetString(key, index, *src); + break; + default: + dst->SetNone(); + break; + } +} template <> void PackValue (OverrideVariant * dst, UInt16 key, UInt8 index, NiColor * src) { switch (key) @@ -197,18 +212,30 @@ template <> void UnpackValue (bool * dst, OverrideVariant * src) } } -template <> void UnpackValue (BSFixedString * dst, OverrideVariant * src) +template <> void UnpackValue (SKEEFixedString * dst, OverrideVariant * src) { switch (src->type) { case OverrideVariant::kType_String: - CALL_MEMBER_FN(dst, Set)(src->data.str); + *dst = *src->str; break; default: break; } } +template <> void UnpackValue (BSFixedString * dst, OverrideVariant * src) +{ + switch (src->type) + { + case OverrideVariant::kType_String: + *dst = *src->str; + break; + default: + break; + } +} + template <> void UnpackValue (NiColor * dst, OverrideVariant * src) { switch (src->type) diff --git a/skee/OverrideVariant.h b/skee/OverrideVariant.h index 0cd9669..fee051a 100644 --- a/skee/OverrideVariant.h +++ b/skee/OverrideVariant.h @@ -3,6 +3,10 @@ #include "skse64/GameTypes.h" #include "skse64/NiTypes.h" +#include "StringTable.h" + +extern StringTable g_stringTable; + struct SKSESerializationInterface; class BGSTextureSet; @@ -16,7 +20,7 @@ class OverrideVariant bool operator==(const OverrideVariant & rhs) const { return key == rhs.key && index == rhs.index; } void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - bool Load(SKSESerializationInterface * intfc, UInt32 kVersion); + bool Load(SKSESerializationInterface * intfc, UInt32 kVersion, const StringIdMap & stringTable); UInt16 key; enum @@ -72,15 +76,15 @@ class OverrideVariant float f; bool b; void * p; - const char * str; // BSFixedString - BSFixedString * GetStr(void) { return (BSFixedString *)(&str); } } data; + StringTableItem str; void SetNone(void) { type = kType_None; index = -1; data.u = 0; + str = nullptr; } void SetInt(UInt16 paramKey, SInt8 controllerIndex, SInt32 i) @@ -89,6 +93,7 @@ class OverrideVariant type = kType_Int; index = controllerIndex; data.i = i; + str = nullptr; } void SetFloat(UInt16 paramKey, SInt8 controllerIndex, float f) @@ -97,6 +102,7 @@ class OverrideVariant type = kType_Float; index = controllerIndex; data.f = f; + str = nullptr; } void SetBool(UInt16 paramKey, SInt8 controllerIndex, bool b) @@ -105,14 +111,16 @@ class OverrideVariant type = kType_Bool; index = controllerIndex; data.b = b; + str = nullptr; } - void SetString(UInt16 paramKey, SInt8 controllerIndex, BSFixedString str) + void SetString(UInt16 paramKey, SInt8 controllerIndex, SKEEFixedString string) { key = paramKey; type = kType_String; index = controllerIndex; - data.str = str.data; + data.p = nullptr; + str = g_stringTable.GetString(string); } void SetColor(UInt16 paramKey, SInt8 controllerIndex, NiColor color) @@ -121,6 +129,7 @@ class OverrideVariant type = kType_Int; index = controllerIndex; data.u = (UInt8)(color.r * 255) << 16 | (UInt8)(color.g * 255) << 8 | (UInt8)(color.b * 255); + str = nullptr; } void SetColorA(UInt16 paramKey, SInt8 controllerIndex, NiColorA color) @@ -129,6 +138,7 @@ class OverrideVariant type = kType_Int; index = controllerIndex; data.u = (UInt8)(color.a * 255) << 24 | (UInt8)(color.r * 255) << 16 | (UInt8)(color.g * 255) << 8 | (UInt8)(color.b * 255); + str = nullptr; } void SetIdentifier(UInt16 paramKey, SInt8 controllerIndex, void * ptr) @@ -137,6 +147,7 @@ class OverrideVariant type = kType_Identifier; index = controllerIndex; data.p = ptr; + str = nullptr; } static bool IsIndexValid(UInt16 key); @@ -149,6 +160,7 @@ template <> void UnpackValue (float * dst, OverrideVariant * src); template <> void UnpackValue (UInt32 * dst, OverrideVariant * src); template <> void UnpackValue (SInt32 * dst, OverrideVariant * src); template <> void UnpackValue (bool * dst, OverrideVariant * src); +template <> void UnpackValue (SKEEFixedString * dst, OverrideVariant * src); template <> void UnpackValue (BSFixedString * dst, OverrideVariant * src); template <> void UnpackValue (NiColor * dst, OverrideVariant * src); template <> void UnpackValue (NiColorA * dst, OverrideVariant * src); @@ -161,6 +173,7 @@ template <> void PackValue (OverrideVariant * dst, UInt16 key, UInt8 inde template <> void PackValue (OverrideVariant * dst, UInt16 key, UInt8 index, UInt32 * src); template <> void PackValue (OverrideVariant * dst, UInt16 key, UInt8 index, SInt32 * src); template <> void PackValue (OverrideVariant * dst, UInt16 key, UInt8 index, bool * src); +template <> void PackValue (OverrideVariant * dst, UInt16 key, UInt8 index, SKEEFixedString * src); template <> void PackValue (OverrideVariant * dst, UInt16 key, UInt8 index, BSFixedString * src); template <> void PackValue (OverrideVariant * dst, UInt16 key, UInt8 index, NiColor * src); template <> void PackValue (OverrideVariant * dst, UInt16 key, UInt8 index, NiColorA * src); diff --git a/skee/PapyrusCharGen.cpp b/skee/PapyrusCharGen.cpp index 05f104a..c9fafac 100644 --- a/skee/PapyrusCharGen.cpp +++ b/skee/PapyrusCharGen.cpp @@ -1,5 +1,5 @@ #include "PapyrusCharGen.h" -#include "MorphHandler.h" +#include "FaceMorphInterface.h" #include "NifUtils.h" #include "common/IFileStream.h" @@ -29,7 +29,7 @@ #include "BodyMorphInterface.h" #include "OverlayInterface.h" -extern MorphHandler g_morphHandler; +extern FaceMorphInterface g_morphInterface; extern bool g_externalHeads; extern bool g_enableHeadExport; @@ -68,7 +68,7 @@ class SKSETaskExportTintMask : public TaskDelegate BSFixedString m_fileName; }; -void ApplyPreset(Actor * actor, TESRace * race, TESNPC * npc, PresetDataPtr presetData, MorphHandler::ApplyTypes applyType) +void ApplyPreset(Actor * actor, TESRace * race, TESNPC * npc, PresetDataPtr presetData, FaceMorphInterface::ApplyTypes applyType) { CALL_MEMBER_FN(actor, SetRace)(race, actor == (*g_thePlayer)); @@ -91,16 +91,16 @@ void ApplyPreset(Actor * actor, TESRace * race, TESNPC * npc, PresetDataPtr pres } // Sculpt data loaded here - g_morphHandler.EraseSculptData(npc); + g_morphInterface.EraseSculptData(npc); if (presetData->sculptData) { if (presetData->sculptData->size() > 0) - g_morphHandler.SetSculptTarget(npc, presetData->sculptData); + g_morphInterface.SetSculptTarget(npc, presetData->sculptData); } // Assign custom morphs here (values only) - g_morphHandler.EraseMorphData(npc); + g_morphInterface.EraseMorphData(npc); for (auto & it : presetData->customMorphs) - g_morphHandler.SetMorphValue(npc, it.name, it.value); + g_morphInterface.SetMorphValue(npc, it.name, it.value); // Wipe the HeadPart list and replace it with the default race list UInt8 gender = CALL_MEMBER_FN(npc, GetSex)(); @@ -143,7 +143,7 @@ void ApplyPreset(Actor * actor, TESRace * race, TESNPC * npc, PresetDataPtr pres // Queue a node update CALL_MEMBER_FN(actor, QueueNiNodeUpdate)(true); - if ((applyType & MorphHandler::ApplyTypes::kPresetApplyOverrides) == MorphHandler::ApplyTypes::kPresetApplyOverrides) { + if ((applyType & FaceMorphInterface::ApplyTypes::kPresetApplyOverrides) == FaceMorphInterface::ApplyTypes::kPresetApplyOverrides) { g_overrideInterface.RemoveAllReferenceNodeOverrides(actor); g_overlayInterface.RevertOverlays(actor, true); @@ -158,7 +158,7 @@ void ApplyPreset(Actor * actor, TESRace * race, TESNPC * npc, PresetDataPtr pres } } - if ((applyType & MorphHandler::ApplyTypes::kPresetApplySkinOverrides) == MorphHandler::ApplyTypes::kPresetApplySkinOverrides) + if ((applyType & FaceMorphInterface::ApplyTypes::kPresetApplySkinOverrides) == FaceMorphInterface::ApplyTypes::kPresetApplySkinOverrides) { for (UInt32 i = 0; i <= 1; i++) { for (auto & slot : presetData->skinData[i]) { @@ -169,7 +169,7 @@ void ApplyPreset(Actor * actor, TESRace * race, TESNPC * npc, PresetDataPtr pres } } - if ((applyType & MorphHandler::ApplyTypes::kPresetApplyTransforms) == MorphHandler::ApplyTypes::kPresetApplyTransforms) { + if ((applyType & FaceMorphInterface::ApplyTypes::kPresetApplyTransforms) == FaceMorphInterface::ApplyTypes::kPresetApplyTransforms) { g_transformInterface.RemoveAllReferenceTransforms(actor); for (UInt32 i = 0; i <= 1; i++) { for (auto & xForms : presetData->transformData[i]) { @@ -183,7 +183,7 @@ void ApplyPreset(Actor * actor, TESRace * race, TESNPC * npc, PresetDataPtr pres g_transformInterface.UpdateNodeAllTransforms(actor); } - if ((applyType & MorphHandler::ApplyTypes::kPresetApplyBodyMorphs) == MorphHandler::ApplyTypes::kPresetApplyBodyMorphs) { + if ((applyType & FaceMorphInterface::ApplyTypes::kPresetApplyBodyMorphs) == FaceMorphInterface::ApplyTypes::kPresetApplyBodyMorphs) { g_bodyMorphInterface.ClearMorphs(actor); for (auto & morph : presetData->bodyMorphData) { @@ -204,7 +204,7 @@ namespace papyrusCharGen char tintPath[MAX_PATH]; sprintf_s(tintPath, "Data\\Textures\\CharGen\\Exported\\"); - g_morphHandler.SaveJsonPreset(slotPath); + g_morphInterface.SaveJsonPreset(slotPath); if(g_enableHeadExport) g_task->AddTask(new SKSETaskExportTintMask(tintPath, fileName.data)); @@ -335,10 +335,10 @@ namespace papyrusCharGen sprintf_s(tintPath, "Textures\\CharGen\\Exported\\%s.dds", fileName.data); auto presetData = std::make_shared(); - bool loadError = g_morphHandler.LoadJsonPreset(slotPath, presetData); + bool loadError = g_morphInterface.LoadJsonPreset(slotPath, presetData); if (loadError) { sprintf_s(slotPath, "SKSE\\Plugins\\CharGen\\Exported\\%s.slot", fileName.data); - loadError = g_morphHandler.LoadBinaryPreset(slotPath, presetData); + loadError = g_morphInterface.LoadBinaryPreset(slotPath, presetData); } if (loadError) { @@ -347,9 +347,9 @@ namespace papyrusCharGen } presetData->tintTexture = tintPath; - g_morphHandler.AssignPreset(npc, presetData); + g_morphInterface.AssignPreset(npc, presetData); - ApplyPreset(actor, race, npc, presetData, (MorphHandler::ApplyTypes)flags); + ApplyPreset(actor, race, npc, presetData, (FaceMorphInterface::ApplyTypes)flags); return true; } @@ -362,7 +362,7 @@ namespace papyrusCharGen char tintPath[MAX_PATH]; sprintf_s(tintPath, "Data\\Textures\\CharGen\\Exported\\%s.dds", fileName.data); - g_morphHandler.SaveJsonPreset(slotPath); + g_morphInterface.SaveJsonPreset(slotPath); if(g_enableHeadExport) g_task->AddTask(new SKSETaskExportHead((*g_thePlayer), nifPath, tintPath)); @@ -384,16 +384,16 @@ namespace papyrusCharGen return false; } - g_morphHandler.ErasePreset(npc); + g_morphInterface.ErasePreset(npc); char slotPath[MAX_PATH]; sprintf_s(slotPath, "SKSE\\Plugins\\CharGen\\Exported\\%s.jslot", fileName.data); auto presetData = std::make_shared(); - bool loadError = g_morphHandler.LoadJsonPreset(slotPath, presetData); + bool loadError = g_morphInterface.LoadJsonPreset(slotPath, presetData); if (loadError) { sprintf_s(slotPath, "SKSE\\Plugins\\CharGen\\Exported\\%s.slot", fileName.data); - loadError = g_morphHandler.LoadBinaryPreset(slotPath, presetData); + loadError = g_morphInterface.LoadBinaryPreset(slotPath, presetData); } if (loadError) { @@ -420,7 +420,7 @@ namespace papyrusCharGen sprintf_s(destPath, "Data\\Textures\\Actors\\Character\\FaceGenData\\FaceTint\\%s\\%08X.dds", modName, modForm); CopyFile(sourcePath, destPath, false); - ApplyPreset(actor, race, npc, presetData, (MorphHandler::ApplyTypes)flags); + ApplyPreset(actor, race, npc, presetData, (FaceMorphInterface::ApplyTypes)flags); g_task->AddTask(new SKSETaskRefreshTintMask(actor, sourcePath)); return true; } @@ -441,10 +441,10 @@ namespace papyrusCharGen sprintf_s(slotPath, "SKSE\\Plugins\\CharGen\\Presets\\%s.jslot", fileName.data); auto presetData = std::make_shared(); - bool loadError = g_morphHandler.LoadJsonPreset(slotPath, presetData); + bool loadError = g_morphInterface.LoadJsonPreset(slotPath, presetData); if (loadError) { sprintf_s(slotPath, "SKSE\\Plugins\\CharGen\\Presets\\%s.slot", fileName.data); - loadError = g_morphHandler.LoadBinaryPreset(slotPath, presetData); + loadError = g_morphInterface.LoadBinaryPreset(slotPath, presetData); } if (loadError) { @@ -461,7 +461,7 @@ namespace papyrusCharGen npc->SetHairColor(hairColor); } - g_morphHandler.ApplyPresetData(actor, presetData, true, (MorphHandler::ApplyTypes)flags); + g_morphInterface.ApplyPresetData(actor, presetData, true, (FaceMorphInterface::ApplyTypes)flags); // Queue a node update CALL_MEMBER_FN(actor, QueueNiNodeUpdate)(true); @@ -472,7 +472,7 @@ namespace papyrusCharGen { char slotPath[MAX_PATH]; sprintf_s(slotPath, "Data\\SKSE\\Plugins\\CharGen\\Presets\\%s.jslot", fileName.data); - g_morphHandler.SaveJsonPreset(slotPath); + g_morphInterface.SaveJsonPreset(slotPath); } bool IsExternalEnabled(StaticFunctionTag*) @@ -487,12 +487,12 @@ namespace papyrusCharGen return false; } - return g_morphHandler.ErasePreset(npc); + return g_morphInterface.ErasePreset(npc); } void ClearPresets(StaticFunctionTag*) { - g_morphHandler.ClearPresets(); + g_morphInterface.ClearPresets(); } void ExportHead(StaticFunctionTag*, BSFixedString fileName) @@ -512,7 +512,7 @@ namespace papyrusCharGen { char slotPath[MAX_PATH]; sprintf_s(slotPath, "Data\\SKSE\\Plugins\\CharGen\\Exported\\%s.jslot", fileName.data); - g_morphHandler.SaveJsonPreset(slotPath); + g_morphInterface.SaveJsonPreset(slotPath); } }; diff --git a/skee/PapyrusNiOverride.cpp b/skee/PapyrusNiOverride.cpp index ca543db..559b134 100644 --- a/skee/PapyrusNiOverride.cpp +++ b/skee/PapyrusNiOverride.cpp @@ -1149,7 +1149,8 @@ namespace papyrusNiOverride return; OverrideVariant value; - PackValue(&value, OverrideVariant::kParam_NodeDestination, -1, &destination); + SKEEFixedString str = destination; + PackValue(&value, OverrideVariant::kParam_NodeDestination, -1, &str); g_transformInterface.AddNodeTransform(refr, isFirstPerson, isFemale, node, "NodeDestination", value); } @@ -1160,8 +1161,9 @@ namespace papyrusNiOverride return ret; OverrideVariant value = g_transformInterface.GetOverrideNodeValue(refr, isFirstPerson, isFemale, node, "NodeDestination", OverrideVariant::kParam_NodeDestination, -1); - UnpackValue(&ret, &value); - return ret; + SKEEFixedString str; + UnpackValue(&str, &value); + return str; } bool RemoveNodeDestination(StaticFunctionTag* base, TESObjectREFR * refr, bool isFirstPerson, bool isFemale, BSFixedString node) @@ -1230,7 +1232,7 @@ namespace papyrusNiOverride if (!refr) return result; - g_transformInterface.VisitNodes(refr, isFirstPerson, isFemale, [&](BSFixedString key, OverrideRegistration*value) + g_transformInterface.VisitNodes(refr, isFirstPerson, isFemale, [&](BSFixedString key, OverrideRegistration*value) { result.push_back(key); return false; @@ -1245,11 +1247,11 @@ namespace papyrusNiOverride if (!refr) return result; - g_transformInterface.VisitNodes(refr, isFirstPerson, isFemale, [&](BSFixedString nodeName, OverrideRegistration*value) + g_transformInterface.VisitNodes(refr, isFirstPerson, isFemale, [&](BSFixedString nodeName, OverrideRegistration*value) { if (nodeName == node) { for (auto key : *value) - result.push_back(key.first); + result.push_back(*key.first); return true; } @@ -1265,7 +1267,7 @@ namespace papyrusNiOverride if (!refr) return result; - g_bodyMorphInterface.VisitMorphs(refr, [&](BSFixedString name, std::unordered_map * map) + g_bodyMorphInterface.VisitMorphs(refr, [&](SKEEFixedString name, std::unordered_map * map) { result.push_back(name); }); @@ -1279,7 +1281,7 @@ namespace papyrusNiOverride if (!refr) return result; - g_bodyMorphInterface.VisitKeys(refr, morphName, [&](BSFixedString name, float value) + g_bodyMorphInterface.VisitKeys(refr, morphName, [&](SKEEFixedString name, float value) { result.push_back(name); }); diff --git a/skee/SKEEHooks.cpp b/skee/SKEEHooks.cpp index 6924d4d..1840457 100644 --- a/skee/SKEEHooks.cpp +++ b/skee/SKEEHooks.cpp @@ -21,7 +21,7 @@ #include "TintMaskInterface.h" #include "ItemDataInterface.h" -#include "MorphHandler.h" +#include "FaceMorphInterface.h" #include "PartHandler.h" #include "SkeletonExtender.h" @@ -38,6 +38,7 @@ #include extern SKSETaskInterface * g_task; +extern SKEETaskInterface g_taskOverride; extern ItemDataInterface g_itemDataInterface; extern TintMaskInterface g_tintMaskInterface; @@ -61,15 +62,13 @@ Actor * g_weaponHookActor = NULL; TESObjectWEAP * g_weaponHookWeapon = NULL; UInt32 g_firstPerson = 0; -extern MorphHandler g_morphHandler; +extern FaceMorphInterface g_morphInterface; extern PartSet g_partSet; extern UInt32 g_customDataMax; extern bool g_externalHeads; extern bool g_extendedMorphs; extern bool g_allowAllMorphs; -extern SKSETaskInterface g_taskOverride; - RelocAddr<_CreateArmorNode> CreateArmorNode(0x001CAE60); typedef void(*_RegenerateHead)(FaceGen * faceGen, BSFaceGenNiNode * headNode, BGSHeadPart * headPart, TESNPC * npc); @@ -97,8 +96,8 @@ RelocAddr<_UpdateNPCMorph> UpdateNPCMorph(0x00360C20); ICriticalSection s_taskQueueLock; std::queue s_tasks; -typedef void(*_ProcessTaskInterface)(); -RelocAddr <_ProcessTaskInterface> ProcessTaskInterface_Hook_Original(0x002E9B40); +typedef UInt32 (*_ProcessTaskInterface)(void * unk1); +RelocAddr <_ProcessTaskInterface> ProcessTaskInterface_Hook_Original(0x00C03E60); static bool IsTaskQueueEmpty() { @@ -106,10 +105,8 @@ static bool IsTaskQueueEmpty() return s_tasks.empty(); } -void ProcessTaskInterface_Hook() +UInt32 ProcessTaskInterface_Hook(void * unk1) { - ProcessTaskInterface_Hook_Original(); - while (!IsTaskQueueEmpty()) { s_taskQueueLock.Enter(); @@ -120,6 +117,8 @@ void ProcessTaskInterface_Hook() cmd->Run(); cmd->Dispose(); } + + return ProcessTaskInterface_Hook_Original(unk1); } void ProcessTaskInterface_AddTask(TaskDelegate * cmd) @@ -628,13 +627,13 @@ void RaceSexMenu_Hooked::RenderMenu_Hooked(void) void RegenerateHead_Hooked(FaceGen * faceGen, BSFaceGenNiNode * headNode, BGSHeadPart * headPart, TESNPC * npc) { RegenerateHead_Original(faceGen, headNode, headPart, npc); - g_morphHandler.ApplyPreset(npc, headNode, headPart); + g_morphInterface.ApplyPreset(npc, headNode, headPart); } bool UsePreprocessedHead(TESNPC * npc) { // For some reason the NPC vanilla preset data is reset when the actor is disable/enabled - auto presetData = g_morphHandler.GetPreset(npc); + auto presetData = g_morphInterface.GetPreset(npc); if (presetData) { if (!npc->faceMorph) npc->faceMorph = (TESNPC::FaceMorphs*)Heap_Allocate(sizeof(TESNPC::FaceMorphs)); @@ -661,7 +660,7 @@ void _cdecl ClearFaceGenCache_Hooked() { ClearFaceGenCache(); - g_morphHandler.RevertInternals(); + g_morphInterface.RevertInternals(); g_partSet.Revert(); // Cleanup HeadPart List before loading new ones } @@ -673,7 +672,7 @@ void UpdateMorphs_Hooked(TESNPC * npc, void * unk1, BSFaceGenNiNode * faceNode) #endif try { - g_morphHandler.ApplyMorphs(npc, faceNode); + g_morphInterface.ApplyMorphs(npc, faceNode); } catch (...) { @@ -689,7 +688,7 @@ void UpdateMorph_Hooked(TESNPC * npc, BGSHeadPart * headPart, BSFaceGenNiNode * #endif try { - g_morphHandler.ApplyMorph(npc, headPart, faceNode); + g_morphInterface.ApplyMorph(npc, headPart, faceNode); } catch (...) { @@ -784,7 +783,7 @@ class MorphVisitor : public MorphMap::Visitor } bool Accept(BSFixedString morphName) { - TRIModelData & morphData = g_morphHandler.GetExtendedModelTri(morphName, true); + TRIModelData & morphData = g_morphInterface.GetExtendedModelTri(morphName, true); if (morphData.morphModel && morphData.triFile) { BSGeometry * geometry = NULL; if (m_headNode && (*m_headNode)) @@ -815,7 +814,7 @@ UInt8 ApplyRaceMorph_Hooked(BSFaceGenModel * model, BSFixedString * morphName, T try { MorphVisitor morphVisitor(model, morphName, headNode, relative, unk1); - g_morphHandler.VisitMorphMap(modelMorph->GetModelName(), morphVisitor); + g_morphInterface.VisitMorphMap(modelMorph->GetModelName(), morphVisitor); } catch (...) { @@ -836,7 +835,7 @@ UInt8 ApplyChargenMorph_Hooked(BSFaceGenModel * model, BSFixedString * morphName try { MorphVisitor morphVisitor(model, morphName, headNode, relative, unk1); - g_morphHandler.VisitMorphMap(BSFixedString(modelMorph->GetModelName()), morphVisitor); + g_morphInterface.VisitMorphMap(BSFixedString(modelMorph->GetModelName()), morphVisitor); } catch (...) { @@ -857,11 +856,11 @@ void SetRelativeMorph(TESNPC * npc, BSFaceGenNiNode * faceNode, BSFixedString na max = 1.0; UInt32 count = (UInt32)absRel; for (UInt32 i = 0; i < count; i++) { - g_morphHandler.SetMorph(npc, faceNode, name.data, max); + g_morphInterface.SetMorph(npc, faceNode, name.data, max); relative -= max; } } - g_morphHandler.SetMorph(npc, faceNode, name.data, relative); + g_morphInterface.SetMorph(npc, faceNode, name.data, relative); } @@ -887,7 +886,7 @@ void InvokeCategoryList_Hook(GFxMovieView * movie, const char * fnName, FxRespon SInt32 AddSlider_Hook(tArray * sliders, RaceMenuSlider * slider) { SInt32 totalSliders = AddRaceMenuSlider(sliders, slider); - totalSliders = g_morphHandler.LoadSliders(sliders, slider); + totalSliders = g_morphInterface.LoadSliders(sliders, slider); return totalSliders; } @@ -919,11 +918,11 @@ void DoubleMorphCallback_Hook(RaceSexMenu * menu, float newValue, UInt32 sliderI #endif if (slider->index >= SLIDER_OFFSET) { UInt32 sliderIndex = slider->index - SLIDER_OFFSET; - SliderInternalPtr sliderInternal = g_morphHandler.GetSliderByIndex(player->race, sliderIndex); + SliderInternalPtr sliderInternal = g_morphInterface.GetSliderByIndex(player->race, sliderIndex); if (!sliderInternal) return; - float currentValue = g_morphHandler.GetMorphValueByName(actorBase, sliderInternal->name); + float currentValue = g_morphInterface.GetMorphValueByName(actorBase, sliderInternal->name); if (newValue == FLT_MAX || newValue == -FLT_MAX) { //slider->value = 0.0f; @@ -946,13 +945,13 @@ void DoubleMorphCallback_Hook(RaceSexMenu * menu, float newValue, UInt32 sliderI char buffer[MAX_PATH]; slider->value = newValue; - sprintf_s(buffer, MAX_PATH, "%s%d", sliderInternal->lowerBound.data, (UInt32)currentValue); - g_morphHandler.SetMorph(actorBase, faceNode, buffer, -1.0); + sprintf_s(buffer, MAX_PATH, "%s%d", sliderInternal->lowerBound.c_str(), (UInt32)currentValue); + g_morphInterface.SetMorph(actorBase, faceNode, buffer, -1.0); memset(buffer, 0, MAX_PATH); - sprintf_s(buffer, MAX_PATH, "%s%d", sliderInternal->lowerBound.data, (UInt32)newValue); - g_morphHandler.SetMorph(actorBase, faceNode, buffer, 1.0); + sprintf_s(buffer, MAX_PATH, "%s%d", sliderInternal->lowerBound.c_str(), (UInt32)newValue); + g_morphInterface.SetMorph(actorBase, faceNode, buffer, 1.0); - g_morphHandler.SetMorphValue(actorBase, sliderInternal->name, newValue); + g_morphInterface.SetMorphValue(actorBase, sliderInternal->name, newValue); return; } @@ -1038,7 +1037,7 @@ void DoubleMorphCallback_Hook(RaceSexMenu * menu, float newValue, UInt32 sliderI #endif SetRelativeMorph(actorBase, faceNode, bound, relative); - g_morphHandler.SetMorphValue(actorBase, sliderInternal->name, newValue); + g_morphInterface.SetMorphValue(actorBase, sliderInternal->name, newValue); return; } } @@ -1108,11 +1107,12 @@ bool InstallSKEEHooks() return false; } - // REMOVE THIS AFTER SKSE64 UPDATE - RelocAddr ProcessTaskInterface_Target(0x005B31E0 + 0x6A0); - g_branchTrampoline.Write5Call(ProcessTaskInterface_Target.GetUIntPtr(), (uintptr_t)ProcessTaskInterface_Hook); - g_taskOverride.AddTask = ProcessTaskInterface_AddTask; - // ------------------------------- + if (g_task->interfaceVersion < 3) + { + RelocAddr ProcessTaskInterface_Target(0x005B31E0 + 0x739); + g_branchTrampoline.Write5Call(ProcessTaskInterface_Target.GetUIntPtr(), (uintptr_t)ProcessTaskInterface_Hook); + g_taskOverride.AddTask = ProcessTaskInterface_AddTask; + } RelocAddr InvokeCategoriesList_Target(0x008B5420 + 0x9FB); g_branchTrampoline.Write5Call(InvokeCategoriesList_Target.GetUIntPtr(), (uintptr_t)InvokeCategoryList_Hook); diff --git a/skee/SKEEHooks.h b/skee/SKEEHooks.h index b2eb0f1..2859cb0 100644 --- a/skee/SKEEHooks.h +++ b/skee/SKEEHooks.h @@ -14,6 +14,11 @@ class NiNode; #include "skse64/GameExtraData.h" #include "skse64/GameMenus.h" +struct SKEETaskInterface : public SKSETaskInterface +{ + void(*AddSKSETask)(TaskDelegate * task); +}; + extern RelocAddr TESModelTri_vtbl; typedef SInt32 (*_AddRaceMenuSlider)(tArray * sliders, RaceMenuSlider * slider); extern RelocAddr<_AddRaceMenuSlider> AddRaceMenuSlider; diff --git a/skee/ScaleformCharGenFunctions.cpp b/skee/ScaleformCharGenFunctions.cpp index b5aad17..e324394 100644 --- a/skee/ScaleformCharGenFunctions.cpp +++ b/skee/ScaleformCharGenFunctions.cpp @@ -4,7 +4,7 @@ #include "skse64_common/skse_version.h" #include "ScaleformCharGenFunctions.h" -#include "MorphHandler.h" +#include "FaceMorphInterface.h" #include "PartHandler.h" #include "skse64/GameAPI.h" @@ -38,7 +38,7 @@ extern NiTransformInterface * g_transformInterface; extern OverlayInterface * g_overlayInterface; extern BodyMorphInterface * g_bodyMorphInterface; -extern MorphHandler g_morphHandler; +extern FaceMorphInterface g_morphInterface; extern PartSet g_partSet; extern SKSETaskInterface * g_task; @@ -81,9 +81,9 @@ void SKSEScaleform_SavePreset::Invoke(Args * args) const char * strData = args->args[0].GetString(); if (saveJson) - args->result->SetBool(g_morphHandler.SaveJsonPreset(strData)); + args->result->SetBool(g_morphInterface.SaveJsonPreset(strData)); else - args->result->SetBool(g_morphHandler.SaveBinaryPreset(strData)); + args->result->SetBool(g_morphInterface.SaveBinaryPreset(strData)); } void SKSEScaleform_LoadPreset::Invoke(Args * args) @@ -102,9 +102,9 @@ void SKSEScaleform_LoadPreset::Invoke(Args * args) object = &args->args[1]; auto presetData = std::make_shared(); - bool loadError = loadJson ? g_morphHandler.LoadJsonPreset(strData, presetData) : g_morphHandler.LoadBinaryPreset(strData, presetData);//g_morphHandler.LoadPreset(strData, args->movie, object); + bool loadError = loadJson ? g_morphInterface.LoadJsonPreset(strData, presetData) : g_morphInterface.LoadBinaryPreset(strData, presetData);//g_morphHandler.LoadPreset(strData, args->movie, object); if (!loadError) { - g_morphHandler.ApplyPresetData(*g_thePlayer, presetData); + g_morphInterface.ApplyPresetData(*g_thePlayer, presetData); RegisterNumber(object, "hairColor", presetData->hairColor); @@ -116,7 +116,7 @@ void SKSEScaleform_LoadPreset::Invoke(Args * args) args->movie->CreateObject(&tintObject); RegisterNumber(&tintObject, "color", tint.color); RegisterNumber(&tintObject, "index", tint.index); - RegisterString(&tintObject, args->movie, "texture", tint.name.data); + RegisterString(&tintObject, args->movie, "texture", tint.name.c_str()); tintArray.PushBack(&tintObject); } @@ -152,7 +152,7 @@ void SKSEScaleform_ReadPreset::Invoke(Args * args) DataHandler * dataHandler = DataHandler::GetSingleton(); auto presetData = std::make_shared(); - bool loadError = loadJson ? g_morphHandler.LoadJsonPreset(strData, presetData) : g_morphHandler.LoadBinaryPreset(strData, presetData);//g_morphHandler.LoadPreset(strData, args->movie, object); + bool loadError = loadJson ? g_morphInterface.LoadJsonPreset(strData, presetData) : g_morphInterface.LoadBinaryPreset(strData, presetData);//g_morphHandler.LoadPreset(strData, args->movie, object); if(!loadError) { PlayerCharacter * player = (*g_thePlayer); TESNPC * npc = DYNAMIC_CAST(player->baseForm, TESForm, TESNPC); @@ -197,7 +197,7 @@ void SKSEScaleform_ReadPreset::Invoke(Args * args) args->movie->CreateObject(&tintObject); RegisterNumber(&tintObject, "color", tint.color); RegisterNumber(&tintObject, "index", tint.index); - RegisterString(&tintObject, args->movie, "texture", tint.name.data); + RegisterString(&tintObject, args->movie, "texture", tint.name.c_str()); tintArray.PushBack(&tintObject); } object->SetMember("tints", &tintArray); @@ -261,7 +261,7 @@ void SKSEScaleform_ReadPreset::Invoke(Args * args) i = 0; for(auto & it : presetData->customMorphs) { std::string morphName = "$"; - morphName.append(it.name.data); + morphName.append(it.name.c_str()); GFxValue customObject; args->movie->CreateObject(&customObject); RegisterString(&customObject, args->movie, "name", morphName.c_str()); @@ -275,7 +275,7 @@ void SKSEScaleform_ReadPreset::Invoke(Args * args) for (auto & it : presetData->bodyMorphData) { GFxValue customObject; args->movie->CreateObject(&customObject); - RegisterString(&customObject, args->movie, "name", it.first.data); + RegisterString(&customObject, args->movie, "name", it.first.c_str()); float morphSum = 0; for (auto & keys : it.second) @@ -364,7 +364,7 @@ void SKSEScaleform_GetSliderData::Invoke(Args * args) // Provide case for custom parts if(slider->index >= SLIDER_OFFSET) { UInt32 sliderIndex = slider->index - SLIDER_OFFSET; - SliderInternalPtr sliderInternal = g_morphHandler.GetSliderByIndex(player->race, sliderIndex); + SliderInternalPtr sliderInternal = g_morphInterface.GetSliderByIndex(player->race, sliderIndex); if(sliderInternal) { RegisterNumber(args->result, "subType", sliderInternal->type); switch (sliderInternal->type) @@ -416,9 +416,8 @@ void SKSEScaleform_ExportHead::Invoke(Args * args) const char * strData = args->args[0].GetString(); -#ifdef FIXME // Get the Editor's working actor - Actor * actor = g_World.GetWorkingActor(); + Actor * actor = (*g_thePlayer);//g_World.GetWorkingActor(); if (!actor) return; @@ -428,7 +427,6 @@ void SKSEScaleform_ExportHead::Invoke(Args * args) ddsPath.append(".dds"); g_task->AddTask(new SKSETaskExportHead(actor, nifPath.c_str(), ddsPath.c_str())); -#endif } void SKSEScaleform_ImportHead::Invoke(Args * args) diff --git a/skee/ShaderUtilities.cpp b/skee/ShaderUtilities.cpp index 88b6b75..e6f91b7 100644 --- a/skee/ShaderUtilities.cpp +++ b/skee/ShaderUtilities.cpp @@ -148,7 +148,7 @@ void GetShaderProperty(NiAVObject * node, OverrideVariant * value) } } -NIOVTaskUpdateTexture::NIOVTaskUpdateTexture(BSGeometry * geometry, UInt32 index, BSFixedString texture) +NIOVTaskUpdateTexture::NIOVTaskUpdateTexture(BSGeometry * geometry, UInt32 index, StringTableItem texture) { m_geometry = geometry; if (m_geometry) @@ -179,7 +179,7 @@ void NIOVTaskUpdateTexture::Run() const char * texturePath = material->textureSet->GetTexturePath(i); newTextureSet->SetTexturePath(i, texturePath); } - newTextureSet->SetTexturePath(m_index, m_texture.data); + newTextureSet->SetTexturePath(m_index, m_texture->AsBSFixedString().c_str()); material->ReleaseTextures(); material->SetTextureSet(newTextureSet); CALL_MEMBER_FN(lightingShader, InvalidateTextures)(0); @@ -297,7 +297,7 @@ void SetShaderProperty(NiAVObject * node, OverrideVariant * value, bool immediat // Special cases case OverrideVariant::kParam_ShaderTexture: { - BSFixedString texture; + SKEEFixedString texture; UnpackValue(&texture, value); if(immediate) @@ -309,14 +309,14 @@ void SetShaderProperty(NiAVObject * node, OverrideVariant * value, bool immediat const char * texturePath = material->textureSet->GetTexturePath(i); newTextureSet->SetTexturePath(i, texturePath); } - newTextureSet->SetTexturePath(value->index, texture.data); + newTextureSet->SetTexturePath(value->index, texture.AsBSFixedString().c_str()); material->ReleaseTextures(); material->SetTextureSet(newTextureSet); CALL_MEMBER_FN(lightingShader, InvalidateTextures)(0); CALL_MEMBER_FN(lightingShader, InitializeShader)(geometry); } } else { - g_task->AddTask(new NIOVTaskUpdateTexture(geometry, value->index, texture)); + g_task->AddTask(new NIOVTaskUpdateTexture(geometry, value->index, g_stringTable.GetString(texture))); } return; } diff --git a/skee/ShaderUtilities.h b/skee/ShaderUtilities.h index e4cdd5b..c43a3e3 100644 --- a/skee/ShaderUtilities.h +++ b/skee/ShaderUtilities.h @@ -7,6 +7,8 @@ #include "skse64/NiNodes.h" #include "skse64/NiTypes.h" +#include "StringTable.h" + #include class NiExtraData; @@ -18,14 +20,14 @@ struct SKSESerializationInterface; class NIOVTaskUpdateTexture : public TaskDelegate { public: - NIOVTaskUpdateTexture(BSGeometry * geometry, UInt32 index, BSFixedString texture); + NIOVTaskUpdateTexture(BSGeometry * geometry, UInt32 index, StringTableItem texture); virtual void Run(); virtual void Dispose(); BSGeometry * m_geometry; UInt32 m_index; - BSFixedString m_texture; + StringTableItem m_texture; }; class NIOVTaskUpdateWorldData : public TaskDelegate diff --git a/skee/SkeletonExtender.cpp b/skee/SkeletonExtender.cpp index aefd753..fcf62b4 100644 --- a/skee/SkeletonExtender.cpp +++ b/skee/SkeletonExtender.cpp @@ -85,7 +85,7 @@ extern NiTransformInterface g_transformInterface; void SkeletonExtender::AddTransforms(TESObjectREFR * refr, bool isFirstPerson, NiNode * skeleton, NiAVObject * objectRoot) { - std::set current_nodes, previous_nodes, diffs, changes, update; + std::set current_nodes, previous_nodes, diffs, changes, update; UInt8 gender = 0; TESNPC * actorBase = DYNAMIC_CAST(refr->baseForm, TESForm, TESNPC); @@ -97,7 +97,7 @@ void SkeletonExtender::AddTransforms(TESObjectREFR * refr, bool isFirstPerson, N { for (int i = 0; i < globalData->m_size; i++) { - BSFixedString node(globalData->m_data[i]); + SKEEFixedString node(globalData->m_data[i]); previous_nodes.insert(node); } } @@ -120,7 +120,7 @@ void SkeletonExtender::AddTransforms(TESObjectREFR * refr, bool isFirstPerson, N { for (auto & objects : root) { - BSFixedString node = objects["name"].asCString(); + SKEEFixedString node = objects["name"].asCString(); current_nodes.insert(node); } } @@ -207,7 +207,7 @@ void SkeletonExtender::AddTransforms(TESObjectREFR * refr, bool isFirstPerson, N } } -void SkeletonExtender::ReadTransforms(TESObjectREFR * refr, const char * jsonData, bool isFirstPerson, bool isFemale, std::set & nodes, std::set & changes) +void SkeletonExtender::ReadTransforms(TESObjectREFR * refr, const char * jsonData, bool isFirstPerson, bool isFemale, std::set & nodes, std::set & changes) { try { @@ -224,7 +224,7 @@ void SkeletonExtender::ReadTransforms(TESObjectREFR * refr, const char * jsonDat bool changed = false; for (auto & objects : root) { - BSFixedString node = objects["name"].asCString(); + SKEEFixedString node = objects["name"].asCString(); nodes.insert(node); Json::Value pos = objects["pos"]; diff --git a/skee/SkeletonExtender.h b/skee/SkeletonExtender.h index 896278e..946ee59 100644 --- a/skee/SkeletonExtender.h +++ b/skee/SkeletonExtender.h @@ -8,6 +8,7 @@ class NiStringExtraData; #include #include "skse64\GameTypes.h" +#include "StringTable.h" class SkeletonExtender { @@ -15,5 +16,5 @@ class SkeletonExtender static void Attach(TESObjectREFR * refr, NiNode * skeleton, NiAVObject * objectRoot); static NiNode * LoadTemplate(NiNode * parent, const char * path); static void AddTransforms(TESObjectREFR * refr, bool isFirstPerson, NiNode * skeleton, NiAVObject * objectRoot); - static void ReadTransforms(TESObjectREFR * refr, const char * jsonData, bool isFirstPerson, bool isFemale, std::set & nodes, std::set & changes); + static void ReadTransforms(TESObjectREFR * refr, const char * jsonData, bool isFirstPerson, bool isFemale, std::set & nodes, std::set & changes); }; \ No newline at end of file diff --git a/skee/StringTable.cpp b/skee/StringTable.cpp index a2c04d8..9c84f96 100644 --- a/skee/StringTable.cpp +++ b/skee/StringTable.cpp @@ -2,86 +2,215 @@ #include "skse64/PluginAPI.h" +extern StringTable g_stringTable; -void StringTable::Save(SKSESerializationInterface * intfc, UInt32 kVersion) +using namespace Serialization; + +void DeleteStringEntry(const SKEEFixedString* string) { - intfc->OpenRecord('STTB', kVersion); + g_stringTable.RemoveString(*string); + delete string; +} - UInt32 totalStrings = m_stringToId.size(); - intfc->WriteRecordData(&totalStrings, sizeof(totalStrings)); +StringTableItem StringTable::GetString(const SKEEFixedString & str) +{ + IScopedCriticalSection locker(&m_lock); - for (auto & str : m_stringToId) - { - UInt16 length = strlen(str.first.data); - UInt32 strId = str.second; - intfc->WriteRecordData(&length, sizeof(length)); - intfc->WriteRecordData(str.first.data, length); - intfc->WriteRecordData(&strId, sizeof(strId)); + auto it = m_table.find(str); + if (it != m_table.end()) { + return it->second.lock(); + } + else { + StringTableItem item = std::shared_ptr(new SKEEFixedString(str), DeleteStringEntry); + m_table.emplace(str, item); + m_tableVector.push_back(item); + return item; } + + return nullptr; } -bool StringTable::Load(SKSESerializationInterface* intfc, UInt32 kVersion) +void StringTable::RemoveString(const SKEEFixedString & str) { - bool error = false; - UInt32 totalStrings = 0; + IScopedCriticalSection locker(&m_lock); + + auto it = m_table.find(str); + if (it != m_table.end()) + { + for (long int i = m_tableVector.size() - 1; i >= 0; --i) + { + if (m_tableVector[i].lock() == it->second.lock()) + m_tableVector.erase(m_tableVector.begin() + i); + } - if (!intfc->ReadRecordData(&totalStrings, sizeof(totalStrings))) + m_table.erase(it); + } +} + +UInt32 StringTable::GetStringID(const StringTableItem & str) +{ + for (long int i = m_tableVector.size() - 1; i >= 0; --i) { - _ERROR("%s - Error loading total strings from table", __FUNCTION__); - error = true; - return error; + auto item = m_tableVector[i].lock(); + if (item == str) + return i; } - for (UInt32 i = 0; i < totalStrings; i++) + return -1; +} + +void StringTable::Save(const SKSESerializationInterface * intfc, UInt32 kVersion) +{ + intfc->OpenRecord('STTB', kVersion); + + UInt32 totalStrings = m_tableVector.size(); + WriteData(intfc, &totalStrings); + + for (auto & str : m_tableVector) { - char * stringName = NULL; - UInt16 stringLength = 0; - if (!intfc->ReadRecordData(&stringLength, sizeof(stringLength))) - { - _ERROR("%s - Error loading string length", __FUNCTION__); - error = true; - return error; + auto data = str.lock(); + UInt16 length = 0; + if (!data) { + WriteData(intfc, &length); } + else { + WriteData(intfc, data.get()); + } + } +} - stringName = new char[stringLength + 1]; +bool StringTable::Load(const SKSESerializationInterface * intfc, UInt32 kVersion, StringIdMap & stringTable) +{ + bool error = false; + UInt32 totalStrings = 0; - if (stringLength > 0 && !intfc->ReadRecordData(stringName, stringLength)) { - _ERROR("%s - Error loading string of length %d", __FUNCTION__, stringLength); + if (kVersion >= kSerializationVersion3) + { + if (!ReadData(intfc, &totalStrings)) + { + _ERROR("%s - Error loading total strings from table", __FUNCTION__); error = true; return error; } - stringName[stringLength] = 0; - - BSFixedString str(stringName); - delete[] stringName; - - UInt32 stringId = 0; - if (!intfc->ReadRecordData(&stringId, sizeof(stringId))) + for (UInt32 i = 0; i < totalStrings; i++) + { + SKEEFixedString str; + if (!ReadData(intfc, &str)) { + _ERROR("%s - Error loading string", __FUNCTION__); + error = true; + return error; + } + + StringTableItem item = GetString(str); + stringTable.emplace(i, item); + } + } + else if (kVersion >= kSerializationVersion2) + { + if (!intfc->ReadRecordData(&totalStrings, sizeof(totalStrings))) { - _ERROR("%s - Error loading string id", __FUNCTION__); + _ERROR("%s - Error loading total strings from table", __FUNCTION__); error = true; return error; } - m_idToString.insert_or_assign(stringId, str); + for (UInt32 i = 0; i < totalStrings; i++) + { + char * stringName = NULL; + UInt16 stringLength = 0; + if (!intfc->ReadRecordData(&stringLength, sizeof(stringLength))) + { + _ERROR("%s - Error loading string length", __FUNCTION__); + error = true; + return error; + } + + stringName = new char[stringLength + 1]; + + if (stringLength > 0 && !intfc->ReadRecordData(stringName, stringLength)) { + _ERROR("%s - Error loading string of length %d", __FUNCTION__, stringLength); + error = true; + return error; + } + + stringName[stringLength] = 0; + + SKEEFixedString str(stringName); + delete[] stringName; + + UInt32 stringId = 0; + if (!intfc->ReadRecordData(&stringId, sizeof(stringId))) + { + _ERROR("%s - Error loading string id", __FUNCTION__); + error = true; + return error; + } + + StringTableItem item = GetString(str); + stringTable.emplace(stringId, item); + } } return error; } -void StringTable::StringToId(const BSFixedString & str, const UInt32 & id) +void StringTable::Revert() { - m_stringToId.insert_or_assign(str, id); + IScopedCriticalSection locker(&m_lock); + m_table.clear(); + m_tableVector.clear(); } -void StringTable::IdToString(const UInt32 & id, const BSFixedString & str) +template +bool Serialization::WriteData(const SKSESerializationInterface * intfc, const T * data) { - m_idToString.insert_or_assign(id, str); + return intfc->WriteRecordData(data, sizeof(T)); } -void StringTable::Clear() +template +bool Serialization::ReadData(const SKSESerializationInterface * intfc, T * data) { - m_stringToId.clear(); - m_idToString.clear(); + return intfc->ReadRecordData(data, sizeof(T)) > 0; } + +template<> +bool Serialization::WriteData(const SKSESerializationInterface * intfc, const SKEEFixedString * str) +{ + UInt16 len = str->length(); + if (len > SHRT_MAX) + return false; + if (!intfc->WriteRecordData(&len, sizeof(len))) + return false; + if (len == 0) + return true; + if (!intfc->WriteRecordData(str->c_str(), len)) + return false; + return true; +} + +template<> +bool Serialization::ReadData(const SKSESerializationInterface * intfc, SKEEFixedString * str) +{ + UInt16 len = 0; + + if (!intfc->ReadRecordData(&len, sizeof(len))) + return false; + if (len == 0) + return true; + if (len > SHRT_MAX) + return false; + + char * buf = new char[len + 1]; + buf[0] = 0; + + if (!intfc->ReadRecordData(buf, len)) { + delete[] buf; + return false; + } + buf[len] = 0; + + *str = SKEEFixedString(buf); + delete[] buf; + return true; +} \ No newline at end of file diff --git a/skee/StringTable.h b/skee/StringTable.h index 05df42d..78cbfcc 100644 --- a/skee/StringTable.h +++ b/skee/StringTable.h @@ -1,9 +1,105 @@ #pragma once #include "skse64/GameTypes.h" +#include "skse64/PluginAPI.h" +#include "common/ICriticalSection.h" +#include +#include +#include struct SKSESerializationInterface; +class SKEEFixedString +{ +public: + SKEEFixedString() : m_internal() { m_hash = hash_lower(m_internal.c_str(), m_internal.size()); } + SKEEFixedString(const char * str) : m_internal(str) { m_hash = hash_lower(m_internal.c_str(), m_internal.size()); } + SKEEFixedString(const std::string & str) : m_internal(str) { m_hash = hash_lower(m_internal.c_str(), m_internal.size()); } + SKEEFixedString(const BSFixedString & str) : m_internal(str.c_str()) { m_hash = hash_lower(m_internal.c_str(), m_internal.size()); } + + bool operator<(const SKEEFixedString& x) const + { + return _stricmp(m_internal.c_str(), x.m_internal.c_str()) < 0; + } + + bool operator==(const SKEEFixedString& x) const + { + if (m_internal.size() != x.m_internal.size()) + return false; + + if (_stricmp(m_internal.c_str(), x.m_internal.c_str()) == 0) + return true; + + return false; + } + + size_t length() const { return m_internal.size(); } + + operator BSFixedString() const { return BSFixedString(m_internal.c_str()); } + BSFixedString AsBSFixedString() const { return operator BSFixedString(); } + + const char * c_str() const { return operator const char *(); } + operator const char *() const { return m_internal.c_str(); } + + size_t hash_lower(const char * str, size_t count) + { + const size_t _FNV_offset_basis = 14695981039346656037ULL; + const size_t _FNV_prime = 1099511628211ULL; + + size_t _Val = _FNV_offset_basis; + for (size_t _Next = 0; _Next < count; ++_Next) + { // fold in another byte + _Val ^= (size_t)tolower(str[_Next]); + _Val *= _FNV_prime; + } + return _Val; + } + + size_t GetHash() const + { + return m_hash; + } + +protected: + std::string m_internal; + size_t m_hash; +}; + +typedef std::shared_ptr StringTableItem; +typedef std::weak_ptr WeakTableItem; + +namespace std { + template <> struct hash + { + size_t operator()(const SKEEFixedString & x) const + { + return x.GetHash(); + } + }; + template <> struct hash + { + size_t operator()(const StringTableItem & x) const + { + return x->GetHash(); + } + }; +} + + +namespace Serialization +{ + template + bool WriteData(const SKSESerializationInterface * intfc, const T * data); + + template + bool ReadData(const SKSESerializationInterface * intfc, T * data); + + template<> bool WriteData(const SKSESerializationInterface * intfc, const SKEEFixedString * str); + template<> bool ReadData(const SKSESerializationInterface * intfc, SKEEFixedString * str); +} + +typedef std::unordered_map StringIdMap; + class StringTable { public: @@ -11,91 +107,47 @@ class StringTable { kSerializationVersion1 = 1, kSerializationVersion2 = 2, - kSerializationVersion = kSerializationVersion2 + kSerializationVersion3 = 3, + kSerializationVersion = kSerializationVersion3 }; - void Save(SKSESerializationInterface * intfc, UInt32 kVersion); - bool Load(SKSESerializationInterface* intfc, UInt32 kVersion); + void Save(const SKSESerializationInterface * intfc, UInt32 kVersion); + bool Load(const SKSESerializationInterface * intfc, UInt32 kVersion, StringIdMap & stringTable); + void Revert(); + + StringTableItem GetString(const SKEEFixedString & str); - void Clear(); + UInt32 GetStringID(const StringTableItem & str); - void StringToId(const BSFixedString & str, const UInt32 & id); - void IdToString(const UInt32 & id, const BSFixedString & str); + void RemoveString(const SKEEFixedString & str); - template - BSFixedString ReadString(SKSESerializationInterface* intfc, UInt32 kVersion) + static StringTableItem ReadString(const SKSESerializationInterface * intfc, const StringIdMap & stringTable) { - BSFixedString str(""); - if (kVersion >= kSerializationVersion2) + UInt32 stringId; + if (!Serialization::ReadData(intfc, &stringId)) { - UInt32 stringId = 0; - if (!intfc->ReadRecordData(&stringId, sizeof(stringId))) - { - _ERROR("%s - Error loading string id", __FUNCTION__); - return BSFixedString(""); - } - - auto & it = m_idToString.find(stringId); - if (it != m_idToString.end()) - { - str = it->second; - } - else - { - _ERROR("%s - Error string lookup failure (%08X)", __FUNCTION__, stringId); - return BSFixedString(""); - } + _ERROR("%s - Error loading string id", __FUNCTION__); + return nullptr; } - else + + auto it = stringTable.find(stringId); + if (it == stringTable.end()) { - char * stringName = NULL; - T stringLength = 0; - if (!intfc->ReadRecordData(&stringLength, sizeof(stringLength))) - { - _ERROR("%s - Error loading string length", __FUNCTION__); - return BSFixedString(""); - } - - stringName = new char[stringLength + 1]; - if (!intfc->ReadRecordData(stringName, stringLength)) { - _ERROR("%s - Error loading string of length %d", __FUNCTION__, stringLength); - return BSFixedString(""); - } - stringName[stringLength] = 0; - - str = stringName; - delete[] stringName; + _ERROR("%s - Error loading string from table", __FUNCTION__); + return nullptr; } - return str; + + return it->second; } - template - void WriteString(SKSESerializationInterface* intfc, const BSFixedString & str, UInt32 kVersion) + void WriteString(const SKSESerializationInterface * intfc, const StringTableItem & str) { - if (kVersion >= kSerializationVersion2) - { - auto & it = m_stringToId.find(str); - if (it != m_stringToId.end()) - { - UInt32 stringId = it->second; - intfc->WriteRecordData(&stringId, sizeof(stringId)); - } - else - { - _ERROR("%s - Error mapping string %s to id", __FUNCTION__, str.data); - UInt32 stringId = (std::numeric_limits::max)(); - intfc->WriteRecordData(&stringId, sizeof(stringId)); - } - } - else - { - T length = strlen(str.data); - intfc->WriteRecordData(&length, sizeof(length)); - intfc->WriteRecordData(str.data, length); - } + UInt32 stringId = GetStringID(str); + Serialization::WriteData(intfc, &stringId); } private: - std::map m_stringToId; - std::map m_idToString; + std::unordered_map m_table; + std::vector m_tableVector; + mutable ICriticalSection m_lock; }; \ No newline at end of file diff --git a/skee/main.cpp b/skee/main.cpp index 469c7ff..2af1263 100644 --- a/skee/main.cpp +++ b/skee/main.cpp @@ -27,7 +27,7 @@ #include "TintMaskInterface.h" #include "NiTransformInterface.h" -#include "MorphHandler.h" +#include "FaceMorphInterface.h" #include "PartHandler.h" #include "ShaderUtilities.h" @@ -46,7 +46,6 @@ IDebugLog gLog; PluginHandle g_pluginHandle = kPluginHandle_Invalid; -const UInt32 kSerializationDataVersion = 1; // Interfaces SKSESerializationInterface * g_serialization = nullptr; @@ -54,9 +53,7 @@ SKSEScaleformInterface * g_scaleform = nullptr; SKSETaskInterface * g_task = nullptr; SKSEMessagingInterface * g_messaging = nullptr; SKSEPapyrusInterface * g_papyrus = nullptr; - -// Temporary custom interface -SKSETaskInterface g_taskOverride; +SKEETaskInterface g_taskOverride; // Handlers IInterfaceMap g_interfaceMap; @@ -67,8 +64,7 @@ OverlayInterface g_overlayInterface; BodyMorphInterface g_bodyMorphInterface; ItemDataInterface g_itemDataInterface; NiTransformInterface g_transformInterface; - -MorphHandler g_morphHandler; +FaceMorphInterface g_morphInterface; PartSet g_partSet; StringTable g_stringTable; @@ -285,7 +281,8 @@ void SKEE64Serialization_Revert(SKSESerializationInterface * intfc) g_itemDataInterface.Revert(); g_dyeMap.Revert(); g_transformInterface.Revert(); - g_morphHandler.Revert(); + g_morphInterface.Revert(); + g_stringTable.Revert(); } class StopWatch @@ -318,31 +315,13 @@ void SKEE64Serialization_Save(SKSESerializationInterface * intfc) StopWatch sw; UInt32 strCount = 0; - sw.Start(); - g_transformInterface.VisitStrings([&](BSFixedString str) - { - g_stringTable.StringToId(str.data, strCount); - strCount++; - }); - g_overrideInterface.VisitStrings([&](BSFixedString str) - { - g_stringTable.StringToId(str.data, strCount); - strCount++; - }); - g_bodyMorphInterface.VisitStrings([&](BSFixedString str) - { - g_stringTable.StringToId(str.data, strCount); - strCount++; - }); - - _DMESSAGE("%s - Pooled strings %dms", __FUNCTION__, sw.Stop()); sw.Start(); g_stringTable.Save(intfc, StringTable::kSerializationVersion); _DMESSAGE("%s - Serialized string table %dms", __FUNCTION__, sw.Stop()); sw.Start(); - g_morphHandler.Save(intfc, kSerializationDataVersion); + g_morphInterface.Save(intfc, FaceMorphInterface::kSerializationVersion); _DMESSAGE("%s - Player morph data %dms", __FUNCTION__, sw.Stop()); sw.Start(); @@ -364,8 +343,6 @@ void SKEE64Serialization_Save(SKSESerializationInterface * intfc) sw.Start(); g_itemDataInterface.Save(intfc, ItemDataInterface::kSerializationVersion); _DMESSAGE("%s - Serialized item data %dms", __FUNCTION__, sw.Stop()); - - g_stringTable.Clear(); } void SKEE64Serialization_Load(SKSESerializationInterface * intfc) @@ -375,23 +352,25 @@ void SKEE64Serialization_Load(SKSESerializationInterface * intfc) UInt32 type, length, version; bool error = false; + std::unordered_map stringTable; + StopWatch sw; sw.Start(); while (intfc->GetNextRecordInfo(&type, &version, &length)) { switch (type) { - case 'STTB': g_stringTable.Load(intfc, version); break; - case 'MRST': g_morphHandler.LoadMorphData(intfc, version); break; - case 'SCDT': g_morphHandler.LoadSculptData(intfc, version); break; - case 'AOVL': g_overlayInterface.Load(intfc, version); break; - case 'ACEN': g_overrideInterface.LoadOverrides(intfc, version); break; - case 'NDEN': g_overrideInterface.LoadNodeOverrides(intfc, version); break; - case 'WPEN': g_overrideInterface.LoadWeaponOverrides(intfc, version); break; - case 'SKNR': g_overrideInterface.LoadSkinOverrides(intfc, version); break; - case 'MRPH': g_bodyMorphInterface.Load(intfc, version); break; - case 'ITEE': g_itemDataInterface.Load(intfc, version); break; - case 'ACTM': g_transformInterface.Load(intfc, version); break; + case 'STTB': g_stringTable.Load(intfc, version, stringTable); break; + case 'MRST': g_morphInterface.LoadMorphData(intfc, version, stringTable); break; + case 'SCDT': g_morphInterface.LoadSculptData(intfc, version, stringTable); break; + case 'AOVL': g_overlayInterface.Load(intfc, version); break; + case 'ACEN': g_overrideInterface.LoadOverrides(intfc, version, stringTable); break; + case 'NDEN': g_overrideInterface.LoadNodeOverrides(intfc, version, stringTable); break; + case 'WPEN': g_overrideInterface.LoadWeaponOverrides(intfc, version, stringTable); break; + case 'SKNR': g_overrideInterface.LoadSkinOverrides(intfc, version, stringTable); break; + case 'MRPH': g_bodyMorphInterface.Load(intfc, version, stringTable); break; + case 'ITEE': g_itemDataInterface.Load(intfc, version); break; + case 'ACTM': g_transformInterface.Load(intfc, version, stringTable); break; default: _MESSAGE("unhandled type %08X (%.4s)", type, &type); error = true; @@ -402,8 +381,7 @@ void SKEE64Serialization_Load(SKSESerializationInterface * intfc) PlayerCharacter * player = (*g_thePlayer); g_task->AddTask(new SKSETaskApplyMorphs(player)); - - g_stringTable.Clear(); + g_firstLoad = true; #ifdef _DEBUG @@ -604,7 +582,7 @@ void SKSEMessageHandler(SKSEMessagingInterface::Message * message) g_bodyMorphInterface.LoadMods(); } - g_morphHandler.LoadMods(); + g_morphInterface.LoadMods(); } break; } @@ -650,9 +628,9 @@ bool SKSEPlugin_Query(const SKSEInterface * skse, PluginInfo * info) _MESSAGE("loaded in editor, marking as incompatible"); return false; } - else if (skse->runtimeVersion != RUNTIME_VERSION_1_5_53) + else if (skse->runtimeVersion != RUNTIME_VERSION_1_5_62) { - UInt32 runtimeVersion = RUNTIME_VERSION_1_5_53; + UInt32 runtimeVersion = RUNTIME_VERSION_1_5_62; char buf[512]; sprintf_s(buf, "RaceMenu Version Error:\nexpected game version %d.%d.%d.%d\nyour game version is %d.%d.%d.%d\nsome features may not work correctly.", GET_EXE_VERSION_MAJOR(runtimeVersion), @@ -720,11 +698,13 @@ bool SKSEPlugin_Query(const SKSEInterface * skse, PluginInfo * info) return false; } - // REMOVE THIS AFTER SKSE64 UPDATE - g_taskOverride.AddTask = g_task->AddTask; - g_taskOverride.AddUITask = g_task->AddUITask; - g_task = &g_taskOverride; - // ------------------------------- + if (g_task->interfaceVersion < 3) + { + g_taskOverride.AddTask = g_task->AddTask; + g_taskOverride.AddSKSETask = g_task->AddTask; + g_taskOverride.AddUITask = g_task->AddUITask; + g_task = &g_taskOverride; + } g_messaging = (SKSEMessagingInterface *)skse->QueryInterface(kInterface_Messaging); if (!g_messaging) { @@ -743,6 +723,7 @@ bool SKSEPlugin_Load(const SKSEInterface * skse) g_interfaceMap.AddInterface("BodyMorph", &g_bodyMorphInterface); g_interfaceMap.AddInterface("ItemData", &g_itemDataInterface); g_interfaceMap.AddInterface("TintMask", &g_tintMaskInterface); + g_interfaceMap.AddInterface("FaceMorph", &g_morphInterface); _DMESSAGE("NetImmerse Override Enabled");