-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathAnimationEventBridge.cpp
More file actions
203 lines (187 loc) · 6.82 KB
/
Copy pathAnimationEventBridge.cpp
File metadata and controls
203 lines (187 loc) · 6.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// I5-D4e-2 — 애니메이션 이벤트의 C++↔CLR 경계.
//
// 구 형태는 Animation(공유 자산) 멤버의 이벤트 표면(InvokeEvent·CRUD)이었다 —
// postLoad가 씬 오버라이드를 공유 자산에 재주입하고 여기서 발화하는 구조라,
// 같은 스켈레톤을 공유하는 Animator 간 이벤트 오염(마지막 로드 승자)이 있었다
// (D0a 판정). 이제 소유가 Animator의 클립 오버라이드(m_clipOverrides)로
// 옮겨졌고, 이 파일은 그 오버라이드의 CRUD와 발화(CLR 큐잉)를 구현한다.
// 죽은 표면(SetEvent·문자열 FindEvent — 호출자 0, return 누락 UB)은 이주하지
// 않았다.
#include "Animator.h"
#include "Entity.h"
#include "ScriptComponent.h"
#include "ClrHost.h"
#include "Assets/ModelAnimationSampler.h" // PHASE 3.75 MBC8: typed 클립 계량
AnimatorClipOverride* Animator::FindClipOverride(int clipIndex)
{
for (AnimatorClipOverride& clipOverride : m_clipOverrides)
{
if (clipOverride.clipIndex == clipIndex) return &clipOverride;
}
return nullptr;
}
const AnimatorClipOverride* Animator::FindClipOverride(int clipIndex) const
{
for (const AnimatorClipOverride& clipOverride : m_clipOverrides)
{
if (clipOverride.clipIndex == clipIndex) return &clipOverride;
}
return nullptr;
}
AnimatorClipOverride& Animator::EnsureClipOverride(int clipIndex)
{
if (AnimatorClipOverride* found = FindClipOverride(clipIndex))
{
return *found;
}
AnimatorClipOverride& created = m_clipOverrides.emplace_back();
created.clipIndex = clipIndex;
return created;
}
bool Animator::IsClipLooping(int clipIndex) const
{
// 오버라이드가 정본, 없으면 자산값 — experiment 핸들이 있으면 그쪽
// (원본 보존), 없으면 legacy 재주입 이전의 자산값이다. 재생 두 경로
// (legacy 재귀·experiment 틱)가 같은 함수를 보므로 판정이 갈리지 않는다.
if (const AnimatorClipOverride* clipOverride = FindClipOverride(clipIndex))
{
if (clipOverride->loopOverride.has_value())
{
return *clipOverride->loopOverride;
}
}
// 오버라이드가 없으면 generation 자산값(MBC9: 유일한 출처).
if (const assets::ModelAnimationAsset* clip = TypedClip(clipIndex))
return clip->looping;
return true;
}
void Animator::SetClipLooping(int clipIndex, bool looping)
{
EnsureClipOverride(clipIndex).loopOverride = looping;
}
namespace
{
void ReportClipPath(bool* outViaExperiment, AnimatorDataPath* outPath,
AnimatorDataPath path) noexcept
{
if (outViaExperiment) *outViaExperiment = false;
if (outPath) *outPath = path;
}
}
std::size_t Animator::GetClipCount(bool* outViaExperiment,
AnimatorDataPath* outPath) const
{
if (nullptr != TypedSkeleton())
{
ReportClipPath(outViaExperiment, outPath, AnimatorDataPath::Generation);
return TypedClipCount();
}
ReportClipPath(outViaExperiment, outPath, AnimatorDataPath::None);
return 0;
}
std::string Animator::GetClipName(int clipIndex, bool* outViaExperiment,
AnimatorDataPath* outPath) const
{
ReportClipPath(outViaExperiment, outPath, AnimatorDataPath::None);
if (clipIndex < 0 || nullptr == TypedSkeleton()) return std::string{};
ReportClipPath(outViaExperiment, outPath, AnimatorDataPath::Generation);
const assets::ModelAnimationAsset* clip = TypedClip(clipIndex);
return clip ? clip->name : std::string{};
}
std::size_t Animator::GetClipFrameCount(int clipIndex,
bool* outViaExperiment, AnimatorDataPath* outPath) const
{
ReportClipPath(outViaExperiment, outPath, AnimatorDataPath::None);
if (clipIndex < 0 || nullptr == TypedSkeleton()) return 0;
ReportClipPath(outViaExperiment, outPath, AnimatorDataPath::Generation);
const assets::ModelAnimationAsset* clip = TypedClip(clipIndex);
return clip ? assets::animation::CountUniqueKeyTimes(*clip) : 0u;
}
void Animator::AddClipEvent(int clipIndex)
{
AnimatorClipOverride& clipOverride = EnsureClipOverride(clipIndex);
std::string baseName = "newEvent";
std::string realName = baseName;
int suffix = 1;
const auto nameExists = [&clipOverride](const std::string& name)
{
for (const KeyFrameEvent& event : clipOverride.events)
{
if (event.m_eventName == name) return true;
}
return false;
};
while (nameExists(realName))
{
realName = baseName + "(" + std::to_string(suffix) + ")";
++suffix;
}
KeyFrameEvent newEvent;
newEvent.m_eventName = realName;
clipOverride.events.push_back(newEvent);
}
void Animator::DeleteClipEvent(int clipIndex, int eventIndex)
{
AnimatorClipOverride* clipOverride = FindClipOverride(clipIndex);
if (nullptr == clipOverride) return;
if (eventIndex >= 0
&& eventIndex < static_cast<int>(clipOverride->events.size()))
{
clipOverride->events.erase(clipOverride->events.begin() + eventIndex);
}
}
std::size_t Animator::InvokeClipEvents(int clipIndex, float currentProgress,
float previousProgress)
{
const AnimatorClipOverride* clipOverride = FindClipOverride(clipIndex);
if (nullptr == clipOverride || clipOverride->events.empty()) return 0;
// 이벤트를 받을 오브젝트: 애니메이터가 붙은 것이 자식이면 부모로 올라간다.
// (구 C++ 경로와 같은 규칙 — 스크립트는 보통 캐릭터 루트에 붙는다)
Entity* owner = GetOwner();
std::vector<ScriptComponent*> scripts;
if (nullptr != owner)
{
const Entity::Index parentIndex = owner->GetParentIndex();
if (parentIndex != 0)
{
Entity* parent = owner->OwnerSceneFindIndex(parentIndex);
if (nullptr != parent) owner = parent;
}
scripts = owner->GetComponents<ScriptComponent>();
}
// 매칭 판정과 큐잉을 분리한다 — 계수는 CLR·스크립트 유무와 무관하게
// 정확해야 게이트가 헤드리스에서 발화 규칙을 판정할 수 있다(큐잉 행동은
// 구 InvokeEvent와 동일: 스크립트·CLR이 없으면 아무것도 안 보낸다).
const bool looping = IsClipLooping(clipIndex);
std::size_t matchedCount = 0;
for (const KeyFrameEvent& event : clipOverride->events)
{
bool shouldTrigger = false;
if (currentProgress > previousProgress)
{
// 일반 진행
shouldTrigger = (previousProgress < event.key
&& event.key <= currentProgress);
}
else if (looping)
{
// 되감긴 경우 — 구간이 끝과 처음으로 갈라진다
shouldTrigger = (event.key > previousProgress && event.key <= 1.0f)
|| (event.key >= 0.0f && event.key <= currentProgress);
}
if (!shouldTrigger) continue;
++matchedCount;
if (scripts.empty()) continue;
auto& clr = ClrHost::Get();
if (!clr.IsReady()) continue;
// 발생 시점에 바로 부르지 않고 큐에 담는다 — 애니메이션 갱신은 잡
// 스레드에서도 돌고, 경계는 틱당 한 번만 넘는다는 규약이 있다(설계
// 문서 02절).
for (ScriptComponent* script : scripts)
{
if (nullptr == script || !script->HasInstance()) continue;
clr.QueueScriptMessage(script->GetInstanceId(), event.m_funName);
}
}
return matchedCount;
}