|
@@ -59,8 +59,6 @@
|
|
|
#include <assimp/config.h>
|
|
|
#include <exception>
|
|
|
|
|
|
-#include <assimp/Importer.hpp>
|
|
|
-
|
|
|
MODULE_BEGIN( AssimpShapeLoader )
|
|
|
MODULE_INIT_AFTER( ShapeLoader )
|
|
|
MODULE_INIT
|
|
@@ -124,7 +122,80 @@ AssimpShapeLoader::~AssimpShapeLoader()
|
|
|
|
|
|
void AssimpShapeLoader::releaseImport()
|
|
|
{
|
|
|
- aiReleaseImport(mScene);
|
|
|
+}
|
|
|
+
|
|
|
+void debugSceneMetaData(const aiScene* scene) {
|
|
|
+ if (!scene->mMetaData) {
|
|
|
+ Con::printf("[ASSIMP] No metadata available.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (unsigned int i = 0; i < scene->mMetaData->mNumProperties; ++i) {
|
|
|
+ const char* key = scene->mMetaData->mKeys[i].C_Str();
|
|
|
+ aiMetadataType type = scene->mMetaData->mValues[i].mType;
|
|
|
+ Con::printf("[ASSIMP] Metadata key: %s", key);
|
|
|
+
|
|
|
+ switch (type) {
|
|
|
+ case AI_BOOL:
|
|
|
+ Con::printf(" Value: %d (bool)", *(bool*)scene->mMetaData->mValues[i].mData);
|
|
|
+ break;
|
|
|
+ case AI_INT32:
|
|
|
+ Con::printf(" Value: %d (int)", *(int*)scene->mMetaData->mValues[i].mData);
|
|
|
+ break;
|
|
|
+ case AI_UINT64:
|
|
|
+ Con::printf(" Value: %llu (uint64)", *(uint64_t*)scene->mMetaData->mValues[i].mData);
|
|
|
+ break;
|
|
|
+ case AI_FLOAT:
|
|
|
+ Con::printf(" Value: %f (float)", *(float*)scene->mMetaData->mValues[i].mData);
|
|
|
+ break;
|
|
|
+ case AI_DOUBLE:
|
|
|
+ Con::printf(" Value: %f (double)", *(double*)scene->mMetaData->mValues[i].mData);
|
|
|
+ break;
|
|
|
+ case AI_AISTRING:
|
|
|
+ Con::printf(" Value: %s (string)", ((aiString*)scene->mMetaData->mValues[i].mData)->C_Str());
|
|
|
+ break;
|
|
|
+ case AI_AIVECTOR3D:
|
|
|
+ {
|
|
|
+ aiVector3D* vec = (aiVector3D*)scene->mMetaData->mValues[i].mData;
|
|
|
+ Con::printf(" Value: (%f, %f, %f) (vector3d)", vec->x, vec->y, vec->z);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ Con::printf(" Unknown metadata type.");
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void applyTransformation(aiNode* node, const aiMatrix4x4& transform) {
|
|
|
+ node->mTransformation = transform * node->mTransformation; // Apply transformation to the node
|
|
|
+}
|
|
|
+
|
|
|
+void reorientGLTFScene(const aiScene* scene) {
|
|
|
+ aiMatrix4x4 rotationMatrix;
|
|
|
+ rotationMatrix = aiMatrix4x4::RotationX(AI_MATH_PI / 2, rotationMatrix); // Rotate -90 degrees around X-axis
|
|
|
+
|
|
|
+ applyTransformation(scene->mRootNode, rotationMatrix);
|
|
|
+
|
|
|
+ rotationMatrix = aiMatrix4x4::RotationZ(AI_MATH_PI, rotationMatrix); // Rotate -90 degrees around X-axis
|
|
|
+ applyTransformation(scene->mRootNode, rotationMatrix);
|
|
|
+}
|
|
|
+
|
|
|
+float getUnitScaleFactor(const aiScene* scene) {
|
|
|
+ float scale = 1.0f;
|
|
|
+
|
|
|
+ if (scene->mMetaData) {
|
|
|
+ double unitScaleFactor;
|
|
|
+ if (scene->mMetaData->Get("UnitScaleFactor", unitScaleFactor)) {
|
|
|
+ scale = static_cast<float>(unitScaleFactor);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return scale;
|
|
|
+}
|
|
|
+
|
|
|
+void scaleScene(const aiScene* scene, float scaleFactor) {
|
|
|
+ aiMatrix4x4 scaleMatrix = aiMatrix4x4::Scaling(aiVector3D(scaleFactor, scaleFactor, scaleFactor), aiMatrix4x4());
|
|
|
+ applyTransformation(scene->mRootNode, scaleMatrix);
|
|
|
}
|
|
|
|
|
|
void AssimpShapeLoader::enumerateScene()
|
|
@@ -132,31 +203,28 @@ void AssimpShapeLoader::enumerateScene()
|
|
|
TSShapeLoader::updateProgress(TSShapeLoader::Load_ReadFile, "Reading File");
|
|
|
Con::printf("[ASSIMP] Attempting to load file: %s", shapePath.getFullPath().c_str());
|
|
|
|
|
|
- // Post-Processing
|
|
|
- unsigned int ppsteps =
|
|
|
- (ColladaUtils::getOptions().convertLeftHanded ? aiProcess_MakeLeftHanded : 0) |
|
|
|
- (ColladaUtils::getOptions().reverseWindingOrder ? aiProcess_FlipWindingOrder : 0) |
|
|
|
- (ColladaUtils::getOptions().calcTangentSpace ? aiProcess_CalcTangentSpace : 0) |
|
|
|
- (ColladaUtils::getOptions().joinIdenticalVerts ? aiProcess_JoinIdenticalVertices : 0) |
|
|
|
- (ColladaUtils::getOptions().removeRedundantMats ? aiProcess_RemoveRedundantMaterials : 0) |
|
|
|
- (ColladaUtils::getOptions().genUVCoords ? aiProcess_GenUVCoords : 0) |
|
|
|
- (ColladaUtils::getOptions().transformUVCoords ? aiProcess_TransformUVCoords : 0) |
|
|
|
- (ColladaUtils::getOptions().flipUVCoords ? aiProcess_FlipUVs : 0) |
|
|
|
- (ColladaUtils::getOptions().findInstances ? aiProcess_FindInstances : 0) |
|
|
|
- (ColladaUtils::getOptions().limitBoneWeights ? aiProcess_LimitBoneWeights : 0);
|
|
|
-
|
|
|
- if (Con::getBoolVariable("$Assimp::OptimizeMeshes", false))
|
|
|
+ // Define post-processing steps
|
|
|
+ unsigned int ppsteps = aiProcess_Triangulate | aiProcess_ValidateDataStructure | aiProcess_MakeLeftHanded | aiProcess_FlipUVs;
|
|
|
+
|
|
|
+ const auto& options = ColladaUtils::getOptions();
|
|
|
+ if (options.convertLeftHanded) ppsteps |= aiProcess_MakeLeftHanded;
|
|
|
+ if (options.reverseWindingOrder) ppsteps |= aiProcess_FlipWindingOrder;
|
|
|
+ if (options.calcTangentSpace) ppsteps |= aiProcess_CalcTangentSpace;
|
|
|
+ if (options.joinIdenticalVerts) ppsteps |= aiProcess_JoinIdenticalVertices;
|
|
|
+ if (options.removeRedundantMats) ppsteps |= aiProcess_RemoveRedundantMaterials;
|
|
|
+ if (options.genUVCoords) ppsteps |= aiProcess_GenUVCoords;
|
|
|
+ if (options.transformUVCoords) ppsteps |= aiProcess_TransformUVCoords;
|
|
|
+ if (options.findInstances) ppsteps |= aiProcess_FindInstances;
|
|
|
+ if (options.limitBoneWeights) ppsteps |= aiProcess_LimitBoneWeights;
|
|
|
+
|
|
|
+ if (Con::getBoolVariable("$Assimp::OptimizeMeshes", false)) {
|
|
|
ppsteps |= aiProcess_OptimizeMeshes | aiProcess_OptimizeGraph;
|
|
|
-
|
|
|
- if (Con::getBoolVariable("$Assimp::SplitLargeMeshes", false))
|
|
|
+ }
|
|
|
+ if (Con::getBoolVariable("$Assimp::SplitLargeMeshes", false)) {
|
|
|
ppsteps |= aiProcess_SplitLargeMeshes;
|
|
|
+ }
|
|
|
|
|
|
- // Mandatory options
|
|
|
- //ppsteps |= aiProcess_ValidateDataStructure | aiProcess_Triangulate | aiProcess_ImproveCacheLocality;
|
|
|
- ppsteps |= aiProcess_Triangulate;
|
|
|
- //aiProcess_SortByPType | // make 'clean' meshes which consist of a single typ of primitives
|
|
|
-
|
|
|
- aiPropertyStore* props = aiCreatePropertyStore();
|
|
|
+ ppsteps |= aiProcess_ValidateDataStructure;
|
|
|
|
|
|
struct aiLogStream shapeLog = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, NULL);
|
|
|
shapeLog.callback = assimpLogCallback;
|
|
@@ -165,94 +233,113 @@ void AssimpShapeLoader::enumerateScene()
|
|
|
#ifdef TORQUE_DEBUG
|
|
|
aiEnableVerboseLogging(true);
|
|
|
#endif
|
|
|
+
|
|
|
+ // Read the file
|
|
|
+ mScene = mImporter.ReadFile(shapePath.getFullPath().c_str(), ppsteps);
|
|
|
|
|
|
- mScene = (aiScene*)aiImportFileExWithProperties(shapePath.getFullPath().c_str(), ppsteps, NULL, props);
|
|
|
+ if (!mScene || (mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) || !mScene->mRootNode) {
|
|
|
+ Con::errorf("[ASSIMP] ERROR: Could not load file: %s", shapePath.getFullPath().c_str());
|
|
|
+ Con::errorf("[ASSIMP] Importer error: %s", mImporter.GetErrorString());
|
|
|
+ TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Import failed");
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- aiReleasePropertyStore(props);
|
|
|
+ //debugSceneMetaData(mScene);
|
|
|
|
|
|
- if ( mScene )
|
|
|
- {
|
|
|
- Con::printf("[ASSIMP] Mesh Count: %d", mScene->mNumMeshes);
|
|
|
- Con::printf("[ASSIMP] Material Count: %d", mScene->mNumMaterials);
|
|
|
+ Con::printf("[ASSIMP] Mesh Count: %d", mScene->mNumMeshes);
|
|
|
+ Con::printf("[ASSIMP] Material Count: %d", mScene->mNumMaterials);
|
|
|
|
|
|
- // Setup default units for shape format
|
|
|
- String importFormat;
|
|
|
+ // Format-specific adjustments
|
|
|
+ String fileExt = String::ToLower(shapePath.getExtension());
|
|
|
+ const aiImporterDesc* importerDescription = aiGetImporterDesc(fileExt.c_str());
|
|
|
+ if (fileExt == String::ToString("gltf") || fileExt == String::ToString("glb")) {
|
|
|
+ Con::printf("[ASSIMP] Detected GLTF format, applying reorientation...");
|
|
|
+ reorientGLTFScene(mScene); // Reorient GLTF
|
|
|
+ }
|
|
|
|
|
|
- String fileExt = String::ToLower(shapePath.getExtension());
|
|
|
- const aiImporterDesc* importerDescription = aiGetImporterDesc(fileExt.c_str());
|
|
|
- if (importerDescription && StringTable->insert(importerDescription->mName) == StringTable->insert("Autodesk FBX Importer"))
|
|
|
- {
|
|
|
- ColladaUtils::getOptions().formatScaleFactor = 0.01f;
|
|
|
+ if (importerDescription && dStrcmp(importerDescription->mName, "Autodesk FBX Importer") == 0) {
|
|
|
+ Con::printf("[ASSIMP] Detected FBX format, checking unit scale...");
|
|
|
+ F32 scaleFactor = getUnitScaleFactor(mScene);
|
|
|
+ if (scaleFactor != 1.0f) {
|
|
|
+ Con::printf("[ASSIMP] Applying FBX scale factor: %f", scaleFactor);
|
|
|
+ scaleScene(mScene, scaleFactor);
|
|
|
}
|
|
|
-
|
|
|
- // Set import options (if they are not set to override)
|
|
|
- if (ColladaUtils::getOptions().unit <= 0.0f)
|
|
|
+ else
|
|
|
{
|
|
|
- F64 unit;
|
|
|
- if (!getMetaDouble("UnitScaleFactor", unit))
|
|
|
- {
|
|
|
- F32 floatVal;
|
|
|
- S32 intVal;
|
|
|
- if (getMetaFloat("UnitScaleFactor", floatVal))
|
|
|
- unit = (F64)floatVal;
|
|
|
- else if (getMetaInt("UnitScaleFactor", intVal))
|
|
|
- unit = (F64)intVal;
|
|
|
- else
|
|
|
- unit = 1.0;
|
|
|
- }
|
|
|
- ColladaUtils::getOptions().unit = (F32)unit;
|
|
|
+ scaleScene(mScene, 0.01f);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if (ColladaUtils::getOptions().upAxis == UPAXISTYPE_COUNT)
|
|
|
- {
|
|
|
- S32 upAxis;
|
|
|
- if (!getMetaInt("UpAxis", upAxis))
|
|
|
- upAxis = UPAXISTYPE_Z_UP;
|
|
|
- ColladaUtils::getOptions().upAxis = (domUpAxisType) upAxis;
|
|
|
- }
|
|
|
+ // Handle scaling and up-axis conversions if necessary
|
|
|
+ configureImportUnitsAndAxis();
|
|
|
|
|
|
- // Extract embedded textures
|
|
|
- for (U32 i = 0; i < mScene->mNumTextures; ++i)
|
|
|
- extractTexture(i, mScene->mTextures[i]);
|
|
|
+ // Extract embedded textures
|
|
|
+ for (unsigned int i = 0; i < mScene->mNumTextures; ++i) {
|
|
|
+ extractTexture(i, mScene->mTextures[i]);
|
|
|
+ }
|
|
|
|
|
|
- // Load all the materials.
|
|
|
- AssimpAppMaterial::sDefaultMatNumber = 0;
|
|
|
- for ( U32 i = 0; i < mScene->mNumMaterials; i++ )
|
|
|
- AppMesh::appMaterials.push_back(new AssimpAppMaterial(mScene->mMaterials[i]));
|
|
|
+ // Load all materials
|
|
|
+ AssimpAppMaterial::sDefaultMatNumber = 0;
|
|
|
+ for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) {
|
|
|
+ AppMesh::appMaterials.push_back(new AssimpAppMaterial(mScene->mMaterials[i]));
|
|
|
+ }
|
|
|
|
|
|
- // Setup LOD checks
|
|
|
- detectDetails();
|
|
|
+ // Setup LOD checks
|
|
|
+ detectDetails();
|
|
|
|
|
|
- // Define the root node, and process down the chain.
|
|
|
- AssimpAppNode* node = new AssimpAppNode(mScene, mScene->mRootNode, 0);
|
|
|
-
|
|
|
- if (!processNode(node))
|
|
|
- delete node;
|
|
|
+ // Process the scene graph
|
|
|
+ AssimpAppNode* rootNode = new AssimpAppNode(mScene, mScene->mRootNode, 0);
|
|
|
+ if (!processNode(rootNode)) {
|
|
|
+ delete rootNode;
|
|
|
+ }
|
|
|
|
|
|
- // add bounds node.
|
|
|
- if (!boundsNode)
|
|
|
- {
|
|
|
- aiNode* req[1];
|
|
|
- req[0] = new aiNode("bounds");
|
|
|
- mScene->mRootNode->addChildren(1, req);
|
|
|
+ // Add a bounds node if none exists
|
|
|
+ if (!boundsNode) {
|
|
|
+ aiNode* req[1];
|
|
|
+ req[0] = new aiNode("bounds");
|
|
|
+ mScene->mRootNode->addChildren(1, req);
|
|
|
|
|
|
- AssimpAppNode* appBounds = new AssimpAppNode(mScene, req[0]);
|
|
|
- if (!processNode(appBounds))
|
|
|
- delete appBounds;
|
|
|
+ auto* appBoundsNode = new AssimpAppNode(mScene, req[0]);
|
|
|
+ if (!processNode(appBoundsNode)) {
|
|
|
+ delete appBoundsNode;
|
|
|
}
|
|
|
-
|
|
|
- // Check for animations and process those.
|
|
|
- processAnimations();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Import failed");
|
|
|
- Con::printf("[ASSIMP] Import Error: %s", aiGetErrorString());
|
|
|
}
|
|
|
|
|
|
+ // Process animations if available
|
|
|
+ processAnimations();
|
|
|
+
|
|
|
+ // Clean up log stream
|
|
|
aiDetachLogStream(&shapeLog);
|
|
|
}
|
|
|
|
|
|
+void AssimpShapeLoader::configureImportUnitsAndAxis() {
|
|
|
+ auto& options = ColladaUtils::getOptions();
|
|
|
+
|
|
|
+ // Configure unit scaling
|
|
|
+ if (options.unit <= 0.0f) {
|
|
|
+ F64 unitScaleFactor = 1.0;
|
|
|
+ if (!getMetaDouble("UnitScaleFactor", unitScaleFactor)) {
|
|
|
+ float floatVal;
|
|
|
+ int intVal;
|
|
|
+ if (getMetaFloat("UnitScaleFactor", floatVal)) {
|
|
|
+ unitScaleFactor = static_cast<F64>(floatVal);
|
|
|
+ }
|
|
|
+ else if (getMetaInt("UnitScaleFactor", intVal)) {
|
|
|
+ unitScaleFactor = static_cast<F64>(intVal);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ options.unit = static_cast<float>(unitScaleFactor);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Configure up-axis
|
|
|
+ if (options.upAxis == UPAXISTYPE_COUNT) {
|
|
|
+ int upAxis = UPAXISTYPE_Z_UP;
|
|
|
+ if (getMetaInt("UpAxis", upAxis)) {
|
|
|
+ options.upAxis = static_cast<domUpAxisType>(upAxis);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void AssimpShapeLoader::processAnimations()
|
|
|
{
|
|
|
// add all animations into 1 ambient animation.
|
|
@@ -266,6 +353,7 @@ void AssimpShapeLoader::processAnimations()
|
|
|
for (U32 i = 0; i < mScene->mNumAnimations; ++i)
|
|
|
{
|
|
|
aiAnimation* anim = mScene->mAnimations[i];
|
|
|
+ duration = anim->mDuration;
|
|
|
for (U32 j = 0; j < anim->mNumChannels; j++)
|
|
|
{
|
|
|
aiNodeAnim* nodeAnim = anim->mChannels[j];
|