载入中...
搜索中...
未找到
ModelData.cpp
浏览该文件的文档.
1#include "model3d/ModelData.h"
2
3#include "common/Exception.h"
4#include "data/ByteData.h"
5#include "image/Image.h"
6
7#include <assimp/anim.h>
8#include <assimp/material.h>
9#include <assimp/matrix4x4.h>
10#include <assimp/mesh.h>
11#include <assimp/scene.h>
12#include <assimp/texture.h>
13
14#include <cmath>
15#include <cstdlib>
16#include <string>
17#include <utility>
18#include <vector>
19
20namespace eve {
21namespace model3d {
22
23namespace {
24
25aiTextureType textureTypeFromName(const std::string &type) {
26 if (type == "base_color") return aiTextureType_BASE_COLOR;
27 if (type == "diffuse") return aiTextureType_DIFFUSE;
28 if (type == "normals") return aiTextureType_NORMALS;
29 if (type == "height") return aiTextureType_HEIGHT;
30 if (type == "emissive") return aiTextureType_EMISSIVE;
31 if (type == "metalness") return aiTextureType_METALNESS;
32 if (type == "roughness") return aiTextureType_DIFFUSE_ROUGHNESS;
33 // Assimp maps glTF occlusionTexture to LIGHTMAP.
34 if (type == "ambient_occlusion" || type == "lightmap") return aiTextureType_LIGHTMAP;
35 if (type == "opacity") return aiTextureType_OPACITY;
36 if (type == "specular") return aiTextureType_SPECULAR;
37 if (type == "shininess") return aiTextureType_SHININESS;
38 return aiTextureType_NONE;
39}
40
41aiColor4D materialBaseColor(const aiMaterial *mat) {
42 aiColor4D c(1.f, 1.f, 1.f, 1.f);
43 if (!mat) return c;
44 if (mat->Get(AI_MATKEY_BASE_COLOR, c) != AI_SUCCESS) {
45 aiColor3D diffuse(1.f, 1.f, 1.f);
46 if (mat->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse) == AI_SUCCESS)
47 c = aiColor4D(diffuse.r, diffuse.g, diffuse.b, 1.f);
48 }
49 return c;
50}
51
52} // namespace
53
54ModelData::ModelData(medialoader::ModelScene scene, std::string uri, bool uvFlipped)
55 : Resource(std::move(uri)), scene(std::move(scene)), uvFlipped_(uvFlipped) {}
56
57ModelData::~ModelData() = default;
58
59void ModelData::adopt(eve::Resource &replacement) {
60 auto &other = static_cast<ModelData &>(replacement);
61 std::swap(scene, other.scene);
62 std::swap(uvFlipped_, other.uvFlipped_);
63}
64
65bool ModelData::empty() const { return scene.empty(); }
66
67const aiScene *ModelData::getScene() const {
68 if (scene.empty())
69 return nullptr;
70 return scene.get();
71}
72
73const aiMesh *ModelData::meshAt(int meshIndex) const {
74 const aiScene *sc = getScene();
75 if (!sc || meshIndex < 0 || static_cast<unsigned>(meshIndex) >= sc->mNumMeshes)
76 return nullptr;
77 return sc->mMeshes[meshIndex];
78}
79
80const aiMaterial *ModelData::materialAt(int matIndex) const {
81 const aiScene *sc = getScene();
82 if (!sc || matIndex < 0 || static_cast<unsigned>(matIndex) >= sc->mNumMaterials)
83 return nullptr;
84 return sc->mMaterials[matIndex];
85}
86
87const aiMesh *ModelData::getMesh(int meshIndex) const { return meshAt(meshIndex); }
88
89aiMesh *ModelData::meshAtMutable(int meshIndex) { return const_cast<aiMesh *>(meshAt(meshIndex)); }
90
92 const aiScene *sc = getScene();
93 return sc ? static_cast<int>(sc->mNumMeshes) : 0;
94}
95
97 const aiScene *sc = getScene();
98 return sc ? static_cast<int>(sc->mNumMaterials) : 0;
99}
100
101int ModelData::getVertexCount(int meshIndex) const {
102 const aiMesh *m = meshAt(meshIndex);
103 if (!m)
104 throw eve::Exception("ModelData::getVertexCount: invalid mesh index");
105 return static_cast<int>(m->mNumVertices);
106}
107
108int ModelData::getFaceCount(int meshIndex) const {
109 const aiMesh *m = meshAt(meshIndex);
110 if (!m)
111 throw eve::Exception("ModelData::getFaceCount: invalid mesh index");
112 return static_cast<int>(m->mNumFaces);
113}
114
115bool ModelData::hasNormals(int meshIndex) const {
116 const aiMesh *m = meshAt(meshIndex);
117 if (!m)
118 throw eve::Exception("ModelData::hasNormals: invalid mesh index");
119 return m->HasNormals();
120}
121
122bool ModelData::hasTexCoords(int meshIndex) const {
123 const aiMesh *m = meshAt(meshIndex);
124 if (!m)
125 throw eve::Exception("ModelData::hasTexCoords: invalid mesh index");
126 return m->HasTextureCoords(0);
127}
128
129int ModelData::getTexCoordChannelCount(int meshIndex) const {
130 const aiMesh *m = meshAt(meshIndex);
131 if (!m) throw eve::Exception("ModelData::getTexCoordChannelCount: invalid mesh index");
132 return static_cast<int>(m->GetNumUVChannels());
133}
134
135bool ModelData::hasTexCoordChannel(int meshIndex, int channel) const {
136 const aiMesh *m = meshAt(meshIndex);
137 if (!m) throw eve::Exception("ModelData::hasTexCoordChannel: invalid mesh index");
138 return channel >= 0 && channel < AI_MAX_NUMBER_OF_TEXTURECOORDS &&
139 m->HasTextureCoords(static_cast<unsigned>(channel));
140}
141
142float ModelData::getTexCoord(int meshIndex, int channel, int vertexIndex, int component) const {
143 const aiMesh *m = meshAt(meshIndex);
144 if (!m || channel < 0 || channel >= AI_MAX_NUMBER_OF_TEXTURECOORDS || vertexIndex < 0 ||
145 static_cast<unsigned>(vertexIndex) >= m->mNumVertices || component < 0 || component > 2 ||
146 !m->HasTextureCoords(static_cast<unsigned>(channel)))
147 throw eve::Exception("ModelData::getTexCoord: invalid mesh/channel/vertex/component");
148 const aiVector3D &v = m->mTextureCoords[channel][vertexIndex];
149 return component == 0 ? v.x : (component == 1 ? v.y : v.z);
150}
151
152float ModelData::getVertexPosition(int meshIndex, int vertexIndex, int component) const {
153 const aiMesh *mesh = meshAt(meshIndex);
154 if (!mesh || vertexIndex < 0 || static_cast<unsigned>(vertexIndex) >= mesh->mNumVertices ||
155 component < 0 || component > 2)
156 throw eve::Exception("ModelData::getVertexPosition: invalid mesh/vertex/component");
157 const aiVector3D &value = mesh->mVertices[vertexIndex];
158 return component == 0 ? value.x : (component == 1 ? value.y : value.z);
159}
160
161float ModelData::getVertexNormal(int meshIndex, int vertexIndex, int component) const {
162 const aiMesh *mesh = meshAt(meshIndex);
163 if (!mesh || !mesh->HasNormals() || vertexIndex < 0 ||
164 static_cast<unsigned>(vertexIndex) >= mesh->mNumVertices || component < 0 || component > 2)
165 throw eve::Exception("ModelData::getVertexNormal: invalid mesh/vertex/component");
166 const aiVector3D &value = mesh->mNormals[vertexIndex];
167 return component == 0 ? value.x : (component == 1 ? value.y : value.z);
168}
169
170int ModelData::getFaceVertexIndex(int meshIndex, int triangleIndex, int corner) const {
171 const aiMesh *mesh = meshAt(meshIndex);
172 if (!mesh || triangleIndex < 0 || static_cast<unsigned>(triangleIndex) >= mesh->mNumFaces ||
173 corner < 0 || corner > 2 || mesh->mFaces[triangleIndex].mNumIndices != 3)
174 throw eve::Exception("ModelData::getFaceVertexIndex: invalid mesh/triangle/corner");
175 return static_cast<int>(mesh->mFaces[triangleIndex].mIndices[corner]);
176}
177
179 float localX, float localY, float localZ,
180 int channel) const {
181 const aiMesh *mesh = meshAt(meshIndex);
182 if (!mesh || triangleIndex < 0 || static_cast<unsigned>(triangleIndex) >= mesh->mNumFaces)
184 eve::DiagnosticCode::InvalidArgument, "invalid mesh or triangle index",
185 "model3d.surfaceUv"));
186 if (channel < 0 || channel >= AI_MAX_NUMBER_OF_TEXTURECOORDS ||
187 !mesh->HasTextureCoords(static_cast<unsigned>(channel)))
189 eve::DiagnosticCode::NotFound, "requested UV channel is not present",
190 "model3d.surfaceUv.channel"));
191 const aiFace &face = mesh->mFaces[triangleIndex];
192 if (face.mNumIndices != 3)
194 eve::DiagnosticCode::Unsupported, "surface UV mapping requires triangulated faces",
195 "model3d.surfaceUv.triangle"));
196
197 const aiVector3D &a = mesh->mVertices[face.mIndices[0]];
198 const aiVector3D &b = mesh->mVertices[face.mIndices[1]];
199 const aiVector3D &c = mesh->mVertices[face.mIndices[2]];
200 const aiVector3D p(localX, localY, localZ);
201 const aiVector3D v0 = b - a;
202 const aiVector3D v1 = c - a;
203 const aiVector3D v2 = p - a;
204 const float d00 = v0 * v0;
205 const float d01 = v0 * v1;
206 const float d11 = v1 * v1;
207 const float d20 = v2 * v0;
208 const float d21 = v2 * v1;
209 const float denominator = d00 * d11 - d01 * d01;
210 if (!std::isfinite(denominator) || std::fabs(denominator) <= 1e-12f)
212 eve::DiagnosticCode::InvariantViolation, "triangle is degenerate",
213 "model3d.surfaceUv.triangle"));
214
215 const float wb = (d11 * d20 - d01 * d21) / denominator;
216 const float wc = (d00 * d21 - d01 * d20) / denominator;
217 const float wa = 1.f - wb - wc;
218 constexpr float tolerance = 1e-3f;
219 if (wa < -tolerance || wb < -tolerance || wc < -tolerance)
222 "point is outside the requested triangle", "model3d.surfaceUv.point"));
223
224 const aiVector3D &ta = mesh->mTextureCoords[channel][face.mIndices[0]];
225 const aiVector3D &tb = mesh->mTextureCoords[channel][face.mIndices[1]];
226 const aiVector3D &tc = mesh->mTextureCoords[channel][face.mIndices[2]];
227 SurfaceUv result;
228 result.u = wa * ta.x + wb * tb.x + wc * tc.x;
229 result.v = wa * ta.y + wb * tb.y + wc * tc.y;
230 result.barycentricA = wa;
231 result.barycentricB = wb;
232 result.barycentricC = wc;
233 result.triangleIndex = triangleIndex;
234 result.uvChannel = channel;
235 return eve::Result<SurfaceUv>::success(result);
236}
237
238bool ModelData::hasTangents(int meshIndex) const {
239 const aiMesh *m = meshAt(meshIndex);
240 if (!m) throw eve::Exception("ModelData::hasTangents: invalid mesh index");
241 return m->HasTangentsAndBitangents();
242}
243
244float ModelData::getTangent(int meshIndex, int vertexIndex, int component) const {
245 const aiMesh *m = meshAt(meshIndex);
246 if (!m || !m->HasTangentsAndBitangents() || vertexIndex < 0 ||
247 static_cast<unsigned>(vertexIndex) >= m->mNumVertices || component < 0 || component > 2)
248 throw eve::Exception("ModelData::getTangent: invalid mesh/vertex/component");
249 const aiVector3D &v = m->mTangents[vertexIndex];
250 return component == 0 ? v.x : (component == 1 ? v.y : v.z);
251}
252
253float ModelData::getBitangent(int meshIndex, int vertexIndex, int component) const {
254 const aiMesh *m = meshAt(meshIndex);
255 if (!m || !m->HasTangentsAndBitangents() || vertexIndex < 0 ||
256 static_cast<unsigned>(vertexIndex) >= m->mNumVertices || component < 0 || component > 2)
257 throw eve::Exception("ModelData::getBitangent: invalid mesh/vertex/component");
258 const aiVector3D &v = m->mBitangents[vertexIndex];
259 return component == 0 ? v.x : (component == 1 ? v.y : v.z);
260}
261
263 const aiMesh *m = meshAt(meshIndex);
264 if (!m) throw eve::Exception("ModelData::getVertexColorChannelCount: invalid mesh index");
265 return static_cast<int>(m->GetNumColorChannels());
266}
267
268bool ModelData::hasVertexColorChannel(int meshIndex, int channel) const {
269 const aiMesh *m = meshAt(meshIndex);
270 if (!m) throw eve::Exception("ModelData::hasVertexColorChannel: invalid mesh index");
271 return channel >= 0 && channel < AI_MAX_NUMBER_OF_COLOR_SETS &&
272 m->HasVertexColors(static_cast<unsigned>(channel));
273}
274
275float ModelData::getVertexColor(int meshIndex, int channel, int vertexIndex, int component) const {
276 const aiMesh *m = meshAt(meshIndex);
277 if (!m || channel < 0 || channel >= AI_MAX_NUMBER_OF_COLOR_SETS || vertexIndex < 0 ||
278 static_cast<unsigned>(vertexIndex) >= m->mNumVertices || component < 0 || component > 3 ||
279 !m->HasVertexColors(static_cast<unsigned>(channel)))
280 throw eve::Exception("ModelData::getVertexColor: invalid mesh/channel/vertex/component");
281 const aiColor4D &v = m->mColors[channel][vertexIndex];
282 if (component == 0) return v.r;
283 if (component == 1) return v.g;
284 if (component == 2) return v.b;
285 return v.a;
286}
287
288int ModelData::getMaterialIndex(int meshIndex) const {
289 const aiMesh *m = meshAt(meshIndex);
290 return m ? static_cast<int>(m->mMaterialIndex) : -1;
291}
292
293std::string ModelData::getMaterialName(int matIndex) const {
294 const aiMaterial *mat = materialAt(matIndex);
295 if (!mat) return {};
296 aiString name;
297 if (mat->Get(AI_MATKEY_NAME, name) != AI_SUCCESS || name.length == 0)
298 return "material" + std::to_string(matIndex);
299 return name.C_Str();
300}
301
302float ModelData::getMaterialBaseColorR(int matIndex) const {
303 return materialBaseColor(materialAt(matIndex)).r;
304}
305
306float ModelData::getMaterialBaseColorG(int matIndex) const {
307 return materialBaseColor(materialAt(matIndex)).g;
308}
309
310float ModelData::getMaterialBaseColorB(int matIndex) const {
311 return materialBaseColor(materialAt(matIndex)).b;
312}
313
314float ModelData::getMaterialBaseColorA(int matIndex) const {
315 return materialBaseColor(materialAt(matIndex)).a;
316}
317
318float ModelData::getMaterialMetallicFactor(int matIndex) const {
319 const aiMaterial *mat = materialAt(matIndex);
320 if (!mat) return 0.f;
321 float metallic = 0.f;
322 if (mat->Get(AI_MATKEY_METALLIC_FACTOR, metallic) != AI_SUCCESS)
323 return 0.f;
324 return metallic;
325}
326
327float ModelData::getMaterialRoughnessFactor(int matIndex) const {
328 const aiMaterial *mat = materialAt(matIndex);
329 if (!mat) return 0.45f;
330 float roughness = 0.45f;
331 if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, roughness) != AI_SUCCESS)
332 return 0.45f;
333 return roughness;
334}
335
336float ModelData::getMaterialOpacity(int matIndex) const {
337 const aiMaterial *mat = materialAt(matIndex);
338 if (!mat) return 1.f;
339 float opacity = 1.f;
340 if (mat->Get(AI_MATKEY_OPACITY, opacity) != AI_SUCCESS)
341 return 1.f;
342 return opacity;
343}
344
345bool ModelData::getMaterialTwoSided(int matIndex) const {
346 const aiMaterial *mat = materialAt(matIndex);
347 if (!mat) return false;
348 int twoSided = 0;
349 if (mat->Get(AI_MATKEY_TWOSIDED, twoSided) != AI_SUCCESS)
350 return false;
351 return twoSided != 0;
352}
353
354std::string ModelData::getMaterialAlphaMode(int matIndex) const {
355 const aiMaterial *mat = materialAt(matIndex);
356 if (!mat) return "OPAQUE";
357 aiString mode;
358 if (mat->Get("$mat.gltf.alphaMode", 0, 0, mode) == AI_SUCCESS) {
359 const std::string value = mode.C_Str();
360 if (value == "MASK" || value == "BLEND") return value;
361 }
362 // Legacy formats generally expose opacity but no explicit alpha mode.
363 return getMaterialOpacity(matIndex) < 0.999f ? "BLEND" : "OPAQUE";
364}
365
366float ModelData::getMaterialAlphaCutoff(int matIndex) const {
367 const aiMaterial *mat = materialAt(matIndex);
368 if (!mat) return 0.5f;
369 float cutoff = 0.5f;
370 if (mat->Get("$mat.gltf.alphaCutoff", 0, 0, cutoff) != AI_SUCCESS) return 0.5f;
371 return cutoff < 0.f ? 0.f : (cutoff > 1.f ? 1.f : cutoff);
372}
373
374int ModelData::getMaterialTextureSlotCount(int matIndex, const std::string &type) const {
375 const aiMaterial *mat = materialAt(matIndex);
376 if (!mat) return 0;
377 const aiTextureType texType = textureTypeFromName(type);
378 if (texType == aiTextureType_NONE) return 0;
379 return static_cast<int>(mat->GetTextureCount(texType));
380}
381
382std::string ModelData::getMaterialTexturePath(int matIndex, const std::string &type,
383 int slot) const {
384 const aiMaterial *mat = materialAt(matIndex);
385 if (!mat || slot < 0) return {};
386 const aiTextureType texType = textureTypeFromName(type);
387 if (texType == aiTextureType_NONE) return {};
388 aiString path;
389 if (mat->GetTexture(texType, static_cast<unsigned>(slot), &path) != AI_SUCCESS)
390 return {};
391 return path.C_Str();
392}
393
394int ModelData::getMaterialTextureEmbeddedIndex(int matIndex, const std::string &type,
395 int slot) const {
396 const std::string path = getMaterialTexturePath(matIndex, type, slot);
397 if (path.empty() || path[0] != '*') return -1;
398 return std::atoi(path.c_str() + 1);
399}
400
402 const aiScene *sc = getScene();
403 return sc ? static_cast<int>(sc->mNumTextures) : 0;
404}
405
407 const aiScene *sc = getScene();
408 if (!sc || idx < 0 || static_cast<unsigned>(idx) >= sc->mNumTextures) return {};
409 const aiTexture *tex = sc->mTextures[idx];
410 if (!tex) return {};
411 if (tex->mFilename.length) return tex->mFilename.C_Str();
412 return "texture" + std::to_string(idx);
413}
414
416 const aiScene *sc = getScene();
417 if (!sc || idx < 0 || static_cast<unsigned>(idx) >= sc->mNumTextures) return 0;
418 const aiTexture *tex = sc->mTextures[idx];
419 return tex ? static_cast<int>(tex->mWidth) : 0;
420}
421
423 const aiScene *sc = getScene();
424 if (!sc || idx < 0 || static_cast<unsigned>(idx) >= sc->mNumTextures) return 0;
425 const aiTexture *tex = sc->mTextures[idx];
426 return tex ? static_cast<int>(tex->mHeight) : 0;
427}
428
430 const aiScene *sc = getScene();
431 if (!sc || idx < 0 || static_cast<unsigned>(idx) >= sc->mNumTextures) return nullptr;
432 const aiTexture *tex = sc->mTextures[idx];
433 if (!tex || !tex->pcData) return nullptr;
434 try {
435 if (tex->mHeight == 0) {
436 // Compressed blob (PNG/JPEG/...) — decode through the image module.
437 eve::data::ByteData bytes(tex->pcData, static_cast<size_t>(tex->mWidth));
438 return eve::image::Image::create()->newImageData(&bytes);
439 }
440 // Uncompressed BGRA8888 texels — convert to RGBA8.
441 const unsigned w = tex->mWidth;
442 const unsigned h = tex->mHeight;
443 std::vector<uint8_t> rgba(size_t(w) * size_t(h) * 4);
444 const aiTexel *src = tex->pcData;
445 for (unsigned i = 0; i < w * h; ++i) {
446 rgba[i * 4 + 0] = src[i].r;
447 rgba[i * 4 + 1] = src[i].g;
448 rgba[i * 4 + 2] = src[i].b;
449 rgba[i * 4 + 3] = src[i].a;
450 }
451 return new eve::image::ImageData(int(w), int(h), "RGBA8", rgba.data(), false);
452 } catch (...) {
453 return nullptr;
454 }
455}
456
457int ModelData::getMorphTargetCount(int meshIndex) const {
458 const aiMesh *m = meshAt(meshIndex);
459 if (!m) return 0;
460 return static_cast<int>(m->mNumAnimMeshes);
461}
462
463std::string ModelData::getMorphTargetName(int meshIndex, int morphIndex) const {
464 const aiMesh *m = meshAt(meshIndex);
465 if (!m || morphIndex < 0 || static_cast<unsigned>(morphIndex) >= m->mNumAnimMeshes)
466 return {};
467 const aiAnimMesh *am = m->mAnimMeshes[morphIndex];
468 if (!am) return {};
469 if (am->mName.length)
470 return am->mName.C_Str();
471 return "morph" + std::to_string(morphIndex);
472}
473
474bool ModelData::hasBones(int meshIndex) const {
475 const aiMesh *m = meshAt(meshIndex);
476 if (!m)
477 throw eve::Exception("ModelData::hasBones: invalid mesh index");
478 return m->HasBones() && m->mNumBones > 0;
479}
480
481int ModelData::getBoneCount(int meshIndex) const {
482 const aiMesh *m = meshAt(meshIndex);
483 if (!m)
484 throw eve::Exception("ModelData::getBoneCount: invalid mesh index");
485 return static_cast<int>(m->mNumBones);
486}
487
488std::string ModelData::getBoneName(int meshIndex, int boneIndex) const {
489 const aiMesh *m = meshAt(meshIndex);
490 if (!m || boneIndex < 0 || static_cast<unsigned>(boneIndex) >= m->mNumBones)
491 return {};
492 const aiBone *b = m->mBones[boneIndex];
493 if (!b) return {};
494 return b->mName.C_Str();
495}
496
497float ModelData::getInverseBindMatrixElement(int meshIndex, int boneIndex,
498 int elementIndex) const {
499 const aiMesh *m = meshAt(meshIndex);
500 if (!m)
501 throw eve::Exception("ModelData::getInverseBindMatrixElement: invalid mesh index");
502 if (boneIndex < 0 || static_cast<unsigned>(boneIndex) >= m->mNumBones)
503 throw eve::Exception("ModelData::getInverseBindMatrixElement: invalid bone index");
504 if (elementIndex < 0 || elementIndex > 15)
505 throw eve::Exception("ModelData::getInverseBindMatrixElement: elementIndex must be 0..15");
506 const aiBone *b = m->mBones[boneIndex];
507 if (!b)
508 throw eve::Exception("ModelData::getInverseBindMatrixElement: null bone");
509 // Assimp row-major → column-major element.
510 const aiMatrix4x4 &mat = b->mOffsetMatrix;
511 const float rows[4][4] = {
512 {mat.a1, mat.a2, mat.a3, mat.a4},
513 {mat.b1, mat.b2, mat.b3, mat.b4},
514 {mat.c1, mat.c2, mat.c3, mat.c4},
515 {mat.d1, mat.d2, mat.d3, mat.d4},
516 };
517 const int col = elementIndex / 4;
518 const int row = elementIndex % 4;
519 return rows[row][col];
520}
521
522int ModelData::getBoneWeightCount(int meshIndex, int boneIndex) const {
523 const aiMesh *m = meshAt(meshIndex);
524 if (!m || boneIndex < 0 || static_cast<unsigned>(boneIndex) >= m->mNumBones)
525 return 0;
526 const aiBone *b = m->mBones[boneIndex];
527 return b ? static_cast<int>(b->mNumWeights) : 0;
528}
529
530int ModelData::getBoneWeightVertex(int meshIndex, int boneIndex, int weightIndex) const {
531 const aiMesh *m = meshAt(meshIndex);
532 if (!m || boneIndex < 0 || static_cast<unsigned>(boneIndex) >= m->mNumBones)
533 return -1;
534 const aiBone *b = m->mBones[boneIndex];
535 if (!b || weightIndex < 0 || static_cast<unsigned>(weightIndex) >= b->mNumWeights)
536 return -1;
537 return static_cast<int>(b->mWeights[weightIndex].mVertexId);
538}
539
540float ModelData::getBoneWeightValue(int meshIndex, int boneIndex, int weightIndex) const {
541 const aiMesh *m = meshAt(meshIndex);
542 if (!m || boneIndex < 0 || static_cast<unsigned>(boneIndex) >= m->mNumBones)
543 return 0.f;
544 const aiBone *b = m->mBones[boneIndex];
545 if (!b || weightIndex < 0 || static_cast<unsigned>(weightIndex) >= b->mNumWeights)
546 return 0.f;
547 return b->mWeights[weightIndex].mWeight;
548}
549
551 const aiScene *sc = getScene();
552 return sc ? static_cast<int>(sc->mNumAnimations) : 0;
553}
554
555std::string ModelData::getAnimationName(int animIndex) const {
556 const aiScene *sc = getScene();
557 if (!sc || animIndex < 0 || static_cast<unsigned>(animIndex) >= sc->mNumAnimations)
558 return {};
559 const aiAnimation *a = sc->mAnimations[animIndex];
560 if (!a) return {};
561 if (a->mName.length)
562 return a->mName.C_Str();
563 return "anim" + std::to_string(animIndex);
564}
565
566} // namespace model3d
567} // namespace eve
double value
float w
Definition AnimClip.cpp:738
glm::vec4 p[6]
int rows
float v
std::int32_t c
int h
std::uint64_t bytes
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float roughness
float tb
float ta
float metallic
int idx
std::string path
Definition PlayHost.cpp:110
std::string uri
std::weak_ptr< PrimitiveScene > scene
Mesh * mesh
float opacity
float m[16]
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
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
Resource is a game object that is managed by the ResourceManager. It can be loaded from a file or gen...
Definition Resource.h:51
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
In-memory byte buffer implementing eve::Data (ref-counted).
Definition ByteData.h:13
Represents raw pixel data.
Definition ImageData.h:38
CPU-side decoded 3D model (Assimp scene owned via medialoader::ModelScene). Does not upload to GPU — ...
Definition ModelData.h:39
bool empty() const
Empty.
Definition ModelData.cpp:65
int getEmbeddedTextureCount() const
Returns the embedded texture count.
int getMeshCount() const
Returns the mesh count.
Definition ModelData.cpp:91
float getMaterialAlphaCutoff(int matIndex) const
Returns the material alpha cutoff.
float getMaterialBaseColorR(int matIndex) const
Base color: glTF BASE_COLOR, falling back to OBJ/legacy DIFFUSE.
float getMaterialMetallicFactor(int matIndex) const
PBR factors; defaults 0 (metallic) / 0.45 (roughness) when absent.
float getMaterialBaseColorB(int matIndex) const
Returns the material base color b.
float getMaterialBaseColorG(int matIndex) const
Returns the material base color g.
float getVertexPosition(int meshIndex, int vertexIndex, int component) const
Source position component for one vertex (component 0=x, 1=y, 2=z).
std::string getMaterialAlphaMode(int matIndex) const
glTF alpha mode normalized to "OPAQUE", "MASK", or "BLEND".
bool hasVertexColorChannel(int meshIndex, int channel) const
Whether a mesh has the requested vertex-color channel.
int getFaceCount(int meshIndex) const
Returns the face count.
bool hasNormals(int meshIndex) const
True when normals.
~ModelData() override
Model data.
float getVertexColor(int meshIndex, int channel, int vertexIndex, int component) const
RGBA component (0..3) in a vertex-color channel.
int getEmbeddedTextureHeight(int idx) const
0 means a compressed blob (use getEmbeddedTextureImageData to decode).
int getBoneCount(int meshIndex) const
Returns the bone count.
int getBoneWeightCount(int meshIndex, int boneIndex) const
Returns the bone weight count.
float getTexCoord(int meshIndex, int channel, int vertexIndex, int component) const
UV component in channel for vertex (component 0=u, 1=v, 2=w).
float getMaterialOpacity(int matIndex) const
Returns the material opacity.
std::string getMaterialTexturePath(int matIndex, const std::string &type, int slot=0) const
External file path, or "*N" for an embedded texture. Empty when absent.
int getMaterialTextureSlotCount(int matIndex, const std::string &type) const
Texture type names (Squirrel strings): "base_color", "diffuse", "normals", "height",...
float getInverseBindMatrixElement(int meshIndex, int boneIndex, int elementIndex) const
Inverse-bind (offset) matrix element, column-major, elementIndex in [0,15].
std::string getMaterialName(int matIndex) const
Returns the material name.
float getTangent(int meshIndex, int vertexIndex, int component) const
Tangent XYZ component for a vertex.
int getEmbeddedTextureWidth(int idx) const
Returns the embedded texture width.
bool getMaterialTwoSided(int matIndex) const
Returns the material two sided.
int getAnimationCount() const
Scene-level animation clips (aiAnimation).
bool hasTangents(int meshIndex) const
Whether imported tangent and bitangent streams exist.
int getTexCoordChannelCount(int meshIndex) const
Number of populated UV channels (0..AI_MAX_NUMBER_OF_TEXTURECOORDS).
float getBitangent(int meshIndex, int vertexIndex, int component) const
Bitangent XYZ component for a vertex.
float getVertexNormal(int meshIndex, int vertexIndex, int component) const
Source normal component for one vertex (component 0=x, 1=y, 2=z).
float getMaterialBaseColorA(int matIndex) const
Returns the material base color a.
const aiScene * getScene() const
Returns the scene.
Definition ModelData.cpp:67
bool hasTexCoordChannel(int meshIndex, int channel) const
Whether a mesh has the requested UV channel.
float getBoneWeightValue(int meshIndex, int boneIndex, int weightIndex) const
Returns the bone weight value.
std::string getMorphTargetName(int meshIndex, int morphIndex) const
Returns the morph target name.
std::string getAnimationName(int animIndex) const
Returns the animation name.
int getVertexColorChannelCount(int meshIndex) const
Number of populated vertex-color channels.
bool hasTexCoords(int meshIndex) const
True when tex coords.
int getBoneWeightVertex(int meshIndex, int boneIndex, int weightIndex) const
Returns the bone weight vertex.
image::ImageData * getEmbeddedTextureImageData(int idx) const
Returns the embedded texture image data.
eve::Result< SurfaceUv > mapSurfacePointToUv(int meshIndex, int triangleIndex, float localX, float localY, float localZ, int channel=0) const
Map a point on one source triangle to UV coordinates by barycentric interpolation.
int getMaterialCount() const
Returns the material count.
Definition ModelData.cpp:96
int getMaterialIndex(int meshIndex) const
Assimp material slot referenced by a mesh; -1 when invalid.
int getMaterialTextureEmbeddedIndex(int matIndex, const std::string &type, int slot=0) const
Scene texture index for embedded "*N" references; -1 for external files.
float getMaterialRoughnessFactor(int matIndex) const
Returns the material roughness factor.
ModelData(medialoader::ModelScene scene, std::string uri="", bool uvFlipped=false)
Own a decoded scene and the UV postprocessing state used to produce it.
Definition ModelData.cpp:54
bool hasBones(int meshIndex) const
Assimp skeletal skin data (aiBone / vertex weights) on a mesh.
const aiMesh * getMesh(int meshIndex) const
Raw Assimp mesh at meshIndex; returns nullptr when out of range.
Definition ModelData.cpp:87
int getFaceVertexIndex(int meshIndex, int triangleIndex, int corner) const
Source vertex index for one triangle corner (corner 0..2).
std::string getBoneName(int meshIndex, int boneIndex) const
Returns the bone name.
int getVertexCount(int meshIndex) const
Returns the vertex count.
void adopt(eve::Resource &replacement) override
Replace this scene with replacement's (cache reload).
Definition ModelData.cpp:59
std::string getEmbeddedTextureName(int idx) const
Returns the embedded texture name.
int getMorphTargetCount(int meshIndex) const
Assimp morph / blend-shape targets on a mesh (aiAnimMesh).
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
Owning result of mapping a triangle-local surface point to one UV channel.
Definition ModelData.h:25