framework2
1288 строк · 38.0 Кб
1#include "ofxAssimpModelLoader.h"
2#include "ofxAssimpUtils.h"
3#include "ofLight.h"
4#include "ofImage.h"
5#include "ofPixels.h"
6#include "ofGraphics.h"
7#include "ofConstants.h"
8
9#include <assimp/cimport.h>
10#include <assimp/scene.h>
11#include <assimp/postprocess.h>
12#include <assimp/config.h>
13#include <assimp/DefaultLogger.hpp>
14
15using std::shared_ptr;
16using std::vector;
17
18ofxAssimpModelLoader::ofxAssimpModelLoader(){
19clear();
20}
21
22ofxAssimpModelLoader::~ofxAssimpModelLoader(){
23clear();
24}
25
26
27// DEPRECATED
28bool ofxAssimpModelLoader::load(string modelName, bool optimize){
29int optimizeFlags = OPTIMIZE_DEFAULT;
30if( optimize ){
31optimizeFlags = OPTIMIZE_HIGH;
32}
33return load(modelName, optimizeFlags);
34}
35
36// DEPRECATED
37bool ofxAssimpModelLoader::load(ofBuffer & buffer, bool optimize, const char * extension){
38int optimizeFlags = OPTIMIZE_DEFAULT;
39if( optimize ){
40optimizeFlags = OPTIMIZE_HIGH;
41}
42return load(buffer, optimizeFlags, extension);
43}
44
45// DEPRECATED
46bool ofxAssimpModelLoader::loadModel(string modelName, bool optimize){
47int optimizeFlags = OPTIMIZE_DEFAULT;
48if( optimize ){
49optimizeFlags = OPTIMIZE_HIGH;
50}
51return load(modelName, optimizeFlags);
52}
53
54// DEPRECATED
55bool ofxAssimpModelLoader::loadModel(ofBuffer & buffer, bool optimize, const char * extension){
56int optimizeFlags = OPTIMIZE_DEFAULT;
57if( optimize ){
58optimizeFlags = OPTIMIZE_HIGH;
59}
60return load(buffer, optimizeFlags, extension);
61}
62
63//------------------------------------------
64bool ofxAssimpModelLoader::load(string modelName, int assimpOptimizeFlags){
65
66file.open(modelName, ofFile::ReadOnly, true); // Since it may be a binary file we should read it in binary -Ed
67if(!file.exists()) {
68ofLogVerbose("ofxAssimpModelLoader") << "load(): model does not exist: \"" << modelName << "\"";
69return false;
70}
71
72ofLogVerbose("ofxAssimpModelLoader") << "load(): loading \"" << file.getFileName()
73<< "\" from \"" << file.getEnclosingDirectory() << "\"";
74
75if(scene.get() != nullptr){
76clear();
77// we reset the shared_ptr explicitly here, to force the old
78// aiScene to be deleted **before** a new aiScene is created.
79scene.reset();
80}
81
82// sets various properties & flags to a default preference
83unsigned int flags = initImportProperties(assimpOptimizeFlags);
84
85// //enable assimp logging based on ofGetLogLevel
86// if( ofGetLogLevel() < OF_LOG_NOTICE ){
87// auto logLevel = Assimp::DefaultLogger::LogSeverity::VERBOSE;
88// Assimp::DefaultLogger::create(ASSIMP_DEFAULT_LOG_NAME, logLevel, aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_STDOUT );
89// }
90
91// loads scene from file
92std::string path = file.getAbsolutePath();
93const aiScene * scenePtr = importer.ReadFile(path.c_str(), flags);
94
95//this is funky but the scenePtr is managed by assimp and so we can't put it in our shared_ptr without disabling the deleter with: [](const aiScene*){}
96scene = shared_ptr<const aiScene>(scenePtr,[](const aiScene*){});
97
98bool bOk = processScene();
99return bOk;
100}
101
102
103bool ofxAssimpModelLoader::load(ofBuffer & buffer, int assimpOptimizeFlags, const char * extension){
104
105ofLogVerbose("ofxAssimpModelLoader") << "load(): loading from memory buffer \"." << extension << "\"";
106
107if(scene.get() != nullptr){
108clear();
109// we reset the shared_ptr explicitly here, to force the old
110// aiScene to be deleted **before** a new aiScene is created.
111scene.reset();
112}
113
114// sets various properties & flags to a default preference
115unsigned int flags = initImportProperties(assimpOptimizeFlags);
116
117// //enable assimp logging based on ofGetLogLevel
118// if( ofGetLogLevel() < OF_LOG_NOTICE ){
119// auto logLevel = Assimp::DefaultLogger::LogSeverity::VERBOSE;
120// Assimp::DefaultLogger::create(ASSIMP_DEFAULT_LOG_NAME, logLevel, aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_STDOUT );
121// }
122
123// loads scene from memory buffer - note this will not work for multipart files (obj, md3, etc)
124const aiScene * scenePtr = importer.ReadFileFromMemory(buffer.getData(), buffer.size(), flags, extension);
125
126// this is funky but the scenePtr is managed by assimp and so we can't put it in our shared_ptr without disabling the deleter with: [](const aiScene*){}
127scene = shared_ptr<const aiScene>(scenePtr,[](const aiScene*){});
128
129bool bOk = processScene();
130return bOk;
131}
132
133unsigned int ofxAssimpModelLoader::initImportProperties(int assimpOptimizeFlags) {
134store.reset(aiCreatePropertyStore(), aiReleasePropertyStore);
135
136// only ever give us triangles.
137aiSetImportPropertyInteger(store.get(), AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT );
138aiSetImportPropertyInteger(store.get(), AI_CONFIG_PP_PTV_NORMALIZE, true);
139
140unsigned int flags = assimpOptimizeFlags;
141
142if( flags <= OPTIMIZE_HIGH ){
143
144if( flags == OPTIMIZE_NONE ){
145flags = 0;
146}else if( flags == OPTIMIZE_DEFAULT || flags == OPTIMIZE_HIGH ){
147flags = aiProcess_CalcTangentSpace | \
148aiProcess_GenSmoothNormals | \
149aiProcess_JoinIdenticalVertices | \
150aiProcess_ImproveCacheLocality | \
151aiProcess_LimitBoneWeights | \
152aiProcess_RemoveRedundantMaterials | \
153aiProcess_SplitLargeMeshes | \
154aiProcess_Triangulate | \
155aiProcess_GenUVCoords | \
156aiProcess_SortByPType | \
157aiProcess_FindDegenerates | \
158aiProcess_FindInstances | \
159aiProcess_OptimizeMeshes;
160}
161
162if( flags == OPTIMIZE_HIGH ){
163flags |= aiProcess_OptimizeGraph | \
164aiProcess_FindInstances | \
165aiProcess_ValidateDataStructure;
166}
167
168// this fixes things for OF both tex uvs and model not flipped in z
169flags |= aiProcess_ConvertToLeftHanded;
170}
171
172return flags;
173}
174
175bool ofxAssimpModelLoader::processScene() {
176
177normalizeFactor = ofGetWidth() / 2.0;
178
179if(scene){
180loadGLResources();
181update();
182calculateDimensions();
183
184if(getAnimationCount())
185ofLogVerbose("ofxAssimpModelLoader") << "load(): scene has " << getAnimationCount() << "animations";
186else {
187ofLogVerbose("ofxAssimpModelLoader") << "load(): no animations";
188}
189
190return true;
191}else{
192ofLogError("ofxAssimpModelLoader") << "load(): " + (string) aiGetErrorString();
193clear();
194return false;
195}
196
197return false;
198}
199
200
201//-------------------------------------------
202void ofxAssimpModelLoader::createEmptyModel(){
203if(scene){
204clear();
205scene.reset();
206}
207}
208
209
210void ofxAssimpModelLoader::setNormalizationFactor(float factor){
211normalizeFactor = factor;
212}
213
214//-------------------------------------------
215void ofxAssimpModelLoader::calculateDimensions(){
216if(!scene) return;
217ofLogVerbose("ofxAssimpModelLoader") << "calculateDimensions(): inited scene with "
218<< scene->mNumMeshes << " meshes & " << scene->mNumAnimations << " animations";
219
220getBoundingBoxWithMinVector(&scene_min, &scene_max);
221scene_center.x = (scene_min.x + scene_max.x) / 2.0f;
222scene_center.y = (scene_min.y + scene_max.y) / 2.0f;
223scene_center.z = (scene_min.z + scene_max.z) / 2.0f;
224
225// optional normalized scaling
226normalizedScale = scene_max.x-scene_min.x;
227normalizedScale = std::max(double(scene_max.y - scene_min.y), normalizedScale);
228normalizedScale = std::max(double(scene_max.z - scene_min.z), normalizedScale);
229if (fabs(normalizedScale) < std::numeric_limits<float>::epsilon()){
230ofLogWarning("ofxAssimpModelLoader") << "Error calculating normalized scale of scene" << std::endl;
231normalizedScale = 1.0;
232} else {
233normalizedScale = 1.f / normalizedScale;
234normalizedScale *= normalizeFactor;
235}
236
237updateModelMatrix();
238}
239
240//-------------------------------------------
241static GLint getGLFormatFromAiFormat(const char * aiFormat){
242
243std::string formatStr(aiFormat);
244
245if( formatStr.size() >= 8 ){
246if( formatStr.substr(0, 4) == "rgba" ){
247if(formatStr.substr(4,4) == "8888"){
248return GL_RGBA;
249}else if(formatStr.substr(4,4) == "8880"){
250return GL_RGB;
251}
252}else{
253ofLogError("getGLFormatFromAiFormat") << " can't parse format " << formatStr;
254}
255}
256
257ofLogWarning("getGLFormatFromAiFormat") << " can't parse format " << formatStr << " returning GL_RGB";
258return GL_RGB;
259}
260
261//-------------------------------------------
262void ofxAssimpModelLoader::createLightsFromAiModel(){
263lights.clear();
264lights.resize(scene->mNumLights);
265for(unsigned int i = 0; i < scene->mNumLights; i++){
266lights[i].enable();
267if(scene->mLights[i]->mType==aiLightSource_DIRECTIONAL){
268lights[i].setDirectional();
269lights[i].setOrientation(aiVecToOfVec(scene->mLights[i]->mDirection));
270}
271if(scene->mLights[i]->mType!=aiLightSource_POINT){
272lights[i].setSpotlight();
273lights[i].setPosition(aiVecToOfVec(scene->mLights[i]->mPosition));
274}
275lights[i].setAmbientColor(aiColorToOfColor(scene->mLights[i]->mColorAmbient));
276lights[i].setDiffuseColor(aiColorToOfColor(scene->mLights[i]->mColorDiffuse));
277lights[i].setSpecularColor(aiColorToOfColor(scene->mLights[i]->mColorSpecular));
278}
279}
280
281//-------------------------------------------
282void ofxAssimpModelLoader::optimizeScene(){
283aiApplyPostProcessing(scene.get(),aiProcess_ImproveCacheLocality | aiProcess_OptimizeGraph |
284aiProcess_OptimizeMeshes | aiProcess_JoinIdenticalVertices |
285aiProcess_RemoveRedundantMaterials);
286}
287
288#ifndef TARGET_WIN32
289//this is a hack to allow for weak definations of functions that might not exist in older assimp versions
290const char *aiTextureTypeToString(enum aiTextureType in)__attribute__((weak));
291#endif
292
293//-------------------------------------------
294void ofxAssimpModelLoader::loadGLResources(){
295
296ofLogVerbose("ofxAssimpModelLoader") << "loadGLResources(): starting";
297
298// we do this as we have textures and vbos and in meshHelper and we don't want to copy them
299// this should really be a vector of shared_ptr's but keeping it as objects for legacy reasons
300modelMeshes.clear();
301modelMeshes.reserve(scene->mNumMeshes);
302
303// create OpenGL buffers and populate them based on each meshes pertinant info.
304for (unsigned int i = 0; i < scene->mNumMeshes; ++i){
305ofLogVerbose("ofxAssimpModelLoader") << "loadGLResources(): loading mesh " << i;
306// current mesh we are introspecting
307aiMesh* mesh = scene->mMeshes[i];
308
309// the current meshHelper we will be populating data into.
310modelMeshes.push_back(ofxAssimpMeshHelper());
311ofxAssimpMeshHelper & meshHelper = modelMeshes[i];
312//ofxAssimpMeshHelper meshHelper;
313
314//meshHelper.texture = NULL;
315
316// Handle material info
317aiMaterial* mtl = scene->mMaterials[mesh->mMaterialIndex];
318aiColor4D tcolor;
319
320if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_DIFFUSE, &tcolor)){
321auto col = aiColorToOfColor(tcolor);
322meshHelper.material.setDiffuseColor(col);
323}
324
325if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_SPECULAR, &tcolor)){
326auto col = aiColorToOfColor(tcolor);
327meshHelper.material.setSpecularColor(col);
328}
329
330if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_AMBIENT, &tcolor)){
331auto col = aiColorToOfColor(tcolor);
332meshHelper.material.setAmbientColor(col);
333}
334
335if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_EMISSIVE, &tcolor)){
336auto col = aiColorToOfColor(tcolor);
337meshHelper.material.setEmissiveColor(col);
338}
339
340float shininess;
341if(AI_SUCCESS == aiGetMaterialFloat(mtl, AI_MATKEY_SHININESS, &shininess)){
342meshHelper.material.setShininess(shininess);
343}
344
345int blendMode;
346if(AI_SUCCESS == aiGetMaterialInteger(mtl, AI_MATKEY_BLEND_FUNC, &blendMode)){
347if(blendMode==aiBlendMode_Default){
348meshHelper.blendMode=OF_BLENDMODE_ALPHA;
349}else{
350meshHelper.blendMode=OF_BLENDMODE_ADD;
351}
352}
353
354// Culling
355unsigned int max = 1;
356int two_sided=0;
357if((AI_SUCCESS == aiGetMaterialIntegerArray(mtl, AI_MATKEY_TWOSIDED, &two_sided, &max)) && two_sided){
358meshHelper.twoSided = true;
359ofLogVerbose("ofxAssimpModelLoader::loadGLResources") <<": mesh is two sided";
360}else{
361meshHelper.twoSided = false;
362ofLogVerbose("ofxAssimpModelLoader::loadGLResources") <<": mesh is one sided";
363}
364
365// Load Textures
366int texIndex = 0;
367aiString texPath;
368
369//this gets the wrapping in u and v coodinates
370//we don't support different wrapping in OF so just read u
371aiTextureMapMode texMapMode[2];
372
373for(int d = 0; d <= AI_TEXTURE_TYPE_MAX; d++){
374if(AI_SUCCESS == mtl->GetTexture((aiTextureType)d, texIndex, &texPath, NULL, NULL, NULL, NULL, &texMapMode[0])){
375
376//this is a solution to support older versions of assimp. see the weak defination above
377if( aiTextureTypeToString ){
378ofLogVerbose("ofxAssimpModelLoader") << "loadGLResource(): loading " << aiTextureTypeToString((aiTextureType)d) << " image from \"" << texPath.data << "\"";
379}
380
381bool bWrap = (texMapMode[0]==aiTextureMapMode_Wrap);
382
383std::string texPathStr = texPath.C_Str();
384
385//deal with Blender putting "//" in front of local file paths
386if( texPathStr.size() > 2 && texPathStr.substr(0, 2) == "//" ){
387texPathStr = texPathStr.substr(2, texPathStr.size()-2);
388}
389
390//glb embedded texture file starts with *0
391bool bTryEmbed = false;
392if( texPathStr.size() >= 2 && texPathStr[0] == '*'){
393bTryEmbed = true;
394}
395
396//stuff for embedded textures
397auto ogPath = texPathStr;
398bool bHasEmbeddedTexture = false;
399
400auto modelFolder = ofFilePath::getEnclosingDirectory( file.path() );
401auto relTexPath = ofFilePath::getEnclosingDirectory(texPathStr,false);
402auto realPath = modelFolder / of::filesystem::path{ texPathStr };
403
404
405#ifndef TARGET_LINUX_ARM
406if(bTryEmbed || ofFile::doesFileExist(realPath) == false) {
407auto embeddedTexture = scene->GetEmbeddedTexture(ogPath.c_str());
408if( embeddedTexture ){
409bHasEmbeddedTexture = true;
410ofLogVerbose("ofxAssimpModelLoader") << "loadGLResource() texture " << realPath.filename() << " is embedded ";
411}else{
412ofLogError("ofxAssimpModelLoader") << "loadGLResource(): texture doesn't exist: \""
413<< file.getFileName() + "\" in \"" << realPath.string() << "\"";
414}
415}
416#endif
417
418bool bTextureAlreadyExists = false;
419if(textures.count(realPath)){
420bTextureAlreadyExists = true;
421}
422
423if(bTextureAlreadyExists) {
424ofxAssimpTexture assimpTexture;
425
426assimpTexture.setup(*textures[realPath].get(), realPath, bWrap);
427assimpTexture.setTextureType((aiTextureType)d);
428meshHelper.addTexture(assimpTexture);
429
430ofLogVerbose("ofxAssimpModelLoader") << "loadGLResource(): texture already loaded: \""
431<< file.getFileName() + "\" from \"" << realPath.string() << "\"" << " adding texture as " << assimpTexture.getTextureTypeAsString() ;
432} else {
433
434shared_ptr<ofTexture> texture = std::make_shared<ofTexture>();
435
436if( bHasEmbeddedTexture ){
437
438#ifndef TARGET_LINUX_ARM
439auto embeddedTexture = scene->GetEmbeddedTexture(ogPath.c_str());
440
441//compressed texture
442if( embeddedTexture->mHeight == 0 && embeddedTexture->mWidth > 0){
443ofImage tmp;
444ofBuffer buffer;
445buffer.set((char *)embeddedTexture->pcData, embeddedTexture->mWidth);
446
447tmp.setUseTexture(false);
448tmp.load(buffer);
449
450ofLogVerbose("ofxAssimpModelLoader") << "loadGLResource() texture size is " << tmp.getWidth() << "x" << tmp.getHeight();
451
452texture->loadData(tmp.getPixels());
453}else{
454//uncompressed texture - might need swizzling from argb to rgba?
455auto glFormat = getGLFormatFromAiFormat(embeddedTexture->achFormatHint);
456texture->loadData((const uint8_t *)embeddedTexture->pcData, embeddedTexture->mWidth, embeddedTexture->mHeight, glFormat);
457}
458#endif
459}else{
460ofLoadImage(*texture.get(), realPath);
461}
462
463if(texture && texture->isAllocated()){
464ofxAssimpTexture tmpTex;
465tmpTex.setup(*texture.get(), realPath, bWrap);
466tmpTex.setTextureType((aiTextureType)d);
467textures[realPath] = texture;
468
469tmpTex.setTextureType((aiTextureType)d);
470meshHelper.addTexture( tmpTex );
471
472ofLogVerbose("ofxAssimpModelLoader") << "loadGLResource(): texture " << tmpTex.getTextureTypeAsString() << " loaded, dimensions: " << texture->getWidth() << "x" << texture->getHeight();
473}else{
474ofLogError("ofxAssimpModelLoader") << "loadGLResource(): couldn't load texture: \""
475<< file.getFileName() + "\" from \"" << realPath.string() << "\"";
476}
477}
478}
479}
480
481meshHelper.mesh = mesh;
482aiMeshToOfMesh(mesh, meshHelper.cachedMesh, &meshHelper);
483meshHelper.cachedMesh.setMode(OF_PRIMITIVE_TRIANGLES);
484meshHelper.validCache = true;
485meshHelper.hasChanged = false;
486
487int numOfAnimations = scene->mNumAnimations;
488for (int i = 0; i<numOfAnimations; i++) {
489aiAnimation * animation = scene->mAnimations[i];
490animations.push_back(ofxAssimpAnimation(scene, animation));
491}
492
493if(hasAnimations()){
494meshHelper.animatedPos.resize(mesh->mNumVertices);
495if(mesh->HasNormals()){
496meshHelper.animatedNorm.resize(mesh->mNumVertices);
497}
498}
499
500
501int usage;
502if(getAnimationCount()){
503#ifndef TARGET_OPENGLES
504if(!ofIsGLProgrammableRenderer()){
505usage = GL_STATIC_DRAW;
506}else{
507usage = GL_STREAM_DRAW;
508}
509#else
510usage = GL_DYNAMIC_DRAW;
511#endif
512}else{
513usage = GL_STATIC_DRAW;
514}
515
516meshHelper.vbo.setVertexData(&mesh->mVertices[0].x,3,mesh->mNumVertices,usage,sizeof(aiVector3D));
517if(mesh->HasVertexColors(0)){
518meshHelper.vbo.setColorData(&mesh->mColors[0][0].r,mesh->mNumVertices,GL_STATIC_DRAW,sizeof(aiColor4D));
519}
520if(mesh->HasNormals()){
521meshHelper.vbo.setNormalData(&mesh->mNormals[0].x,mesh->mNumVertices,usage,sizeof(aiVector3D));
522}
523if (meshHelper.cachedMesh.hasTexCoords()){
524meshHelper.vbo.setTexCoordData(&meshHelper.cachedMesh.getTexCoords()[0].x, mesh->mNumVertices,GL_STATIC_DRAW,sizeof(glm::vec2));
525}
526
527meshHelper.indices.resize(mesh->mNumFaces * 3);
528int j=0;
529for (unsigned int x = 0; x < mesh->mNumFaces; ++x){
530for (unsigned int a = 0; a < mesh->mFaces[x].mNumIndices; ++a){
531meshHelper.indices[j++]=mesh->mFaces[x].mIndices[a];
532}
533}
534
535meshHelper.vbo.setIndexData(&meshHelper.indices[0],meshHelper.indices.size(),GL_STATIC_DRAW);
536
537//modelMeshes.push_back(meshHelper);
538}
539ofLogVerbose("ofxAssimpModelLoader") << "loadGLResource(): finished";
540}
541
542//-------------------------------------------
543void ofxAssimpModelLoader::clear(){
544
545if( scene ){
546scene.reset();
547importer.FreeScene();
548}
549
550ofLogVerbose("ofxAssimpModelLoader") << "clear(): deleting GL resources";
551
552// clear out everything.
553modelMeshes.clear();
554animations.clear();
555pos = glm::vec3(0,0,0);
556scale = glm::vec3(1,1,1);
557rotAngle.clear();
558rotAxis.clear();
559lights.clear();
560
561scale = glm::vec3(1, 1, 1);
562normalizeScale = true;
563bUsingMaterials = true;
564bUsingNormals = true;
565bUsingTextures = true;
566bUsingColors = true;
567
568currentAnimation = -1;
569
570textures.clear();
571
572updateModelMatrix();
573}
574
575//------------------------------------------- update.
576void ofxAssimpModelLoader::update() {
577if(!scene) return;
578updateAnimations();
579updateMeshes(scene->mRootNode, glm::mat4());
580if(hasAnimations() == false) {
581return;
582}
583updateBones();
584updateGLResources();
585}
586
587void ofxAssimpModelLoader::updateAnimations() {
588for(size_t i = 0; i < animations.size(); i++) {
589animations[i].update();
590}
591}
592
593void ofxAssimpModelLoader::updateMeshes(aiNode * node, glm::mat4 parentMatrix) {
594
595aiMatrix4x4 m = node->mTransformation;
596m.Transpose();
597ofMatrix4x4 matrix(m.a1, m.a2, m.a3, m.a4,
598m.b1, m.b2, m.b3, m.b4,
599m.c1, m.c2, m.c3, m.c4,
600m.d1, m.d2, m.d3, m.d4);
601matrix *= parentMatrix;
602
603for(unsigned int i = 0; i < node->mNumMeshes; i++) {
604int meshIndex = node->mMeshes[i];
605ofxAssimpMeshHelper & mesh = modelMeshes[meshIndex];
606mesh.matrix = matrix;
607}
608
609for(unsigned int i = 0; i < node->mNumChildren; i++) {
610updateMeshes(node->mChildren[i], matrix);
611}
612}
613
614void ofxAssimpModelLoader::updateBones() {
615if (!hasAnimations()){
616return;
617}
618// update mesh position for the animation
619for(size_t i = 0; i < modelMeshes.size(); ++i) {
620// current mesh we are introspecting
621const aiMesh* mesh = modelMeshes[i].mesh;
622
623// calculate bone matrices
624vector<aiMatrix4x4> boneMatrices(mesh->mNumBones);
625for(unsigned int a = 0; a < mesh->mNumBones; ++a) {
626const aiBone* bone = mesh->mBones[a];
627
628// find the corresponding node by again looking recursively through the node hierarchy for the same name
629aiNode* node = scene->mRootNode->FindNode(bone->mName);
630
631// start with the mesh-to-bone matrix
632boneMatrices[a] = bone->mOffsetMatrix;
633// and now append all node transformations down the parent chain until we're back at mesh coordinates again
634const aiNode* tempNode = node;
635while(tempNode) {
636// check your matrix multiplication order here!!!
637boneMatrices[a] = tempNode->mTransformation * boneMatrices[a];
638// boneMatrices[a] = boneMatrices[a] * tempNode->mTransformation;
639tempNode = tempNode->mParent;
640}
641modelMeshes[i].hasChanged = true;
642modelMeshes[i].validCache = false;
643}
644
645modelMeshes[i].animatedPos.assign(modelMeshes[i].animatedPos.size(), aiVector3D(0.0f));
646if(mesh->HasNormals()){
647modelMeshes[i].animatedNorm.assign(modelMeshes[i].animatedNorm.size(), aiVector3D(0.0f));
648}
649// loop through all vertex weights of all bones
650for(unsigned int a = 0; a < mesh->mNumBones; ++a) {
651const aiBone* bone = mesh->mBones[a];
652const aiMatrix4x4& posTrafo = boneMatrices[a];
653
654for(unsigned int b = 0; b < bone->mNumWeights; ++b) {
655const aiVertexWeight& weight = bone->mWeights[b];
656
657size_t vertexId = weight.mVertexId;
658const aiVector3D& srcPos = mesh->mVertices[vertexId];
659
660modelMeshes[i].animatedPos[vertexId] += weight.mWeight * (posTrafo * srcPos);
661}
662if(mesh->HasNormals()){
663// 3x3 matrix, contains the bone matrix without the translation, only with rotation and possibly scaling
664aiMatrix3x3 normTrafo = aiMatrix3x3( posTrafo);
665for(unsigned int b = 0; b < bone->mNumWeights; ++b) {
666const aiVertexWeight& weight = bone->mWeights[b];
667size_t vertexId = weight.mVertexId;
668
669const aiVector3D& srcNorm = mesh->mNormals[vertexId];
670modelMeshes[i].animatedNorm[vertexId] += weight.mWeight * (normTrafo * srcNorm);
671}
672}
673}
674}
675}
676
677void ofxAssimpModelLoader::updateGLResources(){
678// now upload the result position and normal along with the other vertex attributes into a dynamic vertex buffer, VBO or whatever
679for (unsigned int i = 0; i < modelMeshes.size(); ++i){
680if(modelMeshes[i].hasChanged){
681const aiMesh* mesh = modelMeshes[i].mesh;
682if(hasAnimations()){
683modelMeshes[i].vbo.updateVertexData(&modelMeshes[i].animatedPos[0].x,mesh->mNumVertices);
684if(mesh->HasNormals()){
685modelMeshes[i].vbo.updateNormalData(&modelMeshes[i].animatedNorm[0].x,mesh->mNumVertices);
686}
687}
688modelMeshes[i].hasChanged = false;
689}
690}
691}
692
693void ofxAssimpModelLoader::updateModelMatrix() {
694modelMatrix = glm::identity<glm::mat4>();
695modelMatrix = glm::translate(modelMatrix, toGlm(pos));
696modelMatrix = glm::rotate(modelMatrix, ofDegToRad(180), glm::vec3(0,0,1));
697
698if(normalizeScale) {
699modelMatrix = glm::scale(modelMatrix, glm::vec3(normalizedScale, normalizedScale, normalizedScale));
700}
701
702for(size_t i = 0; i < rotAngle.size(); i++){
703modelMatrix = glm::rotate(modelMatrix, ofDegToRad(rotAngle[i]), glm::vec3(rotAxis[i].x, rotAxis[i].y, rotAxis[i].z));
704}
705
706modelMatrix = glm::scale(modelMatrix, toGlm(scale));
707}
708
709//------------------------------------------- animations.
710bool ofxAssimpModelLoader::hasAnimations() {
711return animations.size() > 0;
712}
713
714unsigned int ofxAssimpModelLoader::getAnimationCount(){
715return animations.size();
716}
717
718ofxAssimpAnimation & ofxAssimpModelLoader::getAnimation(int animationIndex) {
719animationIndex = ofClamp(animationIndex, 0, animations.size()-1);
720return animations[animationIndex];
721}
722
723void ofxAssimpModelLoader::playAllAnimations() {
724for(size_t i = 0; i < animations.size(); i++) {
725animations[i].play();
726}
727}
728
729void ofxAssimpModelLoader::stopAllAnimations() {
730for(size_t i = 0; i < animations.size(); i++) {
731animations[i].stop();
732}
733}
734
735void ofxAssimpModelLoader::resetAllAnimations() {
736for(size_t i = 0; i < animations.size(); i++) {
737animations[i].reset();
738}
739}
740
741void ofxAssimpModelLoader::setPausedForAllAnimations(bool pause) {
742for(size_t i = 0; i < animations.size(); i++) {
743animations[i].setPaused(pause);
744}
745}
746
747void ofxAssimpModelLoader::setLoopStateForAllAnimations(ofLoopType state) {
748for(size_t i = 0; i < animations.size(); i++) {
749animations[i].setLoopState(state);
750}
751}
752
753void ofxAssimpModelLoader::setPositionForAllAnimations(float position) {
754for(size_t i = 0; i < animations.size(); i++) {
755animations[i].setPosition(position);
756}
757}
758
759// DEPRECATED.
760void ofxAssimpModelLoader::setAnimation(int animationIndex) {
761if(!hasAnimations()) {
762return;
763}
764currentAnimation = ofClamp(animationIndex, 0, getAnimationCount() - 1);
765}
766
767// DEPRECATED.
768void ofxAssimpModelLoader::setNormalizedTime(float time) {
769if(!hasAnimations()) {
770return;
771}
772currentAnimation = ofClamp(currentAnimation, 0, getAnimationCount() - 1);
773ofxAssimpAnimation & animation = animations[currentAnimation];
774float realT = ofMap(time, 0.0, 1.0, 0.0, animation.getDurationInSeconds(), false);
775animation.setPosition(realT);
776update();
777}
778
779// DEPRECATED.
780void ofxAssimpModelLoader::setTime(float time) {
781if(!hasAnimations()) {
782return;
783}
784currentAnimation = ofClamp(currentAnimation, 0, getAnimationCount() - 1);
785ofxAssimpAnimation & animation = animations[currentAnimation];
786animation.setPosition(time);
787update();
788}
789
790// DEPRECATED.
791float ofxAssimpModelLoader::getDuration(int animationIndex) {
792if(!hasAnimations()) {
793return 0;
794}
795animationIndex = ofClamp(animationIndex, 0, getAnimationCount() - 1);
796float duration = animations[animationIndex].getDurationInSeconds();
797return duration;
798}
799
800//------------------------------------------- meshes.
801bool ofxAssimpModelLoader::hasMeshes() {
802return modelMeshes.size() > 0;
803}
804
805unsigned int ofxAssimpModelLoader::getMeshCount() {
806return modelMeshes.size();
807}
808
809ofxAssimpMeshHelper & ofxAssimpModelLoader::getMeshHelper(int meshIndex) {
810meshIndex = ofClamp(meshIndex, 0, modelMeshes.size()-1);
811return modelMeshes[meshIndex];
812}
813
814//-------------------------------------------
815void ofxAssimpModelLoader::getBoundingBoxWithMinVector( aiVector3D* min, aiVector3D* max )
816{
817aiMatrix4x4 trafo;
818aiIdentityMatrix4(&trafo);
819
820min->x = min->y = min->z = 1e10f;
821max->x = max->y = max->z = -1e10f;
822
823for(auto & mesh: modelMeshes){
824this->getBoundingBoxForNode(mesh, min, max);
825}
826}
827
828//-------------------------------------------
829void ofxAssimpModelLoader::getBoundingBoxForNode(const ofxAssimpMeshHelper & mesh, aiVector3D* min, aiVector3D* max){
830if (!hasAnimations()){
831for(unsigned int i = 0; i < mesh.mesh->mNumVertices; i++){
832auto vertex = mesh.mesh->mVertices[i];
833auto tmp = mesh.matrix * glm::vec4(vertex.x,vertex.y,vertex.z,1.0f);
834
835min->x = std::min(min->x,tmp.x);
836min->y = std::min(min->y,tmp.y);
837min->z = std::min(min->z,tmp.z);
838
839max->x = std::max(max->x,tmp.x);
840max->y = std::max(max->y,tmp.y);
841max->z = std::max(max->z,tmp.z);
842}
843} else {
844for (auto & animPos: mesh.animatedPos){
845auto tmp = mesh.matrix * glm::vec4(animPos.x,animPos.y,animPos.z,1.0f);
846
847min->x = std::min(min->x,tmp.x);
848min->y = std::min(min->y,tmp.y);
849min->z = std::min(min->z,tmp.z);
850
851max->x = std::max(max->x,tmp.x);
852max->y = std::max(max->y,tmp.y);
853max->z = std::max(max->z,tmp.z);
854}
855}
856}
857
858//-------------------------------------------
859void ofxAssimpModelLoader::setPosition(float x, float y, float z){
860pos.x = x;
861pos.y = y;
862pos.z = z;
863
864updateModelMatrix();
865}
866
867//-------------------------------------------
868void ofxAssimpModelLoader::setScale(float x, float y, float z){
869scale.x = x;
870scale.y = y;
871scale.z = z;
872
873updateModelMatrix();
874}
875
876//-------------------------------------------
877void ofxAssimpModelLoader::setScaleNormalization(bool normalize) {
878normalizeScale = normalize;
879
880updateModelMatrix();
881}
882
883//-------------------------------------------
884void ofxAssimpModelLoader::setRotation(int which, float angle, float rot_x, float rot_y, float rot_z){
885if(which + 1 > (int)rotAngle.size()){
886int diff = 1 + (which - rotAngle.size());
887for(int i = 0; i < diff; i++){
888rotAngle.push_back(0);
889rotAxis.push_back(glm::vec3(0.0,0.0,0.0));
890}
891}
892
893rotAngle[which] = angle;
894rotAxis[which].x = rot_x;
895rotAxis[which].y = rot_y;
896rotAxis[which].z = rot_z;
897
898updateModelMatrix();
899}
900
901//--------------------------------------------------------------
902void ofxAssimpModelLoader::drawWireframe(){
903draw(OF_MESH_WIREFRAME);
904}
905
906//--------------------------------------------------------------
907void ofxAssimpModelLoader::drawFaces(){
908draw(OF_MESH_FILL);
909}
910
911//--------------------------------------------------------------
912void ofxAssimpModelLoader::drawVertices(){
913draw(OF_MESH_POINTS);
914}
915
916//-------------------------------------------
917void ofxAssimpModelLoader::enableCulling(int glCullType){
918mCullType = glCullType;
919}
920
921//-------------------------------------------
922void ofxAssimpModelLoader::disableCulling(){
923mCullType = -1;
924}
925
926//-------------------------------------------
927void ofxAssimpModelLoader::draw(ofPolyRenderMode renderType) {
928if(scene == NULL) {
929return;
930}
931
932ofPushStyle();
933
934ofPushMatrix();
935ofMultMatrix(modelMatrix);
936
937#ifndef TARGET_OPENGLES
938glPolygonMode(GL_FRONT_AND_BACK, ofGetGLPolyMode(renderType));
939#endif
940
941for(size_t i = 0; i < modelMeshes.size(); i++) {
942ofxAssimpMeshHelper & mesh = modelMeshes[i];
943
944ofPushMatrix();
945ofMultMatrix(mesh.matrix);
946
947if(bUsingTextures){
948if(mesh.hasTexture(aiTextureType_DIFFUSE)) {
949mesh.getTextureRef(aiTextureType_DIFFUSE).bind();
950}
951}
952
953if(bUsingMaterials){
954mesh.material.begin();
955}
956
957// this was broken / backwards
958if(!mesh.twoSided && mCullType >= 0) {
959glEnable(GL_CULL_FACE);
960glCullFace(GL_BACK);
961glFrontFace(mCullType);
962}
963else {
964glDisable(GL_CULL_FACE);
965}
966
967
968
969ofEnableBlendMode(mesh.blendMode);
970
971#ifndef TARGET_OPENGLES
972mesh.vbo.drawElements(GL_TRIANGLES,mesh.indices.size());
973#else
974switch(renderType){
975case OF_MESH_FILL:
976mesh.vbo.drawElements(GL_TRIANGLES,mesh.indices.size());
977break;
978case OF_MESH_WIREFRAME:
979//note this won't look the same as on non ES renderers.
980//there is no easy way to convert GL_TRIANGLES to outlines for each triangle
981mesh.vbo.drawElements(GL_LINES,mesh.indices.size());
982break;
983case OF_MESH_POINTS:
984mesh.vbo.drawElements(GL_POINTS,mesh.indices.size());
985break;
986}
987#endif
988
989if(bUsingTextures){
990if(mesh.hasTexture(aiTextureType_DIFFUSE)) {
991mesh.getTextureRef(aiTextureType_DIFFUSE).unbind();
992}
993}
994
995if(!mesh.twoSided) {
996glDisable(GL_CULL_FACE);
997}
998
999if(bUsingMaterials){
1000mesh.material.end();
1001}
1002
1003ofPopMatrix();
1004}
1005
1006#ifndef TARGET_OPENGLES
1007//set the drawing mode back to FILL if its drawn the model with a different mode.
1008if( renderType != OF_MESH_FILL ){
1009glPolygonMode(GL_FRONT_AND_BACK, ofGetGLPolyMode(OF_MESH_FILL));
1010}
1011#endif
1012
1013ofPopMatrix();
1014ofPopStyle();
1015}
1016
1017//-------------------------------------------
1018vector<string> ofxAssimpModelLoader::getMeshNames(){
1019if(!scene)return vector<string>();
1020
1021vector<string> names(scene->mNumMeshes);
1022for(unsigned int i=0; i< scene->mNumMeshes; i++){
1023names[i] = scene->mMeshes[i]->mName.data;
1024}
1025return names;
1026}
1027
1028//-------------------------------------------
1029unsigned int ofxAssimpModelLoader::getNumMeshes(){
1030if( scene ){
1031return scene->mNumMeshes;
1032}
1033return 0;
1034}
1035
1036//-------------------------------------------
1037ofMesh ofxAssimpModelLoader::getMesh(string name){
1038ofMesh ofm;
1039if( scene ){
1040// default to triangle mode
1041ofm.setMode(OF_PRIMITIVE_TRIANGLES);
1042aiMesh * aim = NULL;
1043for(unsigned int i=0; i < scene->mNumMeshes; i++){
1044if(string(scene->mMeshes[i]->mName.data)==name){
1045aim = scene->mMeshes[i];
1046break;
1047}
1048}
1049
1050if(!aim){
1051ofLogError("ofxAssimpModelLoader") <<"getMesh(): couldn't find mesh: \"" << name << "\"";
1052return ofm;
1053}
1054
1055aiMeshToOfMesh(aim,ofm);
1056}
1057return ofm;
1058}
1059
1060//-------------------------------------------
1061ofMesh ofxAssimpModelLoader::getMesh(unsigned int num){
1062ofMesh ofm;
1063if( scene ){
1064if(scene->mNumMeshes <= num){
1065ofLogError("ofxAssimpModelLoader") << "getMesh(): mesh id " << num
1066<< " out of range for total num meshes: " << scene->mNumMeshes;
1067return ofm;
1068}
1069aiMeshToOfMesh(scene->mMeshes[num],ofm);
1070}
1071return ofm;
1072}
1073
1074//-------------------------------------------
1075ofMesh ofxAssimpModelLoader::getCurrentAnimatedMesh(string name){
1076for(size_t i=0; i < modelMeshes.size(); i++){
1077if(string(modelMeshes[i].mesh->mName.data)==name){
1078if(!modelMeshes[i].validCache){
1079modelMeshes[i].cachedMesh.clearVertices();
1080modelMeshes[i].cachedMesh.clearNormals();
1081if(hasAnimations()){
1082modelMeshes[i].cachedMesh.addVertices(aiVecVecToOfVecVec(modelMeshes[i].animatedPos));
1083modelMeshes[i].cachedMesh.addNormals(aiVecVecToOfVecVec(modelMeshes[i].animatedNorm));
1084}
1085modelMeshes[i].validCache = true;
1086}
1087return modelMeshes[i].cachedMesh;
1088}
1089}
1090
1091ofLogError("ofxAssimpModelLoader") << "getCurrentAnimatedMesh(): couldn't find mesh: \"" + name << "\"";
1092return ofMesh();
1093
1094}
1095
1096//-------------------------------------------
1097ofMesh ofxAssimpModelLoader::getCurrentAnimatedMesh(unsigned int num){
1098if(modelMeshes.size() <= num){
1099ofLogError("ofxAssimpModelLoader") << "getCurrentAnimatedMesh(): mesh id: " << num
1100<< "out of range for total num meshes: " << scene->mNumMeshes;
1101return ofMesh();
1102}
1103if(!modelMeshes[num].validCache){
1104modelMeshes[num].cachedMesh.clearVertices();
1105modelMeshes[num].cachedMesh.clearNormals();
1106modelMeshes[num].cachedMesh.addVertices(aiVecVecToOfVecVec(modelMeshes[num].animatedPos));
1107modelMeshes[num].cachedMesh.addNormals(aiVecVecToOfVecVec(modelMeshes[num].animatedNorm));
1108modelMeshes[num].validCache = true;
1109}
1110return modelMeshes[num].cachedMesh;
1111}
1112
1113//-------------------------------------------
1114ofMaterial ofxAssimpModelLoader::getMaterialForMesh(string name){
1115for(size_t i = 0; i < modelMeshes.size(); i++){
1116if(string(modelMeshes[i].mesh->mName.data)==name){
1117return modelMeshes[i].material;
1118}
1119}
1120ofLogError("ofxAssimpModelLoader") << "getMaterialForMesh(): couldn't find mesh: \"" + name << "\"";
1121return ofMaterial();
1122}
1123
1124//-------------------------------------------
1125ofMaterial ofxAssimpModelLoader::getMaterialForMesh(unsigned int num){
1126if(modelMeshes.size() <= num){
1127ofLogError("ofxAssimpModelLoader") << "getMaterialForMesh(): mesh id: " << num
1128<< "out of range for total num meshes: " << scene->mNumMeshes;
1129return ofMaterial();
1130}
1131return modelMeshes[num].material;
1132}
1133
1134//-------------------------------------------
1135ofTexture ofxAssimpModelLoader::getTextureForMesh(string name){
1136for(size_t i = 0; i < modelMeshes.size(); i++){
1137if(string(modelMeshes[i].mesh->mName.data)==name){
1138if(modelMeshes[i].hasTexture()) {
1139return modelMeshes[i].getTextureRef();
1140}
1141}
1142}
1143ofLogError("ofxAssimpModelLoader") << "getTextureForMesh(): couldn't find mesh: \"" + name << "\"";
1144return ofTexture();
1145}
1146
1147//-------------------------------------------
1148ofTexture ofxAssimpModelLoader::getTextureForMesh(unsigned int i){
1149if(i < modelMeshes.size()){
1150if(modelMeshes[i].hasTexture()) {
1151return modelMeshes[i].getTextureRef();
1152}
1153}
1154ofLogError("ofxAssimpModelLoader") << "getTextureForMesh(): mesh id: " << i
1155<< "out of range for total num meshes: " << scene->mNumMeshes;
1156return ofTexture();
1157}
1158
1159//-------------------------------------------
1160glm::vec3 ofxAssimpModelLoader::getPosition(){
1161return pos;
1162}
1163
1164//-------------------------------------------
1165glm::vec3 ofxAssimpModelLoader::getSceneCenter(){
1166return aiVecToOfVec(scene_center);
1167}
1168
1169//-------------------------------------------
1170float ofxAssimpModelLoader::getNormalizedScale(){
1171return normalizedScale;
1172}
1173
1174//-------------------------------------------
1175glm::vec3 ofxAssimpModelLoader::getScale(){
1176return scale;
1177}
1178
1179//-------------------------------------------
1180glm::mat4 ofxAssimpModelLoader::getModelMatrix() {
1181return modelMatrix;
1182}
1183
1184//-------------------------------------------
1185glm::vec3 ofxAssimpModelLoader::getSceneMin(bool bScaled ){
1186glm::vec3 sceneMin(scene_min.x, scene_min.y, scene_min.z);
1187if( bScaled ){
1188return sceneMin * scale;
1189}else{
1190return sceneMin;
1191}
1192}
1193
1194//-------------------------------------------
1195glm::vec3 ofxAssimpModelLoader::getSceneMax(bool bScaled ){
1196glm::vec3 sceneMax(scene_max.x, scene_max.y, scene_max.z);
1197if( bScaled ){
1198return sceneMax * scale;
1199}else{
1200return sceneMax;
1201}
1202}
1203
1204//-------------------------------------------
1205glm::vec3 ofxAssimpModelLoader::getSceneMinModelSpace(){
1206glm::vec3 sceneMax(scene_min.x, scene_min.y, scene_min.z);
1207return glm::vec3(modelMatrix * glm::vec4(scene_min.x, scene_min.y, scene_min.z, 1.0));
1208}
1209
1210//-------------------------------------------
1211glm::vec3 ofxAssimpModelLoader::getSceneMaxModelSpace(){
1212glm::vec3 sceneMax(scene_max.x, scene_max.y, scene_max.z);
1213return glm::vec3(modelMatrix * glm::vec4(scene_max.x, scene_max.y, scene_max.z, 1.0));
1214}
1215
1216//-------------------------------------------
1217glm::vec3 ofxAssimpModelLoader::getSceneCenterModelSpace(){
1218glm::vec3 center = getSceneCenter();
1219return glm::vec3(modelMatrix * glm::vec4(center.x, center.y, center.z, 1.0));
1220}
1221
1222//-------------------------------------------
1223int ofxAssimpModelLoader::getNumRotations(){
1224return rotAngle.size();
1225}
1226
1227//-------------------------------------------
1228glm::vec3 ofxAssimpModelLoader::getRotationAxis(int which){
1229if((int)rotAxis.size() > which){
1230return rotAxis[which];
1231}else{
1232return glm::vec3(0.0,0.0,0.0);
1233}
1234}
1235
1236//-------------------------------------------
1237float ofxAssimpModelLoader::getRotationAngle(int which){
1238if((int)rotAngle.size() > which){
1239return rotAngle[which];
1240}else{
1241return 0.0;
1242}
1243}
1244
1245//-------------------------------------------
1246const aiScene* ofxAssimpModelLoader::getAssimpScene(){
1247return scene.get();
1248}
1249
1250//--------------------------------------------------------------
1251void ofxAssimpModelLoader::enableTextures(){
1252bUsingTextures = true;
1253}
1254
1255//--------------------------------------------------------------
1256void ofxAssimpModelLoader::enableNormals(){
1257bUsingNormals = true;
1258}
1259
1260//--------------------------------------------------------------
1261void ofxAssimpModelLoader::enableColors(){
1262bUsingColors = true;
1263}
1264
1265//--------------------------------------------------------------
1266void ofxAssimpModelLoader::enableMaterials(){
1267bUsingMaterials = true;
1268}
1269
1270//--------------------------------------------------------------
1271void ofxAssimpModelLoader::disableTextures(){
1272bUsingTextures = false;
1273}
1274
1275//--------------------------------------------------------------
1276void ofxAssimpModelLoader::disableNormals(){
1277bUsingNormals = false;
1278}
1279
1280//--------------------------------------------------------------
1281void ofxAssimpModelLoader::disableColors(){
1282bUsingColors = false;
1283}
1284
1285//--------------------------------------------------------------
1286void ofxAssimpModelLoader::disableMaterials(){
1287bUsingMaterials = false;
1288}
1289