载入中...
搜索中...
未找到
AvatarVroid.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 {
36// ---- vroid ----
37
39 if (kind_ != "vroid" || released_ || path.empty())
41 eve::DiagnosticCode::InvalidArgument, "Expected a live VRoid Avatar and a nonempty path", "path"));
42 auto prepared = VrmRuntime::prepare(path);
43 if (!prepared.ok()) return eve::Result<void>::failure(prepared.status());
44 auto candidate = std::move(prepared).takeValue();
45 std::unordered_map<std::string, std::string> humanoid;
46 for (const auto &[name, node] : candidate->document.humanoid) humanoid[name] = candidate->document.nodes[node];
47 decltype(parameters_) parameters;
48 decltype(parameterOrder_) order;
49 decltype(parameterMetadata_) metadata;
50 auto add = [&](const std::string &name) {
51 if (name.empty() || parameters.contains(name)) return;
52 parameters.emplace(name, 0.f);
53 order.push_back(name);
54 metadata.emplace(name, ParameterMetadata{});
55 };
56 for (const auto &expression : candidate->document.expressions) add(expression.name);
57 for (int mesh = 0; mesh < candidate->model->getMeshCount(); ++mesh)
58 for (int morph = 0; morph < candidate->model->getMorphTargetCount(mesh); ++morph)
59 add(candidate->model->getMorphTargetName(mesh, morph));
60 std::string importedPath(path);
61 animPlayer_ = nullptr;
62 animSkeleton_ = nullptr;
63 animSkin_ = nullptr;
64 animStateMachine_ = nullptr;
65 animLayerMixer_ = nullptr;
66 animConstraintStack_.reset();
67 footIKSolver_.reset();
68 dynamicBoneSolver_.reset();
69 skinnedParts_.clear();
70 motions_.clear();
71 attachmentPoseMatrices_.clear();
72 humanoidBones_ = std::move(humanoid);
73 visemeMorphs_.clear();
74 activeViseme_.clear();
75 parameters_.swap(parameters);
76 parameterOrder_.swap(order);
77 parameterMetadata_.swap(metadata);
78 expressionDefs_.clear();
79 expressionBlendFrom_.clear();
80 expressionBlendTo_.clear();
81 expression_.clear();
82 motion_.clear();
83 lookAtApplied_ = false;
84 hasPreviousRoot_ = false;
85 vrm_ = std::move(candidate);
86 vroidPath_ = std::move(importedPath);
87 vroidData_ = vrm_->model.get();
88 animPlayer_ = vrm_->player.get();
89 animSkeleton_ = vrm_->skeleton.get();
91}
93 float roll) {
94 if (!vrm_ || !std::isfinite(yaw) || !std::isfinite(pitch) || !std::isfinite(roll))
96 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "Expected imported VRM and finite angles"));
97 auto found = vrm_->document.humanoid.find(std::string(semantic));
98 if (found == vrm_->document.humanoid.end())
100 eve::DiagnosticCode::NotFound, "Humanoid semantic is not mapped", std::string(semantic)));
101 vrm_->rotations[vrm_->nodeBones[found->second]] = {yaw, pitch, roll};
103}
104bool AvatarInstance::loadVroidModelPath(const std::string &path) { return loadVroidModel(path).ok(); }
105int AvatarInstance::getVroidMeshCount() const { return vrm_ ? static_cast<int>(vrm_->parts.size()) : 0; }
106int AvatarInstance::getVroidSpringCount() const { return vrm_ ? static_cast<int>(vrm_->document.springs.size()) : 0; }
107std::string AvatarInstance::getVroidVersion() const { return vrm_ ? vrm_->document.version : std::string{}; }
109 if (vrm_ && !vrm_->parts.empty())
110 return dynamic_cast<graphics::Renderable3D *>(ecs::try_get(vrm_->parts.front().entity));
111 return renderable3d_;
112}
113
115 if (kind_ != "vroid" || vrm_) return false;
116 vroidData_ = data;
117 if (data) loadMorphNamesFromModel(0);
118 return data != nullptr;
119}
120
122 if (kind_ != "vroid" || !vroidData_) return 0;
123 const int n = vroidData_->getMorphTargetCount(meshIndex);
124 int added = 0;
125 for (int i = 0; i < n; ++i) {
126 const std::string name = vroidData_->getMorphTargetName(meshIndex, i);
127 if (name.empty()) continue;
128 if (!hasParameter(name)) {
129 ensureParameter(name, 0.f);
130 ++added;
131 }
132 }
133 return added;
134}
135
137 if (kind_ != "vroid" || vrm_) return;
138 boundMesh_ = mesh;
139 if (!renderable3d_) renderable3d_ = graphics::Renderable3D::create();
140 renderable3d_->setMesh(mesh);
141 if (mesh && mesh->hasMorphData()) {
142 for (int i = 0; i < mesh->getMorphCount(); ++i) {
143 const std::string name = mesh->getMorphName(i);
144 ensureParameter(name, mesh->getMorphWeight(name));
145 }
146 }
147}
148
150 if (kind_ != "vroid" || vrm_) return;
151 if (!renderable3d_) renderable3d_ = graphics::Renderable3D::create();
152 renderable3d_->setTexture(texture);
153}
154
155void AvatarInstance::setPosition3D(float x, float y, float z) {
156 x3_ = x;
157 y3_ = y;
158 z3_ = z;
159}
160
161void AvatarInstance::setRotation3D(float yaw, float pitch, float roll) {
162 yaw_ = yaw;
163 pitch_ = pitch;
164 roll_ = roll;
165}
166
167void AvatarInstance::setScale3D(float sx, float sy, float sz) {
168 sx3_ = sx;
169 sy3_ = sy;
170 sz3_ = sz;
171}
172
174 return vrm_ ? (!vrm_->providerLifetime.expired() && !vrm_->parts.empty() ? vrm_->parts.front().mesh : nullptr)
175 : boundMesh_;
176}
177
178void AvatarInstance::syncMorphWeightsToMesh() {
179 if (!boundMesh_ || !boundMesh_->hasMorphData()) return;
180 for (int i = 0; i < boundMesh_->getMorphCount(); ++i) {
181 const std::string name = boundMesh_->getMorphName(i);
182 boundMesh_->setMorphWeight(name, getParameter(name));
183 }
184}
185
187 if (vrm_) {
188 vrm_->morphs(*this);
189 return true;
190 }
191 syncMorphWeightsToMesh();
192 if (!boundMesh_ || !boundMesh_->isMorphDirty()) return false;
193 auto *gfx = ModuleManager::getInstance<graphics::Graphics>("Graphics");
194 if (!gfx) return false;
195 return gfx->bakeMeshMorph(boundMesh_);
196}
197
198void AvatarInstance::syncVroid() {
199 if (vrm_) {
200 glm::mat4 world = glm::translate(glm::mat4(1), glm::vec3(x3_, y3_, z3_));
201 world = glm::rotate(world, yaw_, glm::vec3(0, 1, 0));
202 world = glm::rotate(world, pitch_, glm::vec3(1, 0, 0));
203 world = glm::rotate(world, roll_, glm::vec3(0, 0, 1));
204 world = glm::scale(world, glm::vec3(sx3_ * sx_, sy3_ * sy_, sz3_));
205 vrm_->sync(*this, world, visible_);
206 vrm_->morphs(*this);
207 return;
208 }
209 if (!renderable3d_) renderable3d_ = graphics::Renderable3D::create();
210 if (!sceneLinked_) {
211 renderable3d_->setPosition(x3_, y3_, z3_);
212 renderable3d_->setRotation(yaw_, pitch_, roll_);
213 renderable3d_->setScale(sx3_ * sx_, sy3_ * sy_, sz3_);
214 renderable3d_->setVisible(visible_);
215 }
216 for (auto &visual : equipmentVisuals_) {
217 const auto active = projectedEquipment_.find(visual.equipmentSlot);
218 if (!visual.renderable || !visual.boneName.empty() || active == projectedEquipment_.end() ||
219 active->second != visual.itemId)
220 continue;
221 visual.renderable->setPosition(x3_ + visual.ox, y3_ + visual.oy, z3_ + visual.oz);
222 visual.renderable->setRotation(yaw_, pitch_, roll_);
223 visual.renderable->setScale(sx3_ * sx_, sy3_ * sy_, sz3_);
224 visual.renderable->setVisible(visible_);
225 }
226 bakeMorphs();
227}
228
229void AvatarInstance::destroyVroid() {
230 vrm_.reset();
231 if (renderable3d_) {
232 ecs::DestroyEntity(renderable3d_);
233 renderable3d_ = nullptr;
234 }
235 boundMesh_ = nullptr;
236 vroidData_ = nullptr;
237 vroidPath_.clear();
238}
239
240} // namespace eve::avatar
bool & active
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
glm::vec3 n
Definition Grass.cpp:63
std::string name
std::vector< std::int32_t > order
World3D * world
std::string path
Definition PlayHost.cpp:110
Mesh * mesh
const RoadNode * node
bool found
uint32_t semantic
Definition WfcSimple.cpp:15
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
bool ok() const noexcept
Whether this result represents a non-failure outcome.
Definition Result.h:480
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
eve::Result< void > setHumanoidBoneRotation(std::string_view semantic, float yaw, float pitch, float roll)
Set an imported humanoid bone's local rotation offset in radians.
int getVroidSpringCount() const
Number of imported VRM spring chains.
float getParameter(const std::string &name) const
Returns the parameter.
graphics::Mesh * getBoundMesh() const
Borrow the first imported mesh, or the manually bound mesh.
bool loadVroidModelPath(const std::string &path)
Compatibility-only bool projection of loadVroidModel; performs the real import.
std::string getVroidVersion() const
Imported VRM specification version, or empty before import.
bool bindVroidModelData(model3d::ModelData *data)
Binds vroid model data.
void setMesh(graphics::Mesh *mesh)
Sets the mesh.
int loadMorphNamesFromModel(int meshIndex=0)
Register morph target names from ModelData as parameters (weights default 0).
bool bakeMorphs()
Push parameter weights onto Mesh morphs and bake GPU verts when possible.
void setTexture(graphics::Texture *texture)
Sets the texture.
void setRotation3D(float yaw, float pitch, float roll)
Sets the rotation 3 d.
void setScale3D(float sx, float sy, float sz)
Sets the scale 3 d.
int getVroidMeshCount() const
Number of imported mesh primitives; zero before successful import.
bool hasParameter(const std::string &name) const
True when parameter.
eve::Result< void > loadVroidModel(std::string_view path)
Import and atomically replace a complete VRM avatar from a VFS path.
graphics::Renderable3D * getRenderable3D() const
Borrow the first imported render projection, or the manual projection.
void setPosition3D(float x, float y, float z)
Sets the position 3 d.
static eve::Result< std::unique_ptr< VrmRuntime > > prepare(std::string_view path)
Prepare.
GPU mesh handle (+ optional CPU morph targets).
Definition Mesh.h:25
bool hasMorphData() const
True when morph data.
Definition Mesh.h:110
bool isMorphDirty() const
True when morph dirty.
Definition Mesh.h:106
bool setMorphWeight(const std::string &name, float weight)
Sets the morph weight.
Definition Mesh.cpp:202
int getMorphCount() const
Returns the morph count.
Definition Mesh.cpp:191
std::string getMorphName(int index) const
Returns the morph name.
Definition Mesh.cpp:193
EVENGINE_API_BACKENDS public API.
void setRotation(float yaw, float pitch, float roll)
void setTexture(Texture *texture)
void setPosition(float x, float y, float z)
void setScale(float sx, float sy, float sz)
void setVisible(bool visible)
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Definition Texture.h:18
CPU-side decoded 3D model (Assimp scene owned via medialoader::ModelScene). Does not upload to GPU — ...
Definition ModelData.h:39
std::string getMorphTargetName(int meshIndex, int morphIndex) const
Returns the morph target name.
int getMorphTargetCount(int meshIndex) const
Assimp morph / blend-shape targets on a mesh (aiAnimMesh).