载入中...
搜索中...
未找到
AvatarSkeletal.cpp
浏览该文件的文档.
1#include <glm/gtc/matrix_transform.hpp>
12#include "animation/Tween.h"
13#include "avatar/Avatar.h"
15#include "avatar/VrmRuntime.h"
16#include "common/ECS.h"
17#include "common/Module.h"
18#include "graphics/DrawItem2D.h"
19#include "graphics/Graphics.h"
20#include "graphics/Mesh.h"
23#include "graphics/Texture.h"
24#include "image/ImageData.h"
25#include "inventory/Equipment.h"
26#include "model3d/ModelData.h"
27#include "scene/Scene.h"
28
29#include <algorithm>
30#include <cctype>
31#include <cstdlib>
32#include <functional>
33#include <sstream>
34
35namespace eve::avatar {
36bool AvatarInstance::mapHumanoidBone(const std::string& semantic, const std::string& boneName) {
37 if (kind_ != "vroid" || !animSkeleton_ || semantic.empty() || boneName.empty()) return false;
38 if (animSkeleton_->findBone(boneName) < 0) return false;
39 humanoidBones_[semantic] = boneName;
40 return true;
41}
42
44 if (kind_ != "vroid" || !animSkeleton_) return 0;
45 static const std::pair<const char*, std::vector<const char*>> aliases[] = {
46 {"hips", {"hips", "Hips", "pelvis", "Pelvis"}},
47 {"spine", {"spine", "Spine"}},
48 {"chest", {"chest", "Chest", "spine1", "Spine1"}},
49 {"upperChest", {"upperChest", "UpperChest", "spine2", "Spine2"}},
50 {"neck", {"neck", "Neck"}},
51 {"head", {"head", "Head"}},
52 {"leftUpperArm", {"leftUpperArm", "LeftUpperArm", "upper_arm.L"}},
53 {"leftLowerArm", {"leftLowerArm", "LeftLowerArm", "lower_arm.L"}},
54 {"leftHand", {"leftHand", "LeftHand", "hand.L"}},
55 {"rightUpperArm", {"rightUpperArm", "RightUpperArm", "upper_arm.R"}},
56 {"rightLowerArm", {"rightLowerArm", "RightLowerArm", "lower_arm.R"}},
57 {"rightHand", {"rightHand", "RightHand", "hand.R"}},
58 {"leftUpperLeg", {"leftUpperLeg", "LeftUpperLeg", "thigh.L"}},
59 {"leftLowerLeg", {"leftLowerLeg", "LeftLowerLeg", "shin.L"}},
60 {"leftFoot", {"leftFoot", "LeftFoot", "foot.L"}},
61 {"rightUpperLeg", {"rightUpperLeg", "RightUpperLeg", "thigh.R"}},
62 {"rightLowerLeg", {"rightLowerLeg", "RightLowerLeg", "shin.R"}},
63 {"rightFoot", {"rightFoot", "RightFoot", "foot.R"}},
64 };
65 int mapped = 0;
66 for (const auto& [semantic, names] : aliases) {
67 if (humanoidBones_.contains(semantic)) continue;
68 for (const char* name : names) {
69 if (animSkeleton_->findBone(name) < 0) continue;
70 humanoidBones_[semantic] = name;
71 ++mapped;
72 break;
73 }
74 }
75 return mapped;
76}
77
78std::string AvatarInstance::getHumanoidBoneName(const std::string& semantic) const {
79 const auto it = humanoidBones_.find(semantic);
80 return it == humanoidBones_.end() ? std::string{} : it->second;
81}
82
83bool AvatarInstance::mapViseme(const std::string& viseme, const std::string& morphName) {
84 if (kind_ != "vroid" || !boundMesh_ || viseme.empty() || !boundMesh_->hasMorph(morphName)) return false;
85 visemeMorphs_[viseme] = morphName;
86 ensureParameter(morphName);
87 return true;
88}
89
90bool AvatarInstance::setViseme(const std::string& viseme, float weight) {
91 if (vrm_ && hasParameter(viseme)) {
92 if (!activeViseme_.empty()) setParameter(activeViseme_, 0);
93 setParameter(viseme, std::clamp(weight, 0.f, 1.f));
94 activeViseme_ = viseme;
95 return true;
96 }
97 const auto it = visemeMorphs_.find(viseme);
98 if (it == visemeMorphs_.end()) return false;
99 if (!activeViseme_.empty() && activeViseme_ != viseme) {
100 const auto previous = visemeMorphs_.find(activeViseme_);
101 if (previous != visemeMorphs_.end()) setParameter(previous->second, 0.f);
102 }
103 setParameter(it->second, std::clamp(weight, 0.f, 1.f));
104 activeViseme_ = viseme;
105 return true;
106}
107
108bool AvatarInstance::setLookAtTarget(float x, float y, float z) {
109 if (kind_ != "vroid" || !animSkeleton_ || !humanoidBones_.contains("head")) return false;
110 lookAtX_ = x;
111 lookAtY_ = y;
112 lookAtZ_ = z;
113 lookAtEnabled_ = true;
114 return true;
115}
116
117void AvatarInstance::setLookAtWeight(float weight) { lookAtWeight_ = std::clamp(weight, 0.f, 1.f); }
118
119void AvatarInstance::clearLookAtTarget() { lookAtEnabled_ = false; }
120
121bool AvatarInstance::attachToBone(const std::string& name, const std::string& boneSemanticOrName,
122 graphics::Renderable3D* renderable, float ox, float oy, float oz) {
123 if (kind_ != "vroid" || name.empty() || !animSkeleton_ || !renderable) return false;
124 std::string boneName = boneSemanticOrName;
125 const auto mapped = humanoidBones_.find(boneSemanticOrName);
126 if (mapped != humanoidBones_.end()) boneName = mapped->second;
127 if (animSkeleton_->findBone(boneName) < 0) return false;
128 for (auto& attachment : attachments_) {
129 if (attachment.name != name) continue;
130 attachment = {name, boneName, renderable, ox, oy, oz};
131 return true;
132 }
133 attachments_.push_back({name, boneName, renderable, ox, oy, oz});
134 return true;
135}
136
137bool AvatarInstance::detachAttachment(const std::string& name) {
138 const auto it = std::find_if(attachments_.begin(), attachments_.end(),
139 [&](const Attachment& item) { return item.name == name; });
140 if (it == attachments_.end()) return false;
141 attachments_.erase(it);
142 return true;
143}
144
147 if (!animSkeleton_ || attachmentPoseMatrices_.empty())
149 eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, "Avatar has no evaluated skeletal pose",
150 "avatar.attachment.pose"));
151 std::string boneName(name);
152 const auto semantic = humanoidBones_.find(boneName);
153 if (semantic != humanoidBones_.end()) boneName = semantic->second;
154 const int bone = animSkeleton_->findBone(boneName);
155 if (bone < 0 || static_cast<std::size_t>(bone) >= attachmentPoseMatrices_.size())
157 eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "Avatar attachment bone was not found", boneName));
158 const auto& matrix = attachmentPoseMatrices_[static_cast<std::size_t>(bone)];
159 const float bx = matrix[0] * localOffset.x + matrix[4] * localOffset.y + matrix[8] * localOffset.z + matrix[12];
160 const float by = matrix[1] * localOffset.x + matrix[5] * localOffset.y + matrix[9] * localOffset.z + matrix[13];
161 const float bz = matrix[2] * localOffset.x + matrix[6] * localOffset.y + matrix[10] * localOffset.z + matrix[14];
162 const float sinYaw = std::sin(yaw_);
163 const float cosYaw = std::cos(yaw_);
164 return eve::Result<eve::AttachmentPoint>::success({x3_ + bx * sx3_ * cosYaw + bz * sz3_ * sinYaw, y3_ + by * sy3_,
165 z3_ - bx * sx3_ * sinYaw + bz * sz3_ * cosYaw});
166}
167
168animation::AnimPose* AvatarInstance::updateSkeletalAnimation(float dt) {
169 if (vrm_) {
170 auto matrix = glm::translate(glm::mat4(1), glm::vec3(x3_, y3_, z3_));
171 matrix = glm::rotate(matrix, yaw_, glm::vec3(0, 1, 0));
172 matrix = glm::rotate(matrix, pitch_, glm::vec3(1, 0, 0));
173 matrix = glm::rotate(matrix, roll_, glm::vec3(0, 0, 1));
174 vrm_->world = glm::scale(matrix, glm::vec3(sx3_ * sx_, sy3_ * sy_, sz3_));
175 }
176 animation::AnimPose* pose = nullptr;
177 rootMotionLoopCount_ = 0;
178 if (animLayerMixer_) {
179 const float oldTime = animPlayer_ ? animPlayer_->getTime() : 0.f;
180 animation::AnimClip* oldClip = animPlayer_ ? animPlayer_->getClip() : nullptr;
181 animLayerMixer_->update(dt);
182 pose = animLayerMixer_->getPose();
183 if (animPlayer_ && oldClip && oldClip == animPlayer_->getClip() && animPlayer_->getLoop() &&
184 oldClip->getDuration() > 0.f) {
185 const float duration = oldClip->getDuration();
186 rootMotionLoopCount_ =
187 static_cast<int>(std::floor(animPlayer_->getTime() / duration) - std::floor(oldTime / duration));
188 }
189 } else if (animStateMachine_) {
190 animStateMachine_->update(dt);
191 pose = animStateMachine_->getPose();
192 } else if (animPlayer_) {
193 const float oldTime = animPlayer_->getTime();
194 animation::AnimClip* oldClip = animPlayer_->getClip();
195 animPlayer_->update(dt);
196 pose = animPlayer_->getPose();
197 if (oldClip && oldClip == animPlayer_->getClip() && animPlayer_->getLoop() && oldClip->getDuration() > 0.f) {
198 const float duration = oldClip->getDuration();
199 rootMotionLoopCount_ =
200 static_cast<int>(std::floor(animPlayer_->getTime() / duration) - std::floor(oldTime / duration));
201 }
202 }
203 if (!pose || !animSkeleton_) {
204 attachmentPoseMatrices_.clear();
205 return nullptr;
206 }
207 if (vrm_ && animPlayer_ == vrm_->player.get() && !animPlayer_->getClip()) animSkeleton_->applyBindPose(pose);
208 pose->computeWorld(animSkeleton_);
209 applyLookAt(pose);
210 if (footIKSolver_) footIKSolver_->apply(pose, dt);
211 if (animConstraintStack_) animConstraintStack_->apply(pose);
212 if (dynamicBoneSolver_) dynamicBoneSolver_->update(pose, dt);
213 if (vrm_) vrm_->animate(*this, *pose, dt);
214 updateRootMotion(pose);
215 updateSkin(pose);
216 updateAttachments(pose);
217 attachmentPoseMatrices_.resize(static_cast<std::size_t>(pose->getBoneCount()));
218 for (int bone = 0; bone < pose->getBoneCount(); ++bone)
219 pose->getWorldMatrix(bone, attachmentPoseMatrices_[static_cast<std::size_t>(bone)].data());
220 return pose;
221}
222
224 if (animLayerMixer_) return animLayerMixer_->getEventCount();
225 if (animPlayer_) return animPlayer_->getEventCount();
226 return 0;
227}
228
229std::size_t AvatarInstance::animationEventCount() const noexcept {
230 const int count = getAnimationEventCount();
231 return count > 0 ? static_cast<std::size_t>(count) : 0u;
232}
233
234std::string AvatarInstance::animationEventName(std::size_t index) const {
235 if (index > static_cast<std::size_t>(std::numeric_limits<int>::max())) return {};
236 return getAnimationEventName(static_cast<int>(index));
237}
238
240 if (animLayerMixer_) return animLayerMixer_->getEventLayer(index);
241 return animPlayer_ && index >= 0 && index < animPlayer_->getEventCount() ? "base" : std::string{};
242}
243
245 if (animLayerMixer_) return animLayerMixer_->getEventName(index);
246 return animPlayer_ ? animPlayer_->getEventName(index) : std::string{};
247}
248
250 if (animLayerMixer_) return animLayerMixer_->getEventPayload(index);
251 return animPlayer_ ? animPlayer_->getEventPayload(index) : std::string{};
252}
253
254void AvatarInstance::updateRootMotion(animation::AnimPose* pose) {
255 rootMotionDeltaX_ = 0.f;
256 rootMotionDeltaZ_ = 0.f;
257 if (!pose || pose->getBoneCount() <= 0) return;
258 const float rootX = pose->getWorldPositionX(0);
259 const float rootZ = pose->getWorldPositionZ(0);
260 if (hasPreviousRoot_) {
261 rootMotionDeltaX_ = rootX - previousRootX_;
262 rootMotionDeltaZ_ = rootZ - previousRootZ_;
263 if (rootMotionLoopCount_ > 0 && animPlayer_ && animPlayer_->getClip()) {
264 animation::AnimClip* clip = animPlayer_->getClip();
265 const int keyCount = clip->getPositionKeyCount(0);
266 if (keyCount > 1) {
267 const float cycleX = clip->getPositionKeyX(0, keyCount - 1) - clip->getPositionKeyX(0, 0);
268 const float cycleZ = clip->getPositionKeyZ(0, keyCount - 1) - clip->getPositionKeyZ(0, 0);
269 rootMotionDeltaX_ += cycleX * static_cast<float>(rootMotionLoopCount_);
270 rootMotionDeltaZ_ += cycleZ * static_cast<float>(rootMotionLoopCount_);
271 }
272 }
273 if (applyRootMotion_) {
274 x3_ += rootMotionDeltaX_;
275 z3_ += rootMotionDeltaZ_;
276 }
277 }
278 previousRootX_ = rootX;
279 previousRootZ_ = rootZ;
280 hasPreviousRoot_ = true;
281}
282
283void AvatarInstance::updateSkin(animation::AnimPose* pose) {
284 if (!pose) return;
285 if (animSkin_ && boundMesh_) (void)updateSkinnedMesh(animSkin_, boundMesh_, pose, skinnedPositions_);
286 for (auto& part : skinnedParts_) part.updateMode = updateSkinnedMesh(part.skin, part.mesh, pose, part.cpuPositions);
287 for (auto& visual : equipmentVisuals_) {
288 if (visual.partIndex < 0 || !visual.skinnedSkin || !visual.skinnedMesh) continue;
289 const auto active = projectedEquipment_.find(visual.equipmentSlot);
290 if (active == projectedEquipment_.end() || active->second != visual.itemId) continue;
291 visual.updateMode = updateSkinnedMesh(visual.skinnedSkin, visual.skinnedMesh, pose, visual.cpuPositions);
292 }
293}
294
295SkinnedPartUpdateMode AvatarInstance::updateSkinnedMesh(animation::AnimSkin* skin, graphics::Mesh* mesh,
296 animation::AnimPose* pose, std::vector<float>& cpuPositions) {
297 if (!skin || !mesh || !pose || mesh->getVertexCount() != skin->getVertexCount())
299 for (int bone = 0; bone < skin->getBoneCount(); ++bone) {
300 const int poseBone = skin->getSkeletonBone(bone);
301 if (poseBone < 0 || poseBone >= pose->getBoneCount()) return SkinnedPartUpdateMode::Unavailable;
302 }
303 auto* gfx = ModuleManager::getInstance<graphics::Graphics>("Graphics");
304 if (gfx && !mesh->hasGpuSkinning()) (void)skin->bindGpuMesh(gfx, mesh);
305 if (mesh->hasGpuSkinning() && skin->updateGpuMesh(mesh, pose)) return SkinnedPartUpdateMode::Gpu;
306 if (!gfx || !skin->skinPositionsTo(pose, cpuPositions)) return SkinnedPartUpdateMode::Unavailable;
307 return gfx->updateMeshVertices(mesh, cpuPositions.data(), nullptr, nullptr, skin->getVertexCount(), nullptr, 0)
310}
311
312void AvatarInstance::updateAttachments(animation::AnimPose* pose) {
313 if (!pose || !animSkeleton_) return;
314 const float sinYaw = std::sin(yaw_);
315 const float cosYaw = std::cos(yaw_);
316 for (auto& attachment : attachments_) {
317 if (!attachment.renderable) continue;
318 const int bone = animSkeleton_->findBone(attachment.boneName);
319 if (bone < 0 || bone >= pose->getBoneCount()) continue;
320 const animation::TransformTRS& world = pose->world(bone);
321 const animation::Mat4 boneMatrix = animation::Mat4::fromTRS(world);
322 float bx = 0.f, by = 0.f, bz = 0.f;
323 boneMatrix.transformPoint(attachment.ox, attachment.oy, attachment.oz, bx, by, bz);
324 const float scaledX = bx * sx3_;
325 const float scaledZ = bz * sz3_;
326 attachment.renderable->setPosition(x3_ + scaledX * cosYaw + scaledZ * sinYaw, y3_ + by * sy3_,
327 z3_ - scaledX * sinYaw + scaledZ * cosYaw);
328
329 const float sinPitch = 2.f * (world.qw * world.qx - world.qy * world.qz);
330 const float bonePitch = std::asin(std::clamp(sinPitch, -1.f, 1.f));
331 const float boneYaw = std::atan2(2.f * (world.qw * world.qy + world.qx * world.qz),
332 1.f - 2.f * (world.qx * world.qx + world.qy * world.qy));
333 const float boneRoll = std::atan2(2.f * (world.qw * world.qz + world.qx * world.qy),
334 1.f - 2.f * (world.qx * world.qx + world.qz * world.qz));
335 attachment.renderable->setRotation(yaw_ + boneYaw, pitch_ + bonePitch, roll_ + boneRoll);
336 }
337}
338
339void AvatarInstance::applyLookAt(animation::AnimPose* pose) {
340 if (vrm_ && pose) {
341 vrm_->gaze(*this, *pose, vrm_->world, lookAtEnabled_, glm::vec3(lookAtX_, lookAtY_, lookAtZ_), lookAtWeight_);
342 return;
343 }
344 if (!pose || !animSkeleton_) return;
345 const auto mapped = humanoidBones_.find("head");
346 if (mapped == humanoidBones_.end()) return;
347 const int head = animSkeleton_->findBone(mapped->second);
348 if (head < 0 || head >= pose->getBoneCount()) return;
349
350 animation::TransformTRS& local = pose->local(head);
351 if (lookAtApplied_) {
352 const float similarity = std::fabs(local.qx * lookAtPostQx_ + local.qy * lookAtPostQy_ +
353 local.qz * lookAtPostQz_ + local.qw * lookAtPostQw_);
354 if (similarity > 0.9999f) {
355 local.qx = lookAtBaseQx_;
356 local.qy = lookAtBaseQy_;
357 local.qz = lookAtBaseQz_;
358 local.qw = lookAtBaseQw_;
359 }
360 lookAtApplied_ = false;
361 pose->computeWorld(animSkeleton_);
362 }
363 if (!lookAtEnabled_ || lookAtWeight_ <= 0.f) return;
364
365 const animation::TransformTRS& headWorld = pose->world(head);
366 const float dx = lookAtX_ - (x3_ + headWorld.px * sx3_);
367 const float dy = lookAtY_ - (y3_ + headWorld.py * sy3_);
368 const float dz = lookAtZ_ - (z3_ + headWorld.pz * sz3_);
369 const float cosYaw = std::cos(yaw_);
370 const float sinYaw = std::sin(yaw_);
371 const float localX = dx * cosYaw - dz * sinYaw;
372 const float localZ = dx * sinYaw + dz * cosYaw;
373 const float horizontal = std::sqrt(localX * localX + localZ * localZ);
374 if (horizontal < 1e-6f && std::fabs(dy) < 1e-6f) return;
375
376 constexpr float kMaxYaw = 1.04719755f;
377 constexpr float kMaxPitch = 0.61086524f;
378 const float targetYaw = std::clamp(std::atan2(localX, localZ), -kMaxYaw, kMaxYaw) * lookAtWeight_;
379 const float targetPitch = std::clamp(-std::atan2(dy, horizontal), -kMaxPitch, kMaxPitch) * lookAtWeight_;
380 const float sinHalfYaw = std::sin(targetYaw * 0.5f);
381 const float cosHalfYaw = std::cos(targetYaw * 0.5f);
382 const float sinHalfPitch = std::sin(targetPitch * 0.5f);
383 const float cosHalfPitch = std::cos(targetPitch * 0.5f);
384 const float deltaX = cosHalfYaw * sinHalfPitch;
385 const float deltaY = sinHalfYaw * cosHalfPitch;
386 const float deltaZ = -sinHalfYaw * sinHalfPitch;
387 const float deltaW = cosHalfYaw * cosHalfPitch;
388
389 lookAtBaseQx_ = local.qx;
390 lookAtBaseQy_ = local.qy;
391 lookAtBaseQz_ = local.qz;
392 lookAtBaseQw_ = local.qw;
393 const float qx = local.qw * deltaX + local.qx * deltaW + local.qy * deltaZ - local.qz * deltaY;
394 const float qy = local.qw * deltaY - local.qx * deltaZ + local.qy * deltaW + local.qz * deltaX;
395 const float qz = local.qw * deltaZ + local.qx * deltaY - local.qy * deltaX + local.qz * deltaW;
396 const float qw = local.qw * deltaW - local.qx * deltaX - local.qy * deltaY - local.qz * deltaZ;
397 local.qx = qx;
398 local.qy = qy;
399 local.qz = qz;
400 local.qw = qw;
401 local.normalizeRotation();
402 lookAtPostQx_ = local.qx;
403 lookAtPostQy_ = local.qy;
404 lookAtPostQz_ = local.qz;
405 lookAtPostQw_ = local.qw;
406 lookAtApplied_ = true;
407 pose->computeWorld(animSkeleton_);
408}
409
410bool AvatarInstance::linkSceneNode(scene::Scene* scene, const std::string& nodeId) {
411 if (!scene || nodeId.empty()) return false;
412 if (kind_ == "vroid") {
413 if (!renderable3d_) renderable3d_ = graphics::Renderable3D::create();
414 sceneLinked_ = scene->linkRenderable3D(nodeId, renderable3d_);
415 return sceneLinked_;
416 }
417 if (kind_ == "image") {
418 if (!sceneAnchor2d_) {
419 sceneAnchor2d_ = graphics::Renderable2D::create();
420 sceneAnchor2d_->setSize(0.f, 0.f);
421 sceneAnchor2d_->setColor(1.f, 1.f, 1.f, 0.f);
422 sceneAnchor2d_->setReceiveLight(false);
423 sceneAnchor2d_->setCastOcclusion(false);
424 }
425 sceneLinked_ = scene->linkRenderable2D(nodeId, sceneAnchor2d_);
426 return sceneLinked_;
427 }
428 return false;
429}
430
432 if (equipment_) (void)syncEquipmentAppearance();
433 if (kind_ == "image")
434 syncImageLayers();
435 else if (kind_ == "live2d") {
436 if (!live2d_) live2d_ = Avatar::createLive2DBackend();
437 if (live2d_) {
438 live2d_->setTransform(x_, y_, sx_, sy_);
439 live2d_->setVisible(visible_);
440 live2d_->setLayer(layer_);
441 }
442 } else if (kind_ == "vroid")
443 syncVroid();
444}
445
446void AvatarInstance::bindTween(animation::Tween* tween) { tween_ = tween; }
447
448void AvatarInstance::unbindTween() { tween_ = nullptr; }
449
450void AvatarInstance::applyTweenTracks() {
451 if (!tween_ || !tween_->isActive()) return;
452 if (tween_->has("x")) x_ = tween_->get("x");
453 if (tween_->has("y")) y_ = tween_->get("y");
454 if (tween_->has("sx")) sx_ = tween_->get("sx");
455 if (tween_->has("sy")) sy_ = tween_->get("sy");
456 if (kind_ == "vroid") {
457 if (tween_->has("x3")) x3_ = tween_->get("x3");
458 if (tween_->has("y3")) y3_ = tween_->get("y3");
459 if (tween_->has("z3")) z3_ = tween_->get("z3");
460 if (tween_->has("yaw")) yaw_ = tween_->get("yaw");
461 }
462 const int n = tween_->getPropertyCount();
463 for (int i = 0; i < n; ++i) {
464 const std::string name = tween_->getPropertyName(i);
465 if (name == "x" || name == "y" || name == "sx" || name == "sy" || name == "x3" || name == "y3" ||
466 name == "z3" || name == "yaw")
467 continue;
468 setParameter(name, tween_->get(name));
469 }
470}
471
472} // namespace eve::avatar
bool & active
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
float duration
eve::EntitySpatialPose pose
int bz
Definition CaveMesh.cpp:114
int bx
Definition CaveMesh.cpp:114
int by
Definition CaveMesh.cpp:114
std::string nodeId
std::uint64_t localOffset
glm::vec4 clip
float u
Definition Grass.cpp:233
glm::vec3 n
Definition Grass.cpp:63
std::string local
std::uint32_t bone
std::string name
graphics::Canvas * previous
World3D * world
std::weak_ptr< PrimitiveScene > scene
Mesh * mesh
float dz
float dy
float dx
std::uint32_t count
uint32_t index
double oy
double ox
uint32_t semantic
Definition WfcSimple.cpp:15
float qy
float qx
float qw
float qz
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
Keyframed skeletal animation clip (local TRS tracks per bone). Script type: AnimClip.
Definition AnimClip.h:145
float getDuration() const
Returns the duration.
Definition AnimClip.h:163
int getPositionKeyCount(int boneIndex) const
Returns the position key count.
Definition AnimClip.cpp:365
void update(float dt)
Legacy seconds facade; explicitly forwards to advance().
std::string getEventName(int index) const override
Returns the event name.
AnimPose * getPose() override
Return the most recently combined pose. @ownership Borrowed @lifetime Valid while the owning animatio...
int getEventCount() const override
Returns the event count.
std::string getEventPayload(int index) const override
Returns the event payload.
std::string getEventLayer(int index) const
Source layer name; "base" identifies the base source.
std::string getEventName(int index) const override
Name of a dispatched event, or empty for invalid index.
float getTime() const
Returns the time.
Definition AnimPlayer.h:67
void update(float dt)
Legacy seconds facade retained for scripts and old callers.
std::string getEventPayload(int index) const override
Payload of a dispatched event, or empty for invalid index.
int getEventCount() const override
Number of events crossed by the most recent play/update call.
Definition AnimPlayer.h:133
bool getLoop() const
Returns the loop.
AnimPose * getPose() override
Borrowed pointer accessor. @ownership Borrowed @lifetime Valid while the owning animation object rema...
AnimClip * getClip() const
Borrow the selected clip, or null if none is selected. The caller owns the clip and must keep it aliv...
Definition AnimPlayer.h:86
Evaluated local (and optional world) pose for an AnimSkeleton. Script type: AnimPose.
Definition AnimPose.h:17
int findBone(const std::string &name) const
Finds bone.
void applyBindPose(class AnimPose *pose) const
Fill pose locals with bind pose.
void update(float dt)
Legacy seconds facade retained for scripts and old callers.
AnimPose * getPose() override
Borrowed pointer accessor. @ownership Borrowed @lifetime Valid while the owning animation object rema...
Property tween — interpolates named float properties from → to over time.
Definition Tween.h:25
int getPropertyCount() const
Names of all property tracks (stable order = insertion order).
Definition Tween.h:131
float get(const std::string &name) const
Returns the value.
Definition Tween.cpp:86
bool isActive() const
True when active.
Definition Tween.h:107
std::string getPropertyName(int index) const
Returns the property name.
Definition Tween.cpp:328
bool has(const std::string &name) const
True when active.
Definition Tween.cpp:84
std::string getAnimationEventLayer(int index) const
Source layer for an animation event ("base" for the base player).
std::string getHumanoidBoneName(const std::string &semantic) const
Return the mapped skeleton bone name, or an empty string.
void clearLookAtTarget()
Disable procedural head look-at.
void sync()
Push image / live2d layers into ECS or draw queues.
int autoMapHumanoidBones()
Auto-map common VRM humanoid semantics from the currently bound skeleton.
bool mapViseme(const std::string &viseme, const std::string &morphName)
Map a VRM viseme/expression semantic to an existing mesh morph.
bool detachAttachment(const std::string &name)
Remove an attachment without destroying its Renderable3D.
std::string getAnimationEventPayload(int index) const
Animation event payload, or empty for an invalid index.
void bindTween(animation::Tween *tween)
Drive x/y/sx/sy and matching parameters from a Tween each update().
bool attachToBone(const std::string &name, const std::string &boneSemanticOrName, graphics::Renderable3D *renderable, float ox=0.f, float oy=0.f, float oz=0.f)
Attach a non-owned Renderable3D to a mapped humanoid semantic or bone name.
bool linkSceneNode(scene::Scene *scene, const std::string &nodeId)
Link this avatar to a node in Scene's current host.
EquipmentVisualChange syncEquipmentAppearance()
Rebuild visuals from the current authoritative slots when their signature changed.
eve::Result< eve::AttachmentPoint > sampleAttachmentPoint(std::string_view name, eve::AttachmentPoint localOffset={}) const override
Sample the latest evaluated semantic/bone point in Avatar world space.
void unbindTween()
Unbinds tween.
std::string animationEventName(std::size_t index) const override
Return one latest animation event name through the consumer contract.
void setParameter(const std::string &name, float value)
Sets the parameter.
std::string getAnimationEventName(int index) const
Animation event name, or empty for an invalid index.
bool setLookAtTarget(float x, float y, float z)
Set a world-space target tracked by the mapped humanoid head bone.
bool setViseme(const std::string &viseme, float weight)
Set a mapped viseme weight and clear the previously active viseme.
bool mapHumanoidBone(const std::string &semantic, const std::string &boneName)
Map a VRM humanoid semantic (for example "head") to a skeleton bone.
std::size_t animationEventCount() const noexcept override
Expose latest animation events through the backend-neutral consumer contract.
void setLookAtWeight(float weight)
Set VRM-style head look-at influence in the range 0..1.
bool hasParameter(const std::string &name) const
True when parameter.
int getAnimationEventCount() const
Number of animation events emitted by the latest update.
static ILive2DBackend * createLive2DBackend()
Always returns a backend: custom factory, else NullLive2DBackend.
Definition Avatar.cpp:165
virtual void setVisible(bool)
Optional: receive the Avatar's effective visibility.
virtual void setLayer(int)
Optional: receive the Avatar's render sorting layer.
virtual void setTransform(float, float, float, float)
Optional: receive the Avatar's 2D transform.
bool hasMorph(const std::string &name) const
True when morph.
Definition Mesh.cpp:198
void setCastOcclusion(bool cast)
Enable or disable volumetric occlusion casting.
void setColor(float r, float g, float b, float a=1.f)
Set RGBA tint.
void setSize(float width, float height)
Set unscaled sprite dimensions in pixels.
void setReceiveLight(bool receive)
Enable or disable 2D light reception.
EVENGINE_API_BACKENDS public API.
Declarative scene module (eve.Scene).
Definition Scene.h:51
SkinnedPartUpdateMode
Observable backend selected for a shared-pose skinned part.
Plain world-space point shared by animation consumers without a math-library dependency.
static Mat4 fromTRS(const TransformTRS &t)
From trs.
Definition AnimMath.h:163