载入中...
搜索中...
未找到
VrmRuntime.cpp
浏览该文件的文档.
1#include "avatar/VrmRuntime.h"
2#include <assimp/scene.h>
3#include <glm/gtc/quaternion.hpp>
4#include <glm/gtx/matrix_decompose.hpp>
5#include <stdexcept>
11#include "avatar/VrmSurface.h"
12#include "common/Module.h"
13#include "filesystem/FileData.h"
15#include "graphics/Graphics.h"
16#include "graphics/Material.h"
18#include "model3d/Model3D.h"
19#include "model3d/ModelData.h"
20
21namespace eve::avatar {
22VrmRuntime::VrmRuntime() = default;
24 for (auto& part : parts) {
25 if (auto* entity = ecs::try_get(part.entity)) ecs::DestroyEntity(entity);
26 if (auto* entity = ecs::try_get(part.outline)) ecs::DestroyEntity(entity);
27 }
28 if (auto* gfx = ModuleManager::getInstance<graphics::Graphics>("Graphics"); gfx && !providerLifetime.expired()) {
29 for (auto& part : parts)
30 if (gfx->releaseMesh(part.mesh)) delete part.mesh;
31 surfaces.clear();
32 for (auto* texture : textures)
33 if (gfx->releaseTexture(texture)) delete texture;
34 }
35}
38 try {
39 auto* fs = filesystem::Filesystem::create();
40 std::unique_ptr<filesystem::FileData> file(fs->read(std::string(path)));
41 if (!file) throw std::runtime_error("VRM file could not be read");
42 auto parsed = parseVrm({static_cast<const std::byte*>(file->getData()), file->getSize()});
43 if (!parsed.ok()) return R::failure(parsed.status());
44 auto* gfx = ModuleManager::getInstance<graphics::Graphics>("Graphics");
45 if (!gfx || gfx->getBackendName() != "vulkan")
47 "VRM import requires an initialized graphics provider",
48 std::string(path)));
49 auto candidate = std::make_unique<VrmRuntime>();
50 candidate->document = std::move(parsed).takeValue();
51 candidate->providerLifetime = gfx->resourceLifetime();
52 auto& d = candidate->document;
53 // Uncached owning model: replacing/reloading a cache entry cannot invalidate this import.
54 candidate->model.reset(model3d::Model3D::create()->newModelData(file.get(), ".glb"));
55 candidate->skeleton.reset(animation::AnimImporter::loadSkeletonFromModel(candidate->model.get()));
56 candidate->player = std::make_unique<animation::AnimPlayer>(candidate->skeleton.get());
57 candidate->nodeBones.resize(d.nodes.size(), -1);
58 for (size_t n = 0; n < d.nodes.size(); ++n) {
59 if (!d.nodes[n].empty()) candidate->nodeBones[n] = candidate->skeleton->findBone(d.nodes[n]);
60 }
61 auto requireNode = [&](int node) {
62 if (node >= 0 && candidate->nodeBones[node] < 0)
63 throw std::runtime_error("Cannot resolve VRM node: " + d.nodes[node]);
64 };
65 for (auto& [semantic, node] : d.humanoid) requireNode(node);
66 for (auto& c : d.colliders) requireNode(c.node);
67 for (auto& s : d.springs) {
68 requireNode(s.center);
69 for (auto& j : s.joints) requireNode(j.node);
70 }
71 std::vector<VrmAtlasRect> rectangles;
72 auto* atlas = &buildVrmAtlas(*gfx, d, rectangles);
73 candidate->textures.push_back(atlas);
74 for (auto& m : d.materials) candidate->surfaces.push_back(buildVrmSurface(*gfx, m, *atlas, rectangles));
75 const auto* scene = candidate->model->getScene();
76 for (size_t n = 0; n < d.nodes.size(); ++n) {
77 if (d.nodeMeshes[n] < 0) continue;
78 const auto* node = scene->mRootNode->FindNode(d.nodes[n].c_str());
79 if (!node) throw std::runtime_error("Cannot resolve VRM mesh node: " + d.nodes[n]);
80 const auto& slots = d.meshMaterials[d.nodeMeshes[n]];
81 if (node->mNumMeshes != slots.size())
82 throw std::runtime_error("VRM primitive mapping changed during decode");
83 for (unsigned p = 0; p < node->mNumMeshes; ++p) {
84 Part part;
85 part.sourceMesh = int(node->mMeshes[p]);
86 part.node = int(n);
87 part.material = slots[p];
88 if (part.material < 0) throw std::runtime_error("VRM primitive is missing its material");
89 candidate->parts.push_back(std::move(part));
90 auto& owned = candidate->parts.back();
91 owned.mesh = gfx->newMeshFromAssimp(*scene->mMeshes[owned.sourceMesh]);
92 if (!owned.mesh) throw std::runtime_error("Mesh upload failed");
93 if (candidate->model->hasBones(owned.sourceMesh)) {
94 owned.skin.reset(animation::AnimSkin::fromModel(candidate->model.get(), owned.sourceMesh,
95 candidate->skeleton.get()));
96 if (!owned.skin->bindGpuMesh(gfx, owned.mesh))
97 throw std::runtime_error("VRM GPU skin upload failed");
98 }
99 auto* entity = graphics::Renderable3D::create();
100 owned.entity = ecs::handle_of(entity);
101 entity->setVisible(false);
102 entity->setMesh(owned.mesh);
103 entity->setMaterial(candidate->surfaces[owned.material]->material.get());
104 if (auto* material = candidate->surfaces[owned.material]->outlineMaterial.get()) {
105 auto* outline = graphics::Renderable3D::create();
106 owned.outline = ecs::handle_of(outline);
107 outline->setVisible(false);
108 outline->setMesh(owned.mesh);
109 outline->setMaterial(material);
110 }
111 }
112 }
113 if (candidate->parts.empty()) throw std::runtime_error("VRM contains no renderable primitives");
114 candidate->particles_.resize(d.springs.size());
115 for (size_t i = 0; i < d.springs.size(); ++i) candidate->particles_[i].resize(d.springs[i].joints.size());
116 return R::success(std::move(candidate));
117 } catch (const std::exception& e) {
118 return R::failure(
119 eve::Diagnostic::error(eve::DiagnosticCode::Failed, e.what(), std::string(path), {}, "avatar.vrm.import"));
120 }
121}
122void VrmRuntime::sync(AvatarInstance&, const glm::mat4& matrix, bool visible) {
123 visible = visible && !providerLifetime.expired();
124 world = matrix;
125 for (auto& part : parts) {
126 glm::mat4 partWorld = matrix;
127 if (!part.skin && nodeBones[part.node] >= 0) {
128 glm::mat4 nodeMatrix(1);
129 player->getPose()->getWorldMatrix(nodeBones[part.node], &nodeMatrix[0][0]);
130 partWorld *= nodeMatrix;
131 }
132 glm::vec3 scale, translation, skew;
133 glm::quat rotation;
134 glm::vec4 perspective;
135 glm::decompose(partWorld, scale, rotation, translation, skew, perspective);
136 const glm::vec3 angles = glm::eulerAngles(rotation);
137 for (auto handle : {part.entity, part.outline})
138 if (auto* entity = dynamic_cast<graphics::Renderable3D*>(ecs::try_get(handle))) {
139 entity->setPosition(translation.x, translation.y, translation.z);
140 entity->setRotation(angles.y, angles.x, angles.z);
141 entity->setScale(scale.x, scale.y, scale.z);
142 entity->setVisible(visible);
143 }
144 }
145}
147 if (providerLifetime.expired()) return;
148 time += dt;
149 springs(pose, dt);
150 for (auto& part : parts)
151 if (part.skin && !part.skin->updateGpuMesh(part.mesh, &pose))
152 throw std::runtime_error("VRM skin palette no longer matches imported skeleton");
153}
155 if (providerLifetime.expired()) return;
156 std::vector<float> weights;
157 float blink = 0, mouth = 0, look = 0;
158 auto overrideValue = [](const std::string& mode, float weight) {
159 return mode == "block" && weight > 0 ? 1.f : mode == "blend" ? weight : 0.f;
160 };
161 for (const auto& e : document.expressions) {
162 float w = std::clamp(avatar.getParameter(e.name) + (gazeWeights_.contains(e.name) ? gazeWeights_[e.name] : 0.f),
163 0.f, 1.f);
164 if (e.binary) w = w > .5f ? 1.f : 0.f;
165 weights.push_back(w);
166 blink += overrideValue(e.overrideBlink, w);
167 mouth += overrideValue(e.overrideMouth, w);
168 look += overrideValue(e.overrideLookAt, w);
169 }
170 for (auto& s : surfaces) {
171 s->current = s->base;
172 s->uvScale = {1, 1};
173 s->uvOffset = {0, 0};
174 }
175 for (auto& part : parts) {
176 part.mesh->clearMorphWeights();
177 for (int i = 0; i < part.mesh->getMorphCount(); ++i) {
178 auto name = part.mesh->getMorphName(i);
179 if (avatar.hasParameter(name)) part.mesh->setMorphWeight(name, avatar.getParameter(name));
180 }
181 }
182 for (size_t i = 0; i < document.expressions.size(); ++i) {
183 const auto& e = document.expressions[i];
184 float suppression = 0;
185 if (e.name == "blink" || e.name == "blinkLeft" || e.name == "blinkRight") suppression = blink;
186 if (e.name == "aa" || e.name == "ih" || e.name == "ou" || e.name == "ee" || e.name == "oh") suppression = mouth;
187 if (e.name == "lookUp" || e.name == "lookDown" || e.name == "lookLeft" || e.name == "lookRight")
188 suppression = look;
189 float w = weights[i] * (1 - std::clamp(suppression, 0.f, 1.f));
190 if (e.binary && suppression > 0) w = 0;
191 for (const auto& bind : e.morphs)
192 for (auto& part : parts)
193 if (part.node == bind.node) {
194 auto name = part.mesh->getMorphName(bind.index);
195 part.mesh->setMorphWeight(name, part.mesh->getMorphWeight(name) + bind.weight * w);
196 }
197 for (const auto& b : e.colors) {
198 auto& s = *surfaces[b.material];
199 auto add = [&](auto& out, const auto& base) {
200 for (size_t c = 0; c < out.size(); ++c) out[c] += (b.target[c] - base[c]) * w;
201 };
202 if (b.type == "color")
203 add(s.current.color, s.base.color);
204 else if (b.type == "emissionColor")
205 add(s.current.emission, s.base.emission);
206 else if (b.type == "shadeColor")
207 add(s.current.shade, s.base.shade);
208 else if (b.type == "matcapColor")
209 add(s.current.matcap, s.base.matcap);
210 else if (b.type == "rimColor")
211 add(s.current.rim, s.base.rim);
212 else if (b.type == "outlineColor")
213 add(s.current.outline, s.base.outline);
214 }
215 for (const auto& b : e.uvs)
216 for (int c = 0; c < 2; ++c) {
217 surfaces[b.material]->uvScale[c] += (b.scale[c] - 1) * w;
218 surfaces[b.material]->uvOffset[c] += b.offset[c] * w;
219 }
220 }
221 auto* gfx = ModuleManager::getInstance<graphics::Graphics>("Graphics");
222 if (gfx)
223 for (auto& part : parts)
224 if (part.mesh->isMorphDirty()) gfx->bakeMeshMorph(part.mesh);
225 for (auto& surface : surfaces) surface->update(time);
226}
227} // namespace eve::avatar
float w
Definition AnimClip.cpp:738
ActiveSource owned
eve::EntitySpatialPose pose
const std::string & s
glm::vec4 p[6]
filesystem::File * file
glm::vec3 n
Definition Grass.cpp:63
std::int32_t c
std::array< float, 4 > rotation
std::array< float, 3 > scale
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
std::string path
Definition PlayHost.cpp:110
std::weak_ptr< PrimitiveScene > scene
PrimitiveHandle handle
float d
Material * material
const RoadNode * node
LocalPageCacheEntry slots[ShadowConfig::kLocalSlots]
float weights[3]
bool visible
float m[16]
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
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static AnimSkeleton * loadSkeletonFromModel(const model3d::ModelData *model)
ModelData wrappers (AnimImporterModel.cpp; requires model3d).
Evaluated local (and optional world) pose for an AnimSkeleton. Script type: AnimPose.
Definition AnimPose.h:17
static AnimSkin * fromModel(const model3d::ModelData *model, int meshIndex, const AnimSkeleton *skeleton)
Build skin binding for meshIndex on model, mapping bone names onto skeleton. Throws if the mesh has n...
Unified avatar instance. Kind is a string: "image" | "live2d" | "vroid". Script-facing API avoids ove...
float getParameter(const std::string &name) const
Returns the parameter.
bool hasParameter(const std::string &name) const
True when parameter.
static eve::Result< std::unique_ptr< VrmRuntime > > prepare(std::string_view path)
Prepare.
std::vector< int > nodeBones
Definition VrmRuntime.h:52
void animate(AvatarInstance &avatar, animation::AnimPose &pose, float dt)
Animate.
std::vector< std::unique_ptr< VrmSurface > > surfaces
Definition VrmRuntime.h:63
void morphs(AvatarInstance &avatar)
Morphs.
std::vector< graphics::Texture * > textures
Definition VrmRuntime.h:64
std::vector< Part > parts
Definition VrmRuntime.h:62
void sync(AvatarInstance &avatar, const glm::mat4 &world, bool visible)
Synchronizes .
std::weak_ptr< const void > providerLifetime
Definition VrmRuntime.h:47
std::unique_ptr< animation::AnimPlayer > player
Definition VrmRuntime.h:51
VrmRuntime()
Constructs a VrmRuntime.
~VrmRuntime()
Releases VrmRuntime resources.
EVENGINE_API_BACKENDS public API.
eve::Result< VrmDocument > parseVrm(std::span< const std::byte > bytes)
Validate a self-contained GLB's VRM extension before publishing runtime state.
graphics::Texture & buildVrmAtlas(graphics::Graphics &gfx, const VrmDocument &d, std::vector< VrmAtlasRect > &rects)
Prepare an atlas using the provider's owning resource factory. @ownership Graphics owns the result; c...
std::unique_ptr< VrmSurface > buildVrmSurface(graphics::Graphics &gfx, const VrmMaterial &data, graphics::Texture &atlas, const std::vector< VrmAtlasRect > &rects)
Builds vrm surface.
std::vector< VrmExpression > expressions
Definition VrmDocument.h:94