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>
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;
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;
41aiColor4D materialBaseColor(
const aiMaterial *mat) {
42 aiColor4D
c(1.f, 1.f, 1.f, 1.f);
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);
60 auto &other =
static_cast<ModelData &
>(replacement);
61 std::swap(scene, other.scene);
62 std::swap(uvFlipped_, other.uvFlipped_);
73const aiMesh *ModelData::meshAt(
int meshIndex)
const {
75 if (!sc || meshIndex < 0 ||
static_cast<unsigned>(meshIndex) >= sc->mNumMeshes)
77 return sc->mMeshes[meshIndex];
80const aiMaterial *ModelData::materialAt(
int matIndex)
const {
82 if (!sc || matIndex < 0 ||
static_cast<unsigned>(matIndex) >= sc->mNumMaterials)
84 return sc->mMaterials[matIndex];
89aiMesh *ModelData::meshAtMutable(
int meshIndex) {
return const_cast<aiMesh *
>(meshAt(meshIndex)); }
93 return sc ?
static_cast<int>(sc->mNumMeshes) : 0;
98 return sc ?
static_cast<int>(sc->mNumMaterials) : 0;
102 const aiMesh *
m = meshAt(meshIndex);
104 throw eve::Exception(
"ModelData::getVertexCount: invalid mesh index");
105 return static_cast<int>(
m->mNumVertices);
109 const aiMesh *
m = meshAt(meshIndex);
111 throw eve::Exception(
"ModelData::getFaceCount: invalid mesh index");
112 return static_cast<int>(
m->mNumFaces);
116 const aiMesh *
m = meshAt(meshIndex);
118 throw eve::Exception(
"ModelData::hasNormals: invalid mesh index");
119 return m->HasNormals();
123 const aiMesh *
m = meshAt(meshIndex);
125 throw eve::Exception(
"ModelData::hasTexCoords: invalid mesh index");
126 return m->HasTextureCoords(0);
130 const aiMesh *
m = meshAt(meshIndex);
131 if (!
m)
throw eve::Exception(
"ModelData::getTexCoordChannelCount: invalid mesh index");
132 return static_cast<int>(
m->GetNumUVChannels());
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));
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);
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);
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);
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]);
179 float localX,
float localY,
float localZ,
181 const aiMesh *
mesh = meshAt(meshIndex);
182 if (!
mesh || triangleIndex < 0 ||
static_cast<unsigned>(triangleIndex) >=
mesh->mNumFaces)
185 "model3d.surfaceUv"));
186 if (channel < 0 || channel >= AI_MAX_NUMBER_OF_TEXTURECOORDS ||
187 !
mesh->HasTextureCoords(
static_cast<unsigned>(channel)))
190 "model3d.surfaceUv.channel"));
191 const aiFace &face =
mesh->mFaces[triangleIndex];
192 if (face.mNumIndices != 3)
195 "model3d.surfaceUv.triangle"));
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)
213 "model3d.surfaceUv.triangle"));
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"));
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]];
228 result.
u = wa *
ta.x + wb *
tb.x + wc * tc.x;
229 result.
v = wa *
ta.y + wb *
tb.y + wc * tc.y;
239 const aiMesh *
m = meshAt(meshIndex);
240 if (!
m)
throw eve::Exception(
"ModelData::hasTangents: invalid mesh index");
241 return m->HasTangentsAndBitangents();
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);
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);
263 const aiMesh *
m = meshAt(meshIndex);
264 if (!
m)
throw eve::Exception(
"ModelData::getVertexColorChannelCount: invalid mesh index");
265 return static_cast<int>(
m->GetNumColorChannels());
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));
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;
289 const aiMesh *
m = meshAt(meshIndex);
290 return m ?
static_cast<int>(
m->mMaterialIndex) : -1;
294 const aiMaterial *mat = materialAt(matIndex);
297 if (mat->Get(AI_MATKEY_NAME,
name) != AI_SUCCESS ||
name.length == 0)
298 return "material" + std::to_string(matIndex);
303 return materialBaseColor(materialAt(matIndex)).r;
307 return materialBaseColor(materialAt(matIndex)).g;
311 return materialBaseColor(materialAt(matIndex)).b;
315 return materialBaseColor(materialAt(matIndex)).a;
319 const aiMaterial *mat = materialAt(matIndex);
320 if (!mat)
return 0.f;
322 if (mat->Get(AI_MATKEY_METALLIC_FACTOR,
metallic) != AI_SUCCESS)
328 const aiMaterial *mat = materialAt(matIndex);
329 if (!mat)
return 0.45f;
331 if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR,
roughness) != AI_SUCCESS)
337 const aiMaterial *mat = materialAt(matIndex);
338 if (!mat)
return 1.f;
340 if (mat->Get(AI_MATKEY_OPACITY,
opacity) != AI_SUCCESS)
346 const aiMaterial *mat = materialAt(matIndex);
347 if (!mat)
return false;
349 if (mat->Get(AI_MATKEY_TWOSIDED, twoSided) != AI_SUCCESS)
351 return twoSided != 0;
355 const aiMaterial *mat = materialAt(matIndex);
356 if (!mat)
return "OPAQUE";
358 if (mat->Get(
"$mat.gltf.alphaMode", 0, 0, mode) == AI_SUCCESS) {
359 const std::string
value = mode.C_Str();
367 const aiMaterial *mat = materialAt(matIndex);
368 if (!mat)
return 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);
375 const aiMaterial *mat = materialAt(matIndex);
377 const aiTextureType texType = textureTypeFromName(
type);
378 if (texType == aiTextureType_NONE)
return 0;
379 return static_cast<int>(mat->GetTextureCount(texType));
384 const aiMaterial *mat = materialAt(matIndex);
385 if (!mat || slot < 0)
return {};
386 const aiTextureType texType = textureTypeFromName(
type);
387 if (texType == aiTextureType_NONE)
return {};
389 if (mat->GetTexture(texType,
static_cast<unsigned>(slot), &
path) != AI_SUCCESS)
397 if (
path.empty() ||
path[0] !=
'*')
return -1;
398 return std::atoi(
path.c_str() + 1);
403 return sc ?
static_cast<int>(sc->mNumTextures) : 0;
408 if (!sc ||
idx < 0 ||
static_cast<unsigned>(
idx) >= sc->mNumTextures)
return {};
409 const aiTexture *tex = sc->mTextures[
idx];
411 if (tex->mFilename.length)
return tex->mFilename.C_Str();
412 return "texture" + std::to_string(
idx);
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;
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;
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;
435 if (tex->mHeight == 0) {
438 return eve::image::Image::create()->newImageData(&
bytes);
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;
458 const aiMesh *
m = meshAt(meshIndex);
460 return static_cast<int>(
m->mNumAnimMeshes);
464 const aiMesh *
m = meshAt(meshIndex);
465 if (!
m || morphIndex < 0 ||
static_cast<unsigned>(morphIndex) >=
m->mNumAnimMeshes)
467 const aiAnimMesh *am =
m->mAnimMeshes[morphIndex];
469 if (am->mName.length)
470 return am->mName.C_Str();
471 return "morph" + std::to_string(morphIndex);
475 const aiMesh *
m = meshAt(meshIndex);
478 return m->HasBones() &&
m->mNumBones > 0;
482 const aiMesh *
m = meshAt(meshIndex);
484 throw eve::Exception(
"ModelData::getBoneCount: invalid mesh index");
485 return static_cast<int>(
m->mNumBones);
489 const aiMesh *
m = meshAt(meshIndex);
490 if (!
m || boneIndex < 0 ||
static_cast<unsigned>(boneIndex) >=
m->mNumBones)
492 const aiBone *
b =
m->mBones[boneIndex];
494 return b->mName.C_Str();
498 int elementIndex)
const {
499 const aiMesh *
m = meshAt(meshIndex);
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];
508 throw eve::Exception(
"ModelData::getInverseBindMatrixElement: null bone");
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},
517 const int col = elementIndex / 4;
518 const int row = elementIndex % 4;
519 return rows[row][col];
523 const aiMesh *
m = meshAt(meshIndex);
524 if (!
m || boneIndex < 0 ||
static_cast<unsigned>(boneIndex) >=
m->mNumBones)
526 const aiBone *
b =
m->mBones[boneIndex];
527 return b ?
static_cast<int>(
b->mNumWeights) : 0;
531 const aiMesh *
m = meshAt(meshIndex);
532 if (!
m || boneIndex < 0 ||
static_cast<unsigned>(boneIndex) >=
m->mNumBones)
534 const aiBone *
b =
m->mBones[boneIndex];
535 if (!
b || weightIndex < 0 ||
static_cast<unsigned>(weightIndex) >=
b->mNumWeights)
537 return static_cast<int>(
b->mWeights[weightIndex].mVertexId);
541 const aiMesh *
m = meshAt(meshIndex);
542 if (!
m || boneIndex < 0 ||
static_cast<unsigned>(boneIndex) >=
m->mNumBones)
544 const aiBone *
b =
m->mBones[boneIndex];
545 if (!
b || weightIndex < 0 ||
static_cast<unsigned>(weightIndex) >=
b->mNumWeights)
547 return b->mWeights[weightIndex].mWeight;
552 return sc ?
static_cast<int>(sc->mNumAnimations) : 0;
557 if (!sc || animIndex < 0 ||
static_cast<unsigned>(animIndex) >= sc->mNumAnimations)
559 const aiAnimation *
a = sc->mAnimations[animIndex];
562 return a->mName.C_Str();
563 return "anim" + std::to_string(animIndex);
std::weak_ptr< PrimitiveScene > scene
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.
EVENGINE_API_FOUNDATION public API.
Resource is a game object that is managed by the ResourceManager. It can be loaded from a file or gen...
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
In-memory byte buffer implementing eve::Data (ref-counted).
Represents raw pixel data.
CPU-side decoded 3D model (Assimp scene owned via medialoader::ModelScene). Does not upload to GPU — ...
int getEmbeddedTextureCount() const
Returns the embedded texture count.
int getMeshCount() const
Returns the mesh count.
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.
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.
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.
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.
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).
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).
Owning result of mapping a triangle-local surface point to one UV channel.