载入中...
搜索中...
未找到
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 <cstdlib>
15#include <string>
16#include <utility>
17#include <vector>
18
19namespace eve {
20namespace model3d {
21
22namespace {
23
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;
32 // Assimp maps glTF occlusionTexture to LIGHTMAP.
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;
38}
39
40aiColor4D materialBaseColor(const aiMaterial *mat) {
41 aiColor4D c(1.f, 1.f, 1.f, 1.f);
42 if (!mat) return c;
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);
47 }
48 return c;
49}
50
51} // namespace
52
53ModelData::ModelData(medialoader::ModelScene scene, std::string uri)
54 : Resource(std::move(uri)), scene(std::move(scene)) {}
55
56ModelData::~ModelData() = default;
57
58void ModelData::adopt(eve::Resource &replacement) {
59 auto &other = static_cast<ModelData &>(replacement);
60 std::swap(scene, other.scene);
61}
62
63bool ModelData::empty() const { return scene.empty(); }
64
65const aiScene *ModelData::getScene() const {
66 if (scene.empty())
67 return nullptr;
68 return scene.get();
69}
70
71const aiMesh *ModelData::meshAt(int meshIndex) const {
72 const aiScene *sc = getScene();
73 if (!sc || meshIndex < 0 || static_cast<unsigned>(meshIndex) >= sc->mNumMeshes)
74 return nullptr;
75 return sc->mMeshes[meshIndex];
76}
77
78const aiMaterial *ModelData::materialAt(int matIndex) const {
79 const aiScene *sc = getScene();
80 if (!sc || matIndex < 0 || static_cast<unsigned>(matIndex) >= sc->mNumMaterials)
81 return nullptr;
82 return sc->mMaterials[matIndex];
83}
84
85const aiMesh *ModelData::getMesh(int meshIndex) const { return meshAt(meshIndex); }
86
88 const aiScene *sc = getScene();
89 return sc ? static_cast<int>(sc->mNumMeshes) : 0;
90}
91
93 const aiScene *sc = getScene();
94 return sc ? static_cast<int>(sc->mNumMaterials) : 0;
95}
96
97int ModelData::getVertexCount(int meshIndex) const {
98 const aiMesh *m = meshAt(meshIndex);
99 if (!m)
100 throw eve::Exception("ModelData::getVertexCount: invalid mesh index");
101 return static_cast<int>(m->mNumVertices);
102}
103
104int ModelData::getFaceCount(int meshIndex) const {
105 const aiMesh *m = meshAt(meshIndex);
106 if (!m)
107 throw eve::Exception("ModelData::getFaceCount: invalid mesh index");
108 return static_cast<int>(m->mNumFaces);
109}
110
111bool ModelData::hasNormals(int meshIndex) const {
112 const aiMesh *m = meshAt(meshIndex);
113 if (!m)
114 throw eve::Exception("ModelData::hasNormals: invalid mesh index");
115 return m->HasNormals();
116}
117
118bool ModelData::hasTexCoords(int meshIndex) const {
119 const aiMesh *m = meshAt(meshIndex);
120 if (!m)
121 throw eve::Exception("ModelData::hasTexCoords: invalid mesh index");
122 return m->HasTextureCoords(0);
123}
124
125int ModelData::getMaterialIndex(int meshIndex) const {
126 const aiMesh *m = meshAt(meshIndex);
127 return m ? static_cast<int>(m->mMaterialIndex) : -1;
128}
129
130std::string ModelData::getMaterialName(int matIndex) const {
131 const aiMaterial *mat = materialAt(matIndex);
132 if (!mat) return {};
133 aiString name;
134 if (mat->Get(AI_MATKEY_NAME, name) != AI_SUCCESS || name.length == 0)
135 return "material" + std::to_string(matIndex);
136 return name.C_Str();
137}
138
139float ModelData::getMaterialBaseColorR(int matIndex) const {
140 return materialBaseColor(materialAt(matIndex)).r;
141}
142
143float ModelData::getMaterialBaseColorG(int matIndex) const {
144 return materialBaseColor(materialAt(matIndex)).g;
145}
146
147float ModelData::getMaterialBaseColorB(int matIndex) const {
148 return materialBaseColor(materialAt(matIndex)).b;
149}
150
151float ModelData::getMaterialBaseColorA(int matIndex) const {
152 return materialBaseColor(materialAt(matIndex)).a;
153}
154
155float ModelData::getMaterialMetallicFactor(int matIndex) const {
156 const aiMaterial *mat = materialAt(matIndex);
157 if (!mat) return 0.f;
158 float metallic = 0.f;
159 if (mat->Get(AI_MATKEY_METALLIC_FACTOR, metallic) != AI_SUCCESS)
160 return 0.f;
161 return metallic;
162}
163
164float ModelData::getMaterialRoughnessFactor(int matIndex) const {
165 const aiMaterial *mat = materialAt(matIndex);
166 if (!mat) return 0.45f;
167 float roughness = 0.45f;
168 if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, roughness) != AI_SUCCESS)
169 return 0.45f;
170 return roughness;
171}
172
173float ModelData::getMaterialOpacity(int matIndex) const {
174 const aiMaterial *mat = materialAt(matIndex);
175 if (!mat) return 1.f;
176 float opacity = 1.f;
177 if (mat->Get(AI_MATKEY_OPACITY, opacity) != AI_SUCCESS)
178 return 1.f;
179 return opacity;
180}
181
182bool ModelData::getMaterialTwoSided(int matIndex) const {
183 const aiMaterial *mat = materialAt(matIndex);
184 if (!mat) return false;
185 int twoSided = 0;
186 if (mat->Get(AI_MATKEY_TWOSIDED, twoSided) != AI_SUCCESS)
187 return false;
188 return twoSided != 0;
189}
190
191int ModelData::getMaterialTextureSlotCount(int matIndex, const std::string &type) const {
192 const aiMaterial *mat = materialAt(matIndex);
193 if (!mat) return 0;
194 const aiTextureType texType = textureTypeFromName(type);
195 if (texType == aiTextureType_NONE) return 0;
196 return static_cast<int>(mat->GetTextureCount(texType));
197}
198
199std::string ModelData::getMaterialTexturePath(int matIndex, const std::string &type,
200 int slot) const {
201 const aiMaterial *mat = materialAt(matIndex);
202 if (!mat || slot < 0) return {};
203 const aiTextureType texType = textureTypeFromName(type);
204 if (texType == aiTextureType_NONE) return {};
205 aiString path;
206 if (mat->GetTexture(texType, static_cast<unsigned>(slot), &path) != AI_SUCCESS)
207 return {};
208 return path.C_Str();
209}
210
211int ModelData::getMaterialTextureEmbeddedIndex(int matIndex, const std::string &type,
212 int slot) const {
213 const std::string path = getMaterialTexturePath(matIndex, type, slot);
214 if (path.empty() || path[0] != '*') return -1;
215 return std::atoi(path.c_str() + 1);
216}
217
219 const aiScene *sc = getScene();
220 return sc ? static_cast<int>(sc->mNumTextures) : 0;
221}
222
224 const aiScene *sc = getScene();
225 if (!sc || idx < 0 || static_cast<unsigned>(idx) >= sc->mNumTextures) return {};
226 const aiTexture *tex = sc->mTextures[idx];
227 if (!tex) return {};
228 if (tex->mFilename.length) return tex->mFilename.C_Str();
229 return "texture" + std::to_string(idx);
230}
231
233 const aiScene *sc = getScene();
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;
237}
238
240 const aiScene *sc = getScene();
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;
244}
245
247 const aiScene *sc = getScene();
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;
251 try {
252 if (tex->mHeight == 0) {
253 // Compressed blob (PNG/JPEG/...) — decode through the image module.
254 eve::data::ByteData bytes(tex->pcData, static_cast<size_t>(tex->mWidth));
255 return eve::image::Image::create()->newImageData(&bytes);
256 }
257 // Uncompressed BGRA8888 texels — convert to RGBA8.
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;
267 }
268 return new eve::image::ImageData(int(w), int(h), "RGBA8", rgba.data(), false);
269 } catch (...) {
270 return nullptr;
271 }
272}
273
274int ModelData::getMorphTargetCount(int meshIndex) const {
275 const aiMesh *m = meshAt(meshIndex);
276 if (!m) return 0;
277 return static_cast<int>(m->mNumAnimMeshes);
278}
279
280std::string ModelData::getMorphTargetName(int meshIndex, int morphIndex) const {
281 const aiMesh *m = meshAt(meshIndex);
282 if (!m || morphIndex < 0 || static_cast<unsigned>(morphIndex) >= m->mNumAnimMeshes)
283 return {};
284 const aiAnimMesh *am = m->mAnimMeshes[morphIndex];
285 if (!am) return {};
286 if (am->mName.length)
287 return am->mName.C_Str();
288 return "morph" + std::to_string(morphIndex);
289}
290
291bool ModelData::hasBones(int meshIndex) const {
292 const aiMesh *m = meshAt(meshIndex);
293 if (!m)
294 throw eve::Exception("ModelData::hasBones: invalid mesh index");
295 return m->HasBones() && m->mNumBones > 0;
296}
297
298int ModelData::getBoneCount(int meshIndex) const {
299 const aiMesh *m = meshAt(meshIndex);
300 if (!m)
301 throw eve::Exception("ModelData::getBoneCount: invalid mesh index");
302 return static_cast<int>(m->mNumBones);
303}
304
305std::string ModelData::getBoneName(int meshIndex, int boneIndex) const {
306 const aiMesh *m = meshAt(meshIndex);
307 if (!m || boneIndex < 0 || static_cast<unsigned>(boneIndex) >= m->mNumBones)
308 return {};
309 const aiBone *b = m->mBones[boneIndex];
310 if (!b) return {};
311 return b->mName.C_Str();
312}
313
314float ModelData::getInverseBindMatrixElement(int meshIndex, int boneIndex,
315 int elementIndex) const {
316 const aiMesh *m = meshAt(meshIndex);
317 if (!m)
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];
324 if (!b)
325 throw eve::Exception("ModelData::getInverseBindMatrixElement: null bone");
326 // Assimp row-major → column-major element.
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},
333 };
334 const int col = elementIndex / 4;
335 const int row = elementIndex % 4;
336 return rows[row][col];
337}
338
339int ModelData::getBoneWeightCount(int meshIndex, int boneIndex) const {
340 const aiMesh *m = meshAt(meshIndex);
341 if (!m || boneIndex < 0 || static_cast<unsigned>(boneIndex) >= m->mNumBones)
342 return 0;
343 const aiBone *b = m->mBones[boneIndex];
344 return b ? static_cast<int>(b->mNumWeights) : 0;
345}
346
347int ModelData::getBoneWeightVertex(int meshIndex, int boneIndex, int weightIndex) const {
348 const aiMesh *m = meshAt(meshIndex);
349 if (!m || boneIndex < 0 || static_cast<unsigned>(boneIndex) >= m->mNumBones)
350 return -1;
351 const aiBone *b = m->mBones[boneIndex];
352 if (!b || weightIndex < 0 || static_cast<unsigned>(weightIndex) >= b->mNumWeights)
353 return -1;
354 return static_cast<int>(b->mWeights[weightIndex].mVertexId);
355}
356
357float ModelData::getBoneWeightValue(int meshIndex, int boneIndex, int weightIndex) const {
358 const aiMesh *m = meshAt(meshIndex);
359 if (!m || boneIndex < 0 || static_cast<unsigned>(boneIndex) >= m->mNumBones)
360 return 0.f;
361 const aiBone *b = m->mBones[boneIndex];
362 if (!b || weightIndex < 0 || static_cast<unsigned>(weightIndex) >= b->mNumWeights)
363 return 0.f;
364 return b->mWeights[weightIndex].mWeight;
365}
366
368 const aiScene *sc = getScene();
369 return sc ? static_cast<int>(sc->mNumAnimations) : 0;
370}
371
372std::string ModelData::getAnimationName(int animIndex) const {
373 const aiScene *sc = getScene();
374 if (!sc || animIndex < 0 || static_cast<unsigned>(animIndex) >= sc->mNumAnimations)
375 return {};
376 const aiAnimation *a = sc->mAnimations[animIndex];
377 if (!a) return {};
378 if (a->mName.length)
379 return a->mName.C_Str();
380 return "anim" + std::to_string(animIndex);
381}
382
383} // namespace model3d
384} // namespace eve
std::string type
int h
int w
uint32_t a
uint32_t b
uint32_t c
float roughness
float metallic
int idx
const char * name
Definition RockMesh.cpp:21
float m[16]
Resource is a game object that is managed by the ResourceManager. It can be loaded from a file or gen...
Definition Resource.h:35
In-memory byte buffer implementing eve::Data (ref-counted).
Definition ByteData.h:11
Represents raw pixel data.
Definition ImageData.h:26
CPU-side decoded 3D model (Assimp scene owned via medialoader::ModelScene). Does not upload to GPU — ...
Definition ModelData.h:23
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
Definition ModelData.cpp:65
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
Definition ModelData.cpp:92
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
Definition ModelData.cpp:85
ModelData(medialoader::ModelScene scene, std::string uri="")
Definition ModelData.cpp:53
std::string getBoneName(int meshIndex, int boneIndex) const
int getVertexCount(int meshIndex) const
Definition ModelData.cpp:97
void adopt(eve::Resource &replacement) override
Replace this scene with replacement's (cache reload).
Definition ModelData.cpp:58
std::string getEmbeddedTextureName(int idx) const
int getMorphTargetCount(int meshIndex) const
Assimp morph / blend-shape targets on a mesh (aiAnimMesh).
Definition Build.cpp:11