Merge branch 'model-prefab-fix' of https://github.com/Menotdan/FlaxEngine into Menotdan-model-prefab-fix

This commit is contained in:
Wojtek Figat
2024-02-19 23:56:58 +01:00
3 changed files with 132 additions and 63 deletions

View File

@@ -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<bool(Options& splitOptions, const StringView& objectName, String& outputPath)> splitImport = [&context, &autoImportOutput](Options& splitOptions, const StringView& objectName, String& outputPath)
Function<bool(Options& splitOptions, const StringView& objectName, String& outputPath, MeshData& meshData)> 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

View File

@@ -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<Transform> 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))

View File

@@ -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<ModelDataNode> 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)