diff --git a/src/animation/condition.h b/src/animation/condition.h index bbb1da3a8..bca1c1099 100644 --- a/src/animation/condition.h +++ b/src/animation/condition.h @@ -81,7 +81,6 @@ struct InputDecl if (idx == 0) return i; --idx; } - ASSERT(false); return -1; } diff --git a/src/animation/controller.h b/src/animation/controller.h index 8d4f22f1f..e9a2a2e21 100644 --- a/src/animation/controller.h +++ b/src/animation/controller.h @@ -42,6 +42,7 @@ public: ANIMATION_SETS, MAX_ROOT_ROTATION_SPEED, INPUT_REFACTOR, + ENTER_EXIT_EVENTS, LAST }; diff --git a/src/animation/editor/state_machine_editor.cpp b/src/animation/editor/state_machine_editor.cpp index a476b7295..f3c51d53c 100644 --- a/src/animation/editor/state_machine_editor.cpp +++ b/src/animation/editor/state_machine_editor.cpp @@ -205,12 +205,12 @@ void Node::removeEvent(int index) } -static const char* getEventTypeName(const Anim::EventHeader& event, AnimEditor::IAnimationEditor& editor) +static const char* getEventTypeName(u32 type, AnimEditor::IAnimationEditor& editor) { int count = editor.getEventTypesCount(); for (int i = 0; i < count; ++i) { - if (editor.getEventTypeByIdx(i).type == event.type) + if (editor.getEventTypeByIdx(i).type == type) { return editor.getEventTypeByIdx(i).label; } @@ -224,15 +224,19 @@ void Node::onGUI() u32 set_input_type = crc32("set_input"); ImGui::InputText("Name", name.data, lengthOf(name.data)); if (!engine_cmp) return; + auto* engine_node = ((Anim::Node*)engine_cmp); + + onGuiEvents(engine_node->enter_events, "Enter Events"); + onGuiEvents(engine_node->exit_events, "Exit Events"); + if (!ImGui::CollapsingHeader("Events")) return; - auto* engine_node = ((Anim::Node*)engine_cmp); auto& events = engine_node->events; auto& editor = m_controller.getEditor(); for(int i = 0; i < engine_node->events_count; ++i) { Anim::EventHeader& header = *(Anim::EventHeader*)&events[sizeof(Anim::EventHeader) * i]; - const char* event_type_name = getEventTypeName(header, editor); + const char* event_type_name = getEventTypeName(header.type, editor); if (ImGui::TreeNode((void*)(uintptr)i, "%s - %fs", event_type_name, header.time)) { if (ImGui::Button("Remove")) @@ -279,6 +283,49 @@ void Node::onGUI() } +void Node::onGuiEvents(Anim::EventArray& events, const char* label) +{ + if (!ImGui::CollapsingHeader(label)) return; + ImGui::PushID(label); + + auto* engine_node = ((Anim::Node*)engine_cmp); + auto& editor = m_controller.getEditor(); + for (int i = 0; i < events.count; ++i) + { + auto& header = *(Anim::EnterExitEventHeader*)&events.data[sizeof(Anim::EnterExitEventHeader) * i]; + const char* event_type_name = getEventTypeName(header.type, editor); + if (ImGui::TreeNode((void*)(uintptr)i, "%s", event_type_name)) + { + if (ImGui::Button("Remove")) + { + events.remove(i); + ImGui::TreePop(); + break; + } + int event_offset = header.offset + sizeof(Anim::EnterExitEventHeader) * events.count; + editor.getEventType(header.type).editor.invoke(&events.data[event_offset], *this); + ImGui::TreePop(); + } + } + + auto getter = [](void* data, int idx, const char** out) -> bool { + auto* node = (Node*)data; + *out = node->m_controller.getEditor().getEventTypeByIdx(idx).label; + return true; + }; + static int current = 0; + ImGui::Combo("", ¤t, getter, this, editor.getEventTypesCount()); + ImGui::SameLine(); + if (ImGui::Button("Add event")) + { + auto& event_type = editor.getEventTypeByIdx(current); + events.append(event_type.size, event_type.type); + } + + ImGui::PopID(); +} + + void Node::serialize(OutputBlob& blob) { blob.write(pos); diff --git a/src/animation/editor/state_machine_editor.h b/src/animation/editor/state_machine_editor.h index a7b98dee4..92a4f62e4 100644 --- a/src/animation/editor/state_machine_editor.h +++ b/src/animation/editor/state_machine_editor.h @@ -11,7 +11,11 @@ namespace Lumix { class ResourceManagerBase; -namespace Anim { class ControllerResource; } +namespace Anim +{ + class ControllerResource; + struct EventArray; +} namespace AnimEditor @@ -82,6 +86,9 @@ public: void removeInEdge(Edge* edge) { m_in_edges.eraseItemFast(edge); } void removeEvent(int index); +protected: + void onGuiEvents(Anim::EventArray& events, const char* label); + public: ImVec2 pos; ImVec2 size; diff --git a/src/animation/events.h b/src/animation/events.h index 2036f4512..e9a46baa4 100644 --- a/src/animation/events.h +++ b/src/animation/events.h @@ -2,6 +2,7 @@ #include "engine/lumix.h" +#include "engine/array.h" namespace Lumix @@ -21,6 +22,28 @@ struct EventHeader }; +struct EnterExitEventHeader +{ + u32 type; + u16 offset; + u8 size; +}; + + +struct EventArray +{ + EventArray(IAllocator& allocator) + : data(allocator) + , count(0) + {} + void remove(int index); + void append(int size, u32 type); + + Array data; + int count; +}; + + struct SetInputEvent { int input_idx; diff --git a/src/animation/state_machine.cpp b/src/animation/state_machine.cpp index a62f6c82c..d7ed04de8 100644 --- a/src/animation/state_machine.cpp +++ b/src/animation/state_machine.cpp @@ -169,6 +169,8 @@ Node::Node(Component::Type type, IAllocator& _allocator) , out_edges(_allocator) , allocator(_allocator) , events(allocator) + , enter_events(allocator) + , exit_events(allocator) { } @@ -182,6 +184,18 @@ void Node::serialize(OutputBlob& blob) blob.write(events.size()); blob.write(&events[0], events.size()); } + blob.write(enter_events.count); + if (enter_events.count > 0) + { + blob.write(enter_events.data.size()); + blob.write(&enter_events.data[0], enter_events.data.size()); + } + blob.write(exit_events.count); + if (exit_events.count > 0) + { + blob.write(exit_events.data.size()); + blob.write(&exit_events.data[0], exit_events.data.size()); + } } @@ -196,6 +210,25 @@ void Node::deserialize(InputBlob& blob, Container* parent, int version) events.resize(size); blob.read(&events[0], size); } + if (version > (int)ControllerResource::Version::ENTER_EXIT_EVENTS) + { + blob.read(enter_events.count); + if (enter_events.count > 0) + { + int size; + blob.read(size); + enter_events.data.resize(size); + blob.read(&enter_events.data[0], size); + } + blob.read(exit_events.count); + if (exit_events.count > 0) + { + int size; + blob.read(size); + exit_events.data.resize(size); + blob.read(&exit_events.data[0], size); + } + } } @@ -273,6 +306,7 @@ void Blend1DNodeInstance::onAnimationSetUpdated(AnimSet& anim_set) void Blend1DNodeInstance::enter(RunningContext& rc, ComponentInstance* from) { + queueEnterEvents(rc); time = 0; if (node.items.size() > lengthOf(instances)) { @@ -412,6 +446,37 @@ void NodeInstance::queueEvents(RunningContext& rc, float old_time, float time, f } +void NodeInstance::queueEnterEvents(RunningContext& rc) +{ + Node& node = (Node&)source; + queueEventArray(rc, node.enter_events); +} + + +void NodeInstance::queueExitEvents(RunningContext& rc) +{ + Node& node = (Node&)source; + queueEventArray(rc, node.exit_events); +} + + +void NodeInstance::queueEventArray(RunningContext& rc, const EventArray& events) +{ + if (events.count <= 0) return; + + auto headers = (EnterExitEventHeader*)&events.data[0]; + for (int i = 0; i < events.count; ++i) + { + auto& header = headers[i]; + rc.event_stream->write(header.type); + rc.event_stream->write(rc.controller); + rc.event_stream->write(header.size); + rc.event_stream->write( + &events.data[0] + header.offset + sizeof(EnterExitEventHeader) * events.count, header.size); + } +} + + ComponentInstance* NodeInstance::checkOutEdges(Node& node, RunningContext& rc) { rc.current = this; @@ -421,6 +486,7 @@ ComponentInstance* NodeInstance::checkOutEdges(Node& node, RunningContext& rc) if (edge->condition(rc)) { ComponentInstance* new_item = edge->createInstance(*rc.allocator); + queueExitEvents(rc); new_item->enter(rc, this); return new_item; } @@ -532,7 +598,8 @@ struct AnimationNodeInstance : public NodeInstance void enter(RunningContext& rc, ComponentInstance* from) override - { + { + queueEnterEvents(rc); time = 0; if (node.animations_hashes.empty()) return; int idx = Math::rand() % node.animations_hashes.size(); @@ -746,6 +813,46 @@ Component* createComponent(Component::Type type, IAllocator& allocator) } +void EventArray::remove(int index) +{ + auto headers = (Anim::EnterExitEventHeader*)&data[0]; + auto header = headers[index]; + u8* headers_end = (u8*)(headers + count); + u8* end = &data.back() + 1; + u8* event_start = headers_end + header.offset; + u8* event_end = event_start + header.size; + + for (int i = index + 1; i < count; ++i) + { + auto& h = headers[i]; + h.offset -= header.size; + } + + u8* header_start = (u8*)&headers[index]; + u8* header_end = header_start + sizeof(Anim::EnterExitEventHeader); + moveMemory(header_start, header_end, event_start - header_end); + moveMemory(event_start - sizeof(Anim::EnterExitEventHeader), event_end, end - event_end); + + data.resize(data.size() - sizeof(Anim::EnterExitEventHeader) - header.size); + + --count; +} + + +void EventArray::append(int size, u32 type) +{ + int old_payload_size = data.size() - sizeof(Anim::EnterExitEventHeader) * count; + data.resize(data.size() + size + sizeof(Anim::EnterExitEventHeader)); + u8* headers_end = &data[count * sizeof(Anim::EnterExitEventHeader)]; + moveMemory(headers_end + sizeof(Anim::EnterExitEventHeader), headers_end, old_payload_size); + auto& event_header = *(Anim::EnterExitEventHeader*)&data[sizeof(Anim::EnterExitEventHeader) * count]; + event_header.type = type; + event_header.size = size; + event_header.offset = old_payload_size; + ++count; +} + + } // namespace Anim diff --git a/src/animation/state_machine.h b/src/animation/state_machine.h index 40ab65cfe..e32f18f39 100644 --- a/src/animation/state_machine.h +++ b/src/animation/state_machine.h @@ -2,6 +2,7 @@ #include "condition.h" +#include "events.h" #include "engine/array.h" #include "engine/lumix.h" @@ -84,6 +85,8 @@ struct Node : public Component Array out_edges; Array events; int events_count = 0; + EventArray enter_events; + EventArray exit_events; }; @@ -123,6 +126,10 @@ struct NodeInstance : public ComponentInstance ComponentInstance* checkOutEdges(Node& node, RunningContext& rc); void queueEvents(RunningContext& rc, float old_time, float time, float length); + void queueEnterEvents(RunningContext& rc); + void queueExitEvents(RunningContext& rc); +protected: + void queueEventArray(RunningContext& rc, const EventArray& events); };