Swim animations
This commit is contained in:
@@ -47,6 +47,7 @@ set(EDITSCENE_SOURCES
|
||||
ui/TriangleBufferEditor.cpp
|
||||
ui/CharacterSlotsEditor.cpp
|
||||
ui/AnimationTreeEditor.cpp
|
||||
ui/AnimationTreeTemplateEditor.cpp
|
||||
ui/CharacterEditor.cpp
|
||||
ui/CellGridEditor.cpp
|
||||
ui/LotEditor.cpp
|
||||
@@ -72,6 +73,7 @@ set(EDITSCENE_SOURCES
|
||||
components/TriangleBufferModule.cpp
|
||||
components/CharacterSlotsModule.cpp
|
||||
components/AnimationTreeModule.cpp
|
||||
components/AnimationTreeTemplateModule.cpp
|
||||
components/AnimationTree.cpp
|
||||
components/CharacterModule.cpp
|
||||
components/CellGridModule.cpp
|
||||
@@ -152,6 +154,7 @@ set(EDITSCENE_HEADERS
|
||||
ui/TriangleBufferEditor.hpp
|
||||
ui/CharacterSlotsEditor.hpp
|
||||
ui/AnimationTreeEditor.hpp
|
||||
ui/AnimationTreeTemplateEditor.hpp
|
||||
ui/CharacterEditor.hpp
|
||||
ui/CellGridEditor.hpp
|
||||
ui/LotEditor.hpp
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include "components/TriangleBuffer.hpp"
|
||||
#include "components/CharacterSlots.hpp"
|
||||
#include "components/AnimationTree.hpp"
|
||||
#include "components/AnimationTreeTemplate.hpp"
|
||||
#include "components/Character.hpp"
|
||||
#include "components/StartupMenu.hpp"
|
||||
#include "components/PlayerController.hpp"
|
||||
@@ -488,6 +489,9 @@ void EditorApp::setupECS()
|
||||
// Register AnimationTree component
|
||||
m_world.component<AnimationTreeComponent>();
|
||||
|
||||
// Register AnimationTreeTemplate component
|
||||
m_world.component<AnimationTreeTemplate>();
|
||||
|
||||
// Register Character component
|
||||
m_world.component<CharacterComponent>();
|
||||
|
||||
|
||||
@@ -150,6 +150,12 @@ struct AnimationTreeComponent {
|
||||
bool useRootMotion = false;
|
||||
bool dirty = true;
|
||||
|
||||
/* If set, the tree root is copied from the named template */
|
||||
Ogre::String templateName;
|
||||
|
||||
/* Runtime: last copied template version (not serialized) */
|
||||
uint64_t templateVersion = 0;
|
||||
|
||||
/* Runtime: current state of each state machine (not serialized) */
|
||||
std::unordered_map<Ogre::String, Ogre::String> currentStates;
|
||||
|
||||
|
||||
20
src/features/editScene/components/AnimationTreeTemplate.hpp
Normal file
20
src/features/editScene/components/AnimationTreeTemplate.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef EDITSCENE_ANIMATIONTREETEMPLATE_HPP
|
||||
#define EDITSCENE_ANIMATIONTREETEMPLATE_HPP
|
||||
#pragma once
|
||||
|
||||
#include <Ogre.h>
|
||||
|
||||
/**
|
||||
* Template marker for reusable animation trees.
|
||||
*
|
||||
* Entities with this component serve as shared animation tree templates.
|
||||
* They should also have an AnimationTreeComponent for editing the tree.
|
||||
* Other entities reference the template by name via
|
||||
* AnimationTreeComponent::templateName.
|
||||
*/
|
||||
struct AnimationTreeTemplate {
|
||||
Ogre::String name;
|
||||
uint64_t version = 1;
|
||||
};
|
||||
|
||||
#endif // EDITSCENE_ANIMATIONTREETEMPLATE_HPP
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "AnimationTreeTemplate.hpp"
|
||||
#include "../ui/ComponentRegistration.hpp"
|
||||
#include "../ui/AnimationTreeTemplateEditor.hpp"
|
||||
|
||||
REGISTER_COMPONENT_GROUP("Animation Tree Template", "Animation",
|
||||
AnimationTreeTemplate, AnimationTreeTemplateEditor)
|
||||
{
|
||||
registry.registerComponent<AnimationTreeTemplate>(
|
||||
AnimationTreeTemplate_name, AnimationTreeTemplate_group,
|
||||
std::make_unique<AnimationTreeTemplateEditor>(),
|
||||
// Adder
|
||||
[](flecs::entity e) {
|
||||
if (!e.has<AnimationTreeTemplate>()) {
|
||||
e.set<AnimationTreeTemplate>({});
|
||||
}
|
||||
},
|
||||
// Remover
|
||||
[](flecs::entity e) {
|
||||
if (e.has<AnimationTreeTemplate>()) {
|
||||
e.remove<AnimationTreeTemplate>();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -228,6 +228,39 @@ void AnimationTreeSystem::initializeTreeStates(
|
||||
initializeTreeStates(child, at);
|
||||
}
|
||||
|
||||
void AnimationTreeSystem::resolveTemplate(AnimationTreeComponent &at)
|
||||
{
|
||||
if (at.templateName.empty())
|
||||
return;
|
||||
|
||||
AnimationTreeTemplate *templ = nullptr;
|
||||
AnimationTreeComponent *templAt = nullptr;
|
||||
|
||||
m_world.query<AnimationTreeTemplate, AnimationTreeComponent>()
|
||||
.each([&](flecs::entity, AnimationTreeTemplate &t,
|
||||
AnimationTreeComponent &ta) {
|
||||
if (t.name == at.templateName) {
|
||||
templ = &t;
|
||||
templAt = &ta;
|
||||
}
|
||||
});
|
||||
|
||||
if (!templ) {
|
||||
m_world.query<AnimationTreeTemplate>()
|
||||
.each([&](flecs::entity, AnimationTreeTemplate &t) {
|
||||
if (t.name == at.templateName)
|
||||
templ = &t;
|
||||
});
|
||||
}
|
||||
|
||||
if (templ && at.templateVersion != templ->version) {
|
||||
if (templAt)
|
||||
at.root = templAt->root;
|
||||
at.templateVersion = templ->version;
|
||||
at.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTreeSystem::update(float deltaTime)
|
||||
{
|
||||
if (!m_initialized)
|
||||
@@ -236,6 +269,8 @@ void AnimationTreeSystem::update(float deltaTime)
|
||||
m_world.query<AnimationTreeComponent>().each(
|
||||
[this, deltaTime](flecs::entity e,
|
||||
AnimationTreeComponent &at) {
|
||||
resolveTemplate(at);
|
||||
|
||||
if (at.dirty) {
|
||||
if (setupEntity(e, at))
|
||||
at.dirty = false;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <set>
|
||||
|
||||
#include "../components/AnimationTree.hpp"
|
||||
#include "../components/AnimationTreeTemplate.hpp"
|
||||
|
||||
/**
|
||||
* System that evaluates an AnimationTreeComponent each frame.
|
||||
@@ -100,6 +101,9 @@ private:
|
||||
const Ogre::String &stateMachineName,
|
||||
const Ogre::String &stateName, bool reset);
|
||||
|
||||
/* Resolve template reference and copy tree if template changed */
|
||||
void resolveTemplate(AnimationTreeComponent &at);
|
||||
|
||||
const AnimationTreeNode *findStateMachineNode(
|
||||
const AnimationTreeNode &root,
|
||||
const Ogre::String &name) const;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "../components/CharacterSlots.hpp"
|
||||
#include "../components/Character.hpp"
|
||||
#include "../components/AnimationTree.hpp"
|
||||
#include "../components/AnimationTreeTemplate.hpp"
|
||||
#include "../components/StartupMenu.hpp"
|
||||
#include "../components/PlayerController.hpp"
|
||||
#include "../components/CellGrid.hpp"
|
||||
@@ -701,6 +702,13 @@ void EditorUISystem::renderComponentList(flecs::entity entity)
|
||||
componentCount++;
|
||||
}
|
||||
|
||||
// Render AnimationTreeTemplate if present
|
||||
if (entity.has<AnimationTreeTemplate>()) {
|
||||
auto &templ = entity.get_mut<AnimationTreeTemplate>();
|
||||
m_componentRegistry.render<AnimationTreeTemplate>(entity, templ);
|
||||
componentCount++;
|
||||
}
|
||||
|
||||
// Render StartupMenu if present
|
||||
if (entity.has<StartupMenuComponent>()) {
|
||||
auto &sm = entity.get_mut<StartupMenuComponent>();
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "../components/Character.hpp"
|
||||
#include "../components/CharacterSlots.hpp"
|
||||
#include "../components/AnimationTree.hpp"
|
||||
#include "../components/AnimationTreeTemplate.hpp"
|
||||
#include "../components/StartupMenu.hpp"
|
||||
#include "../components/PlayerController.hpp"
|
||||
#include "../components/CellGrid.hpp"
|
||||
@@ -211,6 +212,10 @@ nlohmann::json SceneSerializer::serializeEntity(flecs::entity entity)
|
||||
json["animationTree"] = serializeAnimationTree(entity);
|
||||
}
|
||||
|
||||
if (entity.has<AnimationTreeTemplate>()) {
|
||||
json["animationTreeTemplate"] = serializeAnimationTreeTemplate(entity);
|
||||
}
|
||||
|
||||
if (entity.has<StartupMenuComponent>()) {
|
||||
json["startupMenu"] = serializeStartupMenu(entity);
|
||||
}
|
||||
@@ -364,6 +369,10 @@ void SceneSerializer::deserializeEntity(const nlohmann::json &json,
|
||||
deserializeAnimationTree(entity, json["animationTree"]);
|
||||
}
|
||||
|
||||
if (json.contains("animationTreeTemplate")) {
|
||||
deserializeAnimationTreeTemplate(entity, json["animationTreeTemplate"]);
|
||||
}
|
||||
|
||||
if (json.contains("startupMenu")) {
|
||||
deserializeStartupMenu(entity, json["startupMenu"]);
|
||||
}
|
||||
@@ -1549,6 +1558,8 @@ nlohmann::json SceneSerializer::serializeAnimationTree(flecs::entity entity)
|
||||
nlohmann::json json;
|
||||
json["enabled"] = at.enabled;
|
||||
json["useRootMotion"] = at.useRootMotion;
|
||||
if (!at.templateName.empty())
|
||||
json["templateName"] = at.templateName;
|
||||
json["root"] = serializeAnimationTreeNode(at.root);
|
||||
return json;
|
||||
}
|
||||
@@ -1559,12 +1570,31 @@ void SceneSerializer::deserializeAnimationTree(flecs::entity entity,
|
||||
AnimationTreeComponent at;
|
||||
at.enabled = json.value("enabled", true);
|
||||
at.useRootMotion = json.value("useRootMotion", false);
|
||||
at.templateName = json.value("templateName", "");
|
||||
if (json.contains("root"))
|
||||
deserializeAnimationTreeNode(at.root, json["root"]);
|
||||
at.dirty = true;
|
||||
entity.set<AnimationTreeComponent>(at);
|
||||
}
|
||||
|
||||
nlohmann::json SceneSerializer::serializeAnimationTreeTemplate(flecs::entity entity)
|
||||
{
|
||||
auto &templ = entity.get<AnimationTreeTemplate>();
|
||||
nlohmann::json json;
|
||||
json["name"] = templ.name;
|
||||
json["version"] = templ.version;
|
||||
return json;
|
||||
}
|
||||
|
||||
void SceneSerializer::deserializeAnimationTreeTemplate(flecs::entity entity,
|
||||
const nlohmann::json &json)
|
||||
{
|
||||
AnimationTreeTemplate templ;
|
||||
templ.name = json.value("name", "");
|
||||
templ.version = json.value("version", 1);
|
||||
entity.set<AnimationTreeTemplate>(templ);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CellGrid/Town Component Serialization
|
||||
// ============================================================================
|
||||
|
||||
@@ -62,6 +62,7 @@ private:
|
||||
nlohmann::json serializeCharacter(flecs::entity entity);
|
||||
nlohmann::json serializeCharacterSlots(flecs::entity entity);
|
||||
nlohmann::json serializeAnimationTree(flecs::entity entity);
|
||||
nlohmann::json serializeAnimationTreeTemplate(flecs::entity entity);
|
||||
nlohmann::json serializeStartupMenu(flecs::entity entity);
|
||||
nlohmann::json serializePlayerController(flecs::entity entity);
|
||||
|
||||
@@ -111,6 +112,8 @@ private:
|
||||
const nlohmann::json &json);
|
||||
void deserializeAnimationTree(flecs::entity entity,
|
||||
const nlohmann::json &json);
|
||||
void deserializeAnimationTreeTemplate(flecs::entity entity,
|
||||
const nlohmann::json &json);
|
||||
void deserializeStartupMenu(flecs::entity entity,
|
||||
const nlohmann::json &json);
|
||||
void deserializePlayerController(flecs::entity entity,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "AnimationTreeEditor.hpp"
|
||||
#include "../systems/AnimationTreeSystem.hpp"
|
||||
#include "../components/AnimationTreeTemplate.hpp"
|
||||
#include <imgui.h>
|
||||
|
||||
AnimationTreeEditor::AnimationTreeEditor(Ogre::SceneManager *sceneMgr)
|
||||
@@ -211,7 +212,8 @@ void AnimationTreeEditor::renderTree(AnimationTreeNode &node,
|
||||
if (ImGui::BeginPopupContextItem()) {
|
||||
if (canHaveChildren(node.type)) {
|
||||
if (node.type == "output" ||
|
||||
node.type == "stateMachine") {
|
||||
node.type == "stateMachine" ||
|
||||
node.type == "state") {
|
||||
if (ImGui::MenuItem("Add State Machine"))
|
||||
queueAddChild(&node, "stateMachine");
|
||||
}
|
||||
@@ -259,7 +261,8 @@ void AnimationTreeEditor::renderTree(AnimationTreeNode &node,
|
||||
}
|
||||
if (ImGui::BeginPopup("AddChild")) {
|
||||
if (node.type == "output" ||
|
||||
node.type == "stateMachine") {
|
||||
node.type == "stateMachine" ||
|
||||
node.type == "state") {
|
||||
if (ImGui::MenuItem("State Machine"))
|
||||
queueAddChild(&node, "stateMachine");
|
||||
}
|
||||
@@ -473,12 +476,44 @@ void AnimationTreeEditor::renderStatePreview(
|
||||
bool AnimationTreeEditor::renderComponent(
|
||||
flecs::entity entity, AnimationTreeComponent &at)
|
||||
{
|
||||
ImGui::PushID("AnimTree");
|
||||
bool modified = false;
|
||||
|
||||
if (ImGui::CollapsingHeader("Animation Tree",
|
||||
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::Indent();
|
||||
|
||||
/* Template reference */
|
||||
char templBuf[256];
|
||||
snprintf(templBuf, sizeof(templBuf), "%s",
|
||||
at.templateName.c_str());
|
||||
if (ImGui::InputText("Template Name", templBuf,
|
||||
sizeof(templBuf))) {
|
||||
at.templateName = templBuf;
|
||||
modified = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"Leave empty for local tree. Set to template name to copy from a template entity.");
|
||||
}
|
||||
|
||||
bool isTemplateEntity = entity.has<AnimationTreeTemplate>();
|
||||
if (isTemplateEntity) {
|
||||
auto &templ = entity.get_mut<AnimationTreeTemplate>();
|
||||
char nameBuf[256];
|
||||
snprintf(nameBuf, sizeof(nameBuf), "%s",
|
||||
templ.name.c_str());
|
||||
if (ImGui::InputText("Template Publish Name", nameBuf,
|
||||
sizeof(nameBuf))) {
|
||||
templ.name = nameBuf;
|
||||
modified = true;
|
||||
}
|
||||
ImGui::TextDisabled("Template version: %llu",
|
||||
(unsigned long long)templ.version);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
/* Global toggles */
|
||||
if (ImGui::Checkbox("Enabled", &at.enabled))
|
||||
modified = true;
|
||||
@@ -515,5 +550,12 @@ bool AnimationTreeEditor::renderComponent(
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
/* Auto-publish template changes */
|
||||
if (modified && entity.has<AnimationTreeTemplate>()) {
|
||||
auto &templ = entity.get_mut<AnimationTreeTemplate>();
|
||||
templ.version++;
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
return modified;
|
||||
}
|
||||
|
||||
37
src/features/editScene/ui/AnimationTreeTemplateEditor.cpp
Normal file
37
src/features/editScene/ui/AnimationTreeTemplateEditor.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "AnimationTreeTemplateEditor.hpp"
|
||||
#include <imgui.h>
|
||||
|
||||
bool AnimationTreeTemplateEditor::renderComponent(
|
||||
flecs::entity entity, AnimationTreeTemplate &templ)
|
||||
{
|
||||
ImGui::PushID("AnimTreeTempl");
|
||||
(void)entity;
|
||||
bool modified = false;
|
||||
|
||||
if (ImGui::CollapsingHeader("Animation Tree Template",
|
||||
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::Indent();
|
||||
|
||||
char nameBuf[256];
|
||||
snprintf(nameBuf, sizeof(nameBuf), "%s", templ.name.c_str());
|
||||
if (ImGui::InputText("Template Name", nameBuf,
|
||||
sizeof(nameBuf))) {
|
||||
templ.name = nameBuf;
|
||||
modified = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"Other entities reference this template by name in their Animation Tree component.");
|
||||
}
|
||||
|
||||
ImGui::TextDisabled("Version: %llu",
|
||||
(unsigned long long)templ.version);
|
||||
ImGui::TextDisabled(
|
||||
"Edit the Animation Tree component on this entity to modify the template.");
|
||||
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
return modified;
|
||||
}
|
||||
24
src/features/editScene/ui/AnimationTreeTemplateEditor.hpp
Normal file
24
src/features/editScene/ui/AnimationTreeTemplateEditor.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef EDITSCENE_ANIMATIONTREETEMPLATEEDITOR_HPP
|
||||
#define EDITSCENE_ANIMATIONTREETEMPLATEEDITOR_HPP
|
||||
#pragma once
|
||||
|
||||
#include "ComponentEditor.hpp"
|
||||
#include "../components/AnimationTreeTemplate.hpp"
|
||||
|
||||
/**
|
||||
* Editor for AnimationTreeTemplate component.
|
||||
* The actual tree editing is done via the co-located AnimationTreeComponent.
|
||||
* This editor only exposes the template name and version.
|
||||
*/
|
||||
class AnimationTreeTemplateEditor
|
||||
: public ComponentEditor<AnimationTreeTemplate> {
|
||||
public:
|
||||
bool renderComponent(flecs::entity entity,
|
||||
AnimationTreeTemplate &templ) override;
|
||||
const char *getName() const override
|
||||
{
|
||||
return "Animation Tree Template";
|
||||
}
|
||||
};
|
||||
|
||||
#endif // EDITSCENE_ANIMATIONTREETEMPLATEEDITOR_HPP
|
||||
Reference in New Issue
Block a user