载入中...
搜索中...
未找到
AnimationEditingCommands.cpp
浏览该文件的文档.
2
4
5#include <cstdint>
6
8namespace {
9
10const editing::Value* field(const editing::Value& value, const char* key) {
11 const auto* object = value.getIf<editing::Value::Object>();
12 if (!object) return nullptr;
13 const auto found = object->find(key);
14 return found == object->end() ? nullptr : &found->second;
15}
16
17bool readNumber(const editing::Value& value, const char* key, double& out) {
18 const editing::Value* entry = field(value, key);
19 if (!entry) return false;
20 if (const auto* real = entry->getIf<double>()) {
21 out = *real;
22 return true;
23 }
24 if (const auto* integer = entry->getIf<std::int64_t>()) {
25 out = static_cast<double>(*integer);
26 return true;
27 }
28 return false;
29}
30
31IAnimationClipEditTarget* clipTarget(editing::IEditableTarget& target) {
32 return static_cast<IAnimationClipEditTarget*>(
34}
35
36editing::Result<void> registerOne(editing::IEditingCommandRegistry& registry, const char* id,
37 const char* displayName, editing::EditingCommandPlanner planner) {
38 editing::EditingCommandDescriptor descriptor;
40 descriptor.ownerModule = "animation_editing";
41 descriptor.displayName = displayName;
42 descriptor.category = "Animation";
43 descriptor.automationAllowed = true;
44 return registry.registerPlannedCommand(std::move(descriptor), std::move(planner));
45}
46
47} // namespace
48
50 auto settings = registerOne(
51 registry, "animation.clip.settings.set.v1", "Set animation clip settings",
53 auto* capability = clipTarget(target);
54 double duration = 0, sampleRate = 0;
55 const editing::Value* loopEntry = field(request.payload, "loop");
56 const auto* loop = loopEntry ? loopEntry->getIf<bool>() : nullptr;
57 if (!capability || !readNumber(request.payload, "duration", duration) ||
58 !readNumber(request.payload, "sampleRate", sampleRate) || !loop)
59 return eve::editing::failed<editing::CommandPlan>(
60 editing::Status::Rejected, editing::RuleId("animation.editing.settings-payload"),
61 "Clip settings require duration, sampleRate and loop");
62 auto operation = capability->makeSetSettings(duration, sampleRate, *loop);
63 if (!operation.ok())
64 return eve::editing::failed<editing::CommandPlan>(
65 operation.code(), editing::RuleId("animation.editing.settings-operation"),
66 "Animation clip rejected the settings");
68 plan.operations.push_back(std::move(operation).takeValue());
69 plan.summary = editing::Value::Object{{"duration", duration}, {"sampleRate", sampleRate}, {"loop", *loop}};
70 return eve::editing::applied<editing::CommandPlan>(std::move(plan));
71 });
72 if (!settings.ok()) return settings;
73
74 auto setTrack = registerOne(
75 registry, "animation.clip.track.set.v1", "Set animation clip bone track",
77 auto* capability = clipTarget(target);
78 if (!capability)
79 return eve::editing::failed<editing::CommandPlan>(editing::Status::Rejected,
80 editing::RuleId("animation.editing.track-target"),
81 "Animation clip track requires a clip target");
82 auto parsed = parseAnimationBoneTrack(request.payload);
83 if (!parsed.ok())
84 return eve::editing::failed<editing::CommandPlan>(parsed.code(),
85 editing::RuleId("animation.editing.track-payload"),
86 "Bone track requires stable id, bone name and keys");
87 auto operation = capability->makeSetTrack(parsed.value());
88 if (!operation.ok())
89 return eve::editing::failed<editing::CommandPlan>(operation.code(),
90 editing::RuleId("animation.editing.track-operation"),
91 "Animation clip rejected the bone track");
93 plan.operations.push_back(std::move(operation).takeValue());
94 plan.summary = editing::Value::Object{{"id", parsed.value().id.value()}, {"bone", parsed.value().bone}};
95 return eve::editing::applied<editing::CommandPlan>(std::move(plan));
96 });
97 if (!setTrack.ok()) return setTrack;
98
99 auto deleteTrack = registerOne(
100 registry, "animation.clip.track.delete.v1", "Delete animation clip bone track",
102 auto* capability = clipTarget(target);
103 const editing::Value* idValue = field(request.payload, "id");
104 const auto* id = idValue ? idValue->getIf<std::string>() : nullptr;
105 if (!capability || !id || id->empty())
106 return eve::editing::failed<editing::CommandPlan>(
107 editing::Status::Rejected, editing::RuleId("animation.editing.track-id"),
108 "Track deletion requires a clip target and stable id");
109 auto operation = capability->makeDeleteTrack(editing::StableId(*id));
110 if (!operation.ok())
111 return eve::editing::failed<editing::CommandPlan>(operation.code(),
112 editing::RuleId("animation.editing.track-delete"),
113 "Animation clip could not delete the bone track");
115 plan.operations.push_back(std::move(operation).takeValue());
116 plan.summary = editing::Value::Object{{"id", *id}};
117 return eve::editing::applied<editing::CommandPlan>(std::move(plan));
118 });
119 if (!deleteTrack.ok()) return deleteTrack;
120
121 auto setEvent = registerOne(
122 registry, "animation.clip.event.set.v1", "Set animation clip event",
124 auto* capability = clipTarget(target);
125 if (!capability)
126 return eve::editing::failed<editing::CommandPlan>(editing::Status::Rejected,
127 editing::RuleId("animation.editing.event-target"),
128 "Animation clip event requires a clip target");
129 auto parsed = parseAnimationEventRecord(request.payload);
130 if (!parsed.ok())
131 return eve::editing::failed<editing::CommandPlan>(parsed.code(),
132 editing::RuleId("animation.editing.event-payload"),
133 "Event requires stable id, time, name and payload");
134 auto operation = capability->makeSetEvent(parsed.value());
135 if (!operation.ok())
136 return eve::editing::failed<editing::CommandPlan>(operation.code(),
137 editing::RuleId("animation.editing.event-operation"),
138 "Animation clip rejected the event");
140 plan.operations.push_back(std::move(operation).takeValue());
141 plan.summary = editing::Value::Object{{"id", parsed.value().id.value()}};
142 return eve::editing::applied<editing::CommandPlan>(std::move(plan));
143 });
144 if (!setEvent.ok()) return setEvent;
145
146 auto deleteEvent = registerOne(
147 registry, "animation.clip.event.delete.v1", "Delete animation clip event",
149 auto* capability = clipTarget(target);
150 const editing::Value* idValue = field(request.payload, "id");
151 const auto* id = idValue ? idValue->getIf<std::string>() : nullptr;
152 if (!capability || !id || id->empty())
153 return eve::editing::failed<editing::CommandPlan>(
154 editing::Status::Rejected, editing::RuleId("animation.editing.event-id"),
155 "Event deletion requires a clip target and stable id");
156 auto operation = capability->makeDeleteEvent(editing::StableId(*id));
157 if (!operation.ok())
158 return eve::editing::failed<editing::CommandPlan>(operation.code(),
159 editing::RuleId("animation.editing.event-delete"),
160 "Animation clip could not delete the event");
162 plan.operations.push_back(std::move(operation).takeValue());
163 plan.summary = editing::Value::Object{{"id", *id}};
164 return eve::editing::applied<editing::CommandPlan>(std::move(plan));
165 });
166 if (!deleteEvent.ok()) return deleteEvent;
167
168 return registerOne(registry, "animation.clip.mask.set.v1", "Set animation clip bone mask",
170 auto* capability = clipTarget(target);
171 const editing::Value* boneValue = field(request.payload, "bone");
172 const auto* bone = boneValue ? boneValue->getIf<std::string>() : nullptr;
173 double weight = 0;
174 if (!capability || !bone || bone->empty() || !readNumber(request.payload, "weight", weight))
175 return eve::editing::failed<editing::CommandPlan>(
176 editing::Status::Rejected, editing::RuleId("animation.editing.mask-payload"),
177 "Mask requires a clip target, bone and weight");
178 auto operation = capability->makeSetMask({*bone, weight});
179 if (!operation.ok())
180 return eve::editing::failed<editing::CommandPlan>(
181 operation.code(), editing::RuleId("animation.editing.mask-operation"),
182 "Animation clip rejected the mask");
184 plan.operations.push_back(std::move(operation).takeValue());
185 plan.summary = editing::Value::Object{{"bone", *bone}, {"weight", weight}};
186 return eve::editing::applied<editing::CommandPlan>(std::move(plan));
187 });
188}
189
190} // namespace eve::animation_editing
LogicalId target
ActionParameterOperation operation
double value
float duration
std::string descriptor
const GltfImportRequest & request
std::uint32_t key
std::uint32_t bone
std::string id
Definition PlayHost.cpp:108
bool found
TerrainThermalSettings settings
Move-only operation result carrying either a value or Status.
Definition Result.h:155
UUID-backed identifier adapter for legacy textual boundaries.
Definition Identity.h:314
static CapabilityId editingCapabilityId()
Stable capability id for clip timeline operations.
IEditableTarget public API.
Lower-layer registration boundary implemented by an editor or runtime editing host.
Deterministic owning value tree shared by authoring hosts.
std::map< std::string, Value > Object
const T * getIf() const
Return an immediately borrowed typed value.
Result< AnimationEventRecord > parseAnimationEventRecord(const EditorValue &value)
Parse one authored event marker from an owning Value payload.
editing::Result< void > registerEditingCommands(editing::IEditingCommandRegistry &registry)
Register animation-clip commands with a generic editing host.
Result< AnimationBoneTrack > parseAnimationBoneTrack(const EditorValue &value)
Parse one authored bone track from an owning Value payload.
std::function< Result< CommandPlan >(IEditableTarget &, const CommandRequest &)> EditingCommandPlanner
Side-effect-free planner invoked with the resolved target.
StrongId< CommandIdTag > CommandId
Definition EditingIds.h:58
animation_editing::IAnimationClipEditTarget IAnimationClipEditTarget
const EditorValue * field(const EditorValue &value, const char *name)
bool readNumber(const EditorValue &value, const char *name, float &output)
CommandPlan public API.
std::vector< DomainOperation > operations
CommandRequest public API.