First Commit on new Git-Base, yey!

This commit is contained in:
2026-01-22 12:30:53 -05:00
parent 2061d588e7
commit 303b835ba7
93 changed files with 17252 additions and 1138 deletions

282
Scripts/AnimationWindow.cpp Normal file
View File

@@ -0,0 +1,282 @@
#include "ScriptRuntime.h"
#include "SceneObject.h"
#include "ThirdParty/imgui/imgui.h"
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
namespace {
struct Keyframe {
float time = 0.0f;
glm::vec3 position = glm::vec3(0.0f);
glm::vec3 rotation = glm::vec3(0.0f);
glm::vec3 scale = glm::vec3(1.0f);
};
int targetId = -1;
char targetName[128] = "";
std::vector<Keyframe> keyframes;
int selectedKey = -1;
float clipLength = 2.0f;
float currentTime = 0.0f;
float playSpeed = 1.0f;
bool isPlaying = false;
bool loop = true;
bool applyOnScrub = true;
glm::vec3 lerpVec3(const glm::vec3& a, const glm::vec3& b, float t) {
return a + (b - a) * t;
}
float clampFloat(float value, float minValue, float maxValue) {
return std::max(minValue, std::min(value, maxValue));
}
SceneObject* resolveTarget(ScriptContext& ctx) {
if (targetId >= 0) {
if (auto* obj = ctx.FindObjectById(targetId)) {
return obj;
}
}
if (targetName[0] != '\0') {
if (auto* obj = ctx.FindObjectByName(targetName)) {
targetId = obj->id;
return obj;
}
}
return nullptr;
}
void syncTargetLabel(SceneObject* obj) {
if (!obj) return;
strncpy(targetName, obj->name.c_str(), sizeof(targetName) - 1);
targetName[sizeof(targetName) - 1] = '\0';
}
void captureKeyframe(SceneObject& obj, float time) {
float clamped = clampFloat(time, 0.0f, clipLength);
auto it = std::find_if(keyframes.begin(), keyframes.end(),
[&](const Keyframe& k) { return std::abs(k.time - clamped) < 0.0001f; });
if (it == keyframes.end()) {
keyframes.push_back(Keyframe{clamped, obj.position, obj.rotation, obj.scale});
} else {
it->position = obj.position;
it->rotation = obj.rotation;
it->scale = obj.scale;
}
std::sort(keyframes.begin(), keyframes.end(),
[](const Keyframe& a, const Keyframe& b) { return a.time < b.time; });
}
void deleteKeyframe(int index) {
if (index < 0 || index >= static_cast<int>(keyframes.size())) return;
keyframes.erase(keyframes.begin() + index);
if (selectedKey == index) selectedKey = -1;
if (selectedKey > index) selectedKey--;
}
void applyPoseAtTime(ScriptContext& ctx, SceneObject& obj, float time) {
if (keyframes.empty()) return;
if (time <= keyframes.front().time) {
ctx.SetPosition(keyframes.front().position);
ctx.SetRotation(keyframes.front().rotation);
ctx.SetScale(keyframes.front().scale);
ctx.MarkDirty();
return;
}
if (time >= keyframes.back().time) {
ctx.SetPosition(keyframes.back().position);
ctx.SetRotation(keyframes.back().rotation);
ctx.SetScale(keyframes.back().scale);
ctx.MarkDirty();
return;
}
for (size_t i = 0; i + 1 < keyframes.size(); ++i) {
const Keyframe& a = keyframes[i];
const Keyframe& b = keyframes[i + 1];
if (time >= a.time && time <= b.time) {
float span = b.time - a.time;
float t = (span > 0.0f) ? (time - a.time) / span : 0.0f;
ctx.SetPosition(lerpVec3(a.position, b.position, t));
ctx.SetRotation(lerpVec3(a.rotation, b.rotation, t));
ctx.SetScale(lerpVec3(a.scale, b.scale, t));
ctx.MarkDirty();
return;
}
}
}
void drawTimeline(float& time, float length, int& selection) {
ImVec2 size = ImVec2(ImGui::GetContentRegionAvail().x, 70.0f);
ImVec2 start = ImGui::GetCursorScreenPos();
ImGui::InvisibleButton("Timeline", size);
ImDrawList* draw = ImGui::GetWindowDrawList();
ImU32 bg = ImGui::GetColorU32(ImGuiCol_FrameBg);
ImU32 border = ImGui::GetColorU32(ImGuiCol_Border);
ImU32 accent = ImGui::GetColorU32(ImGuiCol_CheckMark);
ImU32 keyColor = ImGui::GetColorU32(ImGuiCol_SliderGrab);
draw->AddRectFilled(start, ImVec2(start.x + size.x, start.y + size.y), bg, 6.0f);
draw->AddRect(start, ImVec2(start.x + size.x, start.y + size.y), border, 6.0f);
float clamped = clampFloat(time, 0.0f, length);
float playheadX = start.x + (length > 0.0f ? (clamped / length) * size.x : 0.0f);
draw->AddLine(ImVec2(playheadX, start.y), ImVec2(playheadX, start.y + size.y), accent, 2.0f);
for (size_t i = 0; i < keyframes.size(); ++i) {
float keyX = start.x + (length > 0.0f ? (keyframes[i].time / length) * size.x : 0.0f);
ImVec2 center(keyX, start.y + size.y * 0.5f);
float radius = (selection == static_cast<int>(i)) ? 6.0f : 4.5f;
draw->AddCircleFilled(center, radius, keyColor);
ImRect hit(ImVec2(center.x - 7.0f, center.y - 7.0f), ImVec2(center.x + 7.0f, center.y + 7.0f));
if (ImGui::IsMouseHoveringRect(hit.Min, hit.Max) && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
selection = static_cast<int>(i);
time = keyframes[i].time;
}
}
if (ImGui::IsItemActive() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
float mouseX = ImGui::GetIO().MousePos.x;
float t = (mouseX - start.x) / size.x;
time = clampFloat(t * length, 0.0f, length);
}
}
void drawKeyframeTable() {
if (keyframes.empty()) {
ImGui::TextDisabled("No keyframes yet.");
return;
}
if (ImGui::BeginTable("KeyframeTable", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
ImGui::TableSetupColumn("Time");
ImGui::TableSetupColumn("Position");
ImGui::TableSetupColumn("Rotation");
ImGui::TableSetupColumn("Scale");
ImGui::TableHeadersRow();
for (size_t i = 0; i < keyframes.size(); ++i) {
const auto& key = keyframes[i];
ImGui::TableNextRow();
ImGui::TableNextColumn();
bool selected = selectedKey == static_cast<int>(i);
std::string label = std::to_string(key.time);
if (ImGui::Selectable(label.c_str(), selected, ImGuiSelectableFlags_SpanAllColumns)) {
selectedKey = static_cast<int>(i);
currentTime = key.time;
}
ImGui::TableNextColumn();
ImGui::Text("%.2f, %.2f, %.2f", key.position.x, key.position.y, key.position.z);
ImGui::TableNextColumn();
ImGui::Text("%.2f, %.2f, %.2f", key.rotation.x, key.rotation.y, key.rotation.z);
ImGui::TableNextColumn();
ImGui::Text("%.2f, %.2f, %.2f", key.scale.x, key.scale.y, key.scale.z);
}
ImGui::EndTable();
}
}
} // namespace
extern "C" void RenderEditorWindow(ScriptContext& ctx) {
ImGui::TextUnformatted("Simple Animation");
ImGui::Separator();
SceneObject* selectedObj = ctx.object;
SceneObject* targetObj = resolveTarget(ctx);
ImGui::TextDisabled("Select a GameObject to animate:");
ImGui::BeginDisabled();
ImGui::InputText("##TargetName", targetName, sizeof(targetName));
ImGui::EndDisabled();
ImGui::SameLine();
if (ImGui::Button("Use Selected") && selectedObj) {
targetId = selectedObj->id;
syncTargetLabel(selectedObj);
}
ImGui::SameLine();
if (ImGui::Button("Clear")) {
targetId = -1;
targetName[0] = '\0';
targetObj = nullptr;
}
ImGui::Spacing();
if (ImGui::BeginTabBar("AnimModeTabs")) {
if (ImGui::BeginTabItem("Pose Mode")) {
ImGui::TextDisabled("Pose Editor");
ImGui::Separator();
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 6.0f);
if (ImGui::Button("Key")) {
if (targetObj) captureKeyframe(*targetObj, currentTime);
}
ImGui::SameLine();
if (ImGui::Button("Delete") && selectedKey >= 0) {
deleteKeyframe(selectedKey);
}
ImGui::PopStyleVar();
ImGui::Spacing();
drawTimeline(currentTime, clipLength, selectedKey);
ImGui::SliderFloat("Time", &currentTime, 0.0f, clipLength, "%.2fs");
ImGui::Spacing();
drawKeyframeTable();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Config Mode")) {
ImGui::TextDisabled("Playback");
ImGui::Separator();
ImGui::Checkbox("Loop", &loop);
ImGui::Checkbox("Apply On Scrub", &applyOnScrub);
ImGui::SliderFloat("Length", &clipLength, 0.1f, 20.0f, "%.2fs");
ImGui::SliderFloat("Speed", &playSpeed, 0.1f, 4.0f, "%.2fx");
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
ImGui::Spacing();
ImGui::Separator();
ImGui::TextDisabled("Transport");
if (ImGui::Button(isPlaying ? "Pause" : "Play")) {
isPlaying = !isPlaying;
}
ImGui::SameLine();
if (ImGui::Button("Stop")) {
isPlaying = false;
currentTime = 0.0f;
}
if (targetObj) {
ImGui::SameLine();
ImGui::TextDisabled("Target: %s", targetObj->name.c_str());
} else {
ImGui::TextDisabled("No target selected.");
}
if (isPlaying && clipLength > 0.0f) {
currentTime += ImGui::GetIO().DeltaTime * playSpeed;
if (currentTime > clipLength) {
if (loop) currentTime = std::fmod(currentTime, clipLength);
else {
currentTime = clipLength;
isPlaying = false;
}
}
}
if (targetObj && (isPlaying || applyOnScrub)) {
applyPoseAtTime(ctx, *targetObj, currentTime);
}
}
extern "C" void ExitRenderEditorWindow(ScriptContext& ctx) {
(void)ctx;
}