mirror of
https://github.com/Jous99/F4MP.git
synced 2026-01-12 17:10:54 +01:00
409 lines
14 KiB
C++
409 lines
14 KiB
C++
|
|
#include <imgui.h>
|
||
|
|
#include <EquipManager.h>
|
||
|
|
#include <services/DebugService.h>
|
||
|
|
|
||
|
|
#include <Games/Skyrim/Forms/TESForm.h>
|
||
|
|
#include <Games/Skyrim/BSGraphics/BSGraphicsRenderer.h>
|
||
|
|
#include <Games/Skyrim/DefaultObjectManager.h>
|
||
|
|
#include <Games/Skyrim/Forms/TESAmmo.h>
|
||
|
|
#include <Games/Skyrim/Misc/InventoryEntry.h>
|
||
|
|
#include <Games/Skyrim/Misc/MiddleProcess.h>
|
||
|
|
|
||
|
|
#include <Games/ActorExtension.h>
|
||
|
|
|
||
|
|
#include <Actor.h>
|
||
|
|
|
||
|
|
#include <BSAnimationGraphManager.h>
|
||
|
|
#include <Havok/hkbStateMachine.h>
|
||
|
|
#include <Havok/BShkbAnimationGraph.h>
|
||
|
|
#include <Havok/BShkbHkxDB.h>
|
||
|
|
#include <Havok/hkbBehaviorGraph.h>
|
||
|
|
|
||
|
|
#include <structs/AnimationGraphDescriptorManager.h>
|
||
|
|
#include <inttypes.h>
|
||
|
|
|
||
|
|
#include <ModCompat/BehaviorVar.h>
|
||
|
|
|
||
|
|
uint64_t DisplayGraphDescriptorKey(BSAnimationGraphManager* pManager) noexcept
|
||
|
|
{
|
||
|
|
auto hash = pManager->GetDescriptorKey();
|
||
|
|
auto pDescriptor = AnimationGraphDescriptorManager::Get().GetDescriptor(hash);
|
||
|
|
|
||
|
|
spdlog::info("Key: {}", hash);
|
||
|
|
std::cout << "uint64_t key = " << hash << ";" << std::endl;
|
||
|
|
if (!pDescriptor)
|
||
|
|
spdlog::error("Descriptor key not found");
|
||
|
|
|
||
|
|
return hash;
|
||
|
|
}
|
||
|
|
|
||
|
|
void DebugService::DrawAnimDebugView()
|
||
|
|
{
|
||
|
|
static uint32_t fetchFormId = 0;
|
||
|
|
static Actor* pActor = nullptr;
|
||
|
|
static Map<uint32_t, uint32_t> s_values;
|
||
|
|
static Map<uint32_t, uint32_t> s_reusedValues;
|
||
|
|
static Map<uint32_t, short> s_valueTypes; // 0 for bool, 1 for float, 2 for int
|
||
|
|
static Vector<uint32_t> s_blacklist{};
|
||
|
|
static SortedMap<uint32_t, String> s_varMap{};
|
||
|
|
static Map<uint64_t, uint32_t> s_cachedKeys{};
|
||
|
|
|
||
|
|
ImGui::Begin("Animation debugging");
|
||
|
|
|
||
|
|
ImGui::InputScalar("Form ID", ImGuiDataType_U32, &fetchFormId, 0, 0, "%" PRIx32, ImGuiInputTextFlags_CharsHexadecimal);
|
||
|
|
|
||
|
|
if (ImGui::Button("Look up"))
|
||
|
|
{
|
||
|
|
if (fetchFormId)
|
||
|
|
{
|
||
|
|
auto* pFetchForm = TESForm::GetById(fetchFormId);
|
||
|
|
if (pFetchForm)
|
||
|
|
pActor = Cast<Actor>(pFetchForm);
|
||
|
|
}
|
||
|
|
|
||
|
|
s_values.clear();
|
||
|
|
s_reusedValues.clear();
|
||
|
|
s_valueTypes.clear();
|
||
|
|
s_blacklist.clear();
|
||
|
|
s_varMap.clear();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!pActor)
|
||
|
|
{
|
||
|
|
ImGui::End();
|
||
|
|
s_varMap.clear();
|
||
|
|
s_values.clear();
|
||
|
|
s_reusedValues.clear();
|
||
|
|
s_valueTypes.clear();
|
||
|
|
s_blacklist.clear();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ImGui::Button("Show cached hash"))
|
||
|
|
{
|
||
|
|
spdlog::info("{}", pActor->GetExtension()->GraphDescriptorHash);
|
||
|
|
BehaviorVar::Get()->Debug();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ImGui::Button("Clear all"))
|
||
|
|
{
|
||
|
|
s_varMap.clear();
|
||
|
|
s_values.clear();
|
||
|
|
s_reusedValues.clear();
|
||
|
|
s_valueTypes.clear();
|
||
|
|
s_blacklist.clear();
|
||
|
|
}
|
||
|
|
|
||
|
|
BSAnimationGraphManager* pManager = nullptr;
|
||
|
|
pActor->animationGraphHolder.GetBSAnimationGraph(&pManager);
|
||
|
|
|
||
|
|
if (!pManager)
|
||
|
|
{
|
||
|
|
ImGui::End();
|
||
|
|
s_varMap.clear();
|
||
|
|
s_values.clear();
|
||
|
|
s_reusedValues.clear();
|
||
|
|
s_valueTypes.clear();
|
||
|
|
s_blacklist.clear();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const auto pGraph = pManager->animationGraphs.Get(pManager->animationGraphIndex);
|
||
|
|
|
||
|
|
if (!pGraph)
|
||
|
|
{
|
||
|
|
ImGui::End();
|
||
|
|
s_varMap.clear();
|
||
|
|
s_values.clear();
|
||
|
|
s_reusedValues.clear();
|
||
|
|
s_valueTypes.clear();
|
||
|
|
s_blacklist.clear();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (s_varMap.empty())
|
||
|
|
{
|
||
|
|
s_varMap = pManager->DumpAnimationVariables(true);
|
||
|
|
|
||
|
|
auto hash = DisplayGraphDescriptorKey(pManager);
|
||
|
|
|
||
|
|
if (s_cachedKeys.find(hash) != std::end(s_cachedKeys))
|
||
|
|
spdlog::warn("Key was detected before! Form ID of last detected actor: {:X}", s_cachedKeys[hash]);
|
||
|
|
s_cachedKeys[hash] = pActor->formID;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ImGui::Button("Revert animation graph"))
|
||
|
|
{
|
||
|
|
spdlog::info("RevertAnimationGraphManager success? {}",
|
||
|
|
pActor->animationGraphHolder.RevertAnimationGraphManager());
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None))
|
||
|
|
{
|
||
|
|
if (ImGui::BeginTabItem("Variables"))
|
||
|
|
{
|
||
|
|
auto hash = pManager->GetDescriptorKey();
|
||
|
|
auto pDescriptor = AnimationGraphDescriptorManager::Get().GetDescriptor(hash);
|
||
|
|
if (pDescriptor)
|
||
|
|
{
|
||
|
|
const auto* pVariableSet = pGraph->behaviorGraph->animationVariables;
|
||
|
|
|
||
|
|
Map<String, bool> bools{};
|
||
|
|
Map<String, float> floats{};
|
||
|
|
Map<String, int> ints{};
|
||
|
|
|
||
|
|
for (size_t i = 0; i < pDescriptor->BooleanLookUpTable.size(); ++i)
|
||
|
|
{
|
||
|
|
const auto idx = pDescriptor->BooleanLookUpTable[i];
|
||
|
|
bools[s_varMap[idx]] = *reinterpret_cast<bool*>(&pVariableSet->data[idx]);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (size_t i = 0; i < pDescriptor->FloatLookupTable.size(); ++i)
|
||
|
|
{
|
||
|
|
const auto idx = pDescriptor->FloatLookupTable[i];
|
||
|
|
floats[s_varMap[idx]] = *reinterpret_cast<float*>(&pVariableSet->data[idx]);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (size_t i = 0; i < pDescriptor->IntegerLookupTable.size(); ++i)
|
||
|
|
{
|
||
|
|
const auto idx = pDescriptor->IntegerLookupTable[i];
|
||
|
|
ints[s_varMap[idx]] = *reinterpret_cast<int*>(&pVariableSet->data[idx]);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ImGui::Button("Dump variable values"))
|
||
|
|
{
|
||
|
|
std::cout << "Bools: " << std::endl;
|
||
|
|
for (auto& [name, value] : bools)
|
||
|
|
{
|
||
|
|
std::cout << "\t" << name << ": " << value << std::endl;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::cout << "Floats: " << std::endl;
|
||
|
|
for (auto& [name, value] : floats)
|
||
|
|
{
|
||
|
|
std::cout << "\t" << name << ": " << value << std::endl;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::cout << "Integers: " << std::endl;
|
||
|
|
for (auto& [name, value] : ints)
|
||
|
|
{
|
||
|
|
std::cout << "\t" << name << ": " << value << std::endl;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ImGui::CollapsingHeader("Bools", ImGuiTreeNodeFlags_DefaultOpen))
|
||
|
|
{
|
||
|
|
for (auto pair : bools)
|
||
|
|
{
|
||
|
|
auto name = pair.first;
|
||
|
|
auto value = pair.second;
|
||
|
|
int iValue = int(value);
|
||
|
|
ImGui::InputInt(name.c_str(), &iValue, 0, 0, ImGuiInputTextFlags_ReadOnly);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ImGui::CollapsingHeader("Floats", ImGuiTreeNodeFlags_DefaultOpen))
|
||
|
|
{
|
||
|
|
for (auto pair : floats)
|
||
|
|
{
|
||
|
|
auto name = pair.first;
|
||
|
|
auto value = pair.second;
|
||
|
|
ImGui::InputFloat(name.c_str(), &value, 0, 0, "%.3f", ImGuiInputTextFlags_ReadOnly);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ImGui::CollapsingHeader("Integers", ImGuiTreeNodeFlags_DefaultOpen))
|
||
|
|
{
|
||
|
|
for (auto pair : ints)
|
||
|
|
{
|
||
|
|
auto name = pair.first;
|
||
|
|
auto value = pair.second;
|
||
|
|
ImGui::InputInt(name.c_str(), &value, 0, 0, ImGuiInputTextFlags_ReadOnly);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ImGui::EndTabItem();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ImGui::BeginTabItem("Blacklist"))
|
||
|
|
{
|
||
|
|
static uint32_t s_selectedBlacklistVar = 0;
|
||
|
|
static uint32_t s_selected = 0;
|
||
|
|
|
||
|
|
ImGui::BeginChild("Blacklisted variables");
|
||
|
|
|
||
|
|
int i = 0;
|
||
|
|
for (auto blacklistedVar : s_blacklist)
|
||
|
|
{
|
||
|
|
if (blacklistedVar >= pGraph->behaviorGraph->animationVariables->size)
|
||
|
|
{
|
||
|
|
++i;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
const auto varName = s_varMap[blacklistedVar];
|
||
|
|
|
||
|
|
char name[256];
|
||
|
|
sprintf_s(name, std::size(name), "k%s (%d)", varName.c_str(), blacklistedVar);
|
||
|
|
|
||
|
|
if (ImGui::Selectable(name, s_selectedBlacklistVar == blacklistedVar))
|
||
|
|
{
|
||
|
|
s_selectedBlacklistVar = blacklistedVar;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (s_selectedBlacklistVar == blacklistedVar)
|
||
|
|
{
|
||
|
|
s_selected = i;
|
||
|
|
}
|
||
|
|
|
||
|
|
++i;
|
||
|
|
}
|
||
|
|
|
||
|
|
ImGui::EndChild();
|
||
|
|
|
||
|
|
if (s_selected < s_blacklist.size())
|
||
|
|
{
|
||
|
|
if (ImGui::Button("Delete"))
|
||
|
|
{
|
||
|
|
s_blacklist.erase(s_blacklist.begin() + s_selected);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static uint32_t s_blacklistVar = 0;
|
||
|
|
ImGui::InputInt("New blacklist var:", (int*)&s_blacklistVar, 0, 0);
|
||
|
|
|
||
|
|
if (ImGui::Button("Add"))
|
||
|
|
{
|
||
|
|
s_blacklist.push_back(s_blacklistVar);
|
||
|
|
}
|
||
|
|
|
||
|
|
ImGui::EndTabItem();
|
||
|
|
}
|
||
|
|
if (ImGui::BeginTabItem("Control"))
|
||
|
|
{
|
||
|
|
ImGui::InputInt("Animation graph count", (int*)&pManager->animationGraphs.size, 0, 0, ImGuiInputTextFlags_ReadOnly);
|
||
|
|
ImGui::InputInt("Animation graph index", (int*)&pManager->animationGraphIndex, 0, 0, ImGuiInputTextFlags_ReadOnly);
|
||
|
|
|
||
|
|
char name[256];
|
||
|
|
sprintf_s(name, std::size(name), "%s", pGraph->behaviorGraph->stateMachine->name);
|
||
|
|
ImGui::InputText("Graph state machine name", name, std::size(name), ImGuiInputTextFlags_ReadOnly);
|
||
|
|
|
||
|
|
const auto pVariableSet = pGraph->behaviorGraph->animationVariables;
|
||
|
|
|
||
|
|
auto pDescriptor = AnimationGraphDescriptorManager::Get().GetDescriptor(pManager->GetDescriptorKey());
|
||
|
|
|
||
|
|
static bool toggleVariableRecord = false;
|
||
|
|
if (ImGui::Button("Toggle variable recording"))
|
||
|
|
{
|
||
|
|
toggleVariableRecord = !toggleVariableRecord;
|
||
|
|
spdlog::info("Toggle variable recording: {}", toggleVariableRecord);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (pVariableSet && toggleVariableRecord)
|
||
|
|
{
|
||
|
|
for (auto i = 0u; i < pVariableSet->size; ++i)
|
||
|
|
{
|
||
|
|
if (std::find(s_blacklist.begin(), s_blacklist.end(), i) != s_blacklist.end())
|
||
|
|
continue;
|
||
|
|
|
||
|
|
if (pDescriptor && pDescriptor->IsSynced(i))
|
||
|
|
continue;
|
||
|
|
|
||
|
|
auto iter = s_values.find(i);
|
||
|
|
if (iter == std::end(s_values))
|
||
|
|
{
|
||
|
|
s_values[i] = pVariableSet->data[i];
|
||
|
|
|
||
|
|
const auto varName = s_varMap[i];
|
||
|
|
|
||
|
|
spdlog::info("Variable k{} ({}) initialized to f: {} i: {}", varName, i, *(float*)&pVariableSet->data[i], *(int32_t*)&pVariableSet->data[i]);
|
||
|
|
}
|
||
|
|
else if (iter->second != pVariableSet->data[i])
|
||
|
|
{
|
||
|
|
const auto varName = s_varMap[i];
|
||
|
|
|
||
|
|
float floatCast = *(float*)&pVariableSet->data[i];
|
||
|
|
int intCast = *(int32_t*)&pVariableSet->data[i];
|
||
|
|
spdlog::warn("Variable k{} ({}) changed to f: {} i: {}", varName, i, floatCast, intCast);
|
||
|
|
|
||
|
|
s_values[i] = pVariableSet->data[i];
|
||
|
|
s_reusedValues[i] = pVariableSet->data[i];
|
||
|
|
|
||
|
|
char varTypeChar = varName[0]; // for guessing type, to see if u can find type char (i, f, b)
|
||
|
|
if (varTypeChar == 'f')
|
||
|
|
{
|
||
|
|
s_valueTypes[i] = 1;
|
||
|
|
}
|
||
|
|
else if (varTypeChar == 'i' && varName[1] != 's')
|
||
|
|
{
|
||
|
|
s_valueTypes[i] = 2;
|
||
|
|
}
|
||
|
|
else if (varTypeChar != 'b' && s_valueTypes[i] != 1) // no char hint to go off of and not assuming float
|
||
|
|
{
|
||
|
|
if (intCast > 1000 || intCast < -1000) // arbitrary int threshold
|
||
|
|
{
|
||
|
|
s_valueTypes[i] = 1; // assume float
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
if (intCast > 1 || intCast < 0)
|
||
|
|
{
|
||
|
|
s_valueTypes[i] = 2; // assume int
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ImGui::Button("Dump variables") && pVariableSet)
|
||
|
|
{
|
||
|
|
// kinda ugly to iterate 3 times but idc cuz its just for debugging and its a small collection
|
||
|
|
// BOOLS
|
||
|
|
std::cout << "{" << std::endl;
|
||
|
|
for (auto& [key, value] : s_reusedValues)
|
||
|
|
{
|
||
|
|
if (s_valueTypes[key] == 0)
|
||
|
|
{
|
||
|
|
const auto varName = s_varMap[key];
|
||
|
|
std::cout << "k" << varName << "," << std::endl;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// FLOATS
|
||
|
|
std::cout << "}," << std::endl << "{" << std::endl;
|
||
|
|
for (auto& [key, value] : s_reusedValues)
|
||
|
|
{
|
||
|
|
if (s_valueTypes[key] == 1)
|
||
|
|
{
|
||
|
|
const auto varName = s_varMap[key];
|
||
|
|
std::cout << "k" << varName << "," << std::endl;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// INTS
|
||
|
|
std::cout << "}," << std::endl << "{" << std::endl;
|
||
|
|
for (auto& [key, value] : s_reusedValues)
|
||
|
|
{
|
||
|
|
if (s_valueTypes[key] == 2)
|
||
|
|
{
|
||
|
|
const auto varName = s_varMap[key];
|
||
|
|
std::cout << "k" << varName << "," << std::endl;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
std::cout << "}" << std::endl;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ImGui::Button("Reset recording"))
|
||
|
|
{
|
||
|
|
s_values.clear();
|
||
|
|
s_reusedValues.clear();
|
||
|
|
spdlog::info("Variable recording has been reset");
|
||
|
|
}
|
||
|
|
|
||
|
|
ImGui::EndTabItem();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ImGui::End();
|
||
|
|
}
|