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>
24aiTextureType textureTypeFromName(
const std::string &
type) {
25 if (
type ==
"base_color")
return aiTextureType_BASE_COLOR;
26 if (
type ==
"diffuse")
return aiTextureType_DIFFUSE;
27 if (
type ==
"normals")
return aiTextureType_NORMALS;
28 if (
type ==
"height")
return aiTextureType_HEIGHT;
29 if (
type ==
"emissive")
return aiTextureType_EMISSIVE;
30 if (
type ==
"metalness")
return aiTextureType_METALNESS;
31 if (
type ==
"roughness")
return aiTextureType_DIFFUSE_ROUGHNESS;
33 if (
type ==
"ambient_occlusion" ||
type ==
"lightmap")
return aiTextureType_LIGHTMAP;
34 if (
type ==
"opacity")
return aiTextureType_OPACITY;
35 if (
type ==
"specular")
return aiTextureType_SPECULAR;
36 if (
type ==
"shininess")
return aiTextureType_SHININESS;
37 return aiTextureType_NONE;
40aiColor4D materialBaseColor(
const aiMaterial *mat) {
41 aiColor4D
c(1.f, 1.f, 1.f, 1.f);
43 if (mat->Get(AI_MATKEY_BASE_COLOR,
c) != AI_SUCCESS) {
44 aiColor3D diffuse(1.f, 1.f, 1.f);
45 if (mat->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse) == AI_SUCCESS)
46 c = aiColor4D(diffuse.r, diffuse.g, diffuse.b, 1.f);
54 :
Resource(std::move(uri)), scene(std::move(scene)) {}
59 auto &other =
static_cast<ModelData &
>(replacement);
60 std::swap(scene, other.scene);
71const aiMesh *ModelData::meshAt(
int meshIndex)
const {
73 if (!sc || meshIndex < 0 ||
static_cast<unsigned>(meshIndex) >= sc->mNumMeshes)
75 return sc->mMeshes[meshIndex];
78const aiMaterial *ModelData::materialAt(
int matIndex)
const {
80 if (!sc || matIndex < 0 ||
static_cast<unsigned>(matIndex) >= sc->mNumMaterials)
82 return sc->mMaterials[matIndex];
89 return sc ?
static_cast<int>(sc->mNumMeshes) : 0;
94 return sc ?
static_cast<int>(sc->mNumMaterials) : 0;
98 const aiMesh *
m = meshAt(meshIndex);
100 throw eve::Exception(
"ModelData::getVertexCount: invalid mesh index");
101 return static_cast<int>(
m->mNumVertices);
105 const aiMesh *
m = meshAt(meshIndex);
107 throw eve::Exception(
"ModelData::getFaceCount: invalid mesh index");
108 return static_cast<int>(
m->mNumFaces);
112 const aiMesh *
m = meshAt(meshIndex);
114 throw eve::Exception(
"ModelData::hasNormals: invalid mesh index");
115 return m->HasNormals();
119 const aiMesh *
m = meshAt(meshIndex);
121 throw eve::Exception(
"ModelData::hasTexCoords: invalid mesh index");
122 return m->HasTextureCoords(0);
126 const aiMesh *
m = meshAt(meshIndex);
127 return m ?
static_cast<int>(
m->mMaterialIndex) : -1;
131 const aiMaterial *mat = materialAt(matIndex);
134 if (mat->Get(AI_MATKEY_NAME,
name) != AI_SUCCESS ||
name.length == 0)
135 return "material" + std::to_string(matIndex);
140 return materialBaseColor(materialAt(matIndex)).r;
144 return materialBaseColor(materialAt(matIndex)).g;
148 return materialBaseColor(materialAt(matIndex)).b;
152 return materialBaseColor(materialAt(matIndex)).a;
156 const aiMaterial *mat = materialAt(matIndex);
157 if (!mat)
return 0.f;
159 if (mat->Get(AI_MATKEY_METALLIC_FACTOR,
metallic) != AI_SUCCESS)
165 const aiMaterial *mat = materialAt(matIndex);
166 if (!mat)
return 0.45f;
168 if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR,
roughness) != AI_SUCCESS)
174 const aiMaterial *mat = materialAt(matIndex);
175 if (!mat)
return 1.f;
177 if (mat->Get(AI_MATKEY_OPACITY, opacity) != AI_SUCCESS)
183 const aiMaterial *mat = materialAt(matIndex);
184 if (!mat)
return false;
186 if (mat->Get(AI_MATKEY_TWOSIDED, twoSided) != AI_SUCCESS)
188 return twoSided != 0;
192 const aiMaterial *mat = materialAt(matIndex);
194 const aiTextureType texType = textureTypeFromName(
type);
195 if (texType == aiTextureType_NONE)
return 0;
196 return static_cast<int>(mat->GetTextureCount(texType));
201 const aiMaterial *mat = materialAt(matIndex);
202 if (!mat || slot < 0)
return {};
203 const aiTextureType texType = textureTypeFromName(
type);
204 if (texType == aiTextureType_NONE)
return {};
206 if (mat->GetTexture(texType,
static_cast<unsigned>(slot), &path) != AI_SUCCESS)
214 if (path.empty() || path[0] !=
'*')
return -1;
215 return std::atoi(path.c_str() + 1);
220 return sc ?
static_cast<int>(sc->mNumTextures) : 0;
225 if (!sc ||
idx < 0 ||
static_cast<unsigned>(
idx) >= sc->mNumTextures)
return {};
226 const aiTexture *tex = sc->mTextures[
idx];
228 if (tex->mFilename.length)
return tex->mFilename.C_Str();
229 return "texture" + std::to_string(
idx);
234 if (!sc ||
idx < 0 ||
static_cast<unsigned>(
idx) >= sc->mNumTextures)
return 0;
235 const aiTexture *tex = sc->mTextures[
idx];
236 return tex ?
static_cast<int>(tex->mWidth) : 0;
241 if (!sc ||
idx < 0 ||
static_cast<unsigned>(
idx) >= sc->mNumTextures)
return 0;
242 const aiTexture *tex = sc->mTextures[
idx];
243 return tex ?
static_cast<int>(tex->mHeight) : 0;
248 if (!sc ||
idx < 0 ||
static_cast<unsigned>(
idx) >= sc->mNumTextures)
return nullptr;
249 const aiTexture *tex = sc->mTextures[
idx];
250 if (!tex || !tex->pcData)
return nullptr;
252 if (tex->mHeight == 0) {
255 return eve::image::Image::create()->newImageData(&bytes);
258 const unsigned w = tex->mWidth;
259 const unsigned h = tex->mHeight;
260 std::vector<uint8_t> rgba(
size_t(
w) *
size_t(
h) * 4);
261 const aiTexel *src = tex->pcData;
262 for (
unsigned i = 0; i <
w *
h; ++i) {
263 rgba[i * 4 + 0] = src[i].r;
264 rgba[i * 4 + 1] = src[i].g;
265 rgba[i * 4 + 2] = src[i].b;
266 rgba[i * 4 + 3] = src[i].a;
275 const aiMesh *
m = meshAt(meshIndex);
277 return static_cast<int>(
m->mNumAnimMeshes);
281 const aiMesh *
m = meshAt(meshIndex);
282 if (!
m || morphIndex < 0 ||
static_cast<unsigned>(morphIndex) >=
m->mNumAnimMeshes)
284 const aiAnimMesh *am =
m->mAnimMeshes[morphIndex];
286 if (am->mName.length)
287 return am->mName.C_Str();
288 return "morph" + std::to_string(morphIndex);
292 const aiMesh *
m = meshAt(meshIndex);
295 return m->HasBones() &&
m->mNumBones > 0;
299 const aiMesh *
m = meshAt(meshIndex);
301 throw eve::Exception(
"ModelData::getBoneCount: invalid mesh index");
302 return static_cast<int>(
m->mNumBones);
306 const aiMesh *
m = meshAt(meshIndex);
307 if (!
m || boneIndex < 0 ||
static_cast<unsigned>(boneIndex) >=
m->mNumBones)
309 const aiBone *
b =
m->mBones[boneIndex];
311 return b->mName.C_Str();
315 int elementIndex)
const {
316 const aiMesh *
m = meshAt(meshIndex);
318 throw eve::Exception(
"ModelData::getInverseBindMatrixElement: invalid mesh index");
319 if (boneIndex < 0 ||
static_cast<unsigned>(boneIndex) >=
m->mNumBones)
320 throw eve::Exception(
"ModelData::getInverseBindMatrixElement: invalid bone index");
321 if (elementIndex < 0 || elementIndex > 15)
322 throw eve::Exception(
"ModelData::getInverseBindMatrixElement: elementIndex must be 0..15");
323 const aiBone *
b =
m->mBones[boneIndex];
325 throw eve::Exception(
"ModelData::getInverseBindMatrixElement: null bone");
327 const aiMatrix4x4 &mat =
b->mOffsetMatrix;
328 const float rows[4][4] = {
329 {mat.a1, mat.a2, mat.a3, mat.a4},
330 {mat.b1, mat.b2, mat.b3, mat.b4},
331 {mat.c1, mat.c2, mat.c3, mat.c4},
332 {mat.d1, mat.d2, mat.d3, mat.d4},
334 const int col = elementIndex / 4;
335 const int row = elementIndex % 4;
336 return rows[row][col];
340 const aiMesh *
m = meshAt(meshIndex);
341 if (!
m || boneIndex < 0 ||
static_cast<unsigned>(boneIndex) >=
m->mNumBones)
343 const aiBone *
b =
m->mBones[boneIndex];
344 return b ?
static_cast<int>(
b->mNumWeights) : 0;
348 const aiMesh *
m = meshAt(meshIndex);
349 if (!
m || boneIndex < 0 ||
static_cast<unsigned>(boneIndex) >=
m->mNumBones)
351 const aiBone *
b =
m->mBones[boneIndex];
352 if (!
b || weightIndex < 0 ||
static_cast<unsigned>(weightIndex) >=
b->mNumWeights)
354 return static_cast<int>(
b->mWeights[weightIndex].mVertexId);
358 const aiMesh *
m = meshAt(meshIndex);
359 if (!
m || boneIndex < 0 ||
static_cast<unsigned>(boneIndex) >=
m->mNumBones)
361 const aiBone *
b =
m->mBones[boneIndex];
362 if (!
b || weightIndex < 0 ||
static_cast<unsigned>(weightIndex) >=
b->mNumWeights)
364 return b->mWeights[weightIndex].mWeight;
369 return sc ?
static_cast<int>(sc->mNumAnimations) : 0;
374 if (!sc || animIndex < 0 ||
static_cast<unsigned>(animIndex) >= sc->mNumAnimations)
376 const aiAnimation *
a = sc->mAnimations[animIndex];
379 return a->mName.C_Str();
380 return "anim" + std::to_string(animIndex);
Resource is a game object that is managed by the ResourceManager. It can be loaded from a file or gen...
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
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
float getMaterialBaseColorG(int matIndex) const
int getFaceCount(int meshIndex) const
bool hasNormals(int meshIndex) const
int getEmbeddedTextureHeight(int idx) const
0 means a compressed blob (use getEmbeddedTextureImageData to decode).
int getBoneCount(int meshIndex) const
int getBoneWeightCount(int meshIndex, int boneIndex) const
float getMaterialOpacity(int matIndex) const
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
int getEmbeddedTextureWidth(int idx) const
bool getMaterialTwoSided(int matIndex) const
int getAnimationCount() const
Scene-level animation clips (aiAnimation).
float getMaterialBaseColorA(int matIndex) const
const aiScene * getScene() const
float getBoneWeightValue(int meshIndex, int boneIndex, int weightIndex) const
std::string getMorphTargetName(int meshIndex, int morphIndex) const
std::string getAnimationName(int animIndex) const
bool hasTexCoords(int meshIndex) const
int getBoneWeightVertex(int meshIndex, int boneIndex, int weightIndex) const
image::ImageData * getEmbeddedTextureImageData(int idx) const
int getMaterialCount() const
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
bool hasBones(int meshIndex) const
Assimp skeletal skin data (aiBone / vertex weights) on a mesh.
const aiMesh * getMesh(int meshIndex) const
ModelData(medialoader::ModelScene scene, std::string uri="")
std::string getBoneName(int meshIndex, int boneIndex) const
int getVertexCount(int meshIndex) const
void adopt(eve::Resource &replacement) override
Replace this scene with replacement's (cache reload).
std::string getEmbeddedTextureName(int idx) const
int getMorphTargetCount(int meshIndex) const
Assimp morph / blend-shape targets on a mesh (aiAnimMesh).