├── .gitignore ├── .hgignore ├── CMakeLists.txt ├── LICENSE ├── src ├── AssimpLoader.cpp └── AssimpLoader.h └── tool └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | CMakeLists.txt.user 3 | -------------------------------------------------------------------------------- /.hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | build/* 3 | *.kdev4 4 | CMakeLists.txt.user 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # /* 2 | # ----------------------------------------------------------------------------- 3 | # This source file is part of 4 | # _ 5 | # ___ __ _ _ __ ___ __ _ ___ ___(_)_ __ ___ _ __ 6 | # / _ \ / _` | '__/ _ \/ _` / __/ __| | '_ ` _ \| '_ \ 7 | # | (_) | (_| | | | __/ (_| \__ \__ \ | | | | | | |_) | 8 | # \___/ \__, |_| \___|\__,_|___/___/_|_| |_| |_| .__/ 9 | # |___/ |_| 10 | # 11 | # For the latest info, see http://code.google.com/p/ogreassimp/ 12 | # 13 | # Copyright (c) 2011 Jacob 'jacmoe' Moen 14 | # 15 | # Licensed under the MIT license: 16 | # 17 | # Permission is hereby granted, free of charge, to any person obtaining a copy 18 | # of this software and associated documentation files (the "Software"), to deal 19 | # in the Software without restriction, including without limitation the rights 20 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | # copies of the Software, and to permit persons to whom the Software is 22 | # furnished to do so, subject to the following conditions: 23 | # 24 | # The above copyright notice and this permission notice shall be included in 25 | # all copies or substantial portions of the Software. 26 | # 27 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 33 | # THE SOFTWARE. 34 | # ----------------------------------------------------------------------------- 35 | #*/ 36 | cmake_minimum_required(VERSION 2.8) 37 | set(CMAKE_CXX_STANDARD 11) 38 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMakeModules;${CMAKE_MODULE_PATH}") 39 | 40 | project(ogreassimp) 41 | 42 | set(OGREASSIMP_VERSION_STRING "0.1.1") 43 | set(OGREASSIMP_VERSION_STRING_MAJOR "0") 44 | set(OGREASSIMP_VERSION_STRING_MINOR "1") 45 | set(OGREASSIMP_VERSION_STRING_PATCH "1") 46 | 47 | if (CMAKE_BUILD_TYPE STREQUAL "") 48 | # CMake defaults to leaving CMAKE_BUILD_TYPE empty. This screws up 49 | # differentiation between debug and release builds. 50 | set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build, options are: None (CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel." FORCE) 51 | endif () 52 | 53 | find_package(OGRE 1.10 REQUIRED) 54 | link_directories(${OGRE_LIBRARY_DIRS}) 55 | find_package(ASSIMP REQUIRED) 56 | 57 | include_directories(${OGRE_INCLUDE_DIRS} ${ASSIMP_INCLUDE_DIRS} src/) 58 | 59 | set(HDRS src/AssimpLoader.h) 60 | add_library(OgreAssimpLoader src/AssimpLoader.cpp ${HDRS}) 61 | set_target_properties(OgreAssimpLoader PROPERTIES DEBUG_POSTFIX _d) 62 | target_link_libraries(OgreAssimpLoader ${OGRE_LIBRARIES} ${ASSIMP_LIBRARIES}) 63 | 64 | install(TARGETS OgreAssimpLoader RUNTIME DESTINATION lib ARCHIVE DESTINATION lib) 65 | install(FILES ${HDRS} DESTINATION include/OgreAssimpLoader) 66 | 67 | add_executable(OgreAssimpConverter tool/main.cpp) 68 | target_link_libraries(OgreAssimpConverter OgreAssimpLoader) 69 | install(TARGETS OgreAssimpConverter RUNTIME DESTINATION bin) 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 OGRECave 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/AssimpLoader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ----------------------------------------------------------------------------- 3 | This source file is part of 4 | _ 5 | ___ __ _ _ __ ___ __ _ ___ ___(_)_ __ ___ _ __ 6 | / _ \ / _` | '__/ _ \/ _` / __/ __| | '_ ` _ \| '_ \ 7 | | (_) | (_| | | | __/ (_| \__ \__ \ | | | | | | |_) | 8 | \___/ \__, |_| \___|\__,_|___/___/_|_| |_| |_| .__/ 9 | |___/ |_| 10 | 11 | For the latest info, see https://bitbucket.org/jacmoe/ogreassimp 12 | 13 | Copyright (c) 2011 Jacob 'jacmoe' Moen 14 | 15 | Licensed under the MIT license: 16 | 17 | Permission is hereby granted, free of charge, to any person obtaining a copy 18 | of this software and associated documentation files (the "Software"), to deal 19 | in the Software without restriction, including without limitation the rights 20 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | copies of the Software, and to permit persons to whom the Software is 22 | furnished to do so, subject to the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be included in 25 | all copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 33 | THE SOFTWARE. 34 | ----------------------------------------------------------------------------- 35 | */ 36 | #include "AssimpLoader.h" 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | 46 | typedef Ogre::Affine3 Affine3; 47 | 48 | struct OgreLogStream : public Assimp::LogStream 49 | { 50 | Ogre::LogMessageLevel _lml; 51 | OgreLogStream(Ogre::LogMessageLevel lml) : _lml(lml) {} 52 | 53 | void write(const char* message) 54 | { 55 | Ogre::String msg(message); 56 | Ogre::StringUtil::trim(msg); 57 | Ogre::LogManager::getSingleton().logMessage("Assimp: " + msg, _lml); 58 | } 59 | }; 60 | 61 | struct OgreIOSystem : public Assimp::MemoryIOSystem 62 | { 63 | std::vector auxStreams; 64 | Ogre::String _group; 65 | 66 | OgreIOSystem(Ogre::MemoryDataStream& mainStream, const Ogre::String& group) 67 | : Assimp::MemoryIOSystem(mainStream.getPtr(), mainStream.size(), NULL), _group(group) 68 | { 69 | } 70 | 71 | Assimp::IOStream* Open(const char* pFile, const char* pMode) override 72 | { 73 | auto ret = Ogre::ResourceGroupManager::getSingleton().openResource(pFile, _group, NULL, false); 74 | if (ret) 75 | { 76 | Ogre::MemoryDataStream buffer(ret, false); 77 | auxStreams.emplace_back(new Assimp::MemoryIOStream(buffer.getPtr(), buffer.size(), true)); 78 | return auxStreams.back(); 79 | } 80 | return Assimp::MemoryIOSystem::Open(pFile, pMode); 81 | } 82 | void Close(Assimp::IOStream* pFile) override 83 | { 84 | auto it = std::find(auxStreams.begin(), auxStreams.end(), pFile); 85 | if (it != auxStreams.end()) 86 | { 87 | delete pFile; 88 | auxStreams.erase(it); 89 | } 90 | return Assimp::MemoryIOSystem::Close(pFile); 91 | } 92 | }; 93 | 94 | int AssimpLoader::msBoneCount = 0; 95 | 96 | AssimpLoader::AssimpLoader() 97 | { 98 | Assimp::DefaultLogger::create(""); 99 | Assimp::DefaultLogger::get()->attachStream(new OgreLogStream(Ogre::LML_NORMAL), 100 | ~Assimp::DefaultLogger::Err); 101 | Assimp::DefaultLogger::get()->attachStream(new OgreLogStream(Ogre::LML_CRITICAL), 102 | Assimp::DefaultLogger::Err); 103 | } 104 | 105 | AssimpLoader::~AssimpLoader() 106 | { 107 | } 108 | 109 | bool AssimpLoader::load(const Ogre::DataStreamPtr& source, const Ogre::String& type, Ogre::Mesh* mesh, 110 | Ogre::SkeletonPtr& skeletonPtr, const Options& options) 111 | { 112 | Assimp::Importer importer; 113 | Ogre::MemoryDataStream buffer(source); 114 | importer.SetIOHandler(new OgreIOSystem(buffer, mesh->getGroup())); 115 | auto name = Ogre::StringUtil::format(AI_MEMORYIO_MAGIC_FILENAME ".%s", type.c_str()); 116 | _load(name.c_str(), importer, mesh, skeletonPtr, options); 117 | return true; 118 | } 119 | 120 | bool AssimpLoader::load(const Ogre::String& source, Ogre::Mesh* mesh, Ogre::SkeletonPtr& skeletonPtr, 121 | const AssimpLoader::Options& options) 122 | { 123 | Assimp::Importer importer; 124 | _load(source.c_str(), importer, mesh, skeletonPtr, options); 125 | return true; 126 | } 127 | 128 | bool AssimpLoader::_load(const char* name, Assimp::Importer& importer, Ogre::Mesh* mesh, Ogre::SkeletonPtr& skeletonPtr, const Options& options) 129 | { 130 | Ogre::uint32 flags = aiProcessPreset_TargetRealtime_Quality | aiProcess_TransformUVCoords | aiProcess_FlipUVs; 131 | importer.SetPropertyFloat("PP_GSN_MAX_SMOOTHING_ANGLE", options.maxEdgeAngle); 132 | importer.SetPropertyInteger("PP_SBP_REMOVE", aiPrimitiveType_LINE | aiPrimitiveType_POINT); 133 | const aiScene* scene = importer.ReadFile(name, flags); 134 | 135 | // If the import failed, report it 136 | if( !scene) 137 | { 138 | Ogre::LogManager::getSingleton().logError("Assimp failed - " + Ogre::String(importer.GetErrorString())); 139 | return false; 140 | } 141 | 142 | mAnimationSpeedModifier = options.animationSpeedModifier; 143 | mLoaderParams = options.params; 144 | mQuietMode = ((mLoaderParams & LP_QUIET_MODE) == 0) ? false : true; 145 | mCustomAnimationName = options.customAnimationName; 146 | mNodeDerivedTransformByName.clear(); 147 | 148 | Ogre::String basename, extension; 149 | Ogre::StringUtil::splitBaseFilename(mesh->getName(), basename, extension); 150 | 151 | grabNodeNamesFromNode(scene, scene->mRootNode); 152 | grabBoneNamesFromNode(scene, scene->mRootNode); 153 | 154 | computeNodesDerivedTransform(scene, scene->mRootNode, scene->mRootNode->mTransformation); 155 | 156 | if(mBonesByName.size()) 157 | { 158 | mSkeleton = Ogre::SkeletonManager::getSingleton().create(basename+".skeleton", Ogre::RGN_DEFAULT, true); 159 | 160 | msBoneCount = 0; 161 | createBonesFromNode(scene, scene->mRootNode); 162 | msBoneCount = 0; 163 | createBoneHiearchy(scene, scene->mRootNode); 164 | 165 | if(scene->HasAnimations()) 166 | { 167 | for(unsigned int i = 0; i < scene->mNumAnimations; ++i) 168 | { 169 | parseAnimation(scene, i, scene->mAnimations[i]); 170 | } 171 | } 172 | } 173 | 174 | loadDataFromNode(scene, scene->mRootNode, mesh); 175 | 176 | Assimp::DefaultLogger::kill(); 177 | 178 | if(mSkeleton) 179 | { 180 | 181 | if(!mQuietMode) 182 | { 183 | Ogre::LogManager::getSingleton().logMessage("Root bone: " + mSkeleton->getRootBones()[0]->getName()); 184 | } 185 | 186 | unsigned short numBones = mSkeleton->getNumBones(); 187 | unsigned short i; 188 | for (i = 0; i < numBones; ++i) 189 | { 190 | Ogre::Bone* pBone = mSkeleton->getBone(i); 191 | assert(pBone); 192 | } 193 | 194 | skeletonPtr = mSkeleton; 195 | mesh->setSkeletonName(mSkeleton->getName()); 196 | } 197 | 198 | for (auto sm : mesh->getSubMeshes()) 199 | { 200 | if (!sm->useSharedVertices) 201 | { 202 | 203 | Ogre::VertexDeclaration* newDcl = 204 | sm->vertexData->vertexDeclaration->getAutoOrganisedDeclaration(mesh->hasSkeleton(), mesh->hasVertexAnimation(), false); 205 | 206 | if (*newDcl != *(sm->vertexData->vertexDeclaration)) 207 | { 208 | sm->vertexData->reorganiseBuffers(newDcl); 209 | } 210 | } 211 | } 212 | 213 | // clean up 214 | mBonesByName.clear(); 215 | mBoneNodesByName.clear(); 216 | boneMap.clear(); 217 | mSkeleton.reset(); 218 | 219 | mCustomAnimationName = ""; 220 | 221 | return true; 222 | } 223 | 224 | /** translation, rotation, scale */ 225 | typedef std::tuple< aiVectorKey*, aiQuatKey*, aiVectorKey* > KeyframeData; 226 | typedef std::map< Ogre::Real, KeyframeData > KeyframesMap; 227 | 228 | template 229 | struct Int2Type 230 | { 231 | enum { value = v }; 232 | }; 233 | 234 | // T should be a Loki::Int2Type<> 235 | template< typename T > void GetInterpolationIterators(KeyframesMap& keyframes, 236 | KeyframesMap::iterator it, 237 | KeyframesMap::reverse_iterator& front, 238 | KeyframesMap::iterator& back) 239 | { 240 | front = KeyframesMap::reverse_iterator(it); 241 | 242 | front++; 243 | for(front; front != keyframes.rend(); front++) 244 | { 245 | if(std::get< T::value >(front->second) != NULL) 246 | { 247 | break; 248 | } 249 | } 250 | 251 | back = it; 252 | back++; 253 | for(back; back != keyframes.end(); back++) 254 | { 255 | if(std::get< T::value >(back->second) != NULL) 256 | { 257 | break; 258 | } 259 | } 260 | } 261 | 262 | aiVector3D getTranslate(aiNodeAnim* node_anim, KeyframesMap& keyframes, KeyframesMap::iterator it, Ogre::Real ticksPerSecond) 263 | { 264 | aiVectorKey* translateKey = std::get<0>(it->second); 265 | aiVector3D vect; 266 | if(translateKey) 267 | { 268 | vect = translateKey->mValue; 269 | } 270 | else 271 | { 272 | KeyframesMap::reverse_iterator front; 273 | KeyframesMap::iterator back; 274 | 275 | 276 | GetInterpolationIterators< Int2Type<0> > (keyframes, it, front, back); 277 | 278 | KeyframesMap::reverse_iterator rend = keyframes.rend(); 279 | KeyframesMap::iterator end = keyframes.end(); 280 | aiVectorKey* frontKey = NULL; 281 | aiVectorKey* backKey = NULL; 282 | 283 | if(front != rend) 284 | frontKey = std::get<0>(front->second); 285 | 286 | if(back != end) 287 | backKey = std::get<0>(back->second); 288 | 289 | // got 2 keys can interpolate 290 | if(frontKey && backKey) 291 | { 292 | float prop = (float)(((double)it->first - frontKey->mTime) / (backKey->mTime - frontKey->mTime)); 293 | prop /= ticksPerSecond; 294 | vect = ((backKey->mValue - frontKey->mValue) * prop) + frontKey->mValue; 295 | } 296 | 297 | else if(frontKey) 298 | { 299 | vect = frontKey->mValue; 300 | } 301 | else if(backKey) 302 | { 303 | vect = backKey->mValue; 304 | } 305 | } 306 | 307 | return vect; 308 | } 309 | 310 | aiQuaternion getRotate(aiNodeAnim* node_anim, KeyframesMap& keyframes, KeyframesMap::iterator it, Ogre::Real ticksPerSecond) 311 | { 312 | aiQuatKey* rotationKey = std::get<1>(it->second); 313 | aiQuaternion rot; 314 | if(rotationKey) 315 | { 316 | rot = rotationKey->mValue; 317 | } 318 | else 319 | { 320 | KeyframesMap::reverse_iterator front; 321 | KeyframesMap::iterator back; 322 | 323 | GetInterpolationIterators< Int2Type<1> > (keyframes, it, front, back); 324 | 325 | KeyframesMap::reverse_iterator rend = keyframes.rend(); 326 | KeyframesMap::iterator end = keyframes.end(); 327 | aiQuatKey* frontKey = NULL; 328 | aiQuatKey* backKey = NULL; 329 | 330 | if(front != rend) 331 | frontKey = std::get<1>(front->second); 332 | 333 | if(back != end) 334 | backKey = std::get<1>(back->second); 335 | 336 | // got 2 keys can interpolate 337 | if(frontKey && backKey) 338 | { 339 | float prop = (float)(((double)it->first - frontKey->mTime) / (backKey->mTime - frontKey->mTime)); 340 | prop /= ticksPerSecond; 341 | aiQuaternion::Interpolate(rot, frontKey->mValue, backKey->mValue, prop); 342 | } 343 | 344 | else if(frontKey) 345 | { 346 | rot = frontKey->mValue; 347 | } 348 | else if(backKey) 349 | { 350 | rot = backKey->mValue; 351 | } 352 | } 353 | 354 | return rot; 355 | } 356 | 357 | aiVector3D getScale(aiNodeAnim* node_anim, KeyframesMap& keyframes, KeyframesMap::iterator it, Ogre::Real ticksPerSecond) 358 | { 359 | aiVectorKey* scaleKey = std::get<2>(it->second); 360 | aiVector3D vect; 361 | if(scaleKey) 362 | { 363 | vect = scaleKey->mValue; 364 | } 365 | else 366 | { 367 | KeyframesMap::reverse_iterator front; 368 | KeyframesMap::iterator back; 369 | 370 | 371 | GetInterpolationIterators< Int2Type<2> > (keyframes, it, front, back); 372 | 373 | KeyframesMap::reverse_iterator rend = keyframes.rend(); 374 | KeyframesMap::iterator end = keyframes.end(); 375 | aiVectorKey* frontKey = NULL; 376 | aiVectorKey* backKey = NULL; 377 | 378 | if(front != rend) 379 | frontKey = std::get<0>(front->second); 380 | 381 | if(back != end) 382 | backKey = std::get<0>(back->second); 383 | 384 | // got 2 keys can interpolate 385 | if(frontKey && backKey) 386 | { 387 | float prop = (float)(((double)it->first - frontKey->mTime) / (backKey->mTime - frontKey->mTime)); 388 | prop /= ticksPerSecond; 389 | vect = ((backKey->mValue - frontKey->mValue) * prop) + frontKey->mValue; 390 | } 391 | 392 | else if(frontKey) 393 | { 394 | vect = frontKey->mValue; 395 | } 396 | else if(backKey) 397 | { 398 | vect = backKey->mValue; 399 | } 400 | } 401 | 402 | return vect; 403 | } 404 | 405 | void AssimpLoader::parseAnimation (const aiScene* mScene, int index, aiAnimation* anim) 406 | { 407 | // DefBonePose a matrix that represents the local bone transform (can build from Ogre bone components) 408 | // PoseToKey a matrix representing the keyframe translation 409 | // What assimp stores aiNodeAnim IS the decomposed form of the transform (DefBonePose * PoseToKey) 410 | // To get PoseToKey which is what Ogre needs we'ed have to build the transform from components in 411 | // aiNodeAnim and then DefBonePose.Inverse() * aiNodeAnim(generated transform) will be the right transform 412 | 413 | Ogre::String animName; 414 | if(mCustomAnimationName != "") 415 | { 416 | animName = mCustomAnimationName; 417 | if(index >= 1) 418 | { 419 | animName += Ogre::StringConverter::toString(index); 420 | } 421 | } 422 | else 423 | { 424 | animName = Ogre::String(anim->mName.data); 425 | } 426 | if(animName.length() < 1) 427 | { 428 | animName = "Animation" + Ogre::StringConverter::toString(index); 429 | } 430 | 431 | if(!mQuietMode) 432 | { 433 | Ogre::LogManager::getSingleton().logMessage("Animation name = '" + animName + "'"); 434 | Ogre::LogManager::getSingleton().logMessage("duration = " + Ogre::StringConverter::toString(Ogre::Real(anim->mDuration))); 435 | Ogre::LogManager::getSingleton().logMessage("tick/sec = " + Ogre::StringConverter::toString(Ogre::Real(anim->mTicksPerSecond))); 436 | Ogre::LogManager::getSingleton().logMessage("channels = " + Ogre::StringConverter::toString(anim->mNumChannels)); 437 | } 438 | Ogre::Animation* animation; 439 | mTicksPerSecond = (Ogre::Real)((0 == anim->mTicksPerSecond) ? 24 : anim->mTicksPerSecond); 440 | mTicksPerSecond *= mAnimationSpeedModifier; 441 | 442 | Ogre::Real cutTime = 0.0; 443 | if(mLoaderParams & LP_CUT_ANIMATION_WHERE_NO_FURTHER_CHANGE) 444 | { 445 | for (int i = 1; i < (int)anim->mNumChannels; i++) 446 | { 447 | aiNodeAnim* node_anim = anim->mChannels[i]; 448 | 449 | // times of the equality check 450 | Ogre::Real timePos = 0.0; 451 | Ogre::Real timeRot = 0.0; 452 | 453 | for(unsigned int i = 1; i < node_anim->mNumPositionKeys; i++) 454 | { 455 | if( node_anim->mPositionKeys[i] != node_anim->mPositionKeys[i-1]) 456 | { 457 | timePos = (Ogre::Real)node_anim->mPositionKeys[i].mTime; 458 | timePos /= mTicksPerSecond; 459 | } 460 | } 461 | 462 | for(unsigned int i = 1; i < node_anim->mNumRotationKeys; i++) 463 | { 464 | if( node_anim->mRotationKeys[i] != node_anim->mRotationKeys[i-1]) 465 | { 466 | timeRot = (Ogre::Real)node_anim->mRotationKeys[i].mTime; 467 | timeRot /= mTicksPerSecond; 468 | } 469 | } 470 | 471 | if(timePos > cutTime){ cutTime = timePos; } 472 | if(timeRot > cutTime){ cutTime = timeRot; } 473 | } 474 | 475 | animation = mSkeleton->createAnimation(Ogre::String(animName), cutTime); 476 | } 477 | else 478 | { 479 | cutTime = Ogre::Math::POS_INFINITY; 480 | animation = mSkeleton->createAnimation(Ogre::String(animName), Ogre::Real(anim->mDuration/mTicksPerSecond)); 481 | } 482 | 483 | animation->setInterpolationMode(Ogre::Animation::IM_LINEAR); //FIXME: Is this always true? 484 | 485 | if(!mQuietMode) 486 | { 487 | Ogre::LogManager::getSingleton().logMessage("Cut Time " + Ogre::StringConverter::toString(cutTime)); 488 | } 489 | 490 | for (int i = 0; i < (int)anim->mNumChannels; i++) 491 | { 492 | Ogre::TransformKeyFrame* keyframe; 493 | 494 | aiNodeAnim* node_anim = anim->mChannels[i]; 495 | if(!mQuietMode) 496 | { 497 | Ogre::LogManager::getSingleton().logMessage("Channel " + Ogre::StringConverter::toString(i)); 498 | Ogre::LogManager::getSingleton().logMessage("affecting node: " + Ogre::String(node_anim->mNodeName.data)); 499 | //Ogre::LogManager::getSingleton().logMessage("position keys: " + Ogre::StringConverter::toString(node_anim->mNumPositionKeys)); 500 | //Ogre::LogManager::getSingleton().logMessage("rotation keys: " + Ogre::StringConverter::toString(node_anim->mNumRotationKeys)); 501 | //Ogre::LogManager::getSingleton().logMessage("scaling keys: " + Ogre::StringConverter::toString(node_anim->mNumScalingKeys)); 502 | } 503 | 504 | Ogre::String boneName = Ogre::String(node_anim->mNodeName.data); 505 | 506 | if(mSkeleton->hasBone(boneName)) 507 | { 508 | Ogre::Bone* bone = mSkeleton->getBone(boneName); 509 | Affine3 defBonePoseInv; 510 | defBonePoseInv.makeInverseTransform(bone->getPosition(), bone->getScale(), bone->getOrientation()); 511 | 512 | Ogre::NodeAnimationTrack* track = animation->createNodeTrack(i, bone); 513 | 514 | // Ogre needs translate rotate and scale for each keyframe in the track 515 | KeyframesMap keyframes; 516 | 517 | for(unsigned int i = 0; i < node_anim->mNumPositionKeys; i++) 518 | { 519 | keyframes[ (Ogre::Real)node_anim->mPositionKeys[i].mTime / mTicksPerSecond ] = KeyframeData( &(node_anim->mPositionKeys[i]), NULL, NULL); 520 | } 521 | 522 | for(unsigned int i = 0; i < node_anim->mNumRotationKeys; i++) 523 | { 524 | KeyframesMap::iterator it = keyframes.find((Ogre::Real)node_anim->mRotationKeys[i].mTime / mTicksPerSecond); 525 | if(it != keyframes.end()) 526 | { 527 | std::get<1>(it->second) = &(node_anim->mRotationKeys[i]); 528 | } 529 | else 530 | { 531 | keyframes[ (Ogre::Real)node_anim->mRotationKeys[i].mTime / mTicksPerSecond ] = KeyframeData( NULL, &(node_anim->mRotationKeys[i]), NULL ); 532 | } 533 | } 534 | 535 | for(unsigned int i = 0; i < node_anim->mNumScalingKeys; i++) 536 | { 537 | KeyframesMap::iterator it = keyframes.find((Ogre::Real)node_anim->mScalingKeys[i].mTime / mTicksPerSecond); 538 | if(it != keyframes.end()) 539 | { 540 | std::get<2>(it->second) = &(node_anim->mScalingKeys[i]); 541 | } 542 | else 543 | { 544 | keyframes[ (Ogre::Real)node_anim->mRotationKeys[i].mTime / mTicksPerSecond ] = KeyframeData( NULL, NULL, &(node_anim->mScalingKeys[i]) ); 545 | } 546 | } 547 | 548 | KeyframesMap::iterator it = keyframes.begin(); 549 | KeyframesMap::iterator it_end = keyframes.end(); 550 | for(it; it != it_end; ++it) 551 | { 552 | if(it->first < cutTime) // or should it be <= 553 | { 554 | aiVector3D aiTrans = getTranslate( node_anim, keyframes, it, mTicksPerSecond); 555 | 556 | Ogre::Vector3 trans(aiTrans.x, aiTrans.y, aiTrans.z); 557 | 558 | aiQuaternion aiRot = getRotate(node_anim, keyframes, it, mTicksPerSecond); 559 | Ogre::Quaternion rot(aiRot.w, aiRot.x, aiRot.y, aiRot.z); 560 | 561 | aiVector3D aiScale = getScale(node_anim, keyframes, it, mTicksPerSecond); 562 | Ogre::Vector3 scale(aiScale.x, aiScale.y, aiScale.z); 563 | 564 | Ogre::Vector3 transCopy = trans; 565 | 566 | Affine3 fullTransform; 567 | fullTransform.makeTransform(trans, scale, rot); 568 | 569 | Affine3 poseTokey = defBonePoseInv * fullTransform; 570 | poseTokey.decomposition(trans, scale, rot); 571 | 572 | keyframe = track->createNodeKeyFrame(Ogre::Real(it->first)); 573 | 574 | // weirdness with the root bone, But this seems to work 575 | if(mSkeleton->getRootBones()[0]->getName() == boneName) 576 | { 577 | trans = transCopy - bone->getPosition(); 578 | } 579 | 580 | keyframe->setTranslate(trans); 581 | keyframe->setRotation(rot); 582 | keyframe->setScale(scale); 583 | } 584 | } 585 | 586 | } // if bone exists 587 | 588 | } // loop through channels 589 | 590 | mSkeleton->optimiseAllAnimations(); 591 | } 592 | 593 | 594 | 595 | void AssimpLoader::markAllChildNodesAsNeeded(const aiNode *pNode) 596 | { 597 | flagNodeAsNeeded(pNode->mName.data); 598 | // Traverse all child nodes of the current node instance 599 | for ( unsigned int childIdx=0; childIdxmNumChildren; ++childIdx ) 600 | { 601 | const aiNode *pChildNode = pNode->mChildren[ childIdx ]; 602 | markAllChildNodesAsNeeded(pChildNode); 603 | } 604 | } 605 | 606 | void AssimpLoader::grabNodeNamesFromNode(const aiScene* mScene, const aiNode* pNode) 607 | { 608 | boneMap.emplace(Ogre::String(pNode->mName.data), false); 609 | mBoneNodesByName[pNode->mName.data] = pNode; 610 | if(!mQuietMode) 611 | { 612 | Ogre::LogManager::getSingleton().logMessage("Node " + Ogre::String(pNode->mName.data) + " found."); 613 | } 614 | 615 | // Traverse all child nodes of the current node instance 616 | for ( unsigned int childIdx=0; childIdxmNumChildren; ++childIdx ) 617 | { 618 | const aiNode *pChildNode = pNode->mChildren[ childIdx ]; 619 | grabNodeNamesFromNode(mScene, pChildNode); 620 | } 621 | } 622 | 623 | 624 | void AssimpLoader::computeNodesDerivedTransform(const aiScene* mScene, const aiNode *pNode, const aiMatrix4x4 accTransform) 625 | { 626 | if(mNodeDerivedTransformByName.find(pNode->mName.data) == mNodeDerivedTransformByName.end()) 627 | { 628 | mNodeDerivedTransformByName[pNode->mName.data] = accTransform; 629 | } 630 | for ( unsigned int childIdx=0; childIdxmNumChildren; ++childIdx ) 631 | { 632 | const aiNode *pChildNode = pNode->mChildren[ childIdx ]; 633 | computeNodesDerivedTransform(mScene, pChildNode, accTransform * pChildNode->mTransformation); 634 | } 635 | } 636 | 637 | void AssimpLoader::createBonesFromNode(const aiScene* mScene, const aiNode *pNode) 638 | { 639 | if(isNodeNeeded(pNode->mName.data)) 640 | { 641 | Ogre::Bone* bone = mSkeleton->createBone(Ogre::String(pNode->mName.data), msBoneCount); 642 | 643 | aiQuaternion rot; 644 | aiVector3D pos; 645 | aiVector3D scale; 646 | 647 | /* 648 | aiMatrix4x4 aiM = mNodeDerivedTransformByName.find(pNode->mName.data)->second; 649 | 650 | const aiNode* parentNode = NULL; 651 | { 652 | boneMapType::iterator it = boneMap.find(pNode->mName.data); 653 | if(it != boneMap.end()) 654 | { 655 | parentNode = it->second.parent; 656 | } 657 | } 658 | if(parentNode) 659 | { 660 | aiMatrix4x4 aiMParent = mNodeDerivedTransformByName.find(parentNode->mName.data)->second; 661 | aiM = aiMParent.Inverse() * aiM; 662 | } 663 | */ 664 | 665 | // above should be the same as 666 | aiMatrix4x4 aiM = pNode->mTransformation; 667 | 668 | aiM.Decompose(scale, rot, pos); 669 | 670 | 671 | /* 672 | // debug render 673 | Ogre::SceneNode* sceneNode = NULL; 674 | if(parentNode) 675 | { 676 | Ogre::SceneNode* parent = static_cast( 677 | GOOF::NodeUtils::GetNodeMatch(getSceneManager()->getRootSceneNode(), parentNode->mName.data, false)); 678 | assert(parent); 679 | sceneNode = parent->createChildSceneNode(pNode->mName.data); 680 | } 681 | else 682 | { 683 | sceneNode = getSceneManager()->getRootSceneNode()->createChildSceneNode(pNode->mName.data); 684 | } 685 | 686 | sceneNode->setScale(scale.x, scale.y, scale.z); 687 | sceneNode->setPosition(pos.x, pos.y, pos.z); 688 | sceneNode->setOrientation(rot.w, rot.x, rot.y, rot.z); 689 | 690 | sceneNode = sceneNode->createChildSceneNode(); 691 | sceneNode->setScale(0.01, 0.01, 0.01); 692 | sceneNode->attachObject(getSceneManager()->createEntity("Box1m.mesh")); 693 | */ 694 | 695 | if (!aiM.IsIdentity()) 696 | { 697 | bone->setPosition(pos.x, pos.y, pos.z); 698 | bone->setOrientation(rot.w, rot.x, rot.y, rot.z); 699 | } 700 | 701 | if(!mQuietMode) 702 | { 703 | Ogre::LogManager::getSingleton().logMessage(Ogre::StringConverter::toString(msBoneCount) + ") Creating bone '" + Ogre::String(pNode->mName.data) + "'"); 704 | } 705 | msBoneCount++; 706 | } 707 | // Traverse all child nodes of the current node instance 708 | for ( unsigned int childIdx=0; childIdxmNumChildren; ++childIdx ) 709 | { 710 | const aiNode *pChildNode = pNode->mChildren[ childIdx ]; 711 | createBonesFromNode(mScene, pChildNode); 712 | } 713 | } 714 | 715 | void AssimpLoader::createBoneHiearchy(const aiScene* mScene, const aiNode *pNode) 716 | { 717 | if(isNodeNeeded(pNode->mName.data)) 718 | { 719 | Ogre::Bone* parent = 0; 720 | Ogre::Bone* child = 0; 721 | if(pNode->mParent) 722 | { 723 | if(mSkeleton->hasBone(pNode->mParent->mName.data)) 724 | { 725 | parent = mSkeleton->getBone(pNode->mParent->mName.data); 726 | } 727 | } 728 | if(mSkeleton->hasBone(pNode->mName.data)) 729 | { 730 | child = mSkeleton->getBone(pNode->mName.data); 731 | } 732 | if(parent && child) 733 | { 734 | parent->addChild(child); 735 | } 736 | } 737 | // Traverse all child nodes of the current node instance 738 | for ( unsigned int childIdx=0; childIdxmNumChildren; childIdx++ ) 739 | { 740 | const aiNode *pChildNode = pNode->mChildren[ childIdx ]; 741 | createBoneHiearchy(mScene, pChildNode); 742 | } 743 | } 744 | 745 | void AssimpLoader::flagNodeAsNeeded(const char* name) 746 | { 747 | boneMapType::iterator iter = boneMap.find(Ogre::String(name)); 748 | if( iter != boneMap.end()) 749 | { 750 | iter->second = true; 751 | } 752 | } 753 | 754 | bool AssimpLoader::isNodeNeeded(const char* name) 755 | { 756 | boneMapType::iterator iter = boneMap.find(Ogre::String(name)); 757 | if( iter != boneMap.end()) 758 | { 759 | return iter->second; 760 | } 761 | return false; 762 | } 763 | 764 | void AssimpLoader::grabBoneNamesFromNode(const aiScene* mScene, const aiNode *pNode) 765 | { 766 | static int meshNum = 0; 767 | meshNum++; 768 | if(pNode->mNumMeshes > 0) 769 | { 770 | for ( unsigned int idx=0; idxmNumMeshes; ++idx ) 771 | { 772 | aiMesh *pAIMesh = mScene->mMeshes[ pNode->mMeshes[ idx ] ]; 773 | 774 | if(pAIMesh->HasBones()) 775 | { 776 | for ( Ogre::uint32 i=0; i < pAIMesh->mNumBones; ++i ) 777 | { 778 | aiBone *pAIBone = pAIMesh->mBones[ i ]; 779 | if ( NULL != pAIBone ) 780 | { 781 | mBonesByName[pAIBone->mName.data] = pAIBone; 782 | 783 | if(!mQuietMode) 784 | { 785 | Ogre::LogManager::getSingleton().logMessage(Ogre::StringConverter::toString(i) + ") REAL BONE with name : " + Ogre::String(pAIBone->mName.data)); 786 | } 787 | 788 | // flag this node and all parents of this node as needed, until we reach the node holding the mesh, or the parent. 789 | aiNode* node = mScene->mRootNode->FindNode(pAIBone->mName.data); 790 | while(node) 791 | { 792 | if(node->mName.data == pNode->mName.data) 793 | { 794 | flagNodeAsNeeded(node->mName.data); 795 | break; 796 | } 797 | if(node->mName.data == pNode->mParent->mName.data) 798 | { 799 | flagNodeAsNeeded(node->mName.data); 800 | break; 801 | } 802 | 803 | // Not a root node, flag this as needed and continue to the parent 804 | flagNodeAsNeeded(node->mName.data); 805 | node = node->mParent; 806 | } 807 | 808 | // Flag all children of this node as needed 809 | node = mScene->mRootNode->FindNode(pAIBone->mName.data); 810 | markAllChildNodesAsNeeded(node); 811 | 812 | } // if we have a valid bone 813 | } // loop over bones 814 | } // if this mesh has bones 815 | } // loop over meshes 816 | } // if this node has meshes 817 | 818 | // Traverse all child nodes of the current node instance 819 | for ( unsigned int childIdx=0; childIdxmNumChildren; childIdx++ ) 820 | { 821 | const aiNode *pChildNode = pNode->mChildren[ childIdx ]; 822 | grabBoneNamesFromNode(mScene, pChildNode); 823 | } 824 | } 825 | 826 | Ogre::String ReplaceSpaces(const Ogre::String& s) 827 | { 828 | Ogre::String res(s); 829 | replace(res.begin(), res.end(), ' ', '_'); 830 | 831 | return res; 832 | } 833 | 834 | Ogre::MaterialPtr AssimpLoader::createMaterial(int index, const aiMaterial* mat) 835 | { 836 | static int dummyMatCount = 0; 837 | 838 | // extreme fallback texture -- 2x2 hot pink 839 | static Ogre::uint8 s_RGB[] = {128, 0, 255, 128, 0, 255, 128, 0, 255, 128, 0, 255}; 840 | 841 | Ogre::MaterialManager* omatMgr = Ogre::MaterialManager::getSingletonPtr(); 842 | enum aiTextureType type = aiTextureType_DIFFUSE; 843 | static aiString path; 844 | aiTextureMapping mapping = aiTextureMapping_UV; // the mapping (should be uv for now) 845 | unsigned int uvindex = 0; // the texture uv index channel 846 | float blend = 1.0f; // blend 847 | aiTextureOp op = aiTextureOp_Multiply; // op 848 | aiTextureMapMode mapmode[3] = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }; // mapmode 849 | std::ostringstream texname; 850 | 851 | aiString szPath; 852 | if(AI_SUCCESS == aiGetMaterialString(mat, AI_MATKEY_NAME, &szPath)) 853 | { 854 | if(!mQuietMode) 855 | { 856 | Ogre::LogManager::getSingleton().logMessage("Using aiGetMaterialString : Name " + Ogre::String(szPath.data)); 857 | } 858 | } 859 | if(szPath.length < 1) 860 | { 861 | if(!mQuietMode) 862 | { 863 | Ogre::LogManager::getSingleton().logMessage("Unnamed material encountered..."); 864 | } 865 | szPath = Ogre::String("dummyMat" + Ogre::StringConverter::toString(dummyMatCount)).c_str(); 866 | dummyMatCount++; 867 | } 868 | 869 | if(!mQuietMode) 870 | { 871 | Ogre::LogManager::getSingleton().logMessage("Creating " + Ogre::String(szPath.data)); 872 | } 873 | 874 | Ogre::ResourceManager::ResourceCreateOrRetrieveResult status = omatMgr->createOrRetrieve(ReplaceSpaces(szPath.data), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); 875 | Ogre::MaterialPtr omat = Ogre::static_pointer_cast(status.first); 876 | 877 | if (!status.second) 878 | return omat; 879 | 880 | // ambient 881 | aiColor4D clr(1.0f, 1.0f, 1.0f, 1.0); 882 | //Ambient is usually way too low! FIX ME! 883 | if (mat->GetTexture(type, 0, &path) != AI_SUCCESS) 884 | aiGetMaterialColor(mat, AI_MATKEY_COLOR_AMBIENT, &clr); 885 | omat->setAmbient(clr.r, clr.g, clr.b); 886 | 887 | // diffuse 888 | clr = aiColor4D(1.0f, 1.0f, 1.0f, 1.0f); 889 | if(AI_SUCCESS == aiGetMaterialColor(mat, AI_MATKEY_COLOR_DIFFUSE, &clr)) 890 | { 891 | omat->setDiffuse(clr.r, clr.g, clr.b, clr.a); 892 | } 893 | 894 | // specular 895 | clr = aiColor4D(1.0f, 1.0f, 1.0f, 1.0f); 896 | if(AI_SUCCESS == aiGetMaterialColor(mat, AI_MATKEY_COLOR_SPECULAR, &clr)) 897 | { 898 | omat->setSpecular(clr.r, clr.g, clr.b, clr.a); 899 | } 900 | 901 | // emissive 902 | clr = aiColor4D(1.0f, 1.0f, 1.0f, 1.0f); 903 | if(AI_SUCCESS == aiGetMaterialColor(mat, AI_MATKEY_COLOR_EMISSIVE, &clr)) 904 | { 905 | omat->setSelfIllumination(clr.r, clr.g, clr.b); 906 | } 907 | 908 | float fShininess; 909 | if(AI_SUCCESS == aiGetMaterialFloat(mat, AI_MATKEY_SHININESS, &fShininess)) 910 | { 911 | omat->setShininess(Ogre::Real(fShininess)); 912 | } 913 | 914 | int shade = aiShadingMode_NoShading; 915 | if (AI_SUCCESS == mat->Get(AI_MATKEY_SHADING_MODEL, shade) && shade != aiShadingMode_NoShading) { 916 | switch (shade) { 917 | case aiShadingMode_Phong: // Phong shading mode was added to opengl and directx years ago to be ready for gpus to support it (in fixed function pipeline), but no gpus ever did, so it has never done anything. From directx 10 onwards it was removed again. 918 | case aiShadingMode_Gouraud: 919 | omat->setShadingMode(Ogre::SO_GOURAUD); 920 | break; 921 | case aiShadingMode_Flat: 922 | omat->setShadingMode(Ogre::SO_FLAT); 923 | break; 924 | default: 925 | break; 926 | } 927 | } 928 | 929 | if (mat->GetTexture(type, 0, &path) == AI_SUCCESS) 930 | { 931 | if(!mQuietMode) 932 | { 933 | Ogre::LogManager::getSingleton().logMessage("Found texture " + Ogre::String(path.data) + " for channel " + Ogre::StringConverter::toString(uvindex)); 934 | } 935 | if(AI_SUCCESS == aiGetMaterialString(mat, AI_MATKEY_TEXTURE_DIFFUSE(0), &szPath)) 936 | { 937 | if(!mQuietMode) 938 | { 939 | Ogre::LogManager::getSingleton().logMessage("Using aiGetMaterialString : Found texture " + Ogre::String(szPath.data) + " for channel " + Ogre::StringConverter::toString(uvindex)); 940 | } 941 | } 942 | 943 | Ogre::String basename; 944 | Ogre::String outPath; 945 | Ogre::StringUtil::splitFilename(Ogre::String(szPath.data), basename, outPath); 946 | omat->getTechnique(0)->getPass(0)->createTextureUnitState(basename); 947 | 948 | // TODO: save embedded images to file 949 | } 950 | 951 | return omat; 952 | } 953 | 954 | 955 | bool AssimpLoader::createSubMesh(const Ogre::String& name, int index, const aiNode* pNode, const aiMesh *mesh, const aiMaterial* mat, Ogre::Mesh* mMesh, Ogre::AxisAlignedBox& mAAB) 956 | { 957 | // if animated all submeshes must have bone weights 958 | if(mBonesByName.size() && !mesh->HasBones()) 959 | { 960 | if(!mQuietMode) 961 | { 962 | Ogre::LogManager::getSingleton().logMessage("Skipping Mesh " + Ogre::String(mesh->mName.data) + "with no bone weights"); 963 | } 964 | return false; 965 | } 966 | 967 | Ogre::MaterialPtr matptr = createMaterial(mesh->mMaterialIndex, mat); 968 | 969 | // now begin the object definition 970 | // We create a submesh per material 971 | Ogre::SubMesh* submesh = mMesh->createSubMesh(name + Ogre::StringConverter::toString(index)); 972 | 973 | // prime pointers to vertex related data 974 | aiVector3D *vec = mesh->mVertices; 975 | aiVector3D *norm = mesh->mNormals; 976 | aiVector3D *uv = mesh->mTextureCoords[0]; 977 | //aiColor4D *col = mesh->mColors[0]; 978 | 979 | // We must create the vertex data, indicating how many vertices there will be 980 | submesh->useSharedVertices = false; 981 | submesh->vertexData = new Ogre::VertexData(); 982 | submesh->vertexData->vertexStart = 0; 983 | submesh->vertexData->vertexCount = mesh->mNumVertices; 984 | 985 | // We must now declare what the vertex data contains 986 | Ogre::VertexDeclaration* declaration = submesh->vertexData->vertexDeclaration; 987 | static const unsigned short source = 0; 988 | size_t offset = 0; 989 | offset += declaration->addElement(source,offset,Ogre::VET_FLOAT3,Ogre::VES_POSITION).getSize(); 990 | 991 | //mLog->logMessage((std::format(" %d vertices ") % m->mNumVertices).str()); 992 | if(!mQuietMode) 993 | { 994 | Ogre::LogManager::getSingleton().logMessage(Ogre::StringConverter::toString(mesh->mNumVertices) + " vertices"); 995 | } 996 | if (norm) 997 | { 998 | if(!mQuietMode) 999 | { 1000 | Ogre::LogManager::getSingleton().logMessage(Ogre::StringConverter::toString(mesh->mNumVertices) + " normals"); 1001 | } 1002 | //mLog->logMessage((std::format(" %d normals ") % m->mNumVertices).str() ); 1003 | offset += declaration->addElement(source,offset,Ogre::VET_FLOAT3,Ogre::VES_NORMAL).getSize(); 1004 | } 1005 | 1006 | if (uv) 1007 | { 1008 | if(!mQuietMode) 1009 | { 1010 | Ogre::LogManager::getSingleton().logMessage(Ogre::StringConverter::toString(mesh->mNumVertices) + " uvs"); 1011 | } 1012 | //mLog->logMessage((std::format(" %d uvs ") % m->mNumVertices).str() ); 1013 | offset += declaration->addElement(source,offset,Ogre::VET_FLOAT2,Ogre::VES_TEXTURE_COORDINATES).getSize(); 1014 | } 1015 | 1016 | /* 1017 | if (col) 1018 | { 1019 | Ogre::LogManager::getSingleton().logMessage(Ogre::StringConverter::toString(mesh->mNumVertices) + " colours"); 1020 | //mLog->logMessage((std::format(" %d colours ") % m->mNumVertices).str() ); 1021 | offset += declaration->addElement(source,offset,Ogre::VET_FLOAT3,Ogre::VES_DIFFUSE).getSize(); 1022 | } 1023 | */ 1024 | 1025 | 1026 | // We create the hardware vertex buffer 1027 | Ogre::HardwareVertexBufferSharedPtr vbuffer = 1028 | Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(declaration->getVertexSize(source), // == offset 1029 | submesh->vertexData->vertexCount, // == nbVertices 1030 | Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY); 1031 | 1032 | aiMatrix4x4 aiM = mNodeDerivedTransformByName.find(pNode->mName.data)->second; 1033 | 1034 | aiMatrix4x4 normalMatrix = aiM; 1035 | normalMatrix.a4 = 0; 1036 | normalMatrix.b4 = 0; 1037 | normalMatrix.c4 = 0; 1038 | normalMatrix.Transpose().Inverse(); 1039 | 1040 | // Now we get access to the buffer to fill it. During so we record the bounding box. 1041 | float* vdata = static_cast(vbuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD)); 1042 | for (size_t i=0;i < mesh->mNumVertices; ++i) 1043 | { 1044 | // Position 1045 | aiVector3D vect; 1046 | vect.x = vec->x; 1047 | vect.y = vec->y; 1048 | vect.z = vec->z; 1049 | 1050 | vect *= aiM; 1051 | 1052 | /* 1053 | if(NULL != mSkeletonRootNode) 1054 | { 1055 | vect *= mSkeletonRootNode->mTransformation; 1056 | } 1057 | */ 1058 | 1059 | Ogre::Vector3 position( vect.x, vect.y, vect.z ); 1060 | *vdata++ = vect.x; 1061 | *vdata++ = vect.y; 1062 | *vdata++ = vect.z; 1063 | mAAB.merge(position); 1064 | vec++; 1065 | 1066 | // Normal 1067 | if (norm) 1068 | { 1069 | vect.x = norm->x; 1070 | vect.y = norm->y; 1071 | vect.z = norm->z; 1072 | 1073 | vect *= normalMatrix; 1074 | vect = vect.Normalize(); 1075 | 1076 | *vdata++ = vect.x; 1077 | *vdata++ = vect.y; 1078 | *vdata++ = vect.z; 1079 | norm++; 1080 | 1081 | //*vdata++ = norm->x; 1082 | //*vdata++ = norm->y; 1083 | //*vdata++ = norm->z; 1084 | //norm++; 1085 | } 1086 | 1087 | // uvs 1088 | if (uv) 1089 | { 1090 | *vdata++ = uv->x; 1091 | *vdata++ = uv->y; 1092 | uv++; 1093 | } 1094 | 1095 | /* 1096 | if (col) 1097 | { 1098 | *vdata++ = col->r; 1099 | *vdata++ = col->g; 1100 | *vdata++ = col->b; 1101 | //*vdata++ = col->a; 1102 | //col++; 1103 | } 1104 | */ 1105 | } 1106 | 1107 | vbuffer->unlock(); 1108 | submesh->vertexData->vertexBufferBinding->setBinding(source,vbuffer); 1109 | 1110 | if(!mQuietMode) 1111 | { 1112 | Ogre::LogManager::getSingleton().logMessage(Ogre::StringConverter::toString(mesh->mNumFaces) + " faces"); 1113 | } 1114 | aiFace *faces = mesh->mFaces; 1115 | 1116 | // Creates the index data 1117 | submesh->indexData->indexStart = 0; 1118 | submesh->indexData->indexCount = mesh->mNumFaces * 3; 1119 | 1120 | if (mesh->mNumVertices >= 65536) // 32 bit index buffer 1121 | { 1122 | submesh->indexData->indexBuffer = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer( 1123 | Ogre::HardwareIndexBuffer::IT_32BIT, submesh->indexData->indexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY); 1124 | 1125 | Ogre::uint32* indexData = static_cast(submesh->indexData->indexBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD)); 1126 | 1127 | for (size_t i=0; i < mesh->mNumFaces;++i) 1128 | { 1129 | *indexData++ = faces->mIndices[0]; 1130 | *indexData++ = faces->mIndices[1]; 1131 | *indexData++ = faces->mIndices[2]; 1132 | 1133 | faces++; 1134 | } 1135 | } 1136 | else // 16 bit index buffer 1137 | { 1138 | submesh->indexData->indexBuffer = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer( 1139 | Ogre::HardwareIndexBuffer::IT_16BIT, submesh->indexData->indexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY); 1140 | 1141 | Ogre::uint16* indexData = static_cast(submesh->indexData->indexBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD)); 1142 | 1143 | for (size_t i=0; i < mesh->mNumFaces;++i) 1144 | { 1145 | *indexData++ = faces->mIndices[0]; 1146 | *indexData++ = faces->mIndices[1]; 1147 | *indexData++ = faces->mIndices[2]; 1148 | 1149 | faces++; 1150 | } 1151 | } 1152 | 1153 | submesh->indexData->indexBuffer->unlock(); 1154 | 1155 | // set bone weigths 1156 | if(mesh->HasBones()) 1157 | { 1158 | for ( Ogre::uint32 i=0; i < mesh->mNumBones; i++ ) 1159 | { 1160 | aiBone *pAIBone = mesh->mBones[ i ]; 1161 | if ( NULL != pAIBone ) 1162 | { 1163 | Ogre::String bname = pAIBone->mName.data; 1164 | for ( Ogre::uint32 weightIdx = 0; weightIdx < pAIBone->mNumWeights; weightIdx++ ) 1165 | { 1166 | aiVertexWeight aiWeight = pAIBone->mWeights[ weightIdx ]; 1167 | 1168 | Ogre::VertexBoneAssignment vba; 1169 | vba.vertexIndex = aiWeight.mVertexId; 1170 | vba.boneIndex = mSkeleton->getBone(bname)->getHandle(); 1171 | vba.weight= aiWeight.mWeight; 1172 | 1173 | submesh->addBoneAssignment(vba); 1174 | } 1175 | } 1176 | } 1177 | } // if mesh has bones 1178 | 1179 | // Finally we set a material to the submesh 1180 | if (matptr) 1181 | submesh->setMaterialName(matptr->getName()); 1182 | 1183 | return true; 1184 | } 1185 | 1186 | void AssimpLoader::loadDataFromNode(const aiScene* mScene, const aiNode *pNode, Ogre::Mesh* mesh) 1187 | { 1188 | if(pNode->mNumMeshes > 0) 1189 | { 1190 | Ogre::AxisAlignedBox mAAB = mesh->getBounds(); 1191 | 1192 | for ( unsigned int idx=0; idxmNumMeshes; ++idx ) 1193 | { 1194 | aiMesh *pAIMesh = mScene->mMeshes[ pNode->mMeshes[ idx ] ]; 1195 | if(!mQuietMode) 1196 | { 1197 | Ogre::LogManager::getSingleton().logMessage("SubMesh " + Ogre::StringConverter::toString(idx) + " for mesh '" + Ogre::String(pNode->mName.data) + "'"); 1198 | } 1199 | 1200 | // Create a material instance for the mesh. 1201 | const aiMaterial *pAIMaterial = mScene->mMaterials[ pAIMesh->mMaterialIndex ]; 1202 | createSubMesh(pNode->mName.data, idx, pNode, pAIMesh, pAIMaterial, mesh, mAAB); 1203 | } 1204 | 1205 | // We must indicate the bounding box 1206 | mesh->_setBounds(mAAB); 1207 | mesh->_setBoundingSphereRadius((mAAB.getMaximum()- mAAB.getMinimum()).length()/2); 1208 | } 1209 | 1210 | // Traverse all child nodes of the current node instance 1211 | for ( unsigned int childIdx=0; childIdxmNumChildren; childIdx++ ) 1212 | { 1213 | const aiNode *pChildNode = pNode->mChildren[ childIdx ]; 1214 | loadDataFromNode(mScene, pChildNode, mesh); 1215 | } 1216 | } 1217 | -------------------------------------------------------------------------------- /src/AssimpLoader.h: -------------------------------------------------------------------------------- 1 | /* 2 | ----------------------------------------------------------------------------- 3 | This source file is part of 4 | _ 5 | ___ __ _ _ __ ___ __ _ ___ ___(_)_ __ ___ _ __ 6 | / _ \ / _` | '__/ _ \/ _` / __/ __| | '_ ` _ \| '_ \ 7 | | (_) | (_| | | | __/ (_| \__ \__ \ | | | | | | |_) | 8 | \___/ \__, |_| \___|\__,_|___/___/_|_| |_| |_| .__/ 9 | |___/ |_| 10 | 11 | For the latest info, see https://bitbucket.org/jacmoe/ogreassimp 12 | 13 | Copyright (c) 2011 Jacob 'jacmoe' Moen 14 | 15 | Licensed under the MIT license: 16 | 17 | Permission is hereby granted, free of charge, to any person obtaining a copy 18 | of this software and associated documentation files (the "Software"), to deal 19 | in the Software without restriction, including without limitation the rights 20 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | copies of the Software, and to permit persons to whom the Software is 22 | furnished to do so, subject to the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be included in 25 | all copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 33 | THE SOFTWARE. 34 | ----------------------------------------------------------------------------- 35 | */ 36 | #ifndef __AssimpLoader_h__ 37 | #define __AssimpLoader_h__ 38 | 39 | #include 40 | 41 | #include 42 | 43 | namespace Assimp 44 | { 45 | class Importer; 46 | } 47 | 48 | class AssimpLoader 49 | { 50 | public: 51 | enum LoaderParams 52 | { 53 | // 3ds max exports the animation over a longer time frame than the animation actually plays for 54 | // this is a fix for that 55 | LP_CUT_ANIMATION_WHERE_NO_FURTHER_CHANGE = 1<<0, 56 | 57 | // Quiet mode - don't output anything 58 | LP_QUIET_MODE = 1<<1 59 | }; 60 | 61 | struct Options 62 | { 63 | float animationSpeedModifier; 64 | int params; 65 | Ogre::String customAnimationName; 66 | float maxEdgeAngle; 67 | 68 | Options() : animationSpeedModifier(1), params(0), maxEdgeAngle(30) {} 69 | }; 70 | 71 | AssimpLoader(); 72 | virtual ~AssimpLoader(); 73 | 74 | bool load(const Ogre::String& source, Ogre::Mesh* mesh, Ogre::SkeletonPtr& skeletonPtr, 75 | const Options& options = Options()); 76 | 77 | bool load(const Ogre::DataStreamPtr& source, const Ogre::String& type, Ogre::Mesh* mesh, 78 | Ogre::SkeletonPtr& skeletonPtr, const Options& options = Options()); 79 | 80 | private: 81 | bool _load(const char* name, Assimp::Importer& importer, Ogre::Mesh* mesh, Ogre::SkeletonPtr& skeletonPtr, const Options& options); 82 | bool createSubMesh(const Ogre::String& name, int index, const aiNode* pNode, const aiMesh *mesh, const aiMaterial* mat, Ogre::Mesh* mMesh, Ogre::AxisAlignedBox& mAAB); 83 | Ogre::MaterialPtr createMaterial(int index, const aiMaterial* mat); 84 | void grabNodeNamesFromNode(const aiScene* mScene, const aiNode* pNode); 85 | void grabBoneNamesFromNode(const aiScene* mScene, const aiNode* pNode); 86 | void computeNodesDerivedTransform(const aiScene* mScene, const aiNode *pNode, const aiMatrix4x4 accTransform); 87 | void createBonesFromNode(const aiScene* mScene, const aiNode* pNode); 88 | void createBoneHiearchy(const aiScene* mScene, const aiNode *pNode); 89 | void loadDataFromNode(const aiScene* mScene, const aiNode *pNode, Ogre::Mesh* mesh); 90 | void markAllChildNodesAsNeeded(const aiNode *pNode); 91 | void flagNodeAsNeeded(const char* name); 92 | bool isNodeNeeded(const char* name); 93 | void parseAnimation (const aiScene* mScene, int index, aiAnimation* anim); 94 | typedef std::map boneMapType; 95 | boneMapType boneMap; 96 | //aiNode* mSkeletonRootNode; 97 | int mLoaderParams; 98 | 99 | Ogre::String mCustomAnimationName; 100 | 101 | typedef std::map BoneNodeMap; 102 | BoneNodeMap mBoneNodesByName; 103 | 104 | typedef std::map BoneMap; 105 | BoneMap mBonesByName; 106 | 107 | typedef std::map NodeTransformMap; 108 | NodeTransformMap mNodeDerivedTransformByName; 109 | 110 | Ogre::SkeletonPtr mSkeleton; 111 | 112 | static int msBoneCount; 113 | 114 | bool mQuietMode; 115 | Ogre::Real mTicksPerSecond; 116 | Ogre::Real mAnimationSpeedModifier; 117 | }; 118 | 119 | #endif // __AssimpLoader_h__ 120 | -------------------------------------------------------------------------------- /tool/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ----------------------------------------------------------------------------- 3 | This source file is part of 4 | _ 5 | ___ __ _ _ __ ___ __ _ ___ ___(_)_ __ ___ _ __ 6 | / _ \ / _` | '__/ _ \/ _` / __/ __| | '_ ` _ \| '_ \ 7 | | (_) | (_| | | | __/ (_| \__ \__ \ | | | | | | |_) | 8 | \___/ \__, |_| \___|\__,_|___/___/_|_| |_| |_| .__/ 9 | |___/ |_| 10 | 11 | For the latest info, see https://bitbucket.org/jacmoe/ogreassimp 12 | 13 | Copyright (c) 2011 Jacob 'jacmoe' Moen 14 | 15 | Licensed under the MIT license: 16 | 17 | Permission is hereby granted, free of charge, to any person obtaining a copy 18 | of this software and associated documentation files (the "Software"), to deal 19 | in the Software without restriction, including without limitation the rights 20 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | copies of the Software, and to permit persons to whom the Software is 22 | furnished to do so, subject to the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be included in 25 | all copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 33 | THE SOFTWARE. 34 | ----------------------------------------------------------------------------- 35 | */ 36 | #include 37 | #include 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #include "AssimpLoader.h" 49 | 50 | namespace 51 | { 52 | // Crappy globals 53 | // NB some of these are not directly used, but are required to 54 | // instantiate the singletons used in the dlls 55 | Ogre::LogManager* logMgr = 0; 56 | Ogre::Math* mth = 0; 57 | Ogre::LodStrategyManager *lodMgr = 0; 58 | Ogre::MaterialManager* matMgr = 0; 59 | Ogre::SkeletonManager* skelMgr = 0; 60 | Ogre::MeshSerializer* meshSerializer = 0; 61 | //Ogre::XMLMeshSerializer* xmlMeshSerializer = 0; 62 | Ogre::SkeletonSerializer* skeletonSerializer = 0; 63 | //Ogre::XMLSkeletonSerializer* xmlSkeletonSerializer = 0; 64 | Ogre::DefaultHardwareBufferManager *bufferManager = 0; 65 | Ogre::MeshManager* meshMgr = 0; 66 | Ogre::ResourceGroupManager* rgm = 0; 67 | Ogre::ScriptCompilerManager* scmgr = 0; 68 | Ogre::ArchiveManager* archmgr = 0; 69 | Ogre::FileSystemArchiveFactory* mfsarchf = 0; 70 | 71 | Ogre::DefaultTextureManager* texMgr = 0; 72 | 73 | void help(void) 74 | { 75 | // Print help message 76 | std::cout << std::endl << "OgreAssimpConverter: Converts data from model formats supported by Assimp" << std::endl; 77 | std::cout << "to OGRE binary formats (mesh and skeleton) and material script." << std::endl; 78 | std::cout << std::endl << "Usage: OgreAssimpConverter [options] sourcefile [destination] " << std::endl; 79 | std::cout << std::endl << "Available options:" << std::endl; 80 | std::cout << "-q = Quiet mode, less output" << std::endl; 81 | std::cout << "-log filename = name of the log file (default: 'OgreAssimp.log')" << std::endl; 82 | std::cout << "-aniSpeedMod value = Factor to scale the animation speed - (default: '1.0')" << std::endl; 83 | std::cout << " (double between 0 and 1)" << std::endl; 84 | std::cout << "-3ds_ani_fix = Fix for the fact that 3ds max exports the animation over a" << std::endl; 85 | std::cout << " longer time frame than the animation actually plays for" << std::endl; 86 | std::cout << "-max_edge_angle deg = When normals are generated, max angle between two faces to smooth over" << std::endl; 87 | std::cout << "sourcefile = name of file to convert" << std::endl; 88 | std::cout << "destination = optional name of directory to write to. If you don't" << std::endl; 89 | std::cout << " specify this the converter will use the same directory as the sourcefile." << std::endl; 90 | std::cout << std::endl; 91 | } 92 | 93 | struct AssOptions 94 | { 95 | Ogre::String source; 96 | Ogre::String dest; 97 | Ogre::String logFile; 98 | 99 | AssimpLoader::Options options; 100 | 101 | AssOptions() 102 | { 103 | logFile = "OgreAssimp.log"; 104 | }; 105 | }; 106 | 107 | AssOptions parseArgs(int numArgs, char **args) 108 | { 109 | AssOptions opts; 110 | 111 | // ignore program name 112 | char* source = 0; 113 | char* dest = 0; 114 | 115 | // Set up options 116 | Ogre::UnaryOptionList unOpt; 117 | Ogre::BinaryOptionList binOpt; 118 | 119 | unOpt["-q"] = false; 120 | unOpt["-3ds_ani_fix"] = false; 121 | binOpt["-log"] = opts.logFile; 122 | binOpt["-aniName"] = ""; 123 | binOpt["-aniSpeedMod"] = "1.0"; 124 | binOpt["-max_edge_angle"] = "30"; 125 | 126 | int startIndex = Ogre::findCommandLineOpts(numArgs, args, unOpt, binOpt); 127 | 128 | if (unOpt["-q"]) 129 | { 130 | opts.options.params |= AssimpLoader::LP_QUIET_MODE; 131 | } 132 | if (unOpt["-3ds_ani_fix"]) 133 | { 134 | opts.options.params |= AssimpLoader::LP_CUT_ANIMATION_WHERE_NO_FURTHER_CHANGE; 135 | } 136 | 137 | opts.logFile = binOpt["-log"]; 138 | Ogre::StringConverter::parse(binOpt["-aniSpeedMod"], opts.options.animationSpeedModifier); 139 | opts.options.customAnimationName = binOpt["-aniName"]; 140 | Ogre::StringConverter::parse(binOpt["-max_edge_angle"], opts.options.maxEdgeAngle); 141 | 142 | // Source / dest 143 | if (numArgs > startIndex) 144 | source = args[startIndex]; 145 | if (numArgs > startIndex+1) 146 | dest = args[startIndex+1]; 147 | if (numArgs > startIndex+2) { 148 | logMgr->logError("Too many command-line arguments supplied"); 149 | help(); 150 | exit(1); 151 | } 152 | 153 | if (!source) 154 | { 155 | logMgr->logError("Missing source file"); 156 | help(); 157 | exit(1); 158 | } 159 | opts.source = source; 160 | 161 | if (dest) 162 | { 163 | opts.dest = dest; 164 | } 165 | 166 | if (!unOpt["-q"]) 167 | { 168 | std::cout << std::endl; 169 | std::cout << "-- OPTIONS --" << std::endl; 170 | 171 | std::cout << "source file = " << opts.source << std::endl; 172 | std::cout << "destination = " << opts.dest << std::endl; 173 | std::cout << "animation speed modifier = " << opts.options.animationSpeedModifier << std::endl; 174 | std::cout << "log file = " << opts.logFile << std::endl; 175 | 176 | std::cout << "-- END OPTIONS --" << std::endl; 177 | std::cout << std::endl; 178 | } 179 | 180 | return opts; 181 | } 182 | } 183 | 184 | int main(int numargs, char** args) 185 | { 186 | if (numargs < 2) 187 | { 188 | help(); 189 | return -1; 190 | } 191 | 192 | // Assume success 193 | int retCode = 0; 194 | 195 | try 196 | { 197 | logMgr = new Ogre::LogManager(); 198 | 199 | // this log catches output from the parseArgs call and routes it to stdout only 200 | logMgr->createLog("Temporary log", false, true, true); 201 | 202 | AssOptions opts = parseArgs(numargs, args); 203 | // use the log specified by the cmdline params 204 | logMgr->setDefaultLog(logMgr->createLog(opts.logFile, false, true)); 205 | // get rid of the temporary log as we use the new log now 206 | logMgr->destroyLog("Temporary log"); 207 | 208 | rgm = new Ogre::ResourceGroupManager(); 209 | mth = new Ogre::Math(); 210 | lodMgr = new Ogre::LodStrategyManager(); 211 | meshMgr = new Ogre::MeshManager(); 212 | matMgr = new Ogre::MaterialManager(); 213 | matMgr->initialise(); 214 | skelMgr = new Ogre::SkeletonManager(); 215 | meshSerializer = new Ogre::MeshSerializer(); 216 | //xmlMeshSerializer = new Ogre::XMLMeshSerializer(); 217 | skeletonSerializer = new Ogre::SkeletonSerializer(); 218 | //xmlSkeletonSerializer = new Ogre::XMLSkeletonSerializer(); 219 | bufferManager = new Ogre::DefaultHardwareBufferManager(); // needed because we don't have a rendersystem 220 | scmgr = new Ogre::ScriptCompilerManager(); 221 | archmgr = new Ogre::ArchiveManager(); 222 | mfsarchf = new Ogre::FileSystemArchiveFactory(); 223 | Ogre::ArchiveManager::getSingleton().addArchiveFactory( mfsarchf ); 224 | 225 | texMgr = new Ogre::DefaultTextureManager(); 226 | 227 | Ogre::String basename, ext, path; 228 | Ogre::StringUtil::splitFullFilename(opts.source, basename, ext, path); 229 | Ogre::ResourceGroupManager::getSingleton().addResourceLocation(path, "FileSystem"); 230 | 231 | Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().createManual(basename+"."+ext, Ogre::RGN_DEFAULT); 232 | Ogre::SkeletonPtr skeleton; 233 | 234 | AssimpLoader loader; 235 | loader.load(opts.source, mesh.get(), skeleton, opts.options); 236 | 237 | if(!opts.dest.empty()) 238 | { 239 | path = opts.dest + "/"; 240 | } 241 | 242 | Ogre::MeshSerializer meshSer; 243 | meshSer.exportMesh(mesh.get(), path + basename + ".mesh"); 244 | 245 | if(skeleton) 246 | { 247 | Ogre::SkeletonSerializer binSer; 248 | binSer.exportSkeleton(skeleton.get(), path + skeleton->getName()); 249 | } 250 | 251 | // serialise the materials 252 | std::set exportNames; 253 | for(Ogre::SubMesh* sm : mesh->getSubMeshes()) 254 | exportNames.insert(sm->getMaterialName()); 255 | 256 | // queue up the materials for serialise 257 | Ogre::MaterialSerializer ms; 258 | for(const Ogre::String& name : exportNames) 259 | ms.queueForExport(Ogre::MaterialManager::getSingleton().getByName(name)); 260 | 261 | if(!exportNames.empty()) 262 | ms.exportQueued(path + basename + ".material"); 263 | } 264 | catch(Ogre::Exception& e) 265 | { 266 | logMgr->logError(e.getDescription()+" Aborting!"); 267 | retCode = 1; 268 | } 269 | 270 | //delete xmlSkeletonSerializer; 271 | delete skeletonSerializer; 272 | //delete xmlMeshSerializer; 273 | delete meshSerializer; 274 | delete skelMgr; 275 | delete matMgr; 276 | //delete meshMgr; FIX this!! 277 | delete bufferManager; 278 | delete scmgr; 279 | delete archmgr; 280 | delete mfsarchf; 281 | delete lodMgr; 282 | delete mth; 283 | delete rgm; 284 | delete logMgr; 285 | 286 | return retCode; 287 | 288 | } 289 | 290 | --------------------------------------------------------------------------------