diff --git a/Source/Engine/ContentImporters/ImportModel.cpp b/Source/Engine/ContentImporters/ImportModel.cpp index baac1defb..cf66762ad 100644 --- a/Source/Engine/ContentImporters/ImportModel.cpp +++ b/Source/Engine/ContentImporters/ImportModel.cpp @@ -269,7 +269,7 @@ CreateAssetResult ImportModel::Import(CreateAssetContext& context) // Import all of the objects recursive but use current model data to skip loading file again options.Cached = &cached; - Function splitImport = [&context, &autoImportOutput](Options& splitOptions, const StringView& objectName, String& outputPath) + Function splitImport = [&context, &autoImportOutput](Options& splitOptions, const StringView& objectName, String& outputPath, MeshData& meshData) { // Recursive importing of the split object String postFix = objectName; @@ -279,6 +279,33 @@ CreateAssetResult ImportModel::Import(CreateAssetContext& context) // TODO: check for name collisions with material/texture assets outputPath = autoImportOutput / String(StringUtils::GetFileNameWithoutExtension(context.TargetAssetPath)) + TEXT(" ") + postFix + TEXT(".flax"); splitOptions.SubAssetFolder = TEXT(" "); // Use the same folder as asset as they all are imported to the subdir for the prefab (see SubAssetFolder usage above) + + if (splitOptions.Type == ModelTool::ModelType::Model) + { + // These settings interfere with submesh reimporting. + splitOptions.CenterGeometry = false; + splitOptions.UseLocalOrigin = false; + + // This properly sets the transformation of the mesh during reimport. + auto* nodes = &splitOptions.Cached->Data->Nodes; + Vector3 scale = Vector3::One; + + // TODO: Improve this hack. + // This is the same hack as in ImportModel::CreatePrefab(), and it is documented further there. + auto* currentNode = &(*nodes)[meshData.NodeIndex]; + while (true) + { + if (currentNode->ParentIndex == -1) + { + scale *= currentNode->LocalTransform.Scale; + break; + } + currentNode = &(*nodes)[currentNode->ParentIndex]; + } + + splitOptions.Translation = meshData.OriginTranslation * scale * -1.0f; + } + return AssetsImportingManager::Import(context.InputPath, outputPath, &splitOptions); }; auto splitOptions = options; @@ -294,7 +321,7 @@ CreateAssetResult ImportModel::Import(CreateAssetContext& context) splitOptions.Type = ModelTool::ModelType::Model; splitOptions.ObjectIndex = groupIndex; - if (!splitImport(splitOptions, group.GetKey(), prefabObject.AssetPath)) + if (!splitImport(splitOptions, group.GetKey(), prefabObject.AssetPath, *group.First())) { prefabObjects.Add(prefabObject); } @@ -305,7 +332,9 @@ CreateAssetResult ImportModel::Import(CreateAssetContext& context) auto& animation = data->Animations[i]; splitOptions.Type = ModelTool::ModelType::Animation; splitOptions.ObjectIndex = i; - splitImport(splitOptions, animation.Name, prefabObject.AssetPath); + + MeshData empty; + splitImport(splitOptions, animation.Name, prefabObject.AssetPath, empty); } } else if (options.SplitObjects) @@ -665,7 +694,42 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, ModelDa // Setup node in hierarchy nodeToActor.Add(nodeIndex, nodeActor); nodeActor->SetName(node.Name); - nodeActor->SetLocalTransform(node.LocalTransform); + + // When use local origin is checked, it shifts everything over the same amount, including the root. This tries to work around that. + if (!(nodeIndex == 0 && options.UseLocalOrigin)) + { + // TODO: Improve this hack. + // Assimp importer has the meter -> centimeter conversion scale applied to the local transform of + // the root node, and only the root node. The OpenFBX importer has the same scale applied + // to each node, *except* the root node. This difference makes it hard to calculate the + // global scale properly. Position offsets are not calculated properly from Assimp without summing up + // the global scale because translations from Assimp don't get scaled with the global scaler option, + // but the OpenFBX importer does scale them. So this hack will end up only applying the global scale + // change if its using Assimp due to the difference in where the nodes' local transform scales are set. + auto* currentNode = &node; + Vector3 scale = Vector3::One; + while (true) + { + if (currentNode->ParentIndex == -1) + { + scale *= currentNode->LocalTransform.Scale; + break; + } + currentNode = &data.Nodes[currentNode->ParentIndex]; + } + + // Only set translation, since scale and rotation is applied earlier. + Transform positionOffset = Transform::Identity; + positionOffset.Translation = node.LocalTransform.Translation * scale; + + if (options.UseLocalOrigin) + { + positionOffset.Translation += data.Nodes[0].LocalTransform.Translation; + } + + nodeActor->SetLocalTransform(positionOffset); + } + if (nodeIndex == 0) { // Special case for root actor to link any unlinked nodes diff --git a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp index e62d03b1c..85e320ee6 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp @@ -483,7 +483,6 @@ bool ProcessMesh(ModelData& result, AssimpImporterData& data, const aiMesh* aMes } } } - return false; } @@ -645,64 +644,31 @@ bool ImportMesh(int32 index, ModelData& result, AssimpImporterData& data, String // Link mesh meshData->NodeIndex = nodeIndex; + AssimpNode* curNode = &data.Nodes[meshData->NodeIndex]; + Vector3 translation = Vector3::Zero; + Vector3 scale = Vector3::One; + Quaternion rotation = Quaternion::Identity; + + while (true) + { + translation += curNode->LocalTransform.Translation; + scale *= curNode->LocalTransform.Scale; + rotation *= curNode->LocalTransform.Orientation; + + if (curNode->ParentIndex == -1) + break; + curNode = &data.Nodes[curNode->ParentIndex]; + } + + meshData->OriginTranslation = translation; + meshData->OriginOrientation = rotation; + meshData->Scaling = scale; + if (result.LODs.Count() <= lodIndex) result.LODs.Resize(lodIndex + 1); result.LODs[lodIndex].Meshes.Add(meshData); } - auto root = data.Scene->mRootNode; - Array points; - if (root->mNumChildren == 0) - { - aiQuaternion aiQuat; - aiVector3D aiPos; - aiVector3D aiScale; - root->mTransformation.Decompose(aiScale, aiQuat, aiPos); - auto quat = ToQuaternion(aiQuat); - auto pos = ToFloat3(aiPos); - auto scale = ToFloat3(aiScale); - Transform trans = Transform(pos, quat, scale); - points.Add(trans); - } - else - { - for (unsigned int j = 0; j < root->mNumChildren; j++) - { - aiQuaternion aiQuat; - aiVector3D aiPos; - aiVector3D aiScale; - root->mChildren[j]->mTransformation.Decompose(aiScale, aiQuat, aiPos); - auto quat = ToQuaternion(aiQuat); - auto pos = ToFloat3(aiPos); - auto scale = ToFloat3(aiScale); - Transform trans = Transform(pos, quat, scale); - points.Add(trans); - } - } - - Float3 translation = Float3::Zero; - Float3 scale = Float3::Zero; - Quaternion orientation = Quaternion::Identity; - for (auto point : points) - { - translation += point.Translation; - scale += point.Scale; - orientation *= point.Orientation; - } - - if (points.Count() > 0) - { - meshData->OriginTranslation = translation / (float)points.Count(); - meshData->OriginOrientation = Quaternion::Invert(orientation); - meshData->Scaling = scale / (float)points.Count(); - } - else - { - meshData->OriginTranslation = translation; - meshData->OriginOrientation = Quaternion::Invert(orientation); - meshData->Scaling = Float3(1); - } - return false; } @@ -790,6 +756,7 @@ bool ModelTool::ImportDataAssimp(const char* path, ModelData& data, Options& opt aiProcess_GenUVCoords | aiProcess_FindDegenerates | aiProcess_FindInvalidData | + aiProcess_GlobalScale | //aiProcess_ValidateDataStructure | aiProcess_ConvertToLeftHanded; if (importMeshes) @@ -807,6 +774,7 @@ bool ModelTool::ImportDataAssimp(const char* path, ModelData& data, Options& opt // Setup import options context.AssimpImporter.SetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE, options.SmoothingNormalsAngle); context.AssimpImporter.SetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE, options.SmoothingTangentsAngle); + context.AssimpImporter.SetPropertyFloat(AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 100.0f); // Convert to cm? //context.AssimpImporter.SetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT, MAX_uint16); context.AssimpImporter.SetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, false); context.AssimpImporter.SetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, false); @@ -827,14 +795,14 @@ bool ModelTool::ImportDataAssimp(const char* path, ModelData& data, Options& opt } // Create root node - AssimpNode& rootNode = context.Nodes.AddOne(); + /*AssimpNode& rootNode = context.Nodes.AddOne(); rootNode.ParentIndex = -1; rootNode.LodIndex = 0; - rootNode.Name = TEXT("Root"); rootNode.LocalTransform = Transform::Identity; + rootNode.Name = TEXT("Root");*/ // Process imported scene nodes - ProcessNodes(context, context.Scene->mRootNode, 0); + ProcessNodes(context, context.Scene->mRootNode, -1); // Import materials if (ImportMaterials(data, context, errorMsg)) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 9578f6406..a8cb0ecd8 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -1320,7 +1320,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option ! #endif } - if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Geometry) && options.Type != ModelType::Prefab) + if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Geometry) && !(options.Type == ModelType::Prefab)) { // Perform simple nodes mapping to single node (will transform meshes to model local space) SkeletonMapping skeletonMapping(data.Nodes, nullptr); @@ -1340,7 +1340,8 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option if (skeletonMapping.SourceToSource[mesh.NodeIndex] != mesh.NodeIndex) { // Transform vertices - const auto transformationMatrix = hierarchyUpdater.CombineMatricesFromNodeIndices(skeletonMapping.SourceToSource[mesh.NodeIndex], mesh.NodeIndex); + auto transformationMatrix = hierarchyUpdater.CombineMatricesFromNodeIndices(skeletonMapping.SourceToSource[mesh.NodeIndex], mesh.NodeIndex); + if (!transformationMatrix.IsIdentity()) mesh.TransformBuffer(transformationMatrix); } @@ -1350,6 +1351,42 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option } } } + if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Geometry) && options.Type == ModelType::Prefab) + { + // Apply just the scale and rotations. + for (int32 lodIndex = 0; lodIndex < data.LODs.Count(); lodIndex++) + { + for (int32 meshIndex = 0; meshIndex < data.LODs[lodIndex].Meshes.Count(); meshIndex++) + { + auto& mesh = *data.LODs[lodIndex].Meshes[meshIndex]; + auto& node = data.Nodes[mesh.NodeIndex]; + + auto currentNode = &data.Nodes[mesh.NodeIndex]; + + Vector3 scale = Vector3::One; + Quaternion rotation = Quaternion::Identity; + while (true) + { + scale *= currentNode->LocalTransform.Scale; + rotation *= currentNode->LocalTransform.Orientation; + + if (currentNode->ParentIndex == -1) + { + break; + } + currentNode = &data.Nodes[currentNode->ParentIndex]; + } + + // Transform vertices + auto transformationMatrix = Matrix::Identity; + transformationMatrix.SetScaleVector(scale); + transformationMatrix = transformationMatrix * Matrix::RotationQuaternion(rotation); + + if (!transformationMatrix.IsIdentity()) + mesh.TransformBuffer(transformationMatrix); + } + } + } if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Animations)) { for (auto& animation : data.Animations)