private void SetupNodeMetadata(Model model, aiNode nd) { var props = model.Props; var unparsedProperties = props.GetUnparsedProperties(); // create metadata on node int numStaticMetaData = 2; var data = nd.MetaData; data.NumProperties = unparsedProperties.Count + numStaticMetaData; data.Keys = new string[data.NumProperties]; data.Values = new AssimpSharp.MetadataEntry[data.NumProperties]; int index = 0; // find user defined properties (3ds Max) data.Set<string>(index++, "UserProperties", PropertyHelper.PropertyGet<string>(props, "UDP3DSMAX", "")); unparsedProperties.Remove("UDP3DSMAX"); data.Set(index++, "IsNull", model.IsNull ? true : false); foreach (var prop in unparsedProperties) { var interpreted = prop.Value.As<TypedProperty<bool>>(); if (interpreted != null) { data.Set(index++, prop.Key, interpreted.Value); } } }
private void ConvertCamera(Model model, Camera cam) { var camera = new AssimpSharp.Camera() { Name = FixNodeName(model.Name), Aspect = cam.AspectWidth.Value / cam.AspectHeight.Value, Position = cam.Position.Value, LookAt = cam.InterestPosition.Value - cam.Position.Value, HorizontalFOV = SharpDX.MathUtil.DegreesToRadians(cam.FieldOfView.Value) }; Cameras.Add(camera); }
private int ConvertMeshSingleMaterial(MeshGeometry mesh, Model model, Matrix nodeGlobalTransform) { var mindices = mesh.MaterialIndices; var outMesh = SetupEmptyMesh(mesh); var vertices = mesh.Vertices; var faces = mesh.FaceIndexCounts; // copy vertices outMesh.NumVertices = vertices.Count; outMesh.Vertices = new Vector3[vertices.Count]; vertices.CopyTo(outMesh.Vertices); // generate dummy faces outMesh.NumFaces = faces.Count; var fac = outMesh.Faces = new AssimpSharp.Face[faces.Count]; for(int i=0; i<faces.Count; i++) { fac[i] = new AssimpSharp.Face(); } int cursor = 0; var facIndex = 0; foreach(var pcount in faces) { var f = fac[facIndex++]; f.Indices = new int[pcount]; switch(pcount) { case 1: outMesh.PrimitiveTypes |= AssimpSharp.PrimitiveType.Point; break; case 2: outMesh.PrimitiveTypes |= AssimpSharp.PrimitiveType.Line; break; case 3: outMesh.PrimitiveTypes |= AssimpSharp.PrimitiveType.Triangle; break; default: outMesh.PrimitiveTypes |= AssimpSharp.PrimitiveType.Polygon; break; } for(int i=0; i<pcount; ++i) { f.Indices[i] = cursor++; } } // copy normals var normals = mesh.Normals; if (normals.Count > 0) { Debug.Assert(normals.Count == vertices.Count); outMesh.Normals = new Vector3[vertices.Count]; normals.CopyTo(outMesh.Normals); } // copy tangents - assimp requires both tangents and bitangents (binormals) // to be present, or neither of them. Compute binormals from normals // and tangents if needed. var tangents = mesh.Tangents; var binormals = mesh.Binormals; if (tangents.Count > 0) { var tempBinormals = new List<Vector3>(); if (binormals.Count == 0) { if (normals.Count > 0) { tempBinormals = new List<Vector3>(normals.Count); for(int i=0; i<tangents.Count; i++) { tempBinormals.Add(Vector3.Cross(normals[i], tangents[i])); } binormals.Clear(); for(int i=0; i<tempBinormals.Count; i++) { binormals.Add(tempBinormals[i]); } } else { binormals = null; } } if (binormals != null) { Debug.Assert(tangents.Count == vertices.Count); Debug.Assert(binormals.Count == vertices.Count); outMesh.Tangents = new Vector3[vertices.Count]; tangents.CopyTo(outMesh.Tangents); outMesh.Bitangents = new Vector3[vertices.Count]; binormals.CopyTo(outMesh.Bitangents); } } // copy texture coords for (int i=0; i<Mesh.AI_MAX_NUMBER_OF_TEXTURECOORDS; i++) { var uvs = mesh.GetTextureCoords(i); if (uvs.Count == 0) { break; } var outUv = outMesh.TextureCoords[i] = new Vector3[vertices.Count]; for(int j=0; j<uvs.Count; j++) { outUv[j] = new Vector3(uvs[j].X, uvs[j].Y, 0); } } // copy vertex colors for (int i = 0; i < aiMesh.AI_MAX_NUMBER_OF_COLOR_SETS; ++i) { var colors = mesh.GetVertexColors(i); if (colors.Count == 0) { break; } outMesh.Colors[i] = new Color4[vertices.Count]; colors.CopyTo(outMesh.Colors[i]); } if (!Doc.Settings.ReadMaterials || mindices.Count == 0) { //FBXImporter.LogError("no material assigned to mesh, setting default material"); outMesh.MaterialIndex = GetDefaultMaterial(); } else { ConvertMaterialForMesh(outMesh, model, mesh, mindices[0]); } if (Doc.Settings.ReadWeights && mesh.DeformerSkin != null) { ConvertWeights(outMesh, model, mesh, nodeGlobalTransform, NO_MATERIAL_SEPARATION); } return Meshes.Count - 1; }
private void ConvertTransformOrder_TRStoSRT( AssimpSharp.QuatKey[] outQuat, AssimpSharp.VectorKey[] outScale, AssimpSharp.VectorKey[] outTranslation, KeyFrameListList scaling, KeyFrameListList translation, KeyFrameListList rotation, KeyTimeList times, ref double maxTime, ref double minTime, Model.RotOrder order, Vector3 defScasle, Vector3 defTranslate, Quaternion defRotation) { if (rotation.Count > 0) { InterpolateKeys(outQuat, times, rotation, false, ref maxTime, ref minTime, order); } else { for (int i = 0; i < times.Count; i++) { outQuat[i].Time = CONVERT_FBX_TIME(times[i]) * AnimFps; outQuat[i].Value = defRotation; } } if (scaling.Count > 0) { InterpolateKeys(outScale, times, scaling, true, ref maxTime, ref minTime); } else { for (int i = 0; i < times.Count; i++) { outScale[i].Time = CONVERT_FBX_TIME(times[i]) * AnimFps; outScale[i].Value = defScasle; } } if (translation.Count > 0) { InterpolateKeys(outTranslation, times, translation, false, ref maxTime, ref minTime); } else { for (int i = 0; i < times.Count; i++) { outTranslation[i].Time = CONVERT_FBX_TIME(times[i]) * AnimFps; outTranslation[i].Value = defTranslate; } } var count = times.Count; for (int i = 0; i < count; i++) { var r = outQuat[i].Value; var s = outScale[i].Value; var t = outTranslation[i].Value; Matrix mat; Matrix.Translation(ref t, out mat); mat *= Matrix.RotationQuaternion(r); mat *= Matrix.Scaling(s); mat.Decompose(out s, out r, out t); outQuat[i].Value = r; outScale[i].Value = s; outTranslation[i].Value = t; } }
private void ConvertMaterialForMesh(aiMesh result, Model model, MeshGeometry geo, int materialIndex) { // locate source materials for this mesh var mats = model.Materials; if (materialIndex >= mats.Count || materialIndex < 0) { FBXImporter.LogError("material index out of bounds, setting default material"); result.MaterialIndex = GetDefaultMaterial(); return; } var mat = mats[materialIndex]; int it; if (MaterialsConverted.TryGetValue(mat, out it)) { result.MaterialIndex = it; return; } result.MaterialIndex = ConvertMaterial(mat, geo); this.MaterialsConverted[mat] = result.MaterialIndex; }
private List<int> ConvertMeshMultiMaterial(MeshGeometry mesh, Model model, Matrix nodeGlobalTransform) { var mindices = mesh.MaterialIndices; Debug.Assert(mindices.Count > 0); var had = new HashSet<int>(); var indices = new List<int>(); foreach (var index in mindices) { if (!had.Contains(index)) { indices.Add(ConvertMeshMultiMaterial(mesh, model, index, nodeGlobalTransform)); had.Add(index); } } return indices; }
/// <remarks> /// memory for output_nodes will be managed by the caller /// </remarks> private void GenerateTransformationNodeChain(Model model, out List<aiNode> outputNodes) { outputNodes = new List<aiNode>(); var props = model.Props; var rot = model.RotationOrder.Value; bool ok; var chain = new Matrix[Enum.GetValues(typeof(TransformationComp)).Length]; for(int i=0; i<chain.Length; i++) { chain[i] = Matrix.Identity; } float zeroEpsilon = 1e-6f; bool isComplex = false; Vector3 preRotation = PropertyHelper.PropertyGet<Vector3>(props, "PreRotation", out ok); if (ok && preRotation.LengthSquared() > zeroEpsilon) { isComplex = true; GetRotationMatrix(rot, preRotation, out chain[(int)TransformationComp.PreRotation]); } Vector3 postRotation = PropertyHelper.PropertyGet<Vector3>(props, "PostRotation", out ok); if (ok && postRotation.LengthSquared() > zeroEpsilon) { isComplex = true; GetRotationMatrix(rot, postRotation, out chain[(int)TransformationComp.PostRotation]); } Vector3 RotationPivot = PropertyHelper.PropertyGet<Vector3>(props, "RotationPivot", out ok); if (ok && RotationPivot.LengthSquared() > zeroEpsilon) { isComplex = true; Matrix.Translation(ref RotationPivot, out chain[(int)TransformationComp.RotationPivot]); chain[(int)TransformationComp.RotationPivotInverse] = Matrix.Translation(-RotationPivot); } Vector3 RotationOffset = PropertyHelper.PropertyGet<Vector3>(props, "RotationOffset", out ok); if (ok && RotationOffset.LengthSquared() > zeroEpsilon) { isComplex = true; Matrix.Translation(ref RotationOffset, out chain[(int)TransformationComp.RotationOffset]); } Vector3 ScalingOffset = PropertyHelper.PropertyGet<Vector3>(props, "ScalingOffset", out ok); if (ok && ScalingOffset.LengthSquared() > zeroEpsilon) { isComplex = true; Matrix.Translation(ref ScalingOffset, out chain[(int)TransformationComp.ScalingOffset]); } Vector3 ScalingPivot = PropertyHelper.PropertyGet<Vector3>(props, "ScalingPivot", out ok); if (ok && ScalingPivot.LengthSquared() > zeroEpsilon) { isComplex = true; chain[(int)TransformationComp.ScalingPivot] = Matrix.Translation(ScalingPivot); chain[(int)TransformationComp.ScalingPivotInverse] = Matrix.Translation(-ScalingPivot); } Vector3 Translation = PropertyHelper.PropertyGet<Vector3>(props, "Lcl Translation", out ok); if (ok && Translation.LengthSquared() > zeroEpsilon) { Matrix.Translation(ref Translation, out chain[(int)TransformationComp.Translation]); } Vector3 Scaling = PropertyHelper.PropertyGet<Vector3>(props, "Lcl Scaling", out ok); if (ok && Math.Abs(Scaling.LengthSquared() - 1.0f) > zeroEpsilon) { Matrix.Scaling(ref Scaling, out chain[(int)TransformationComp.Scaling]); } Vector3 Rotation = PropertyHelper.PropertyGet<Vector3>(props, "Lcl Rotation", out ok); if (ok && Rotation.LengthSquared() > zeroEpsilon) { GetRotationMatrix(rot, Rotation, out chain[(int)TransformationComp.Rotation]); } Vector3 GeometricScaling = PropertyHelper.PropertyGet<Vector3>(props, "GeometricScaling", out ok); if (ok && Math.Abs(GeometricScaling.LengthSquared() - 1.0f) > zeroEpsilon) { Matrix.Scaling(ref GeometricScaling, out chain[(int)TransformationComp.GeometricScaling]); } Vector3 GeometricRotation = PropertyHelper.PropertyGet<Vector3>(props, "GeometricRotation", out ok); if (ok && GeometricRotation.LengthSquared() > zeroEpsilon) { GetRotationMatrix(rot, GeometricRotation, out chain[(int)TransformationComp.GeometricRotation]); } Vector3 GeometricTranslation = PropertyHelper.PropertyGet<Vector3>(props, "GeometricTranslation", out ok); if (ok && GeometricTranslation.LengthSquared() > zeroEpsilon) { Matrix.Translation(ref GeometricTranslation, out chain[(int)TransformationComp.GeometricTranslation]); } // is_complex needs to be consistent with NeedsComplexTransformationChain() // or the interplay between this code and the animation converter would // not be guaranteed. Debug.Assert(NeedsComplexTransformationChain(model) == isComplex); string name = FixNodeName(model.Name); // now, if we have more than just Translation, Scaling and Rotation, // we need to generate a full node chain to accommodate for assimp's // lack to express pivots and offsets. if (isComplex && this.Doc.Settings.PreservePivots) { FBXImporter.LogInfo("generating full transformation chain for node: " + name); // query the anim_chain_bits dictionary to find out which chain elements // have associated node animation channels. These can not be dropped // even if they have identity transform in bind pose. uint animChainBitmask; if (!NodeAnimChainBits.TryGetValue(name, out animChainBitmask)) { animChainBitmask = 0; } uint bit = 0x1; for (int i = 0; i < Enum.GetValues(typeof(TransformationComp)).Length; ++i, bit <<= 1) { TransformationComp comp = (TransformationComp)i; if (chain[i].IsIdentity && (animChainBitmask & bit) == 0) { continue; } aiNode nd = new aiNode(); outputNodes.Add(nd); nd.Name = NameTransformationChainNode(name, comp); nd.Transformation = chain[i]; } Debug.Assert(outputNodes.Count > 0); return; } // else, we can just multiply the matrices together aiNode nd_ = new aiNode(); outputNodes.Add(nd_); nd_.Name = name; nd_.Transformation = Matrix.Identity; for (int i = 0; i < Enum.GetValues(typeof(TransformationComp)).Length; ++i) { nd_.Transformation = nd_.Transformation * chain[i]; } }
private void ConvertLight(Model model, Light light) { var outLight = new AssimpSharp.Light(); Lights.Add(outLight); outLight.Name = FixNodeName(model.Name); float intensity = light.Intensity.Value; Vector3 col = light.Color.Value; outLight.ColorDiffuse = new SharpDX.Vector3(col.X, col.Y, col.Z); outLight.ColorDiffuse.X *= intensity; outLight.ColorDiffuse.Y *= intensity; outLight.ColorDiffuse.Z *= intensity; outLight.ColorSpecular = outLight.ColorDiffuse; switch (light.LightType.Value) { case Light.Type.Point: outLight.Type = AssimpSharp.LightSourceType.Point; break; case Light.Type.Directional: outLight.Type = AssimpSharp.LightSourceType.Directional; break; case Light.Type.Spot: outLight.Type = AssimpSharp.LightSourceType.Spot; outLight.AngleOuterCone = SharpDX.MathUtil.DegreesToRadians(light.OuterAngle.Value); outLight.AngleInnerCone = SharpDX.MathUtil.DegreesToRadians(light.InnerAngle.Value); break; case Light.Type.Area: outLight.Type = AssimpSharp.LightSourceType.Undefined; break; case Light.Type.Volume: outLight.Type = AssimpSharp.LightSourceType.Undefined; break; default: System.Diagnostics.Debug.Assert(false); break; } switch (light.DecayType.Value) { case Light.Decay.None: outLight.AttenuationConstant = 1.0f; break; case Light.Decay.Linear: outLight.AttenuationLinear = 1.0f; break; case Light.Decay.Quadratic: outLight.AttenuationQuadratic = 1.0f; break; case Light.Decay.Cubic: Debug.WriteLine("cannot represent cubic attenuation, set to Quadratic"); outLight.AttenuationQuadratic = 1.0f; break; } }
private AssimpSharp.NodeAnim GenerateScalingNodeAnim(string name, Model target, List<AnimationCurveNode> curves, LayerMap layerMap, long start, long stop, double maxTime, double minTime) { var na = new AssimpSharp.NodeAnim(); na.NodeName = name; ConvertScaleKeys(na, curves, layerMap, start, stop, ref maxTime, ref minTime); // dummy rotation key na.RotationKeys = new AssimpSharp.QuatKey[1]; na.RotationKeys[0] = new AssimpSharp.QuatKey(0, new Quaternion()); // dummy position key na.PositionKeys = new AssimpSharp.VectorKey[1]; na.PositionKeys[0] = new AssimpSharp.VectorKey(0, new Vector3()); return na; }
/// <summary> /// generate node anim, extracting only Rotation, Scaling and Translation from the given chain /// </summary> private AssimpSharp.NodeAnim GenerateSimpleNodeAnim(string name, Model target, List<AnimationCurveNode>[] chain, LayerMap layerMap, long start, long stop, ref double maxTime, ref double minTime, bool reverseOrder = false) { var na = new AssimpSharp.NodeAnim(); na.NodeName = name; var props = target.Props; // need to convert from TRS order to SRT? if (reverseOrder) { var defScale = Vector3.Zero; var defTranslate = Vector3.Zero; var defRot = Quaternion.Identity; var scaling = new KeyFrameListList(); var translation = new KeyFrameListList(); var rotation = new KeyFrameListList(); if (chain[(int)TransformationComp.Scaling] != null) { scaling = GetKeyframeList(chain[(int)TransformationComp.Scaling], start, stop); } else { defScale = PropertyHelper.PropertyGet(props, "Lcl Scaling", new Vector3(1, 1, 1)); } if (chain[(int)TransformationComp.Translation] != null) { translation = GetKeyframeList(chain[(int)TransformationComp.Translation], start, stop); } else { defTranslate = PropertyHelper.PropertyGet(props, "Lcl Translation", new Vector3(0, 0, 0)); } if (chain[(int)TransformationComp.Rotation] != null) { rotation = GetKeyframeList(chain[(int)TransformationComp.Rotation], start, stop); } else { defRot = EulerToQuaternion(PropertyHelper.PropertyGet(props, "Lcl Rotation", new Vector3(0, 0, 0)), target.RotationOrder.Value); } var joined = new KeyFrameListList(); joined.AddRange(scaling); joined.AddRange(translation); joined.AddRange(rotation); var times = GetKeyTimeList(joined); var outQuat = new AssimpSharp.QuatKey[times.Count]; var outScale = new AssimpSharp.VectorKey[times.Count]; var outTranslation = new AssimpSharp.VectorKey[times.Count]; if (times.Count > 0) { ConvertTransformOrder_TRStoSRT(outQuat, outScale, outTranslation, scaling, translation, rotation, times, ref maxTime, ref minTime, target.RotationOrder.Value, defScale, defTranslate, defRot); } // XXX remove duplicates / redundant keys which this operation did // likely produce if not all three channels were equally dense. na.ScalingKeys = outScale; na.RotationKeys = outQuat; na.PositionKeys = outTranslation; } else { // if a particular transformation is not given, grab it from // the corresponding node to meet the semantics of aiNodeAnim, // which requires all of rotation, scaling and translation // to be set. if (chain[(int)TransformationComp.Scaling] != null) { ConvertScaleKeys(na, chain[(int)TransformationComp.Scaling], layerMap, start, stop, ref maxTime, ref minTime); } else { na.ScalingKeys = new AssimpSharp.VectorKey[1]; na.ScalingKeys[0].Time = 0; na.ScalingKeys[0].Value = PropertyHelper.PropertyGet(props, "Lcl Scaling", new Vector3(1, 1, 1)); } if (chain[(int)TransformationComp.Rotation] != null) { ConvertRotationKeys(na, chain[(int)TransformationComp.Rotation], layerMap, start, stop, ref maxTime, ref minTime, target.RotationOrder.Value); } else { na.RotationKeys = new AssimpSharp.QuatKey[1]; na.RotationKeys[0].Time = 0; na.RotationKeys[0].Value = EulerToQuaternion(PropertyHelper.PropertyGet(props, "Lcl Rotation", new Vector3(0, 0, 0)), target.RotationOrder.Value); } if (chain[(int)TransformationComp.Translation] != null) { ConvertTranslationKeys(na, chain[(int)TransformationComp.Translation], layerMap, start, stop, ref maxTime, ref minTime); } else { na.PositionKeys = new AssimpSharp.VectorKey[1]; na.PositionKeys[0].Time = 0; na.PositionKeys[0].Value = PropertyHelper.PropertyGet(props, "Lcl Translation", new Vector3(0, 0, 0)); } } return na; }
private AssimpSharp.NodeAnim GenerateRotationNodeAnim(string name, Model target, List<AnimationCurveNode> curves, LayerMap layerMap, long start, long stop, ref double maxTime, ref double minTime) { var na = new AssimpSharp.NodeAnim(); na.NodeName = name; ConvertRotationKeys(na, curves, layerMap, start, stop, ref maxTime, ref minTime, target.RotationOrder.Value); // dummy scaling key na.ScalingKeys = new AssimpSharp.VectorKey[1]; na.ScalingKeys[0].Time = 0; na.ScalingKeys[0].Value = new Vector3(1.0f, 1.0f, 1.0f); // dummy position key na.PositionKeys = new AssimpSharp.VectorKey[1]; na.PositionKeys[0].Time = 0; na.PositionKeys[0].Value = new Vector3(); return na; }
private Quaternion EulerToQuaternion(Vector3 rot, Model.RotOrder order) { Matrix m; GetRotationMatrix(order, rot, out m); return Quaternion.RotationMatrix(m); }
private void ConvertWeights(aiMesh result, Model model, MeshGeometry geo, Matrix nodeGlobalTransform, int materialIndex = FbxConverter.NO_MATERIAL_SEPARATION, int[] outputVertStartIndices = null) { Debug.Assert(geo.DeformerSkin != null); var outIndices = new List<int>(); var indexOutIndices = new List<int>(); var countOutIndices = new List<int>(); var sk = geo.DeformerSkin; var bones = new List<aiBone>(sk.Clusters.Count); var noMatCheck = (materialIndex == FbxConverter.NO_MATERIAL_SEPARATION); Debug.Assert(noMatCheck || outputVertStartIndices != null); try { foreach (var cluster in sk.Clusters) { Debug.Assert(cluster != null); var indices = cluster.Indices; if (indices.Count == 0) { continue; } var mats = geo.MaterialIndices; var ok = false; int noIndexSentine = int.MaxValue; countOutIndices.Clear(); indexOutIndices.Clear(); outIndices.Clear(); foreach (var index in indices) { var outIdx = geo.ToOutputVertexIndex((uint)index); var count = outIdx.Count; // ToOutputVertexIndex only returns NULL if index is out of bounds // which should never happen Debug.Assert(outIdx.Count > 0); indexOutIndices.Add(noIndexSentine); countOutIndices.Add(0); for (int i = 0; i < count; i++) { if (noMatCheck || mats[(int)geo.FaceForVertexIndex((uint)outIdx.Array[i+outIdx.Offset])] == materialIndex) { if (indexOutIndices[indexOutIndices.Count] == noIndexSentine) { indexOutIndices[indexOutIndices.Count] = outIndices.Count; } if (noMatCheck) { outIndices.Add((int)outIdx.Array[i+outIdx.Offset]); } else { int it; for (it = 0; it < outputVertStartIndices.Length; it++) { if (outIdx.Array[i+outIdx.Offset] == outputVertStartIndices[it]) { break; } } outIndices.Add(it); } countOutIndices[countOutIndices.Count] += 1; ok = true; } } } // if we found at least one, generate the output bones // XXX this could be heavily simplified by collecting the bone // data in a single step. if (ok) { ConvertCluster(bones, model, cluster, outIndices.ToArray(), indexOutIndices.ToArray(), countOutIndices.ToArray(), nodeGlobalTransform); } } } catch (Exception e) { bones.Clear(); throw (e); } if (bones.Count == 0) { return; } result.Bones = bones.ToArray(); result.NumBones = bones.Count; }
private void ConvertCameras(Model model) { var nodeAttrs = model.Attributes; foreach (var attr in nodeAttrs) { var camera = attr as Camera; if (camera != null) { ConvertCamera(model, camera); } } }
private AssimpSharp.NodeAnim GenerateTranslationNodeAnim(string name, Model target, List<AnimationCurveNode> curves, LayerMap layerMap, long start, long stop, double maxTime, double minTime, bool inverse = false) { var na = new AssimpSharp.NodeAnim(); na.NodeName = name; ConvertRotationKeys(na, curves, layerMap, start, stop, ref maxTime, ref minTime, target.RotationOrder.Value); if (inverse) { for (int i = 0; i < na.PositionKeys.Length; i++) { na.PositionKeys[i].Value *= -1.0f; } } // dummy scaling key na.ScalingKeys = new AssimpSharp.VectorKey[1]; na.ScalingKeys[0].Time = 0; na.ScalingKeys[0].Value = new Vector3(1, 1, 1); // dummy rotation key na.RotationKeys = new AssimpSharp.QuatKey[1]; na.RotationKeys[0].Time = 0; na.RotationKeys[0].Value = Quaternion.Identity; return na; }
private void ConvertCluster(List<aiBone> bones, Model model, Cluster cl, int[] outIndices, int[] indexOutIndices, int[] countOutIndices, Matrix nodeGlobalTransform) { var bone = new aiBone(); bones.Add(bone); bone.Name = FixNodeName(cl.TargetNode.Name); bone.OffsetMatrix = cl.TransformLink; bone.OffsetMatrix.Invert(); bone.OffsetMatrix = bone.OffsetMatrix * nodeGlobalTransform; bone.NumWeights = outIndices.Length; var cursor = bone.Weights = new aiVertexWeight[outIndices.Length]; int cursor_index = 0; int noIndexSentinel = int.MaxValue; var weights = cl.Weights; int c = indexOutIndices.Length; for (int i = 0; i < c; i++) { int indexIndex = indexOutIndices[i]; if (indexIndex == noIndexSentinel) { continue; } int cc = countOutIndices[i]; for (int j = 0; j < cc; j++) { cursor[cursor_index].VertexId = outIndices[indexIndex + j]; cursor[cursor_index].Weight = weights[i]; j++; } } }
private void GetRotationMatrix(Model.RotOrder mode, Vector3 rotation, out Matrix result) { if (mode == Model.RotOrder.SphericXYZ) { FBXImporter.LogError("Unsupported RotationMode: SphericXYZ"); result = new Matrix(); return; } const float angleEpsilon = 1e-6f; result = Matrix.Identity; var isId = new bool[3] { true, true, true }; var temp = new Matrix[3]; if (Math.Abs(rotation.Z) > angleEpsilon) { Matrix.RotationZ(MathUtil.DegreesToRadians(rotation.Z), out temp[2]); isId[2] = false; } if (Math.Abs(rotation.Y) > angleEpsilon) { Matrix.RotationY(MathUtil.DegreesToRadians(rotation.Y), out temp[1]); isId[1] = false; } if (Math.Abs(rotation.X) > angleEpsilon) { Matrix.RotationX(MathUtil.DegreesToRadians(rotation.X), out temp[0]); isId[0] = false; } var order = new int[3] { -1, -1, -1 }; switch (mode) { case Model.RotOrder.EulerXYZ: order[0] = 2; order[1] = 1; order[2] = 0; break; case Model.RotOrder.EulerXZY: order[0] = 1; order[1] = 2; order[2] = 0; break; case Model.RotOrder.EulerYZX: order[0] = 0; order[1] = 2; order[2] = 1; break; case Model.RotOrder.EulerYXZ: order[0] = 2; order[1] = 0; order[2] = 1; break; case Model.RotOrder.EulerZXY: order[0] = 1; order[1] = 0; order[2] = 2; break; case Model.RotOrder.EulerZYX: order[0] = 0; order[1] = 1; order[2] = 2; break; default: Debug.Assert(false); break; } Debug.Assert((order[0] >= 0) && (order[0] <= 2)); Debug.Assert((order[1] >= 0) && (order[1] <= 2)); Debug.Assert((order[2] >= 0) && (order[2] <= 2)); if (!isId[order[0]]) { result = temp[order[0]]; } if (!isId[order[1]]) { result = result * temp[order[1]]; } if (!isId[order[2]]) { result = result * temp[order[2]]; } }
private void ConvertLights(Model model) { var nodeAttrs = model.Attributes; foreach (var attr in nodeAttrs) { var light = attr as Light; if (light != null) { ConvertLight(model, light); } } }
private void InterpolateKeys(AssimpSharp.QuatKey[] valOut, KeyTimeList keys, KeyFrameListList inputs, bool geom, ref double maxTime, ref double minTime, Model.RotOrder order) { Debug.Assert(keys.Count > 0); Debug.Assert(valOut != null); var temp = new AssimpSharp.VectorKey[keys.Count]; InterpolateKeys(temp, keys, inputs, geom, ref maxTime, ref minTime); Matrix m; Quaternion lastq = Quaternion.Identity; for (int i = 0; i < keys.Count; i++) { valOut[i].Time = temp[i].Time; GetRotationMatrix(order, temp[i].Value, out m); var quat = Quaternion.RotationMatrix(m); if (Quaternion.Dot(quat, lastq) < 0) { quat = -quat; } lastq = quat; valOut[i].Value = quat; } }
private int[] ConvertMesh(MeshGeometry mesh, Model model, Matrix nodeGlobalTransform) { List<int> temp; if (MeshesConverted.TryGetValue(mesh, out temp)) { return temp.ToArray(); } else { temp = new List<int>(); } var vertices = mesh.Vertices; var faces = mesh.FaceIndexCounts; if (vertices.Count == 0 || faces.Count == 0) { Debug.WriteLine("ignore empty geometry: " + mesh.Name); return temp.ToArray(); } var mindices = mesh.MaterialIndices; if (Doc.Settings.ReadMaterials && !(mindices.Count == 0)) { var b = mindices[0]; foreach (var index in mindices) { if (index != b) { return ConvertMeshMultiMaterial(mesh, model, nodeGlobalTransform).ToArray(); } } } temp.Add(ConvertMeshSingleMaterial(mesh, model, nodeGlobalTransform)); return temp.ToArray(); }
private bool IsRedundantAnimationData(Model target, TransformationComp comp, List<AnimationCurveNode> curves) { Debug.Assert(curves.Count > 0); // look for animation nodes with // * sub channels for all relevant components set // * one key/value pair per component // * combined values match up the corresponding value in the bind pose node transformation // only such nodes are 'redundant' for this function. if (curves.Count > 1) { return false; } var nd = curves[0]; var subCurves = nd.Curves; AnimationCurve dx; AnimationCurve dy; AnimationCurve dz; subCurves.TryGetValue("d|X", out dx); subCurves.TryGetValue("d|Y", out dy); subCurves.TryGetValue("d|Z", out dz); if (dx == null || dy == null || dz == null) { return false; } var vx = dx.Values; var vy = dy.Values; var vz = dz.Values; if (vx.Count != 1 || vy.Count != 1 || vz.Count != 1) { return false; } var dynVal = new Vector3(vx[0], vy[0], vz[0]); var staticVal = PropertyHelper.PropertyGet<Vector3>(target.Props, NameTransformationCompProperty(comp), TransformationCompDefaultValue(comp)); const float epsilon = 1e-6f; return (dynVal - staticVal).LengthSquared() < epsilon; }
private int ConvertMeshMultiMaterial(MeshGeometry mesh, Model model, int index, Matrix nodeGlobalTransform) { var outMesh = SetupEmptyMesh(mesh); var mindices = mesh.MaterialIndices; var vertices = mesh.Vertices; var faces = mesh.FaceIndexCounts; var processWeights = Doc.Settings.ReadWeights && (mesh.DeformerSkin != null); int countFaces = 0; int countVertices = 0; // count faces var itf = faces.GetEnumerator(); foreach(var it in mindices) { itf.MoveNext(); if (it != index) { continue; } ++countFaces; countVertices += (int)itf.Current; } Debug.Assert(countFaces > 0); Debug.Assert(countVertices > 0); // mapping from output indices to DOM indexing, needed to resolve weights var reverseMappigng = new int[0]; if (processWeights) { reverseMappigng = new int[countVertices]; } // allocate output data arrays, but don't fill them yet outMesh.Vertices = new Vector3[countVertices]; var fac = outMesh.Faces = new AssimpSharp.Face[countFaces]; // allocate normals var normals = mesh.Normals; if (normals.Count > 0) { Debug.Assert(normals.Count == vertices.Count); outMesh.Normals = new Vector3[vertices.Count]; } // allocate tangents, binormals. var tangets = mesh.Tangents; var binormals = mesh.Binormals; if (tangets.Count > 0) { Vector3[] tempBinormals = new Vector3[0]; if (binormals.Count == 0) { if (normals.Count > 0) { // XXX this computes the binormals for the entire mesh, not only // the part for which we need them. tempBinormals = new Vector3[normals.Count]; for (int i = 0; i < tangets.Count; i++) { tempBinormals[i] = Vector3.Cross(normals[i], tangets[i]); } binormals.Clear(); for (int i = 0; i < tempBinormals.Length; i++) { binormals.Add(tempBinormals[i]); } } else { binormals = null; } } if (binormals.Count > 0) { Debug.Assert(tangets.Count == vertices.Count); Debug.Assert(binormals.Count == vertices.Count); outMesh.Tangents = new Vector3[vertices.Count]; outMesh.Bitangents = new Vector3[vertices.Count]; } } // allocate texture coords int numUvs = 0; for (int i = 0; i < AssimpSharp.Mesh.AI_MAX_NUMBER_OF_TEXTURECOORDS; i++, numUvs++) { var uvs = mesh.GetTextureCoords(i); if (uvs.Count == 0) { break; } outMesh.TextureCoords[i] = new Vector3[vertices.Count]; outMesh.NumUVComponents[i] = 2; } // allocate vertex colors int numVcs = 0; for (int i = 0; i < AssimpSharp.Mesh.AI_MAX_NUMBER_OF_COLOR_SETS; i++, numVcs++) { var colors = mesh.GetVertexColors(i); if (colors.Count == 0) { break; } outMesh.Colors[i] = new Color4[vertices.Count]; } int cursor = 0; int inCursor = 0; int facesIndex = 0; int facIndex = 0; foreach (var it in mindices) { int pcount = (int)faces[facesIndex]; if (it != index) { inCursor += pcount; continue; } var f = fac[facIndex++] = new Face(); f.Indices = new int[pcount]; switch (pcount) { case 1: outMesh.PrimitiveTypes |= AssimpSharp.PrimitiveType.Point; break; case 2: outMesh.PrimitiveTypes |= AssimpSharp.PrimitiveType.Line; break; case 3: outMesh.PrimitiveTypes |= AssimpSharp.PrimitiveType.Triangle; break; default: outMesh.PrimitiveTypes |= AssimpSharp.PrimitiveType.Polygon; break; } for (int i = 0; i < pcount; ++i, ++cursor, ++inCursor) { f.Indices[i] = cursor; if (reverseMappigng.Length > 0) { reverseMappigng[cursor] = inCursor; } outMesh.Vertices[cursor] = vertices[inCursor]; if (outMesh.Normals.Length > 0) { outMesh.Normals[cursor] = normals[inCursor]; } if (outMesh.Tangents.Length > 0) { outMesh.Tangents[cursor] = tangets[inCursor]; outMesh.Bitangents[cursor] = binormals[inCursor]; } for (int j = 0; j < numUvs; j++) { var uvs = mesh.GetTextureCoords(j); outMesh.TextureCoords[j][cursor] = new Vector3(uvs[inCursor], 0.0f); } for (int j = 0; j < numVcs; j++) { var cols = mesh.GetVertexColors(j); outMesh.Colors[j][cursor] = cols[inCursor]; } } } ConvertMaterialForMesh(outMesh, model, mesh, index); if (processWeights) { ConvertWeights(outMesh, model, mesh, nodeGlobalTransform, index, reverseMappigng); } return Meshes.Count - 1; }
private bool NeedsComplexTransformationChain(Model model) { var props = model.Props; var zeroEpsilon = 1e-6f; bool ok = false; foreach (var i in Enum.GetValues(typeof(TransformationComp))) { var comp = (TransformationComp)i; if (comp == TransformationComp.Rotation || comp == TransformationComp.Scaling || comp == TransformationComp.Translation || comp == TransformationComp.GeometricScaling || comp == TransformationComp.GeometricRotation || comp == TransformationComp.GeometricTranslation) { continue; } Vector3 v = PropertyHelper.PropertyGet<Vector3>(props, NameTransformationCompProperty(comp), out ok); if (ok && v.LengthSquared() > zeroEpsilon) { return true; } } return false; }
private void ConvertModel(Model model, aiNode nd, Matrix nodeGlobalTransform) { var geos = model.Geometry; var meshes = new List<int>(geos.Count); foreach (var geo in geos) { var mesh = geo as MeshGeometry; if (mesh != null) { var indices = ConvertMesh(mesh, model, nodeGlobalTransform); meshes.AddRange(indices); } else { Debug.WriteLine("ignoring unrecognized geometry: " + geo.Name); } } if (meshes.Count > 0) { nd.Meshes.AddRange(meshes); } }
private void ConvertRotationKeys(AssimpSharp.NodeAnim na, List<AnimationCurveNode> nodes, LayerMap layers, long start, long stop, ref double maxTime, ref double minTime, Model.RotOrder order) { Debug.Assert(nodes.Count > 0); var inputs = GetKeyframeList(nodes, start, stop); var keys = GetKeyTimeList(inputs); na.RotationKeys = new AssimpSharp.QuatKey[keys.Count]; InterpolateKeys(na.RotationKeys, keys, inputs, false, ref maxTime, ref minTime, order); }