static void ReadMeshElements(MeshData data, ImportOptions importOptions, ref Matrix4 transform) { ReadUvSets(data); //flip with disabled FlipUVs, no flip when enabled if (!importOptions.ImportPostProcessFlags.HasFlag(ImportPostProcessFlags.FlipUVs)) { CalcMiscProcess.FlipUVs(data.Vertices, data.VertexComponents); //ToDo : !!! Доделать FlipUV для Materials - там тоже Flip для Transform текстуры .... tex->UVScaling(); tex->UVTranslation(); } ReadColor(data); TransformVertices(data, transform); int uvSetIndexForNormalsAndTangents = 0; //ToDo : пока 0, может не всегда? if (3 <= data.PolygonSize) { ReadNormals(data, importOptions, uvSetIndexForNormalsAndTangents, data.CalcCache, ref transform); if (importOptions.ImportPostProcessFlags.HasFlag(ImportPostProcessFlags.FixInfacingNormals) && data.NormalsSource != TangentsAndNormalsSource.None) { if (CalcNormalsProcess.FixInfacingNormals(data.Vertices)) { FbxImportLog.LogMessage(data.Node, "Infacing Normals Fixed"); } } ReadTangents(data, importOptions, uvSetIndexForNormalsAndTangents, data.CalcCache, ref transform); } }
public static MeshData[] ProcMesh(FbxManager manager, FbxNode node, FbxMesh mesh, ImportOptions importOptions, Matrix4 additionalTransform) { var transform = additionalTransform * FbxMath.EvaluateGlobalTransform(node).ToMatrix4(); transform = transform * FbxMath.GetGeometryOffset(node).ToMatrix4(); var converter = new FbxGeometryConverter(manager); bool success = false; try { mesh = FbxMesh.Cast(converter.Triangulate(mesh, false)); //ToDo :? Может true? Чтобы не создавать второй mesh в Attribute success = true; } catch (Exception ex) { FbxImportLog.LogError(node, "Inside Triangulate error: " + ex); } if (!success || mesh == null) { return(null); } MeshData data = ReadTriangles(mesh, node); var ret = ReadMaterialsAndSplitByMaterials(data); foreach (var m in ret) { ReadMeshElements(m, importOptions, ref transform); } return(ret); }
// key - control point index public static Dictionary <int, BoneAssignment> GetBoneAssignments(FbxMesh mesh, Skeleton skeleton) { var boneAssignments = new Dictionary <int, BoneAssignment>(); var weightLists = new Dictionary <int, List <(int, double)> >(); int skinCount = mesh.GetDeformerCount(FbxDeformer.EDeformerType.eSkin); if (skinCount == 0) { return(null); } if (1 < skinCount) { FbxImportLog.LogMessage(mesh.GetNode(), "Warning! Multiple skins for the mesh"); //??? Может ли быть в одном Mesh несколько Skins? Скорее всего нет, хоть API позволяет. } FbxSkin pSkin = FbxSkin.Cast(mesh.GetDeformer(0, FbxDeformer.EDeformerType.eSkin)); int clusterCount = pSkin.GetClusterCount(); for (int iCluster = 0; iCluster < clusterCount; iCluster++) { FbxCluster pCluster = pSkin.GetCluster(iCluster); FbxNode pLink = pCluster.GetLink(); if (pLink == null) { continue; } int weightCount = pCluster.GetControlPointIndicesCount(); if (weightCount == 0) { continue; } int boneIndex = skeleton.GetBoneIndexByNode(pLink); var weightIndices = IntArray.frompointer(pCluster.GetControlPointIndices()); var weightValues = DoubleArray.frompointer(pCluster.GetControlPointWeights()); for (int i = 0; i < weightCount; i++) { int vertexIndex = weightIndices.getitem(i); double weight = weightValues.getitem(i); if (!weightLists.TryGetValue(vertexIndex, out var lst)) { lst = new List <(int, double)>(); weightLists[vertexIndex] = lst; } lst.Add((boneIndex, weight)); } } foreach (var pair in weightLists) { boneAssignments[pair.Key] = ConvertBoneWeightListToBoneAssignment(pair.Value); } return(boneAssignments); }
//Find the cluster that links to the skeleton bone node public static FbxCluster FindCluster(FbxNode boneNode, FbxSkin[] skins, out FbxSkin skin) { for (int i = 0; i < skins.Length; i++) { skin = skins[i]; int nClusterCount = skin.GetClusterCount(); for (int j = 0; j < nClusterCount; j++) { FbxCluster fbxCluster = skin.GetCluster(j); if (fbxCluster == null) { continue; } if (fbxCluster.GetLinkMode() == FbxCluster.ELinkMode.eAdditive && fbxCluster.GetAssociateModel() != null) { FbxImportLog.LogMessage(boneNode, "Warning! Associated model."); } if (fbxCluster.GetLink()?.GetUniqueID() == boneNode.GetUniqueID()) { return(fbxCluster); } } } skin = null; return(null); }
// Compute per-face normals but store them per-vertex static void CalculateNormalForFace(VertexInfo[] vertices, RangeI indexRange, bool normalize) { if (indexRange.Size < 3) { for (int i = indexRange.Minimum; i < indexRange.Maximum; i++) { vertices[i].Vertex.Normal = new Vector3F(Single.NaN, Single.NaN, Single.NaN); } FbxImportLog.LogWarning("To calculate the tangents a polygon must have > 2 vertices"); } ref Vector3F p0 = ref vertices[indexRange.Minimum].Vertex.Position;
static void CalculateTangentForFace(VertexInfo[] vertices, RangeI indexRange, Vector3F[] tangents, Vector3F[] bitangents, GetTextureCoord getTextureCoord = null) { // triangle or polygon... we always use only the first three indices. A polygon // is supposed to be planar anyways.... if (indexRange.Size < 3) { for (int i = indexRange.Minimum; i < indexRange.Maximum; i++) { tangents[i] = new Vector3F(float.NaN, float.NaN, float.NaN); bitangents[i] = new Vector3F(float.NaN, float.NaN, float.NaN); } FbxImportLog.LogWarning("To calculate the tangents a polygon must have > 2 vertices"); return; } ref StandardVertex pt0 = ref vertices[indexRange.Minimum].Vertex;
static void ReadColor(MeshData data) { FbxLayerElementVertexColor pVertexColors = data.Mesh.GetElementVertexColor(); if (pVertexColors == null) { for (int i = 0; i < data.Vertices.Length; i++) { data.Vertices[i].Vertex.Color = new ColorValue(1, 1, 1); } return; } var mappingMode = pVertexColors.GetMappingMode(); if (!CheckPolygonVertexOrControlPoint(mappingMode)) { FbxImportLog.LogWarning(data.Node, $"has unsupported VertexColors mapping mode: {pVertexColors.GetMappingMode()}"); return; } data.VertexComponents |= StandardVertex.Components.Color; var indexArray = pVertexColors.GetReferenceMode() != FbxLayerElement.EReferenceMode.eDirect ? pVertexColors.GetIndexArray() : null; var directArray = pVertexColors.GetDirectArray(); for (int i = 0; i < data.Vertices.Length; i++) { ref VertexInfo vertex = ref data.Vertices[i]; FbxColor color = null; switch (pVertexColors.GetMappingMode()) { case FbxLayerElement.EMappingMode.eByPolygonVertex: color = directArray.GetAt(indexArray?.GetAt(vertex.PolygonVertexIndex) ?? vertex.PolygonVertexIndex); break; case FbxLayerElement.EMappingMode.eByControlPoint: color = directArray.GetAt(indexArray?.GetAt(vertex.ControlPointIndex) ?? vertex.ControlPointIndex); break; } data.Vertices[i].Vertex.Color = color?.ToColorValue() ?? new ColorValue(1, 1, 1); }
//Calculates global transform of the node. //This function was taken from FBX SDK/Smaples/Transformations. Originaly it did not take into account AxisSystem, so the corrections to use AxisSystem was added. //Has the fbx sdk equivalent pNode.EvaluateGlobalTransform(); // //Note: FBX Documentation for ConvertScene states - "The adjustment will affect the translation animation curves and the objects pivots values //(the rotation transformation is applied as a pre-rotation transform therefore the rotation animation curves do not need to be transformed)" //But ConvertScene, does not realy change node.PreRotation, these changes are in the matrix : node.GetScene().GetGlobalSettings().GetAxisSystem().GetMatrix // /* * Terminology: * Suffix "M" means this is a matrix, suffix "V" means it is a vector. * T is translation. * R is rotation. * S is scaling. * SH is shear. * GlobalRM(x) means the Global Rotation Matrix of node "x". * GlobalRM(P(x)) means the Global Rotation Matrix of the parent node of node "x". * All other transforms are described in the similar way. * * The algorithm description: * To calculate global transform of a node x according to different InheritType, * we need to calculate GlobalTM(x) and [GlobalRM(x) * (GlobalSHM(x) * GlobalSM(x))] separately. * GlobalM(x) = GlobalTM(x) * [GlobalRM(x) * (GlobalSHM(x) * GlobalSM(x))]; * * InhereitType = RrSs: * GlobalRM(x) * (GlobalSHM(x) * GlobalSM(x)) = GlobalRM(P(x)) * LocalRM(x) * [GlobalSHM(P(x)) * GlobalSM(P(x))] * LocalSM(x); * * InhereitType = RSrs: * GlobalRM(x) * (GlobalSHM(x) * GlobalSM(x)) = GlobalRM(P(x)) * [GlobalSHM(P(x)) * GlobalSM(P(x))] * LocalRM(x) * LocalSM(x); * * InhereitType = Rrs: * GlobalRM(x) * (GlobalSHM(x) * GlobalSM(x)) = GlobalRM(P(x)) * LocalRM(x) * LocalSM(x); * * LocalM(x)= TM(x) * RoffsetM(x) * RpivotM(x) * RpreM(x) * RM(x) * RpostM(x) * RpivotM(x)^-1 * SoffsetM(x) *SpivotM(x) * SM(x) * SpivotM(x)^-1 * LocalTWithAllPivotAndOffsetInformationV(x) = Local(x).GetT(); * GlobalTV(x) = GlobalM(P(x)) * LocalTWithAllPivotAndOffsetInformationV(x); * * Notice: FBX SDK does not support shear yet, so all local transform won't have shear. * However, global transform might bring in shear by combine the global transform of node in higher hierarchy. * For example, if you scale the parent by a non-uniform scale and then rotate the child node, then a shear will * be generated on the child node's global transform. * In this case, we always compensates shear and store it in the scale matrix too according to following formula: * Shear*Scaling = RotationMatrix.Inverse * TranslationMatrix.Inverse * WholeTranformMatrix */ // ReSharper disable InconsistentNaming static FbxAMatrix CalculateGlobalTransform(FbxNode node) { if (node == null) { var ret = new FbxAMatrix(); ret.SetIdentity(); return(ret); } var lTranlationM = new FbxAMatrix(); var lScalingM = new FbxAMatrix(); var lScalingPivotM = new FbxAMatrix(); var lScalingOffsetM = new FbxAMatrix(); var lRotationOffsetM = new FbxAMatrix(); var lRotationPivotM = new FbxAMatrix(); var lPreRotationM = new FbxAMatrix(); var lRotationM = new FbxAMatrix(); var lPostRotationM = new FbxAMatrix(); FbxAMatrix lParentGX = new FbxAMatrix(); FbxAMatrix lGlobalT = new FbxAMatrix(); FbxAMatrix lGlobalRS = new FbxAMatrix(); // Construct translation matrix FbxVector4 lTranslation = new FbxVector4(node.LclTranslation.Get()); //The fourth component of this object is assigned 1. lTranlationM.SetT(lTranslation); // Construct rotation matrices FbxVector4 lRotation = new FbxVector4(node.LclRotation.Get()); FbxVector4 lPreRotation = new FbxVector4(node.PreRotation.Get()); FbxVector4 lPostRotation = new FbxVector4(node.PostRotation.Get()); lRotationM.SetR(lRotation); lPreRotationM.SetR(lPreRotation); lPostRotationM.SetR(lPostRotation); // Construct scaling matrix FbxVector4 lScaling = new FbxVector4(node.LclScaling.Get()); lScalingM.SetS(lScaling); // Construct offset and pivot matrices FbxVector4 lScalingOffset = new FbxVector4(node.ScalingOffset.Get()); FbxVector4 lScalingPivot = new FbxVector4(node.ScalingPivot.Get()); FbxVector4 lRotationOffset = new FbxVector4(node.RotationOffset.Get()); FbxVector4 lRotationPivot = new FbxVector4(node.RotationPivot.Get()); lScalingOffsetM.SetT(lScalingOffset); lScalingPivotM.SetT(lScalingPivot); lRotationOffsetM.SetT(lRotationOffset); lRotationPivotM.SetT(lRotationPivot); // Calculate the global transform matrix of the parent node FbxNode lParentNode = node.GetParent(); if (lParentNode != null) { //Children of the root node must take into account the axis matrix. //Warning: this function CalculateGlobalTransform was taken from FBX SDK/Smaples/Transformations. Originaly it did not take into account AxisSystem if (lParentNode.GetParent() == null) { FbxAMatrix axisMarix = new FbxAMatrix(); node.GetScene().GetGlobalSettings().GetAxisSystem().GetMatrix(axisMarix); lPreRotationM = axisMarix.mul(lPreRotationM); } lParentGX = CalculateGlobalTransform(lParentNode); } else { lParentGX.SetIdentity(); } //Construct Global Rotation FbxAMatrix lParentGRM = new FbxAMatrix(); FbxVector4 lParentGR = lParentGX.GetR(); lParentGRM.SetR(lParentGR); var lLRM = lPreRotationM.mul(lRotationM).mul(lPostRotationM); //Construct Global Shear*Scaling //FBX SDK does not support shear, to patch this, we use: //Shear*Scaling = RotationMatrix.Inverse * TranslationMatrix.Inverse * WholeTranformMatrix FbxAMatrix lParentTM = new FbxAMatrix(); FbxVector4 lParentGT = lParentGX.GetT(); lParentTM.SetT(lParentGT); var lParentGRSM = lParentTM.Inverse().mul(lParentGX); var lParentGSM = lParentGRM.Inverse().mul(lParentGRSM); var lLSM = lScalingM; //Do not consider translation now FbxTransform.EInheritType lInheritType = node.InheritType.Get(); if (lInheritType == FbxTransform.EInheritType.eInheritRrSs) { lGlobalRS = lParentGRM.mul(lLRM).mul(lParentGSM).mul(lLSM); } else if (lInheritType == FbxTransform.EInheritType.eInheritRSrs) { lGlobalRS = lParentGRM.mul(lParentGSM).mul(lLRM).mul(lLSM); } else if (lInheritType == FbxTransform.EInheritType.eInheritRrs) { if (lParentNode != null) { FbxAMatrix lParentLSM = new FbxAMatrix(); FbxVector4 lParentLS = new FbxVector4(lParentNode.LclScaling.Get()); lParentLSM.SetS(lParentLS); FbxAMatrix lParentGSM_noLocal = lParentGSM.mul(lParentLSM.Inverse()); lGlobalRS = lParentGRM.mul(lLRM).mul(lParentGSM_noLocal).mul(lLSM); } else { lGlobalRS = lParentGRM.mul(lLRM).mul(lLSM); } } else { FbxImportLog.LogError(node, "error, unknown inherit type!"); } // Construct translation matrix // Calculate the local transform matrix var lTransform = lTranlationM.mul(lRotationOffsetM).mul(lRotationPivotM).mul(lPreRotationM).mul(lRotationM).mul(lPostRotationM) .mul(lRotationPivotM.Inverse()).mul(lScalingOffsetM).mul(lScalingPivotM).mul(lScalingM).mul(lScalingPivotM.Inverse()); FbxVector4 lLocalTWithAllPivotAndOffsetInfo = lTransform.GetT(); // Calculate global translation vector according to: // GlobalTranslation = ParentGlobalTransform * LocalTranslationWithPivotAndOffsetInfo FbxVector4 lGlobalTranslation = lParentGX.MultT(lLocalTWithAllPivotAndOffsetInfo); lGlobalT.SetT(lGlobalTranslation); //Construct the whole global transform lTransform = lGlobalT.mul(lGlobalRS); return(lTransform); }