F4MP/codigos originales/tiltedcode/Code/client/Services/Debug/Views/AnimDebugView.cpp

409 lines
14 KiB
C++
Raw Normal View History

#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();
}