载入中...
搜索中...
未找到
AvatarInstance.cpp
浏览该文件的文档.
2#include "animation/Tween.h"
3#include "avatar/Avatar.h"
4#include "common/ECS.h"
5#include "common/Module.h"
7#include "graphics/Graphics.h"
8#include "graphics/Mesh.h"
11#include "graphics/Texture.h"
12#include "image/ImageData.h"
13#include "model3d/ModelData.h"
14
15#include <algorithm>
16#include <cctype>
17#include <cstdlib>
18#include <functional>
19#include <sstream>
20
21namespace eve::avatar {
22namespace {
23
24std::string trimCopy(const std::string &s) {
25 size_t b = 0;
26 while (b < s.size() && std::isspace(static_cast<unsigned char>(s[b]))) ++b;
27 size_t e = s.size();
28 while (e > b && std::isspace(static_cast<unsigned char>(s[e - 1]))) --e;
29 return s.substr(b, e - b);
30}
31
32bool parseBoolish(const std::string &v, bool &out) {
33 std::string t;
34 t.reserve(v.size());
35 for (char c : v) t.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(c))));
36 if (t == "1" || t == "true" || t == "on" || t == "yes") {
37 out = true;
38 return true;
39 }
40 if (t == "0" || t == "false" || t == "off" || t == "no") {
41 out = false;
42 return true;
43 }
44 return false;
45}
46
47} // namespace
48
49AvatarInstance::AvatarInstance(std::string kind) : kind_(std::move(kind)) {
50 if (auto *mod = ModuleManager::getInstance<Avatar>("Avatar"))
51 mod->registerInstance(this);
52}
53
55
57 if (released_) return;
58 released_ = true;
59 tween_ = nullptr;
60 if (auto *mod = ModuleManager::getInstance<Avatar>("Avatar"))
61 mod->unregisterInstance(this);
62 destroyLayers();
63 destroyVroid();
64 delete live2d_;
65 live2d_ = nullptr;
66}
67
68void AvatarInstance::setPosition(float x, float y) {
69 x_ = x;
70 y_ = y;
71}
72
73void AvatarInstance::setScale(float sx, float sy) {
74 sx_ = sx;
75 sy_ = sy;
76}
77
78void AvatarInstance::setVisible(bool visible) { visible_ = visible; }
79
80void AvatarInstance::setLayer(int layer) { layer_ = layer; }
81
82void AvatarInstance::ensureParameter(const std::string &name, float value) {
83 if (name.empty()) return;
84 if (parameters_.find(name) == parameters_.end()) parameterOrder_.push_back(name);
85 parameters_[name] = value;
86}
87
88void AvatarInstance::setExpression(const std::string &name) {
89 expression_ = name;
90 if (kind_ == "image") {
92 } else if (kind_ == "live2d") {
93 if (!live2d_) live2d_ = Avatar::createLive2DBackend();
94 if (live2d_) live2d_->setExpression(name);
95 } else if (kind_ == "vroid") {
96 if (expressionDefs_.count(name)) {
98 } else if (!name.empty()) {
99 // Solo morph: zero known morph params then set named weight to 1.
100 if (boundMesh_ && boundMesh_->hasMorphData()) {
101 for (int i = 0; i < boundMesh_->getMorphCount(); ++i) {
102 const std::string mn = boundMesh_->getMorphName(i);
103 ensureParameter(mn, 0.f);
104 }
105 }
106 ensureParameter(name, 1.f);
107 syncMorphWeightsToMesh();
108 }
109 }
110}
111
112void AvatarInstance::setMotion(const std::string &name) {
113 motion_ = name;
114 if (kind_ == "live2d") {
115 if (!live2d_) live2d_ = Avatar::createLive2DBackend();
116 if (live2d_) live2d_->setMotion(name);
117 }
118 if (kind_ == "vroid") setParameter("motion:" + name, 1.f);
119}
120
121void AvatarInstance::setParameter(const std::string &name, float value) {
122 if (name.empty()) return;
123 ensureParameter(name, value);
124 if (kind_ == "live2d") {
125 if (!live2d_) live2d_ = Avatar::createLive2DBackend();
126 if (live2d_) live2d_->setParameter(name, value);
127 } else if (kind_ == "vroid") {
128 if (boundMesh_ && boundMesh_->hasMorph(name)) boundMesh_->setMorphWeight(name, value);
129 } else if (kind_ == "image") {
130 // Parameter name matching a layer drives that layer's alpha (lip-sync etc.).
131 if (Layer *L = findLayer(name)) {
132 float a = value;
133 if (a < 0.f) a = 0.f;
134 if (a > 1.f) a = 1.f;
135 L->a = a;
136 L->visible = a > 0.001f;
137 }
138 }
139}
140
141float AvatarInstance::getParameter(const std::string &name) const {
142 if (kind_ == "live2d" && live2d_) return live2d_->getParameter(name);
143 auto it = parameters_.find(name);
144 return it == parameters_.end() ? 0.f : it->second;
145}
146
147bool AvatarInstance::hasParameter(const std::string &name) const {
148 if (kind_ == "live2d" && live2d_) {
149 // Backend may not expose has; fall through to local cache.
150 }
151 return parameters_.find(name) != parameters_.end();
152}
153
154int AvatarInstance::getParameterCount() const { return int(parameterOrder_.size()); }
155
156std::string AvatarInstance::getParameterName(int index) const {
157 if (index < 0 || size_t(index) >= parameterOrder_.size()) return {};
158 return parameterOrder_[size_t(index)];
159}
160
162 applyTweenTracks();
163 if (kind_ == "live2d") {
164 if (!live2d_) live2d_ = Avatar::createLive2DBackend();
165 if (live2d_) live2d_->update(dt);
166 }
167 (void)dt;
168}
169
171 if (kind_ == "image")
172 syncImageLayers();
173 else if (kind_ == "vroid")
174 syncVroid();
175}
176
177void AvatarInstance::bindTween(animation::Tween *tween) { tween_ = tween; }
178
179void AvatarInstance::unbindTween() { tween_ = nullptr; }
180
181void AvatarInstance::applyTweenTracks() {
182 if (!tween_ || !tween_->isActive()) return;
183 if (tween_->has("x")) x_ = tween_->get("x");
184 if (tween_->has("y")) y_ = tween_->get("y");
185 if (tween_->has("sx")) sx_ = tween_->get("sx");
186 if (tween_->has("sy")) sy_ = tween_->get("sy");
187 if (kind_ == "vroid") {
188 if (tween_->has("x3")) x3_ = tween_->get("x3");
189 if (tween_->has("y3")) y3_ = tween_->get("y3");
190 if (tween_->has("z3")) z3_ = tween_->get("z3");
191 if (tween_->has("yaw")) yaw_ = tween_->get("yaw");
192 }
193 const int n = tween_->getPropertyCount();
194 for (int i = 0; i < n; ++i) {
195 const std::string name = tween_->getPropertyName(i);
196 if (name == "x" || name == "y" || name == "sx" || name == "sy" || name == "x3" ||
197 name == "y3" || name == "z3" || name == "yaw")
198 continue;
199 setParameter(name, tween_->get(name));
200 }
201}
202
203// ---- image ----
204
205AvatarInstance::Layer *AvatarInstance::findLayer(const std::string &name) {
206 for (auto &L : layers_)
207 if (L.name == name) return &L;
208 return nullptr;
209}
210
211const AvatarInstance::Layer *AvatarInstance::findLayer(const std::string &name) const {
212 for (const auto &L : layers_)
213 if (L.name == name) return &L;
214 return nullptr;
215}
216
217bool AvatarInstance::addLayer(const std::string &name, graphics::Texture *texture, int zIndex) {
218 if (kind_ != "image" || name.empty() || findLayer(name)) return false;
219 Layer L;
220 L.name = name;
221 L.texture = texture;
222 L.zIndex = zIndex;
223 if (texture) {
224 L.w = float(texture->getWidth());
225 L.h = float(texture->getHeight());
226 }
227 layers_.push_back(L);
228 return true;
229}
230
231bool AvatarInstance::setLayerTexture(const std::string &name, graphics::Texture *texture) {
232 Layer *L = findLayer(name);
233 if (!L) return false;
234 L->texture = texture;
235 if (texture && L->w <= 0.f && L->h <= 0.f) {
236 L->w = float(texture->getWidth());
237 L->h = float(texture->getHeight());
238 }
239 return true;
240}
241
242bool AvatarInstance::setLayerVisible(const std::string &name, bool visible) {
243 Layer *L = findLayer(name);
244 if (!L) return false;
245 L->visible = visible;
246 return true;
247}
248
249bool AvatarInstance::setLayerOffset(const std::string &name, float ox, float oy) {
250 Layer *L = findLayer(name);
251 if (!L) return false;
252 L->ox = ox;
253 L->oy = oy;
254 return true;
255}
256
257bool AvatarInstance::setLayerColor(const std::string &name, float r, float g, float b, float a) {
258 Layer *L = findLayer(name);
259 if (!L) return false;
260 L->r = r;
261 L->g = g;
262 L->b = b;
263 L->a = a;
264 return true;
265}
266
267bool AvatarInstance::setLayerZ(const std::string &name, int zIndex) {
268 Layer *L = findLayer(name);
269 if (!L) return false;
270 L->zIndex = zIndex;
271 return true;
272}
273
274bool AvatarInstance::setLayerSize(const std::string &name, float w, float h) {
275 Layer *L = findLayer(name);
276 if (!L) return false;
277 L->w = w;
278 L->h = h;
279 return true;
280}
281
282int AvatarInstance::getLayerCount() const { return int(layers_.size()); }
283
284std::string AvatarInstance::getLayerName(int index) const {
285 if (index < 0 || size_t(index) >= layers_.size()) return {};
286 return layers_[size_t(index)].name;
287}
288
289bool AvatarInstance::hasLayer(const std::string &name) const { return findLayer(name) != nullptr; }
290
291bool AvatarInstance::defineExpression(const std::string &name, const std::string &spec) {
292 if ((kind_ != "image" && kind_ != "vroid") || name.empty()) return false;
293 expressionDefs_[name] = spec;
294 return true;
295}
296
297bool AvatarInstance::applyExpressionSpec(const std::string &spec) {
298 if (spec.empty()) return true;
299 std::stringstream ss(spec);
300 std::string part;
301 while (std::getline(ss, part, ';')) {
302 part = trimCopy(part);
303 if (part.empty()) continue;
304 const auto eq = part.find('=');
305 if (eq == std::string::npos) continue;
306 const std::string key = trimCopy(part.substr(0, eq));
307 const std::string val = trimCopy(part.substr(eq + 1));
308 if (key.empty()) continue;
309
310 if (kind_ == "vroid") {
311 bool flag = false;
312 if (parseBoolish(val, flag)) {
313 setParameter(key, flag ? 1.f : 0.f);
314 } else {
315 char *end = nullptr;
316 const float f = std::strtof(val.c_str(), &end);
317 if (end && end != val.c_str())
318 setParameter(key, f);
319 else
320 setParameter(key, 1.f);
321 }
322 continue;
323 }
324
325 Layer *L = findLayer(key);
326 if (!L) {
327 // Unknown layer name still recorded as parameter for tooling.
328 setParameter(key, 1.f);
329 continue;
330 }
331 bool flag = false;
332 if (parseBoolish(val, flag)) {
333 L->visible = flag;
334 } else {
335 // Non-bool value: show layer and stash as parameter (texture swap by name).
336 L->visible = true;
337 setParameter(key, 1.f);
338 setParameter(key + ".variant", float(std::hash<std::string>{}(val) & 0xffff));
339 }
340 }
341 return true;
342}
343
344bool AvatarInstance::applyExpression(const std::string &name) {
345 auto it = expressionDefs_.find(name);
346 if (it == expressionDefs_.end()) return false;
347 expression_ = name;
348 const bool ok = applyExpressionSpec(it->second);
349 if (kind_ == "vroid") syncMorphWeightsToMesh();
350 return ok;
351}
352
353void AvatarInstance::syncImageLayers() {
354 for (Layer &L : layers_) {
355 if (!L.entity) L.entity = graphics::Renderable2D::create();
356 auto tf = L.entity->transform();
357 auto sp = L.entity->sprite();
358 tf->x = x_ + L.ox * sx_;
359 tf->y = y_ + L.oy * sy_;
360 tf->rot = 0.f;
361 tf->sx = sx_;
362 tf->sy = sy_;
363 float w = L.w;
364 float h = L.h;
365 if ((w <= 0.f || h <= 0.f) && L.texture) {
366 w = float(L.texture->getWidth());
367 h = float(L.texture->getHeight());
368 }
369 if (w <= 0.f) w = 32.f;
370 if (h <= 0.f) h = 32.f;
371 sp->width = w;
372 sp->height = h;
373 sp->r = L.r;
374 sp->g = L.g;
375 sp->b = L.b;
376 sp->a = L.a;
377 sp->layer = layer_ + L.zIndex;
378 sp->visible = visible_ && L.visible;
379 sp->texture = L.texture;
380 sp->quad = nullptr;
381 sp->shader = nullptr;
382 sp->canvas = nullptr;
383 sp->camera = nullptr;
384 }
385}
386
387void AvatarInstance::destroyLayers() {
388 for (Layer &L : layers_) {
389 if (L.entity) {
390 ecs::DestroyEntity(L.entity);
391 L.entity = nullptr;
392 }
393 }
394 layers_.clear();
395}
396
397// ---- live2d ----
398
399bool AvatarInstance::loadLive2DModel(const std::string &path) {
400 if (kind_ != "live2d") return false;
401 if (!live2d_) live2d_ = Avatar::createLive2DBackend();
402 if (!live2d_) return false;
403 return live2d_->loadModel(path);
404}
405
407 if (live2d_) return live2d_->getName();
409}
410
412
413// ---- vroid ----
414
415bool AvatarInstance::loadVroidModelPath(const std::string &path) {
416 if (kind_ != "vroid") return false;
417 vroidPath_ = path;
418 return !path.empty();
419}
420
422 if (kind_ != "vroid") return false;
423 vroidData_ = data;
425 return data != nullptr;
426}
427
429 if (kind_ != "vroid" || !vroidData_) return 0;
430 const int n = vroidData_->getMorphTargetCount(meshIndex);
431 int added = 0;
432 for (int i = 0; i < n; ++i) {
433 const std::string name = vroidData_->getMorphTargetName(meshIndex, i);
434 if (name.empty()) continue;
435 if (!hasParameter(name)) {
436 ensureParameter(name, 0.f);
437 ++added;
438 }
439 }
440 return added;
441}
442
444 if (kind_ != "vroid") return;
445 boundMesh_ = mesh;
446 if (!renderable3d_) renderable3d_ = graphics::Renderable3D::create();
447 renderable3d_->setMesh(mesh);
448 if (mesh && mesh->hasMorphData()) {
449 for (int i = 0; i < mesh->getMorphCount(); ++i) {
450 const std::string name = mesh->getMorphName(i);
451 ensureParameter(name, mesh->getMorphWeight(name));
452 }
453 }
454}
455
457 if (kind_ != "vroid") return;
458 if (!renderable3d_) renderable3d_ = graphics::Renderable3D::create();
459 renderable3d_->setTexture(texture);
460}
461
462void AvatarInstance::setPosition3D(float x, float y, float z) {
463 x3_ = x;
464 y3_ = y;
465 z3_ = z;
466}
467
468void AvatarInstance::setRotation3D(float yaw, float pitch, float roll) {
469 yaw_ = yaw;
470 pitch_ = pitch;
471 roll_ = roll;
472}
473
474void AvatarInstance::setScale3D(float sx, float sy, float sz) {
475 sx3_ = sx;
476 sy3_ = sy;
477 sz3_ = sz;
478}
479
480graphics::Mesh *AvatarInstance::getBoundMesh() const { return boundMesh_; }
481
482void AvatarInstance::syncMorphWeightsToMesh() {
483 if (!boundMesh_ || !boundMesh_->hasMorphData()) return;
484 for (int i = 0; i < boundMesh_->getMorphCount(); ++i) {
485 const std::string name = boundMesh_->getMorphName(i);
486 boundMesh_->setMorphWeight(name, getParameter(name));
487 }
488}
489
491 syncMorphWeightsToMesh();
492 if (!boundMesh_ || !boundMesh_->isMorphDirty()) return false;
493 auto *gfx = ModuleManager::getInstance<graphics::Graphics>("Graphics");
494 if (!gfx) return false;
495 return gfx->bakeMeshMorph(boundMesh_);
496}
497
498void AvatarInstance::syncVroid() {
499 if (!renderable3d_) renderable3d_ = graphics::Renderable3D::create();
500 renderable3d_->setPosition(x3_, y3_, z3_);
501 renderable3d_->setRotation(yaw_, pitch_, roll_);
502 renderable3d_->setScale(sx3_ * sx_, sy3_ * sy_, sz3_);
503 renderable3d_->setVisible(visible_);
504 bakeMorphs();
505}
506
507void AvatarInstance::destroyVroid() {
508 if (renderable3d_) {
509 ecs::DestroyEntity(renderable3d_);
510 renderable3d_ = nullptr;
511 }
512 boundMesh_ = nullptr;
513 vroidData_ = nullptr;
514 vroidPath_.clear();
515}
516
517} // namespace eve::avatar
Tok kind
std::string value
int y
Definition Grass.cpp:135
int z
Definition Grass.cpp:135
int x
Definition Grass.cpp:135
glm::vec3 n
Definition Grass.cpp:64
int h
int w
uint32_t a
uint32_t b
uint32_t c
TileLayer * layer
float f
Mesh * mesh
Light2D::Data * data
const char * name
Definition RockMesh.cpp:21
int v
uint32_t s
Definition Weather.cpp:28
Property tween — interpolates named float properties from → to over time.
Definition Tween.h:21
int getPropertyCount() const
Names of all property tracks (stable order = insertion order).
Definition Tween.h:92
float get(const std::string &name) const
Definition Tween.cpp:84
bool isActive() const
Definition Tween.h:71
std::string getPropertyName(int index) const
Definition Tween.cpp:306
bool has(const std::string &name) const
Definition Tween.cpp:82
void setScale(float sx, float sy)
void setExpression(const std::string &name)
bool setLayerColor(const std::string &name, float r, float g, float b, float a)
bool setLayerOffset(const std::string &name, float ox, float oy)
void sync()
Push image / live2d layers into ECS or draw queues.
float getParameter(const std::string &name) const
void bindTween(animation::Tween *tween)
Drive x/y/sx/sy and matching parameters from a Tween each update().
bool hasLayer(const std::string &name) const
bool applyExpression(const std::string &name)
std::string getParameterName(int index) const
graphics::Mesh * getBoundMesh() const
void setVisible(bool visible)
bool loadVroidModelPath(const std::string &path)
bool bindVroidModelData(model3d::ModelData *data)
void setMesh(graphics::Mesh *mesh)
int loadMorphNamesFromModel(int meshIndex=0)
Register morph target names from ModelData as parameters (weights default 0).
void setParameter(const std::string &name, float value)
std::string getLayerName(int index) const
bool bakeMorphs()
Push parameter weights onto Mesh morphs and bake GPU verts when possible.
void setTexture(graphics::Texture *texture)
void setPosition(float x, float y)
void setMotion(const std::string &name)
AvatarInstance(std::string kind)
void setRotation3D(float yaw, float pitch, float roll)
bool loadLive2DModel(const std::string &path)
bool setLayerZ(const std::string &name, int zIndex)
bool setLayerTexture(const std::string &name, graphics::Texture *texture)
bool addLayer(const std::string &name, graphics::Texture *texture, int zIndex)
std::string getLive2DBackendName() const
bool setLayerSize(const std::string &name, float w, float h)
void setScale3D(float sx, float sy, float sz)
bool setLayerVisible(const std::string &name, bool visible)
bool hasParameter(const std::string &name) const
bool defineExpression(const std::string &name, const std::string &spec)
Spec: "eyes=1;mouth=smile;blush=0" (semicolon-separated name=value).
void setPosition3D(float x, float y, float z)
static Live2DBackendFactory live2DBackendFactory()
Definition Avatar.cpp:69
static std::string getLive2DBackendName()
Backend name currently in effect ("null" when using the built-in stub).
Definition Avatar.cpp:76
static ILive2DBackend * createLive2DBackend()
Always returns a backend: custom factory, else NullLive2DBackend.
Definition Avatar.cpp:71
virtual void setParameter(const std::string &name, float value)=0
virtual void update(float dt)=0
virtual std::string getName() const =0
virtual bool setMotion(const std::string &name)=0
virtual float getParameter(const std::string &name) const =0
virtual bool loadModel(const std::string &path)=0
virtual bool setExpression(const std::string &name)=0
GPU mesh handle (+ optional CPU morph targets).
Definition Mesh.h:18
bool hasMorphData() const
Definition Mesh.h:67
bool isMorphDirty() const
Definition Mesh.h:65
bool setMorphWeight(const std::string &name, float weight)
Definition Mesh.cpp:116
bool hasMorph(const std::string &name) const
Definition Mesh.cpp:112
int getMorphCount() const
Definition Mesh.cpp:105
std::string getMorphName(int index) const
Definition Mesh.cpp:107
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)
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Definition Texture.h:17
int getWidth() const
Definition Texture.h:26
int getHeight() const
Definition Texture.h:27
CPU-side decoded 3D model (Assimp scene owned via medialoader::ModelScene). Does not upload to GPU — ...
Definition ModelData.h:23
std::string getMorphTargetName(int meshIndex, int morphIndex) const
int getMorphTargetCount(int meshIndex) const
Assimp morph / blend-shape targets on a mesh (aiAnimMesh).