From bf458c9fe5a439a2bc54bf88b81ca8a9735332cc Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Sun, 7 Mar 2021 00:51:02 +0100 Subject: [PATCH 01/29] Re added gltf importer --- build/Stride.sln | 15 + .../Stride.Assets.Models/GltfAssetImporter.cs | 88 ++++ .../Stride.Assets.Models/ImportGltfCommand.cs | 68 +++ .../Stride.Assets.Models.csproj | 1 + .../GltfAnimationParser.cs | 166 ++++++++ .../Stride.Importer.Gltf/GltfImporter.cs | 391 ++++++++++++++++++ .../Stride.Importer.Gltf/GltfMeshParser.cs | 286 +++++++++++++ .../tools/Stride.Importer.Gltf/GltfUtils.cs | 25 ++ .../Stride.Importer.Gltf.csproj | 23 ++ 9 files changed, 1063 insertions(+) create mode 100644 sources/engine/Stride.Assets.Models/GltfAssetImporter.cs create mode 100644 sources/engine/Stride.Assets.Models/ImportGltfCommand.cs create mode 100644 sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs create mode 100644 sources/tools/Stride.Importer.Gltf/GltfImporter.cs create mode 100644 sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs create mode 100644 sources/tools/Stride.Importer.Gltf/GltfUtils.cs create mode 100644 sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj diff --git a/build/Stride.sln b/build/Stride.sln index 3248b08870..d3becdcd37 100644 --- a/build/Stride.sln +++ b/build/Stride.sln @@ -422,6 +422,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.VisualStudio.Command EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.Engine.NoAssets.Tests.Windows", "..\sources\engine\Stride.Engine.NoAssets.Tests\Stride.Engine.NoAssets.Tests.Windows.csproj", "{1C94168A-3C0D-4C6B-883B-91627D2EF3A1}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.Importer.Gltf", "..\sources\tools\Stride.Importer.Gltf\Stride.Importer.Gltf.csproj", "{69CA1F59-5735-403C-8009-2CE282C72DE6}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution ..\sources\shared\Stride.NuGetResolver\Stride.NuGetResolver.projitems*{00b72ed7-00e9-47f7-868d-8162027cd068}*SharedItemsImports = 13 @@ -1614,6 +1616,18 @@ Global {1C94168A-3C0D-4C6B-883B-91627D2EF3A1}.Release|Mixed Platforms.Build.0 = Release|Any CPU {1C94168A-3C0D-4C6B-883B-91627D2EF3A1}.Release|Win32.ActiveCfg = Release|Any CPU {1C94168A-3C0D-4C6B-883B-91627D2EF3A1}.Release|Win32.Build.0 = Release|Any CPU + {69CA1F59-5735-403C-8009-2CE282C72DE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {69CA1F59-5735-403C-8009-2CE282C72DE6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {69CA1F59-5735-403C-8009-2CE282C72DE6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {69CA1F59-5735-403C-8009-2CE282C72DE6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {69CA1F59-5735-403C-8009-2CE282C72DE6}.Debug|Win32.ActiveCfg = Debug|Any CPU + {69CA1F59-5735-403C-8009-2CE282C72DE6}.Debug|Win32.Build.0 = Debug|Any CPU + {69CA1F59-5735-403C-8009-2CE282C72DE6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {69CA1F59-5735-403C-8009-2CE282C72DE6}.Release|Any CPU.Build.0 = Release|Any CPU + {69CA1F59-5735-403C-8009-2CE282C72DE6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {69CA1F59-5735-403C-8009-2CE282C72DE6}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {69CA1F59-5735-403C-8009-2CE282C72DE6}.Release|Win32.ActiveCfg = Release|Any CPU + {69CA1F59-5735-403C-8009-2CE282C72DE6}.Release|Win32.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1741,6 +1755,7 @@ Global {7FA381C8-8EBE-4515-969D-01369CC47D0E} = {1AE1AC60-5D2F-4CA7-AE20-888F44551185} {83E4E436-659A-4D82-90DD-0DC67BE38D17} = {E4508D15-6503-4A29-ADC4-27B3A5E99545} {1C94168A-3C0D-4C6B-883B-91627D2EF3A1} = {A7ED9F01-7D78-4381-90A6-D50E51C17250} + {69CA1F59-5735-403C-8009-2CE282C72DE6} = {6F473FA6-4F8B-4FBA-AE33-EE5AF997D50C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FF877973-604D-4EA7-B5F5-A129961F9EF2} diff --git a/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs b/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs new file mode 100644 index 0000000000..3a32b5a16e --- /dev/null +++ b/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs @@ -0,0 +1,88 @@ +// Copyright (c) Stride contributors (https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +using System; +using System.Collections.Generic; +using Stride.Core.Assets; +using Stride.Core; +using Stride.Core.Diagnostics; +using Stride.Core.IO; +using Stride.Animations; +using Stride.Assets.Textures; +using Stride.Importer.Common; +using Stride.Importer.Gltf; + +namespace Stride.Assets.Models +{ + public class GltfAssetImporter : ModelAssetImporter + { + + // Supported file extensions for this importer + internal const string FileExtensions = ".gltf;.glb;"; + + private static readonly Guid Uid = new Guid("A5A08530-4856-4EB7-91F8-B0A148B3A627"); + + public override Guid Id => Uid; + + public override string Description => "Gltf importer used for creating entities, 3D Models or animations assets"; + + public override string SupportedFileExtensions => FileExtensions; + + /// + public override EntityInfo GetEntityInfo(UFile localPath, Logger logger, AssetImporterParameters importParameters) + { + //var meshConverter = new Importer.AssimpNET.MeshConverter(logger); + + //if (!importParameters.InputParameters.TryGet(DeduplicateMaterialsKey, out var deduplicateMaterials)) + // deduplicateMaterials = true; // Dedupe is the default value + + //var entityInfo = meshConverter.ExtractEntity(localPath.FullPath, null, importParameters.IsTypeSelectedForOutput(typeof(TextureAsset)), deduplicateMaterials); + //return entityInfo; + return null; + } + + /// + public override void GetAnimationDuration(UFile localPath, Logger logger, AssetImporterParameters importParameters, out TimeSpan startTime, out TimeSpan endTime) + { + + var sceneData = GltfMeshParser.ConvertAnimations(SharpGLTF.Schema2.ModelRoot.Load(localPath)); + + startTime = CompressedTimeSpan.MaxValue; // This will go down, so we start from positive infinity + endTime = CompressedTimeSpan.MinValue; // This will go up, so we start from negative infinity + + foreach (var animationClip in sceneData.Values) + { + foreach (var animationCurve in animationClip.Curves) + { + foreach (var compressedTimeSpan in animationCurve.Keys) + { + if (compressedTimeSpan < startTime) + startTime = compressedTimeSpan; + if (compressedTimeSpan > endTime) + endTime = compressedTimeSpan; + } + } + } + + if (startTime == CompressedTimeSpan.MaxValue) + startTime = CompressedTimeSpan.Zero; + if (endTime == CompressedTimeSpan.MinValue) + endTime = CompressedTimeSpan.Zero; + } + + public override IEnumerable Import(UFile localPath, AssetImporterParameters importParameters) + { + var assetItems = base.Import(localPath, importParameters); + // Need to remember the DeduplicateMaterials setting per ModelAsset since the importer may be rerun on this asset + if (!importParameters.InputParameters.TryGet(DeduplicateMaterialsKey, out var deduplicateMaterials)) + deduplicateMaterials = true; // Dedupe is the default value + foreach (var item in assetItems) + { + if (item.Asset is ModelAsset modelAsset) + { + modelAsset.DeduplicateMaterials = deduplicateMaterials; + } + } + return assetItems; + } + } +} diff --git a/sources/engine/Stride.Assets.Models/ImportGltfCommand.cs b/sources/engine/Stride.Assets.Models/ImportGltfCommand.cs new file mode 100644 index 0000000000..cc06646f27 --- /dev/null +++ b/sources/engine/Stride.Assets.Models/ImportGltfCommand.cs @@ -0,0 +1,68 @@ +// Copyright (c) Stride contributors (https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using Stride.Core.BuildEngine; +using Stride.Core.Serialization.Contents; +using Stride.Animations; +using Stride.Rendering; +using Stride.Importer.Gltf; + +namespace Stride.Assets.Models +{ + [Description("Import GLTF")] + public class ImportGltfCommand : ImportModelCommand + { + private static string[] supportedExtensions = GltfAssetImporter.FileExtensions.Split(';'); + + /// + public override string Title { get { string title = "Import GLTF "; try { title += Path.GetFileName(SourcePath) ?? "[File]"; } catch { title += "[INVALID PATH]"; } return title; } } + + public static bool IsSupportingExtensions(string ext) + { + if (string.IsNullOrEmpty(ext)) + return false; + + var extToLower = ext.ToLowerInvariant(); + + return supportedExtensions.Any(supExt => supExt.Equals(extToLower)); + } + + protected override Model LoadModel(ICommandContext commandContext, ContentManager contentManager) + { + var materialMapping = Materials.Select((s, i) => new { Value = s, Index = i }).ToDictionary(x => x.Value.Name, x => x.Index); + var sceneData = GltfMeshParser.LoadFirstModel(SharpGLTF.Schema2.ModelRoot.Load(SourcePath)); + return sceneData; + } + + protected override Dictionary LoadAnimation(ICommandContext commandContext, ContentManager contentManager, out TimeSpan duration) + { + var file = SharpGLTF.Schema2.ModelRoot.Load(SourcePath); + var sceneData = GltfMeshParser.ConvertAnimations(file); + duration = GltfMeshParser.GetAnimationDuration(file); + return sceneData; + } + + protected override Skeleton LoadSkeleton(ICommandContext commandContext, ContentManager contentManager) + { + var file = SharpGLTF.Schema2.ModelRoot.Load(SourcePath); + var sceneData = GltfAnimationParser.ConvertSkeleton(file); + return sceneData; + } + + + + public override bool ShouldSpawnNewProcess() + { + return true; + } + + public override string ToString() + { + return "Import GLTF " + base.ToString(); + } + } +} diff --git a/sources/engine/Stride.Assets.Models/Stride.Assets.Models.csproj b/sources/engine/Stride.Assets.Models/Stride.Assets.Models.csproj index a35ef329f0..468b3453e0 100644 --- a/sources/engine/Stride.Assets.Models/Stride.Assets.Models.csproj +++ b/sources/engine/Stride.Assets.Models/Stride.Assets.Models.csproj @@ -19,6 +19,7 @@ + diff --git a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs new file mode 100644 index 0000000000..288612858a --- /dev/null +++ b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs @@ -0,0 +1,166 @@ +using Stride.Animations; +using System; +using System.Collections.Generic; +using System.Text; +using Stride.Core.Mathematics; +using System.Linq; +using Stride.Core.Collections; + +using static Stride.Importer.Gltf.GltfUtils; +using Stride.Rendering; + +namespace Stride.Importer.Gltf +{ + public class GltfAnimationParser + { + public static Skeleton ConvertSkeleton(SharpGLTF.Schema2.ModelRoot root) + { + Skeleton result = new Skeleton(); + var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes.First()).Skin; + var jointList = Enumerable.Range(0, skin.JointsCount).Select(x => skin.GetJoint(x).Joint).ToList(); + var mnd = + jointList + .Select( + x => + new ModelNodeDefinition + { + Name = x.Name, + Flags = ModelNodeFlags.Default, + ParentIndex = jointList.IndexOf(x.VisualParent) + 1, + Transform = new TransformTRS + { + Position = ConvertNumerics(x.LocalTransform.Translation), + Rotation = ConvertNumerics(x.LocalTransform.Rotation), + Scale = ConvertNumerics(x.LocalTransform.Scale) + } + + } + ) + .ToList(); + mnd.Insert( + 0, + new ModelNodeDefinition + { + Name = "Armature", + Flags = ModelNodeFlags.EnableRender, + ParentIndex = -1, + Transform = new TransformTRS + { + Position = Vector3.Zero, + Rotation = Quaternion.Identity, + Scale = Vector3.Zero + } + }); + result.Nodes = mnd.ToArray(); + return result; + } + public static AnimationCurve CreateRotationCurve() + { + return new AnimationCurve + { + InterpolationType = AnimationCurveInterpolationType.Linear, + KeyFrames = + { + CreateKeyFrame(0.00f, Quaternion.RotationX(0)), + CreateKeyFrame(0.25f, Quaternion.RotationX(MathUtil.PiOverTwo)), + CreateKeyFrame(0.50f, Quaternion.RotationX(MathUtil.Pi)), + CreateKeyFrame(0.75f, Quaternion.RotationX(-MathUtil.PiOverTwo)), + CreateKeyFrame(1.00f, Quaternion.RotationX(MathUtil.TwoPi)) + } + }; + } + + public static KeyFrameData CreateKeyFrame(float keyTime, T value) + { + return new KeyFrameData((CompressedTimeSpan)TimeSpan.FromSeconds(keyTime), value); + } + + public static Dictionary ConvertCurves(IReadOnlyList channels, SharpGLTF.Schema2.ModelRoot root) + { + var result = new Dictionary(); + string baseString = "[ModelComponent.Key].Skeleton.NodeTransformations[index].Transform.type"; + var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes.First()).Skin; + var jointList = Enumerable.Range(0, skin.JointsCount).Select(x => skin.GetJoint(x).Joint).ToList(); + foreach (var chan in channels) + { + var index = jointList.IndexOf(chan.TargetNode) + 1; + switch (chan.TargetNodePath) + { + case SharpGLTF.Schema2.PropertyPath.translation: + result.Add(baseString.Replace("index", $"{index}").Replace("type", "Position"), ConvertCurve(chan.GetTranslationSampler())); + break; + case SharpGLTF.Schema2.PropertyPath.rotation: + result.Add(baseString.Replace("index", $"{index}").Replace("type", "Rotation"), ConvertCurve(chan.GetRotationSampler())); + break; + case SharpGLTF.Schema2.PropertyPath.scale: + result.Add(baseString.Replace("index", $"{index}").Replace("type", "Scale"), ConvertCurve(chan.GetScaleSampler())); + break; + }; + + } + return result; + + } + + public static AnimationCurve ConvertCurve(SharpGLTF.Schema2.IAnimationSampler sampler) + { + var interpolationType = + sampler.InterpolationMode switch + { + SharpGLTF.Schema2.AnimationInterpolationMode.LINEAR => AnimationCurveInterpolationType.Linear, + SharpGLTF.Schema2.AnimationInterpolationMode.STEP => AnimationCurveInterpolationType.Constant, + SharpGLTF.Schema2.AnimationInterpolationMode.CUBICSPLINE => AnimationCurveInterpolationType.Cubic, + _ => throw new NotImplementedException(), + }; + + var keyframes = + interpolationType switch + { + AnimationCurveInterpolationType.Constant => + sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), + AnimationCurveInterpolationType.Linear => + sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), + AnimationCurveInterpolationType.Cubic => + sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), + //sampler.GetCubicKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), + _ => throw new NotImplementedException() + }; + + return new AnimationCurve + { + InterpolationType = interpolationType, + KeyFrames = new FastList>(keyframes) + }; + } + public static AnimationCurve ConvertCurve(SharpGLTF.Schema2.IAnimationSampler sampler) + { + var interpolationType = + sampler.InterpolationMode switch + { + SharpGLTF.Schema2.AnimationInterpolationMode.LINEAR => AnimationCurveInterpolationType.Linear, + SharpGLTF.Schema2.AnimationInterpolationMode.STEP => AnimationCurveInterpolationType.Constant, + SharpGLTF.Schema2.AnimationInterpolationMode.CUBICSPLINE => AnimationCurveInterpolationType.Cubic, + _ => throw new NotImplementedException(), + }; + + var keyframes = + interpolationType switch + { + AnimationCurveInterpolationType.Constant => + sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), + AnimationCurveInterpolationType.Linear => + sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), + AnimationCurveInterpolationType.Cubic => + sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), + //sampler.GetCubicKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), + _ => throw new NotImplementedException() + }; + + return new AnimationCurve + { + InterpolationType = interpolationType, + KeyFrames = new FastList>(keyframes) + }; + } + } +} diff --git a/sources/tools/Stride.Importer.Gltf/GltfImporter.cs b/sources/tools/Stride.Importer.Gltf/GltfImporter.cs new file mode 100644 index 0000000000..064fe9521f --- /dev/null +++ b/sources/tools/Stride.Importer.Gltf/GltfImporter.cs @@ -0,0 +1,391 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using Stride.Core.Mathematics; +using Stride.Rendering; +using Stride.Graphics; +using Stride.Rendering.Materials; +using Stride.Rendering.Materials.ComputeColors; +using Stride.Core.Diagnostics; +using Stride.Core; +using Stride.Core.Extensions; + +namespace Stride.Importer.Gltf +{ + public enum VertexType + { + VertexPositionTexture, + VertexPositionNormalTexture, + VertexPositionNormalColor + } + class GltfImporter + { + private SharpGLTF.Schema2.ModelRoot GltfRoot { get; set; } + private SharpGLTF.Schema2.Mesh CurrentMesh { get; set; } + public Stride.Core.Serialization.Contents.ContentManager Content { get; set; } + + + public Model GetModel(int modelID) + { + var model = new Model(); + CurrentMesh = GltfRoot.LogicalMeshes[modelID]; + for (int i = 0; i < GltfRoot.LogicalMeshes[modelID].Primitives.Count; i++) + { + var matDesc = GetMaterial(i); + // var matDesc = new MaterialDescriptor{ + // Attributes = new MaterialAttributes { + // Diffuse = new MaterialDiffuseMapFeature(new ComputeColor(Stride.Core.Mathematics.Color.AliceBlue)), + // DiffuseModel = new MaterialDiffuseLambertModelFeature() + // } + // }; + var material = Material.New(Device, matDesc); + model.Add(material); + //var pc = new ParameterCollection(); + //var p1 = new ParameterKeyInfo(new ObjectParameterKey("Material.HasSkinningPosition",1,Array.Empty()),0, + //pc.ParameterKeyInfos.Add(p); + + + + + var mesh = new Mesh { Draw = GetMeshDraw(i) }; + + mesh.Parameters.Set(MaterialKeys.HasSkinningPosition, true); + mesh.Parameters.Set(MaterialKeys.HasSkinningNormal, true); + + //Logger.Info("Index of material is : " + model.Materials.IndexOf(material)); + mesh.MaterialIndex = model.Materials.IndexOf(material); + model.Add(mesh); + model.Skeleton = GetSkeleton(i, mesh); + } + return model; + } + public MaterialDescriptor GetMaterial(int primitiveID) + { + var material = new MaterialDescriptor + { + Attributes = new MaterialAttributes() + }; + foreach (var chan in CurrentMesh.Primitives[primitiveID].Material.Channels) + { + if (chan.Texture != null) + { + using var fs = new FileStream(chan.Texture.PrimaryImage.Content.SourcePath, FileMode.Open); + var image = Stride.Graphics.Image.Load(fs, true); + var texture = Texture.New(Device, image, TextureFlags.None); + + + switch (chan.Key) + { + case "BaseColor": + var vt = new ComputeTextureColor(texture) + { + AddressModeU = TextureAddressMode.Wrap, + AddressModeV = TextureAddressMode.Wrap, + TexcoordIndex = TextureCoordinate.Texcoord0 + }; + + material.Attributes.Diffuse = new MaterialDiffuseMapFeature(vt); + + material.Attributes.DiffuseModel = new MaterialDiffuseLambertModelFeature(); + //material.Attributes.DiffuseModel = new MaterialDiffuseCelShadingModelFeature(); + //Logger.Info("Added diffuse material"); + break; + case "MetallicRoughness": + material.Attributes.MicroSurface = new MaterialGlossinessMapFeature(new ComputeTextureScalar(texture, TextureCoordinate.Texcoord0, Vector2.One, Vector2.Zero)); + break; + case "Normal": + material.Attributes.Surface = new MaterialNormalMapFeature(new ComputeTextureColor(texture)); + break; + case "Occlusion": + material.Attributes.Occlusion = new MaterialOcclusionMapFeature(); + break; + case "Emissive": + material.Attributes.Emissive = new MaterialEmissiveMapFeature(new ComputeTextureColor(texture)); + break; + } + + } + + } + material.Attributes.CullMode = CullMode.Back; + return material; + } + public MeshDraw GetMeshDraw(int primitiveID) + { + Stride.Graphics.Buffer vBuff; + var vs = GetVertexBuffer(primitiveID); + var vt = GetVertexType(primitiveID); + //if (vt == VertexType.VertexPositionNormalTexture) + //{ + // vBuff = Stride.Graphics.Buffer.Vertex.New( + // Device, + // AsVPNTJW(vs), + // GraphicsResourceUsage.Default + // ); + //} + //else + //{ + // vBuff = Stride.Graphics.Buffer.Vertex.New( + // Device, + // AsVPT(vs), + // GraphicsResourceUsage.Dynamic + // ); + //} + vBuff = Stride.Graphics.Buffer.Vertex.New( + Device, + AsVPNTJW(vs), + GraphicsResourceUsage.Default + ); + + var iBuff = Stride.Graphics.Buffer.Index.New( + Device, + GetIndices(primitiveID) + ); + var primitiveType = GetPrimitiveType(primitiveID); + + return new MeshDraw + { + PrimitiveType = primitiveType, + DrawCount = iBuff.ElementCount, + VertexBuffers = new[] { new VertexBufferBinding(vBuff, VertexPNTJW.Layout, vBuff.ElementCount) }, + IndexBuffer = new IndexBufferBinding(iBuff, true, iBuff.ElementCount) + }; + + + } + public bool IsSkinnedMesh() => CurrentMesh.AllPrimitivesHaveJoints; + //public VertexElement[] getVertexDefinition(int primitiveID) + //{ + // //TODO: Support multiple joints (up to 4) and multiple weights (up to 4) if needed + // var result = new List(); + // if(IsSkinnedMesh()) + // { + // var joints = CurrentMesh.Primitives[primitiveID].VertexAccessors["JOINTS_0"].AsVector4Array().Select(ToStrideVector4).ToArray(); + // var weights = CurrentMesh.Primitives[primitiveID].VertexAccessors["WEIGHTS_0"].AsVector4Array().Select(ToStrideVector4).ToArray(); + // for(int i = 0; i< joints.Length; i++) + // { + // //result.Add(new VertexElement(VertexElementUsage.BlendWeight, (int)joints[i].X, PixelFormat.None, -1)); + // } + // } + // return new VertexElement[](); + //} + public VertexType GetVertexType(int primitiveID) + { + var keys = CurrentMesh.Primitives[primitiveID].VertexAccessors.Keys; + if (keys.Contains("NORMAL")) + return VertexType.VertexPositionNormalTexture; + else + return VertexType.VertexPositionTexture; + } + public IVertex[] GetVertexBuffer(int primitiveID) + { + + + var result = new List(); + var elems = new List(); + if (GetVertexType(primitiveID) == VertexType.VertexPositionNormalTexture) + { + var positions = CurrentMesh.Primitives[primitiveID].VertexAccessors["POSITION"].AsVector3Array(); + var normals = CurrentMesh.Primitives[primitiveID].VertexAccessors["NORMAL"].AsVector3Array(); + var joints = CurrentMesh.Primitives[primitiveID].VertexAccessors["JOINTS_0"].AsVector4Array(); + var weights = CurrentMesh.Primitives[primitiveID].VertexAccessors["WEIGHTS_0"].AsVector4Array(); + var texAccessor = CurrentMesh.Primitives[primitiveID].VertexAccessors.Where(x => x.Key.Contains("TEXCOORD")).Select(x => x.Key).First(); + var texCoords = CurrentMesh.Primitives[primitiveID].VertexAccessors[texAccessor].AsVector2Array(); + + for (int i = 0; i < positions.Count(); i++) + { + result.Add( + new VertexPNTJW( + ToStrideVector3(positions[i]), + ToStrideVector3(normals[i]), + ToStrideVector2(texCoords[i]), + ToStrideInt4(joints[i]), + ToStrideVector4(weights[i]) + ) + ); + } + return result.ToArray(); + } + else + { + var positions = CurrentMesh.Primitives[primitiveID].VertexAccessors["POSITION"].AsVector3Array(); + + var texAccessor = CurrentMesh.Primitives[primitiveID].VertexAccessors.Where(x => x.Key.Contains("TEXCOORD")).Select(x => x.Key).First(); + var texCoords = CurrentMesh.Primitives[primitiveID].VertexAccessors[texAccessor].AsVector2Array(); + for (int i = 0; i < positions.Count(); i++) + { + result.Add(new VertexPositionTexture(ToStrideVector3(positions[i]), ToStrideVector2(texCoords[i]))); + } + return result.ToArray(); + } + } + + + + public uint[] GetIndices(int primitiveID) + { + var prim = CurrentMesh.Primitives[primitiveID]; + + var result = new List(); + prim.GetTriangleIndices().ToList().ForEach(x => { result.Add((uint)x.A); result.Add((uint)x.C); result.Add((uint)x.B); }); + return result.ToArray(); + } + + public int GetTriIndicesCount(int primitiveID) + { + return CurrentMesh.Primitives[primitiveID].GetTriangleIndices().Count(); + } + public Stride.Graphics.PrimitiveType GetPrimitiveType(int primitiveID) + { + return CurrentMesh.Primitives[primitiveID].DrawPrimitiveType switch + { + SharpGLTF.Schema2.PrimitiveType.TRIANGLES => Stride.Graphics.PrimitiveType.TriangleList, + SharpGLTF.Schema2.PrimitiveType.TRIANGLE_STRIP => Stride.Graphics.PrimitiveType.TriangleStrip, + SharpGLTF.Schema2.PrimitiveType.LINES => Stride.Graphics.PrimitiveType.LineList, + SharpGLTF.Schema2.PrimitiveType.POINTS => Stride.Graphics.PrimitiveType.PointList, + SharpGLTF.Schema2.PrimitiveType.LINE_LOOP => Stride.Graphics.PrimitiveType.Undefined, + SharpGLTF.Schema2.PrimitiveType.LINE_STRIP => Stride.Graphics.PrimitiveType.Undefined, + SharpGLTF.Schema2.PrimitiveType.TRIANGLE_FAN => Stride.Graphics.PrimitiveType.Undefined, + _ => Stride.Graphics.PrimitiveType.Undefined, + }; + } + + // public Object GetIndicesByType(int primitiveID) + // { + // List indices = new List(); + // switch(CurrentMesh.Primitives[primitiveID].DrawPrimitiveType) + // { + // case SharpGLTF.Schema2.PrimitiveType.TRIANGLES : + // CurrentMesh.Primitives[primitiveID].GetTriangleIndices().ToList().ForEach(i => {indices.Add(i.A);indices.Add(i.B);indices.Add(i.C);}); + // return indices; + // case SharpGLTF.Schema2.PrimitiveType.TRIANGLE_STRIP : + // CurrentMesh.Primitives[primitiveID].GetTriangleIndices(); + // case SharpGLTF.Schema2.PrimitiveType.LINES : + // Stride.Graphics.PrimitiveType.LineList; + // case SharpGLTF.Schema2.PrimitiveType.POINTS : + // Stride.Graphics.PrimitiveType.PointList; + // case SharpGLTF.Schema2.PrimitiveType.LINE_LOOP : + // Stride.Graphics.PrimitiveType.Undefined; + // case SharpGLTF.Schema2.PrimitiveType.LINE_STRIP : + // Stride.Graphics.PrimitiveType.Undefined; + // case SharpGLTF.Schema2.PrimitiveType.TRIANGLE_FAN : + // Stride.Graphics.PrimitiveType.Undefined; + // _ => Stride.Graphics.PrimitiveType.Undefined, + // }; + // } + + public Skeleton GetSkeleton(int primitiveID, Mesh mesh) + { + var foxSkin = GltfRoot.LogicalNodes.First(x => x.Mesh == CurrentMesh).Skin; + List BoneInfluencing = new List(); + var glSK = foxSkin.Skeleton; + CurrentMesh + .Primitives[primitiveID] + .VertexAccessors["JOINTS_0"] + .AsVector4Array() + .ToList() + .ForEach( + x => { BoneInfluencing.Add((int)x.X); BoneInfluencing.Add((int)x.Y); BoneInfluencing.Add((int)x.Z); BoneInfluencing.Add((int)x.W); } + ); + BoneInfluencing = BoneInfluencing.Distinct().ToList(); + var sk = new Skeleton(); + var nodes = new List(); + var glnodes = new List(); + var matNodes = new List(); + //var glJoints = new List<(SharpGLTF.Schema2.Node, System.Numerics.Matrix4x4)>(); + for (int i = 0; i < foxSkin.JointsCount; i++) + { + glnodes.Add(foxSkin.GetJoint(i).Joint); + matNodes.Add(foxSkin.GetJoint(i).InverseBindMatrix); + } + glnodes.ForEach( + x => + { + var pn = GetParentNodeIndex(glnodes, x); + nodes.Add( + new ModelNodeDefinition + { + Flags = pn == -1 ? ModelNodeFlags.EnableRender : ModelNodeFlags.Default, + ParentIndex = pn, + Transform = + new TransformTRS + { + Position = ToStrideVector3(x.LocalTransform.Translation), + Rotation = ToStrideQuaternion(x.LocalTransform.Rotation), + Scale = ToStrideVector3(x.LocalTransform.Scale) + }, + Name = x.Name + } + ); + } + ); + var rootBone = nodes[0]; + Matrix.Transformation(ref rootBone.Transform.Scale, ref rootBone.Transform.Rotation, ref rootBone.Transform.Position, out Matrix tmpMat); + Matrix.Transformation(ref rootBone.Transform.Scale, ref rootBone.Transform.Rotation, ref rootBone.Transform.Position, out Matrix tmpMat2); + tmpMat2.Invert(); + var mbd = new List(); + foreach (int boneId in BoneInfluencing) + { + var mat = ToStrideMatrix1(matNodes[boneId]); + //var currentNode = nodes[boneId]; + //Matrix.Transformation(ref currentNode.Transform.Scale, ref currentNode.Transform.Rotation, ref currentNode.Transform.Position, out Matrix mat); + //mat.Invert(); + + mbd.Add(new MeshBoneDefinition + { + NodeIndex = boneId, + LinkToMeshMatrix = tmpMat2 * mat * tmpMat + }); + } + sk.Nodes = nodes.ToArray(); + mesh.Skinning = new MeshSkinningDefinition + { + Bones = mbd.ToArray() + }; + + return sk; + } + private int GetParentNodeIndex(List glnodes, SharpGLTF.Schema2.Node current) + { + try + { + return glnodes.IndexOf(glnodes.First(y => y.Name == current.VisualParent.Name)); + } + catch (Exception) + { + return -1; + } + } + private Vector4 ToStrideVector4(System.Numerics.Vector4 a) => new Vector4(a.X, a.Y, a.Z, a.W); + private Int4 ToStrideInt4(System.Numerics.Vector4 a) => new Int4((int)a.X, (int)a.Y, (int)a.Z, (int)a.W); + + private Vector3 ToStrideVector3(System.Numerics.Vector3 a) => new Vector3(a.X, a.Y, a.Z); + private Vector2 ToStrideVector2(System.Numerics.Vector2 a) => new Vector2(a.X, a.Y); + private VertexPNTJW[] AsVPNTJW(IVertex[] v) => v.Select(x => (VertexPNTJW)x).ToArray(); + private VertexPositionNormalTexture[] AsVPNT(IVertex[] v) => v.Select(x => (VertexPositionNormalTexture)x).ToArray(); + private VertexPositionTexture[] AsVPT(IVertex[] v) => v.Select(x => (VertexPositionTexture)x).ToArray(); + public Vector3 ToStridePosition(SharpGLTF.Transforms.AffineTransform tr) => new Vector3(tr.Translation.X, tr.Translation.Y, tr.Translation.Z); + public Quaternion ToStrideQuaternion(System.Numerics.Quaternion rot) => new Quaternion(rot.X, rot.Y, rot.Z, rot.W); + public Matrix ToStrideMatrix1(System.Numerics.Matrix4x4 mat) + { + return new Matrix( + mat.M11, mat.M21, mat.M31, mat.M41, + mat.M12, mat.M22, mat.M32, mat.M42, + mat.M13, mat.M23, mat.M33, mat.M43, + mat.M14, mat.M24, mat.M34, mat.M44 + ); + } + public Matrix ToStrideMatrix2(System.Numerics.Matrix4x4 mat) + { + return new Matrix( + mat.M11, mat.M12, mat.M13, mat.M14, + mat.M21, mat.M22, mat.M23, mat.M24, + mat.M31, mat.M32, mat.M33, mat.M34, + mat.M41, mat.M42, mat.M43, mat.M44 + ); + } + } +} +} + diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs new file mode 100644 index 0000000000..6325e73555 --- /dev/null +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -0,0 +1,286 @@ +using Stride.Graphics; +using Stride.Rendering; +using System; +using System.Collections.Generic; +using System.Linq; +using Stride.Core.Mathematics; +using System.Runtime.InteropServices; +using Stride.Rendering.Materials; +using Stride.Rendering.Materials.ComputeColors; +using System.IO; +//using Stride.Animations; +using Stride.Core.Collections; +using Stride.Graphics.Data; + +using static Stride.Importer.Gltf.GltfUtils; +using static Stride.Importer.Gltf.GltfAnimationParser; +using Stride.Animations; +using Stride.Shaders; +using Stride.Core.Serialization; +using Stride.Core.Assets; + +namespace Stride.Importer.Gltf +{ + public class GltfMeshParser + { + + public static Model LoadFirstModel(SharpGLTF.Schema2.ModelRoot root) + { + var result = new Model + { + Meshes = + root.LogicalMeshes[0].Primitives + .Select(x => LoadMesh(x)) + .ToList() + }; + //LoadMaterials(root).ForEach(x => result.Add(x)); + //result.Add(RedMaterial(device)); + //result.Meshes.ForEach(x => x.MaterialIndex = 0); + result.Skeleton = ConvertSkeleton(root); + return result; + } + + public static TimeSpan GetAnimationDuration(SharpGLTF.Schema2.ModelRoot root) + { + var time = root.LogicalAnimations.Select(x => x.Duration).Sum(); + return TimeSpan.FromSeconds(time); + } + + + public static MeshSkinningDefinition ConvertInverseBindMatrices(SharpGLTF.Schema2.ModelRoot root) + { + var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes[0]).Skin; + var jointList = Enumerable.Range(0, skin.JointsCount).Select(skin.GetJoint); + var mnt = + new MeshSkinningDefinition + { + Bones = + jointList + .Select((x, i) => + new MeshBoneDefinition + { + NodeIndex = i + 1, + LinkToMeshMatrix = ConvertNumerics(x.InverseBindMatrix) + } + ) + .ToArray() + }; + return mnt; + } + + public static Dictionary ConvertAnimations(SharpGLTF.Schema2.ModelRoot root) + { + var animations = root.LogicalAnimations; + + var clips = + animations + .Select(x => + { + //Create animation clip with + var clip = new AnimationClip { Duration = TimeSpan.FromSeconds(x.Duration) }; + clip.RepeatMode = AnimationRepeatMode.LoopInfinite; + // Add Curve + ConvertCurves(x.Channels, root).ToList().ForEach(x => clip.AddCurve(x.Key, x.Value)); + + return (x.Name, clip); + } + ) + .ToList() + .ToDictionary(x => x.Name, x => x.clip); + return clips; + } + + //public static List LoadMaterials(SharpGLTF.Schema2.ModelRoot root) + //{ + // var result = new List(); + // foreach (var mat in root.LogicalMaterials) + // { + // var material = new MaterialAsset + // { + // Attributes = new MaterialAttributes() + // }; + // foreach (var chan in mat.Channels) + // { + // if (chan.Texture != null) { + + // var gltfImg = chan.Texture.PrimaryImage; + // var imgBuf = gltfImg.Content.Content.ToArray(); + // var imgPtr = new DataPointer(GCHandle.Alloc(imgBuf, GCHandleType.Pinned).AddrOfPinnedObject(), imgBuf.Length); + + // //var image = Stride.Graphics.Image.Load(imgPtr); + // //var shader = new ShaderClassSource("ComputeColorTextureRepeat",gltfImg.Name,"TEXCOORD","float2(1.0f,1.0f)"); + // var texture = AttachedReferenceManager.CreateProxyObject(AssetId.Empty,gltfImg.Name); + + + // switch (chan.Key) + // { + // case "BaseColor": + // var vt = new ComputeTextureColor(texture) + // { + // AddressModeU = TextureAddressMode.Wrap, + // AddressModeV = TextureAddressMode.Wrap, + // TexcoordIndex = TextureCoordinate.Texcoord0 + // }; + + // material.Attributes.Diffuse = new MaterialDiffuseMapFeature(vt); + + // material.Attributes.DiffuseModel = new MaterialDiffuseLambertModelFeature(); + // break; + // case "MetallicRoughness": + // material.Attributes.MicroSurface = new MaterialGlossinessMapFeature(new ComputeTextureScalar(texture, TextureCoordinate.Texcoord0, Vector2.One, Vector2.Zero)); + // break; + // case "Normal": + // material.Attributes.Surface = new MaterialNormalMapFeature(new ComputeTextureColor(texture)); + // break; + // case "Occlusion": + // material.Attributes.Occlusion = new MaterialOcclusionMapFeature(); + // break; + // case "Emissive": + // material.Attributes.Emissive = new MaterialEmissiveMapFeature(new ComputeTextureColor(texture)); + // break; + // } + + // } + + // } + // material.Attributes.CullMode = CullMode.Back; + // result.Add(material); + // } + // return result; + //} + + public static Mesh LoadMesh(SharpGLTF.Schema2.MeshPrimitive mesh) + { + + var draw = new MeshDraw + { + PrimitiveType = ConvertPrimitiveType(mesh.DrawPrimitiveType), + IndexBuffer = ConvertSerializedIndexBufferBinding(mesh), + VertexBuffers = ConvertSerializedVertexBufferBinding(mesh), + DrawCount = GetDrawCount(mesh) + }; + + + + var result = new Mesh(draw, new ParameterCollection()) + { + Skinning = ConvertInverseBindMatrices(mesh.LogicalParent.LogicalParent), + Name = mesh.LogicalParent.Name, + MaterialIndex = mesh.LogicalParent.LogicalParent.LogicalMaterials.ToList().IndexOf(mesh.Material) + }; + + + //TODO : Add parameter collection only after checking if it has + result.Parameters.Set(MaterialKeys.HasSkinningPosition, true); + result.Parameters.Set(MaterialKeys.HasSkinningNormal, true); + return result; + } + + private static int GetDrawCount(SharpGLTF.Schema2.MeshPrimitive mesh) + { + var tmp = mesh.GetTriangleIndices().Select(x => new int[] { x.A, x.C, x.B }).SelectMany(x => x).Select(x => (uint)x).ToArray(); + return mesh.GetTriangleIndices().Select(x => new int[] { x.A, x.C, x.B }).SelectMany(x => x).Select(x => (uint)x).ToArray().Length; + } + + public static IndexBufferBinding ConvertSerializedIndexBufferBinding(SharpGLTF.Schema2.MeshPrimitive mesh) + { + var indices = + mesh.GetTriangleIndices() + .Select(x => new int[] { x.A, x.C, x.B }) + .SelectMany(x => x).Select(x => (uint)x) + .Select(BitConverter.GetBytes) + .SelectMany(x => x).ToArray(); + var buf = GraphicsSerializerExtensions.ToSerializableVersion(new BufferData(BufferFlags.IndexBuffer, indices)); + return new IndexBufferBinding(buf, true, indices.Length); + } + + public static VertexBufferBinding[] ConvertSerializedVertexBufferBinding(SharpGLTF.Schema2.MeshPrimitive mesh) + { + var offset = 0; + var vertElem = + mesh.VertexAccessors + .Select( + x => + { + var y = ConvertVertexElement(x, offset); + offset += y.Item2; + return y.Item1; + }) + .ToList(); + + var declaration = + new VertexDeclaration( + vertElem.ToArray() + ); + + var size = mesh.VertexAccessors.First().Value.Count; + var byteBuffer = Enumerable.Range(0, size) + .Select( + x => + declaration.EnumerateWithOffsets() + .Select(y => y.VertexElement.SemanticName + .Replace("ORD", "ORD_" + y.VertexElement.SemanticIndex) + .Replace("BLENDINDICES", "JOINTS_0") + .Replace("BLENDWEIGHT", "WEIGHTS_0") + ) + .Select(y => mesh.GetVertexAccessor(y).TryGetVertexBytes(x).ToArray()) + ) + .SelectMany(x => x) + .SelectMany(x => x) + .ToArray(); + + var buffer = + GraphicsSerializerExtensions.ToSerializableVersion( + new BufferData(BufferFlags.VertexBuffer,byteBuffer) + ); + var binding = new VertexBufferBinding(buffer, declaration, size); + + return new List() { binding }.ToArray(); + } + + + + public static (VertexElement, int) ConvertVertexElement(KeyValuePair accessor, int offset) + { + + return (accessor.Key, accessor.Value.Format.ByteSize) switch + { + ("POSITION", 12) => (VertexElement.Position(0, offset), Vector3.SizeInBytes), + ("NORMAL", 12) => (VertexElement.Normal(0, offset), Vector3.SizeInBytes), + ("TANGENT", 12) => (VertexElement.Tangent(0, offset), Vector3.SizeInBytes), + ("COLOR", 16) => (VertexElement.Color(0, offset), Vector4.SizeInBytes), + ("TEXCOORD_0", 8) => (VertexElement.TextureCoordinate(0, offset), Vector2.SizeInBytes), + ("TEXCOORD_1", 8) => (VertexElement.TextureCoordinate(1, offset), Vector2.SizeInBytes), + ("TEXCOORD_2", 8) => (VertexElement.TextureCoordinate(2, offset), Vector2.SizeInBytes), + ("TEXCOORD_3", 8) => (VertexElement.TextureCoordinate(3, offset), Vector2.SizeInBytes), + ("TEXCOORD_4", 8) => (VertexElement.TextureCoordinate(4, offset), Vector2.SizeInBytes), + ("TEXCOORD_5", 8) => (VertexElement.TextureCoordinate(5, offset), Vector2.SizeInBytes), + ("TEXCOORD_6", 8) => (VertexElement.TextureCoordinate(6, offset), Vector2.SizeInBytes), + ("TEXCOORD_7", 8) => (VertexElement.TextureCoordinate(7, offset), Vector2.SizeInBytes), + ("TEXCOORD_8", 8) => (VertexElement.TextureCoordinate(8, offset), Vector2.SizeInBytes), + ("TEXCOORD_9", 8) => (VertexElement.TextureCoordinate(9, offset), Vector2.SizeInBytes), + ("JOINTS_0", 8) => (new VertexElement(VertexElementUsage.BlendIndices, 0, PixelFormat.R16G16B16A16_UInt, offset), 8), + ("JOINTS_0", 4) => (new VertexElement(VertexElementUsage.BlendIndices, 0, PixelFormat.R8G8B8A8_UInt, offset), 4), + ("WEIGHTS_0", 16) => (new VertexElement(VertexElementUsage.BlendWeight, 0, PixelFormat.R32G32B32A32_Float, offset), Vector4.SizeInBytes), + _ => throw new NotImplementedException(), + }; + } + + public static PrimitiveType ConvertPrimitiveType(SharpGLTF.Schema2.PrimitiveType gltfType) + { + return gltfType switch + { + SharpGLTF.Schema2.PrimitiveType.LINES => PrimitiveType.LineList, + SharpGLTF.Schema2.PrimitiveType.POINTS => PrimitiveType.PointList, + SharpGLTF.Schema2.PrimitiveType.LINE_LOOP => PrimitiveType.Undefined, + SharpGLTF.Schema2.PrimitiveType.LINE_STRIP => PrimitiveType.LineStrip, + SharpGLTF.Schema2.PrimitiveType.TRIANGLES => PrimitiveType.TriangleList, + SharpGLTF.Schema2.PrimitiveType.TRIANGLE_STRIP => PrimitiveType.TriangleStrip, + SharpGLTF.Schema2.PrimitiveType.TRIANGLE_FAN => PrimitiveType.Undefined, + _ => throw new NotImplementedException() + }; + } + + + } +} diff --git a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs new file mode 100644 index 0000000000..a4c7c479b7 --- /dev/null +++ b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Stride.Core.Mathematics; + +namespace Stride.Importer.Gltf +{ + class GltfUtils + { + public static Matrix ConvertNumerics(System.Numerics.Matrix4x4 mat) + { + return new Matrix( + mat.M11, mat.M12, mat.M13, mat.M14, + mat.M21, mat.M22, mat.M23, mat.M24, + mat.M31, mat.M32, mat.M33, mat.M34, + mat.M41, mat.M42, mat.M43, mat.M44 + ); + } + + public static Quaternion ConvertNumerics(System.Numerics.Quaternion v) => new Quaternion(v.X, v.Y, v.Z, v.W); + public static Vector4 ConvertNumerics(System.Numerics.Vector4 v) => new Vector4(v.X, v.Y, v.Z, v.W); + public static Vector3 ConvertNumerics(System.Numerics.Vector3 v) => new Vector3(v.X, v.Y, v.Z); + public static Vector2 ConvertNumerics(System.Numerics.Vector2 v) => new Vector2(v.X, v.Y); + } +} diff --git a/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj b/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj new file mode 100644 index 0000000000..7b7e34a668 --- /dev/null +++ b/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj @@ -0,0 +1,23 @@ + + + + false + Library + $(StrideEditorTargetFrameworks) + + + + + + + + + + + + + + + + + From 0ed55b594d1625fee5c0a288c21e2b6bd1b8e7fc Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Thu, 25 Mar 2021 12:09:49 +0100 Subject: [PATCH 02/29] file corrections --- .../Stride.Importer.Gltf/GltfImporter.cs | 391 ------------------ .../Stride.Importer.Gltf/GltfMeshParser.cs | 2 +- .../Stride.Importer.Gltf.csproj | 11 +- 3 files changed, 5 insertions(+), 399 deletions(-) delete mode 100644 sources/tools/Stride.Importer.Gltf/GltfImporter.cs diff --git a/sources/tools/Stride.Importer.Gltf/GltfImporter.cs b/sources/tools/Stride.Importer.Gltf/GltfImporter.cs deleted file mode 100644 index 064fe9521f..0000000000 --- a/sources/tools/Stride.Importer.Gltf/GltfImporter.cs +++ /dev/null @@ -1,391 +0,0 @@ -using System; -using System.IO; -using System.Collections.Generic; -using System.Linq; -using Stride.Core.Mathematics; -using Stride.Rendering; -using Stride.Graphics; -using Stride.Rendering.Materials; -using Stride.Rendering.Materials.ComputeColors; -using Stride.Core.Diagnostics; -using Stride.Core; -using Stride.Core.Extensions; - -namespace Stride.Importer.Gltf -{ - public enum VertexType - { - VertexPositionTexture, - VertexPositionNormalTexture, - VertexPositionNormalColor - } - class GltfImporter - { - private SharpGLTF.Schema2.ModelRoot GltfRoot { get; set; } - private SharpGLTF.Schema2.Mesh CurrentMesh { get; set; } - public Stride.Core.Serialization.Contents.ContentManager Content { get; set; } - - - public Model GetModel(int modelID) - { - var model = new Model(); - CurrentMesh = GltfRoot.LogicalMeshes[modelID]; - for (int i = 0; i < GltfRoot.LogicalMeshes[modelID].Primitives.Count; i++) - { - var matDesc = GetMaterial(i); - // var matDesc = new MaterialDescriptor{ - // Attributes = new MaterialAttributes { - // Diffuse = new MaterialDiffuseMapFeature(new ComputeColor(Stride.Core.Mathematics.Color.AliceBlue)), - // DiffuseModel = new MaterialDiffuseLambertModelFeature() - // } - // }; - var material = Material.New(Device, matDesc); - model.Add(material); - //var pc = new ParameterCollection(); - //var p1 = new ParameterKeyInfo(new ObjectParameterKey("Material.HasSkinningPosition",1,Array.Empty()),0, - //pc.ParameterKeyInfos.Add(p); - - - - - var mesh = new Mesh { Draw = GetMeshDraw(i) }; - - mesh.Parameters.Set(MaterialKeys.HasSkinningPosition, true); - mesh.Parameters.Set(MaterialKeys.HasSkinningNormal, true); - - //Logger.Info("Index of material is : " + model.Materials.IndexOf(material)); - mesh.MaterialIndex = model.Materials.IndexOf(material); - model.Add(mesh); - model.Skeleton = GetSkeleton(i, mesh); - } - return model; - } - public MaterialDescriptor GetMaterial(int primitiveID) - { - var material = new MaterialDescriptor - { - Attributes = new MaterialAttributes() - }; - foreach (var chan in CurrentMesh.Primitives[primitiveID].Material.Channels) - { - if (chan.Texture != null) - { - using var fs = new FileStream(chan.Texture.PrimaryImage.Content.SourcePath, FileMode.Open); - var image = Stride.Graphics.Image.Load(fs, true); - var texture = Texture.New(Device, image, TextureFlags.None); - - - switch (chan.Key) - { - case "BaseColor": - var vt = new ComputeTextureColor(texture) - { - AddressModeU = TextureAddressMode.Wrap, - AddressModeV = TextureAddressMode.Wrap, - TexcoordIndex = TextureCoordinate.Texcoord0 - }; - - material.Attributes.Diffuse = new MaterialDiffuseMapFeature(vt); - - material.Attributes.DiffuseModel = new MaterialDiffuseLambertModelFeature(); - //material.Attributes.DiffuseModel = new MaterialDiffuseCelShadingModelFeature(); - //Logger.Info("Added diffuse material"); - break; - case "MetallicRoughness": - material.Attributes.MicroSurface = new MaterialGlossinessMapFeature(new ComputeTextureScalar(texture, TextureCoordinate.Texcoord0, Vector2.One, Vector2.Zero)); - break; - case "Normal": - material.Attributes.Surface = new MaterialNormalMapFeature(new ComputeTextureColor(texture)); - break; - case "Occlusion": - material.Attributes.Occlusion = new MaterialOcclusionMapFeature(); - break; - case "Emissive": - material.Attributes.Emissive = new MaterialEmissiveMapFeature(new ComputeTextureColor(texture)); - break; - } - - } - - } - material.Attributes.CullMode = CullMode.Back; - return material; - } - public MeshDraw GetMeshDraw(int primitiveID) - { - Stride.Graphics.Buffer vBuff; - var vs = GetVertexBuffer(primitiveID); - var vt = GetVertexType(primitiveID); - //if (vt == VertexType.VertexPositionNormalTexture) - //{ - // vBuff = Stride.Graphics.Buffer.Vertex.New( - // Device, - // AsVPNTJW(vs), - // GraphicsResourceUsage.Default - // ); - //} - //else - //{ - // vBuff = Stride.Graphics.Buffer.Vertex.New( - // Device, - // AsVPT(vs), - // GraphicsResourceUsage.Dynamic - // ); - //} - vBuff = Stride.Graphics.Buffer.Vertex.New( - Device, - AsVPNTJW(vs), - GraphicsResourceUsage.Default - ); - - var iBuff = Stride.Graphics.Buffer.Index.New( - Device, - GetIndices(primitiveID) - ); - var primitiveType = GetPrimitiveType(primitiveID); - - return new MeshDraw - { - PrimitiveType = primitiveType, - DrawCount = iBuff.ElementCount, - VertexBuffers = new[] { new VertexBufferBinding(vBuff, VertexPNTJW.Layout, vBuff.ElementCount) }, - IndexBuffer = new IndexBufferBinding(iBuff, true, iBuff.ElementCount) - }; - - - } - public bool IsSkinnedMesh() => CurrentMesh.AllPrimitivesHaveJoints; - //public VertexElement[] getVertexDefinition(int primitiveID) - //{ - // //TODO: Support multiple joints (up to 4) and multiple weights (up to 4) if needed - // var result = new List(); - // if(IsSkinnedMesh()) - // { - // var joints = CurrentMesh.Primitives[primitiveID].VertexAccessors["JOINTS_0"].AsVector4Array().Select(ToStrideVector4).ToArray(); - // var weights = CurrentMesh.Primitives[primitiveID].VertexAccessors["WEIGHTS_0"].AsVector4Array().Select(ToStrideVector4).ToArray(); - // for(int i = 0; i< joints.Length; i++) - // { - // //result.Add(new VertexElement(VertexElementUsage.BlendWeight, (int)joints[i].X, PixelFormat.None, -1)); - // } - // } - // return new VertexElement[](); - //} - public VertexType GetVertexType(int primitiveID) - { - var keys = CurrentMesh.Primitives[primitiveID].VertexAccessors.Keys; - if (keys.Contains("NORMAL")) - return VertexType.VertexPositionNormalTexture; - else - return VertexType.VertexPositionTexture; - } - public IVertex[] GetVertexBuffer(int primitiveID) - { - - - var result = new List(); - var elems = new List(); - if (GetVertexType(primitiveID) == VertexType.VertexPositionNormalTexture) - { - var positions = CurrentMesh.Primitives[primitiveID].VertexAccessors["POSITION"].AsVector3Array(); - var normals = CurrentMesh.Primitives[primitiveID].VertexAccessors["NORMAL"].AsVector3Array(); - var joints = CurrentMesh.Primitives[primitiveID].VertexAccessors["JOINTS_0"].AsVector4Array(); - var weights = CurrentMesh.Primitives[primitiveID].VertexAccessors["WEIGHTS_0"].AsVector4Array(); - var texAccessor = CurrentMesh.Primitives[primitiveID].VertexAccessors.Where(x => x.Key.Contains("TEXCOORD")).Select(x => x.Key).First(); - var texCoords = CurrentMesh.Primitives[primitiveID].VertexAccessors[texAccessor].AsVector2Array(); - - for (int i = 0; i < positions.Count(); i++) - { - result.Add( - new VertexPNTJW( - ToStrideVector3(positions[i]), - ToStrideVector3(normals[i]), - ToStrideVector2(texCoords[i]), - ToStrideInt4(joints[i]), - ToStrideVector4(weights[i]) - ) - ); - } - return result.ToArray(); - } - else - { - var positions = CurrentMesh.Primitives[primitiveID].VertexAccessors["POSITION"].AsVector3Array(); - - var texAccessor = CurrentMesh.Primitives[primitiveID].VertexAccessors.Where(x => x.Key.Contains("TEXCOORD")).Select(x => x.Key).First(); - var texCoords = CurrentMesh.Primitives[primitiveID].VertexAccessors[texAccessor].AsVector2Array(); - for (int i = 0; i < positions.Count(); i++) - { - result.Add(new VertexPositionTexture(ToStrideVector3(positions[i]), ToStrideVector2(texCoords[i]))); - } - return result.ToArray(); - } - } - - - - public uint[] GetIndices(int primitiveID) - { - var prim = CurrentMesh.Primitives[primitiveID]; - - var result = new List(); - prim.GetTriangleIndices().ToList().ForEach(x => { result.Add((uint)x.A); result.Add((uint)x.C); result.Add((uint)x.B); }); - return result.ToArray(); - } - - public int GetTriIndicesCount(int primitiveID) - { - return CurrentMesh.Primitives[primitiveID].GetTriangleIndices().Count(); - } - public Stride.Graphics.PrimitiveType GetPrimitiveType(int primitiveID) - { - return CurrentMesh.Primitives[primitiveID].DrawPrimitiveType switch - { - SharpGLTF.Schema2.PrimitiveType.TRIANGLES => Stride.Graphics.PrimitiveType.TriangleList, - SharpGLTF.Schema2.PrimitiveType.TRIANGLE_STRIP => Stride.Graphics.PrimitiveType.TriangleStrip, - SharpGLTF.Schema2.PrimitiveType.LINES => Stride.Graphics.PrimitiveType.LineList, - SharpGLTF.Schema2.PrimitiveType.POINTS => Stride.Graphics.PrimitiveType.PointList, - SharpGLTF.Schema2.PrimitiveType.LINE_LOOP => Stride.Graphics.PrimitiveType.Undefined, - SharpGLTF.Schema2.PrimitiveType.LINE_STRIP => Stride.Graphics.PrimitiveType.Undefined, - SharpGLTF.Schema2.PrimitiveType.TRIANGLE_FAN => Stride.Graphics.PrimitiveType.Undefined, - _ => Stride.Graphics.PrimitiveType.Undefined, - }; - } - - // public Object GetIndicesByType(int primitiveID) - // { - // List indices = new List(); - // switch(CurrentMesh.Primitives[primitiveID].DrawPrimitiveType) - // { - // case SharpGLTF.Schema2.PrimitiveType.TRIANGLES : - // CurrentMesh.Primitives[primitiveID].GetTriangleIndices().ToList().ForEach(i => {indices.Add(i.A);indices.Add(i.B);indices.Add(i.C);}); - // return indices; - // case SharpGLTF.Schema2.PrimitiveType.TRIANGLE_STRIP : - // CurrentMesh.Primitives[primitiveID].GetTriangleIndices(); - // case SharpGLTF.Schema2.PrimitiveType.LINES : - // Stride.Graphics.PrimitiveType.LineList; - // case SharpGLTF.Schema2.PrimitiveType.POINTS : - // Stride.Graphics.PrimitiveType.PointList; - // case SharpGLTF.Schema2.PrimitiveType.LINE_LOOP : - // Stride.Graphics.PrimitiveType.Undefined; - // case SharpGLTF.Schema2.PrimitiveType.LINE_STRIP : - // Stride.Graphics.PrimitiveType.Undefined; - // case SharpGLTF.Schema2.PrimitiveType.TRIANGLE_FAN : - // Stride.Graphics.PrimitiveType.Undefined; - // _ => Stride.Graphics.PrimitiveType.Undefined, - // }; - // } - - public Skeleton GetSkeleton(int primitiveID, Mesh mesh) - { - var foxSkin = GltfRoot.LogicalNodes.First(x => x.Mesh == CurrentMesh).Skin; - List BoneInfluencing = new List(); - var glSK = foxSkin.Skeleton; - CurrentMesh - .Primitives[primitiveID] - .VertexAccessors["JOINTS_0"] - .AsVector4Array() - .ToList() - .ForEach( - x => { BoneInfluencing.Add((int)x.X); BoneInfluencing.Add((int)x.Y); BoneInfluencing.Add((int)x.Z); BoneInfluencing.Add((int)x.W); } - ); - BoneInfluencing = BoneInfluencing.Distinct().ToList(); - var sk = new Skeleton(); - var nodes = new List(); - var glnodes = new List(); - var matNodes = new List(); - //var glJoints = new List<(SharpGLTF.Schema2.Node, System.Numerics.Matrix4x4)>(); - for (int i = 0; i < foxSkin.JointsCount; i++) - { - glnodes.Add(foxSkin.GetJoint(i).Joint); - matNodes.Add(foxSkin.GetJoint(i).InverseBindMatrix); - } - glnodes.ForEach( - x => - { - var pn = GetParentNodeIndex(glnodes, x); - nodes.Add( - new ModelNodeDefinition - { - Flags = pn == -1 ? ModelNodeFlags.EnableRender : ModelNodeFlags.Default, - ParentIndex = pn, - Transform = - new TransformTRS - { - Position = ToStrideVector3(x.LocalTransform.Translation), - Rotation = ToStrideQuaternion(x.LocalTransform.Rotation), - Scale = ToStrideVector3(x.LocalTransform.Scale) - }, - Name = x.Name - } - ); - } - ); - var rootBone = nodes[0]; - Matrix.Transformation(ref rootBone.Transform.Scale, ref rootBone.Transform.Rotation, ref rootBone.Transform.Position, out Matrix tmpMat); - Matrix.Transformation(ref rootBone.Transform.Scale, ref rootBone.Transform.Rotation, ref rootBone.Transform.Position, out Matrix tmpMat2); - tmpMat2.Invert(); - var mbd = new List(); - foreach (int boneId in BoneInfluencing) - { - var mat = ToStrideMatrix1(matNodes[boneId]); - //var currentNode = nodes[boneId]; - //Matrix.Transformation(ref currentNode.Transform.Scale, ref currentNode.Transform.Rotation, ref currentNode.Transform.Position, out Matrix mat); - //mat.Invert(); - - mbd.Add(new MeshBoneDefinition - { - NodeIndex = boneId, - LinkToMeshMatrix = tmpMat2 * mat * tmpMat - }); - } - sk.Nodes = nodes.ToArray(); - mesh.Skinning = new MeshSkinningDefinition - { - Bones = mbd.ToArray() - }; - - return sk; - } - private int GetParentNodeIndex(List glnodes, SharpGLTF.Schema2.Node current) - { - try - { - return glnodes.IndexOf(glnodes.First(y => y.Name == current.VisualParent.Name)); - } - catch (Exception) - { - return -1; - } - } - private Vector4 ToStrideVector4(System.Numerics.Vector4 a) => new Vector4(a.X, a.Y, a.Z, a.W); - private Int4 ToStrideInt4(System.Numerics.Vector4 a) => new Int4((int)a.X, (int)a.Y, (int)a.Z, (int)a.W); - - private Vector3 ToStrideVector3(System.Numerics.Vector3 a) => new Vector3(a.X, a.Y, a.Z); - private Vector2 ToStrideVector2(System.Numerics.Vector2 a) => new Vector2(a.X, a.Y); - private VertexPNTJW[] AsVPNTJW(IVertex[] v) => v.Select(x => (VertexPNTJW)x).ToArray(); - private VertexPositionNormalTexture[] AsVPNT(IVertex[] v) => v.Select(x => (VertexPositionNormalTexture)x).ToArray(); - private VertexPositionTexture[] AsVPT(IVertex[] v) => v.Select(x => (VertexPositionTexture)x).ToArray(); - public Vector3 ToStridePosition(SharpGLTF.Transforms.AffineTransform tr) => new Vector3(tr.Translation.X, tr.Translation.Y, tr.Translation.Z); - public Quaternion ToStrideQuaternion(System.Numerics.Quaternion rot) => new Quaternion(rot.X, rot.Y, rot.Z, rot.W); - public Matrix ToStrideMatrix1(System.Numerics.Matrix4x4 mat) - { - return new Matrix( - mat.M11, mat.M21, mat.M31, mat.M41, - mat.M12, mat.M22, mat.M32, mat.M42, - mat.M13, mat.M23, mat.M33, mat.M43, - mat.M14, mat.M24, mat.M34, mat.M44 - ); - } - public Matrix ToStrideMatrix2(System.Numerics.Matrix4x4 mat) - { - return new Matrix( - mat.M11, mat.M12, mat.M13, mat.M14, - mat.M21, mat.M22, mat.M23, mat.M24, - mat.M31, mat.M32, mat.M33, mat.M34, - mat.M41, mat.M42, mat.M43, mat.M44 - ); - } - } -} -} - diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 6325e73555..539fd0732e 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -80,7 +80,7 @@ public static Dictionary ConvertAnimations(SharpGLTF.Sche var clip = new AnimationClip { Duration = TimeSpan.FromSeconds(x.Duration) }; clip.RepeatMode = AnimationRepeatMode.LoopInfinite; // Add Curve - ConvertCurves(x.Channels, root).ToList().ForEach(x => clip.AddCurve(x.Key, x.Value)); + ConvertCurves(x.Channels, root).ToList().ForEach(v => clip.AddCurve(v.Key, v.Value)); return (x.Name, clip); } diff --git a/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj b/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj index 7b7e34a668..38d3bb2210 100644 --- a/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj +++ b/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj @@ -1,20 +1,17 @@ - false - Library $(StrideEditorTargetFrameworks) - + 8.0 - - + + - - + From 97c4899583a03966ffc7ee9c168fb201580fe782 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Thu, 25 Mar 2021 12:11:05 +0100 Subject: [PATCH 03/29] bump SharpGltf version --- .../tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj b/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj index 38d3bb2210..632681dc71 100644 --- a/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj +++ b/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj @@ -6,8 +6,8 @@ - - + + From 01fa0d9ee17b99a4aad332d2049337447023ba9e Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Thu, 25 Mar 2021 23:19:57 +0100 Subject: [PATCH 04/29] first MVP import gltf model + skeleton --- .../AssimpAssetImporter.cs | 2 +- .../Stride.Assets.Models/GltfAssetImporter.cs | 10 +---- .../ImportModelCommand.cs | 2 + .../Stride.Importer.Gltf/GltfMeshParser.cs | 43 ++++++++++++++++++- .../Stride.Importer.Gltf.csproj | 15 ++++++- 5 files changed, 60 insertions(+), 12 deletions(-) diff --git a/sources/engine/Stride.Assets.Models/AssimpAssetImporter.cs b/sources/engine/Stride.Assets.Models/AssimpAssetImporter.cs index 68451d2dd6..fd8723f62a 100644 --- a/sources/engine/Stride.Assets.Models/AssimpAssetImporter.cs +++ b/sources/engine/Stride.Assets.Models/AssimpAssetImporter.cs @@ -20,7 +20,7 @@ static AssimpAssetImporter() } // Supported file extensions for this importer - internal const string FileExtensions = ".dae;.3ds;.gltf;.glb;.obj;.blend;.x;.md2;.md3;.dxf;.ply;.stl;.stp"; + internal const string FileExtensions = ".dae;.3ds;.obj;.blend;.x;.md2;.md3;.dxf;.ply;.stl;.stp"; private static readonly Guid Uid = new Guid("30243FC0-CEC7-4433-977E-95DCA29D846E"); diff --git a/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs b/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs index 3a32b5a16e..db9d68c02c 100644 --- a/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs +++ b/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs @@ -30,14 +30,8 @@ public class GltfAssetImporter : ModelAssetImporter /// public override EntityInfo GetEntityInfo(UFile localPath, Logger logger, AssetImporterParameters importParameters) { - //var meshConverter = new Importer.AssimpNET.MeshConverter(logger); - - //if (!importParameters.InputParameters.TryGet(DeduplicateMaterialsKey, out var deduplicateMaterials)) - // deduplicateMaterials = true; // Dedupe is the default value - - //var entityInfo = meshConverter.ExtractEntity(localPath.FullPath, null, importParameters.IsTypeSelectedForOutput(typeof(TextureAsset)), deduplicateMaterials); - //return entityInfo; - return null; + + return GltfMeshParser.ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot.Load(localPath.FullPath)); } /// diff --git a/sources/engine/Stride.Assets.Models/ImportModelCommand.cs b/sources/engine/Stride.Assets.Models/ImportModelCommand.cs index a94bffbbf5..40d1206d99 100644 --- a/sources/engine/Stride.Assets.Models/ImportModelCommand.cs +++ b/sources/engine/Stride.Assets.Models/ImportModelCommand.cs @@ -28,6 +28,8 @@ public abstract partial class ImportModelCommand : SingleFileImportCommand public static ImportModelCommand Create(string extension) { + if (ImportGltfCommand.IsSupportingExtensions(extension)) + return new ImportGltfCommand(); if (ImportFbxCommand.IsSupportingExtensions(extension)) return new ImportFbxCommand(); if (ImportAssimpCommand.IsSupportingExtensions(extension)) diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 539fd0732e..913374b671 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -18,6 +18,8 @@ using Stride.Shaders; using Stride.Core.Serialization; using Stride.Core.Assets; +using Stride.Importer.Common; +using Stride.Assets.Materials; namespace Stride.Importer.Gltf { @@ -46,7 +48,46 @@ public static TimeSpan GetAnimationDuration(SharpGLTF.Schema2.ModelRoot root) return TimeSpan.FromSeconds(time); } - + public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot) + { + + var skin = + modelRoot.LogicalSkins + //.Where(x => x.Skeleton.Mesh == modelRoot.LogicalMeshes[0]) + .First(); + var nodeNames = + Enumerable.Range(0, skin.JointsCount) + .Select(x => skin.GetJoint(x).Joint.Name); + var meshes = + modelRoot + .LogicalMeshes[0].Primitives + .Select((x,i) => modelRoot.LogicalMeshes[0].Name + "_" + i) + .Select( + x => + new MeshParameters() + { + MeshName = x, + BoneNodes = nodeNames.ToHashSet(), + MaterialName = "", + NodeName = "" + } + ) + .ToList(); + var animNodes = + modelRoot.LogicalAnimations.Select(x => x.Name).ToList(); + + var entityInfo = new EntityInfo + { + Models = meshes, + AnimationNodes = animNodes, + Materials = new Dictionary(), + Nodes = new List(), + TextureDependencies = new List() + + }; + return entityInfo; + } + public static MeshSkinningDefinition ConvertInverseBindMatrices(SharpGLTF.Schema2.ModelRoot root) { var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes[0]).Skin; diff --git a/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj b/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj index 632681dc71..6af727ecb0 100644 --- a/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj +++ b/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj @@ -1,7 +1,8 @@ + - $(StrideEditorTargetFrameworks) + $(StrideEditorTargetFramework) 8.0 @@ -12,9 +13,19 @@ + - + + + + + ..\Stride.Importer.Common\bin\net5.0-windows\x64\Debug\Stride.Importer.Common.dll + + + + + From 468c5b34ee9b9ec77ebd6031a87402201e875eea Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Sun, 28 Mar 2021 18:14:55 +0200 Subject: [PATCH 05/29] Finished importing all types of materials --- .../Stride.Assets.Models/GltfAssetImporter.cs | 2 +- .../GltfAnimationParser.cs | 24 +- .../Stride.Importer.Gltf/GltfMeshParser.cs | 233 ++++++++---------- .../tools/Stride.Importer.Gltf/GltfUtils.cs | 126 ++++++++++ 4 files changed, 233 insertions(+), 152 deletions(-) diff --git a/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs b/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs index db9d68c02c..84f0d826f8 100644 --- a/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs +++ b/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs @@ -31,7 +31,7 @@ public class GltfAssetImporter : ModelAssetImporter public override EntityInfo GetEntityInfo(UFile localPath, Logger logger, AssetImporterParameters importParameters) { - return GltfMeshParser.ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot.Load(localPath.FullPath)); + return GltfMeshParser.ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot.Load(localPath.FullPath), localPath); } /// diff --git a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs index 288612858a..81bd8eb9fa 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs @@ -1,13 +1,11 @@ -using Stride.Animations; using System; using System.Collections.Generic; -using System.Text; -using Stride.Core.Mathematics; using System.Linq; +using Stride.Animations; using Stride.Core.Collections; - -using static Stride.Importer.Gltf.GltfUtils; +using Stride.Core.Mathematics; using Stride.Rendering; +using static Stride.Importer.Gltf.GltfUtils; namespace Stride.Importer.Gltf { @@ -54,21 +52,7 @@ public static Skeleton ConvertSkeleton(SharpGLTF.Schema2.ModelRoot root) result.Nodes = mnd.ToArray(); return result; } - public static AnimationCurve CreateRotationCurve() - { - return new AnimationCurve - { - InterpolationType = AnimationCurveInterpolationType.Linear, - KeyFrames = - { - CreateKeyFrame(0.00f, Quaternion.RotationX(0)), - CreateKeyFrame(0.25f, Quaternion.RotationX(MathUtil.PiOverTwo)), - CreateKeyFrame(0.50f, Quaternion.RotationX(MathUtil.Pi)), - CreateKeyFrame(0.75f, Quaternion.RotationX(-MathUtil.PiOverTwo)), - CreateKeyFrame(1.00f, Quaternion.RotationX(MathUtil.TwoPi)) - } - }; - } + public static KeyFrameData CreateKeyFrame(float keyTime, T value) { diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 913374b671..dc63337092 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -1,25 +1,19 @@ -using Stride.Graphics; -using Stride.Rendering; using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using Stride.Animations; +using Stride.Assets.Materials; +using Stride.Core.IO; using Stride.Core.Mathematics; -using System.Runtime.InteropServices; +using Stride.Graphics; +using Stride.Graphics.Data; +using Stride.Importer.Common; +using Stride.Rendering; using Stride.Rendering.Materials; using Stride.Rendering.Materials.ComputeColors; -using System.IO; -//using Stride.Animations; -using Stride.Core.Collections; -using Stride.Graphics.Data; - -using static Stride.Importer.Gltf.GltfUtils; using static Stride.Importer.Gltf.GltfAnimationParser; -using Stride.Animations; -using Stride.Shaders; -using Stride.Core.Serialization; -using Stride.Core.Assets; -using Stride.Importer.Common; -using Stride.Assets.Materials; +using static Stride.Importer.Gltf.GltfUtils; namespace Stride.Importer.Gltf { @@ -35,9 +29,7 @@ public static Model LoadFirstModel(SharpGLTF.Schema2.ModelRoot root) .Select(x => LoadMesh(x)) .ToList() }; - //LoadMaterials(root).ForEach(x => result.Add(x)); - //result.Add(RedMaterial(device)); - //result.Meshes.ForEach(x => x.MaterialIndex = 0); + result.Skeleton = ConvertSkeleton(root); return result; } @@ -48,26 +40,26 @@ public static TimeSpan GetAnimationDuration(SharpGLTF.Schema2.ModelRoot root) return TimeSpan.FromSeconds(time); } - public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot) + public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot, UFile sourcePath) { var skin = modelRoot.LogicalSkins //.Where(x => x.Skeleton.Mesh == modelRoot.LogicalMeshes[0]) .First(); - var nodeNames = + var boneNames = Enumerable.Range(0, skin.JointsCount) .Select(x => skin.GetJoint(x).Joint.Name); var meshes = modelRoot .LogicalMeshes[0].Primitives - .Select((x,i) => modelRoot.LogicalMeshes[0].Name + "_" + i) + .Select((x, i) => modelRoot.LogicalMeshes[0].Name + "_" + i) .Select( - x => - new MeshParameters() + x => + new MeshParameters() { - MeshName = x, - BoneNodes = nodeNames.ToHashSet(), + MeshName = x, + BoneNodes = boneNames.ToHashSet(), MaterialName = "", NodeName = "" } @@ -80,14 +72,16 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot { Models = meshes, AnimationNodes = animNodes, - Materials = new Dictionary(), + Materials = LoadMaterials(modelRoot, sourcePath), Nodes = new List(), - TextureDependencies = new List() - + TextureDependencies = GenerateTextureFullPaths(modelRoot, sourcePath) }; return entityInfo; } + + + public static MeshSkinningDefinition ConvertInverseBindMatrices(SharpGLTF.Schema2.ModelRoot root) { var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes[0]).Skin; @@ -131,64 +125,84 @@ public static Dictionary ConvertAnimations(SharpGLTF.Sche return clips; } - //public static List LoadMaterials(SharpGLTF.Schema2.ModelRoot root) - //{ - // var result = new List(); - // foreach (var mat in root.LogicalMaterials) - // { - // var material = new MaterialAsset - // { - // Attributes = new MaterialAttributes() - // }; - // foreach (var chan in mat.Channels) - // { - // if (chan.Texture != null) { - - // var gltfImg = chan.Texture.PrimaryImage; - // var imgBuf = gltfImg.Content.Content.ToArray(); - // var imgPtr = new DataPointer(GCHandle.Alloc(imgBuf, GCHandleType.Pinned).AddrOfPinnedObject(), imgBuf.Length); - - // //var image = Stride.Graphics.Image.Load(imgPtr); - // //var shader = new ShaderClassSource("ComputeColorTextureRepeat",gltfImg.Name,"TEXCOORD","float2(1.0f,1.0f)"); - // var texture = AttachedReferenceManager.CreateProxyObject(AssetId.Empty,gltfImg.Name); - - - // switch (chan.Key) - // { - // case "BaseColor": - // var vt = new ComputeTextureColor(texture) - // { - // AddressModeU = TextureAddressMode.Wrap, - // AddressModeV = TextureAddressMode.Wrap, - // TexcoordIndex = TextureCoordinate.Texcoord0 - // }; - - // material.Attributes.Diffuse = new MaterialDiffuseMapFeature(vt); - - // material.Attributes.DiffuseModel = new MaterialDiffuseLambertModelFeature(); - // break; - // case "MetallicRoughness": - // material.Attributes.MicroSurface = new MaterialGlossinessMapFeature(new ComputeTextureScalar(texture, TextureCoordinate.Texcoord0, Vector2.One, Vector2.Zero)); - // break; - // case "Normal": - // material.Attributes.Surface = new MaterialNormalMapFeature(new ComputeTextureColor(texture)); - // break; - // case "Occlusion": - // material.Attributes.Occlusion = new MaterialOcclusionMapFeature(); - // break; - // case "Emissive": - // material.Attributes.Emissive = new MaterialEmissiveMapFeature(new ComputeTextureColor(texture)); - // break; - // } - - // } - - // } - // material.Attributes.CullMode = CullMode.Back; - // result.Add(material); - // } - // return result; - //} + public static Dictionary LoadMaterials(SharpGLTF.Schema2.ModelRoot root, UFile sourcePath) + { + var result = new Dictionary(); + foreach (var mat in root.LogicalMaterials) + { + var material = new MaterialAsset + { + Attributes = new MaterialAttributes() + }; + foreach (var chan in mat.Channels) + { + + if (chan.Texture != null && !chan.HasDefaultContent) + { + + var gltfImg = chan.Texture.PrimaryImage; + string imgPath; + if (gltfImg.Content.SourcePath == null) + { + imgPath = Path.Join(sourcePath.GetFullDirectory(), gltfImg.Name + "." + gltfImg.Content.FileExtension); + gltfImg.Content.SaveToFile(imgPath); + } + else + { + imgPath = gltfImg.Content.SourcePath; + } + + switch (chan.Key) + { + case "BaseColor": + material.Attributes.Diffuse = new MaterialDiffuseMapFeature(GenerateTextureColor(imgPath, (TextureCoordinate)chan.TextureCoordinate, Vector2.One)); + material.Attributes.DiffuseModel = new MaterialDiffuseLambertModelFeature(); + break; + case "MetallicRoughness": + material.Attributes.MicroSurface = new MaterialGlossinessMapFeature(GenerateTextureScalar(imgPath, (TextureCoordinate)chan.TextureCoordinate, Vector2.One)); + break; + case "Normal": + material.Attributes.Surface = new MaterialNormalMapFeature(GenerateTextureColor(imgPath, (TextureCoordinate)chan.TextureCoordinate, Vector2.One)); + break; + case "Occlusion": + material.Attributes.Occlusion = new MaterialOcclusionMapFeature(); + break; + case "Emissive": + material.Attributes.Emissive = new MaterialEmissiveMapFeature(GenerateTextureColor(imgPath, (TextureCoordinate)chan.TextureCoordinate, Vector2.One)); + break; + } + } + else if(chan.Texture == null && !chan.HasDefaultContent) + { + var vt = new ComputeColor(new Color(ConvertNumerics(chan.Parameter))); + var x = new ComputeFloat(chan.Parameter.X); + switch (chan.Key) + { + case "BaseColor": + material.Attributes.Diffuse = new MaterialDiffuseMapFeature(vt); + material.Attributes.DiffuseModel = new MaterialDiffuseLambertModelFeature(); + break; + case "MetallicRoughness": + material.Attributes.MicroSurface = new MaterialGlossinessMapFeature(x); + break; + case "Normal": + material.Attributes.Surface = new MaterialNormalMapFeature(vt); + break; + case "Occlusion": + material.Attributes.Occlusion = new MaterialOcclusionMapFeature(); + break; + case "Emissive": + material.Attributes.Emissive = new MaterialEmissiveMapFeature(vt); + break; + } + } + + } + material.Attributes.CullMode = CullMode.Back; + result.Add(mat.Name, material); + } + return result; + } public static Mesh LoadMesh(SharpGLTF.Schema2.MeshPrimitive mesh) { @@ -225,7 +239,7 @@ private static int GetDrawCount(SharpGLTF.Schema2.MeshPrimitive mesh) public static IndexBufferBinding ConvertSerializedIndexBufferBinding(SharpGLTF.Schema2.MeshPrimitive mesh) { - var indices = + var indices = mesh.GetTriangleIndices() .Select(x => new int[] { x.A, x.C, x.B }) .SelectMany(x => x).Select(x => (uint)x) @@ -272,7 +286,7 @@ public static VertexBufferBinding[] ConvertSerializedVertexBufferBinding(SharpGL var buffer = GraphicsSerializerExtensions.ToSerializableVersion( - new BufferData(BufferFlags.VertexBuffer,byteBuffer) + new BufferData(BufferFlags.VertexBuffer, byteBuffer) ); var binding = new VertexBufferBinding(buffer, declaration, size); @@ -280,48 +294,5 @@ public static VertexBufferBinding[] ConvertSerializedVertexBufferBinding(SharpGL } - - public static (VertexElement, int) ConvertVertexElement(KeyValuePair accessor, int offset) - { - - return (accessor.Key, accessor.Value.Format.ByteSize) switch - { - ("POSITION", 12) => (VertexElement.Position(0, offset), Vector3.SizeInBytes), - ("NORMAL", 12) => (VertexElement.Normal(0, offset), Vector3.SizeInBytes), - ("TANGENT", 12) => (VertexElement.Tangent(0, offset), Vector3.SizeInBytes), - ("COLOR", 16) => (VertexElement.Color(0, offset), Vector4.SizeInBytes), - ("TEXCOORD_0", 8) => (VertexElement.TextureCoordinate(0, offset), Vector2.SizeInBytes), - ("TEXCOORD_1", 8) => (VertexElement.TextureCoordinate(1, offset), Vector2.SizeInBytes), - ("TEXCOORD_2", 8) => (VertexElement.TextureCoordinate(2, offset), Vector2.SizeInBytes), - ("TEXCOORD_3", 8) => (VertexElement.TextureCoordinate(3, offset), Vector2.SizeInBytes), - ("TEXCOORD_4", 8) => (VertexElement.TextureCoordinate(4, offset), Vector2.SizeInBytes), - ("TEXCOORD_5", 8) => (VertexElement.TextureCoordinate(5, offset), Vector2.SizeInBytes), - ("TEXCOORD_6", 8) => (VertexElement.TextureCoordinate(6, offset), Vector2.SizeInBytes), - ("TEXCOORD_7", 8) => (VertexElement.TextureCoordinate(7, offset), Vector2.SizeInBytes), - ("TEXCOORD_8", 8) => (VertexElement.TextureCoordinate(8, offset), Vector2.SizeInBytes), - ("TEXCOORD_9", 8) => (VertexElement.TextureCoordinate(9, offset), Vector2.SizeInBytes), - ("JOINTS_0", 8) => (new VertexElement(VertexElementUsage.BlendIndices, 0, PixelFormat.R16G16B16A16_UInt, offset), 8), - ("JOINTS_0", 4) => (new VertexElement(VertexElementUsage.BlendIndices, 0, PixelFormat.R8G8B8A8_UInt, offset), 4), - ("WEIGHTS_0", 16) => (new VertexElement(VertexElementUsage.BlendWeight, 0, PixelFormat.R32G32B32A32_Float, offset), Vector4.SizeInBytes), - _ => throw new NotImplementedException(), - }; - } - - public static PrimitiveType ConvertPrimitiveType(SharpGLTF.Schema2.PrimitiveType gltfType) - { - return gltfType switch - { - SharpGLTF.Schema2.PrimitiveType.LINES => PrimitiveType.LineList, - SharpGLTF.Schema2.PrimitiveType.POINTS => PrimitiveType.PointList, - SharpGLTF.Schema2.PrimitiveType.LINE_LOOP => PrimitiveType.Undefined, - SharpGLTF.Schema2.PrimitiveType.LINE_STRIP => PrimitiveType.LineStrip, - SharpGLTF.Schema2.PrimitiveType.TRIANGLES => PrimitiveType.TriangleList, - SharpGLTF.Schema2.PrimitiveType.TRIANGLE_STRIP => PrimitiveType.TriangleStrip, - SharpGLTF.Schema2.PrimitiveType.TRIANGLE_FAN => PrimitiveType.Undefined, - _ => throw new NotImplementedException() - }; - } - - } } diff --git a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs index a4c7c479b7..1b0d3121db 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs @@ -1,12 +1,21 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Text; +using Stride.Core.Assets; +using Stride.Core.IO; using Stride.Core.Mathematics; +using Stride.Core.Serialization; +using Stride.Graphics; +using Stride.Rendering.Materials; +using Stride.Rendering.Materials.ComputeColors; namespace Stride.Importer.Gltf { class GltfUtils { + public static Matrix ConvertNumerics(System.Numerics.Matrix4x4 mat) { return new Matrix( @@ -21,5 +30,122 @@ public static Matrix ConvertNumerics(System.Numerics.Matrix4x4 mat) public static Vector4 ConvertNumerics(System.Numerics.Vector4 v) => new Vector4(v.X, v.Y, v.Z, v.W); public static Vector3 ConvertNumerics(System.Numerics.Vector3 v) => new Vector3(v.X, v.Y, v.Z); public static Vector2 ConvertNumerics(System.Numerics.Vector2 v) => new Vector2(v.X, v.Y); + + + public static (VertexElement, int) ConvertVertexElement(KeyValuePair accessor, int offset) + { + + return (accessor.Key, accessor.Value.Format.ByteSize) switch + { + ("POSITION", 12) => (VertexElement.Position(0, offset), Vector3.SizeInBytes), + ("NORMAL", 12) => (VertexElement.Normal(0, offset), Vector3.SizeInBytes), + ("TANGENT", 12) => (VertexElement.Tangent(0, offset), Vector3.SizeInBytes), + ("COLOR", 16) => (VertexElement.Color(0, offset), Vector4.SizeInBytes), + ("TEXCOORD_0", 8) => (VertexElement.TextureCoordinate(0, offset), Vector2.SizeInBytes), + ("TEXCOORD_1", 8) => (VertexElement.TextureCoordinate(1, offset), Vector2.SizeInBytes), + ("TEXCOORD_2", 8) => (VertexElement.TextureCoordinate(2, offset), Vector2.SizeInBytes), + ("TEXCOORD_3", 8) => (VertexElement.TextureCoordinate(3, offset), Vector2.SizeInBytes), + ("TEXCOORD_4", 8) => (VertexElement.TextureCoordinate(4, offset), Vector2.SizeInBytes), + ("TEXCOORD_5", 8) => (VertexElement.TextureCoordinate(5, offset), Vector2.SizeInBytes), + ("TEXCOORD_6", 8) => (VertexElement.TextureCoordinate(6, offset), Vector2.SizeInBytes), + ("TEXCOORD_7", 8) => (VertexElement.TextureCoordinate(7, offset), Vector2.SizeInBytes), + ("TEXCOORD_8", 8) => (VertexElement.TextureCoordinate(8, offset), Vector2.SizeInBytes), + ("TEXCOORD_9", 8) => (VertexElement.TextureCoordinate(9, offset), Vector2.SizeInBytes), + ("JOINTS_0", 8) => (new VertexElement(VertexElementUsage.BlendIndices, 0, PixelFormat.R16G16B16A16_UInt, offset), 8), + ("JOINTS_0", 4) => (new VertexElement(VertexElementUsage.BlendIndices, 0, PixelFormat.R8G8B8A8_UInt, offset), 4), + ("WEIGHTS_0", 16) => (new VertexElement(VertexElementUsage.BlendWeight, 0, PixelFormat.R32G32B32A32_Float, offset), Vector4.SizeInBytes), + _ => throw new NotImplementedException(), + }; + } + + public static PrimitiveType ConvertPrimitiveType(SharpGLTF.Schema2.PrimitiveType gltfType) + { + return gltfType switch + { + SharpGLTF.Schema2.PrimitiveType.LINES => PrimitiveType.LineList, + SharpGLTF.Schema2.PrimitiveType.POINTS => PrimitiveType.PointList, + SharpGLTF.Schema2.PrimitiveType.LINE_LOOP => PrimitiveType.Undefined, + SharpGLTF.Schema2.PrimitiveType.LINE_STRIP => PrimitiveType.LineStrip, + SharpGLTF.Schema2.PrimitiveType.TRIANGLES => PrimitiveType.TriangleList, + SharpGLTF.Schema2.PrimitiveType.TRIANGLE_STRIP => PrimitiveType.TriangleStrip, + SharpGLTF.Schema2.PrimitiveType.TRIANGLE_FAN => PrimitiveType.Undefined, + _ => throw new NotImplementedException() + }; + } + + public static List GenerateTextureFullPaths(SharpGLTF.Schema2.ModelRoot root, UFile sourcePath) + { + return root.LogicalTextures.Select(tex => + { + var gltfImg = tex.PrimaryImage; + string imgPath; + if (gltfImg.Content.SourcePath == null) + { + return imgPath = Path.Join(sourcePath.GetFullDirectory(), gltfImg.Name + "." + gltfImg.Content.FileExtension); + } + else + { + return imgPath = gltfImg.Content.SourcePath; + } + }).ToList(); + } + + public static ComputeTextureColor GenerateTextureColor(string sourceTextureFile, TextureCoordinate textureUVSetIndex, Vector2 textureUVscaling, TextureAddressMode addressModeU = TextureAddressMode.Wrap, TextureAddressMode addressModeV = TextureAddressMode.Wrap, string vfsOutputPath = "") + { + var textureFileName = Path.GetFileNameWithoutExtension(sourceTextureFile); + var url = vfsOutputPath + "_" + textureFileName; + + if (File.Exists(sourceTextureFile)) + { + //if (logger != nullptr) + //{ + // logger->Warning(String::Format("The texture '{0}' referenced in the mesh material can not be found on the system. Loading will probably fail at run time.", sourceTextureFile), + // nullptr, CallerInfo::Get(__FILEW__, __FUNCTIONW__, __LINE__)); + //} + } + + var uvScaling = textureUVscaling; + var textureName = textureFileName; + + var texture = AttachedReferenceManager.CreateProxyObject(AssetId.Empty, textureName); + + var currentTexture = + new ComputeTextureColor(texture, (TextureCoordinate)textureUVSetIndex, uvScaling, Vector2.Zero) + { + AddressModeU = addressModeU, + AddressModeV = addressModeV + }; + + return currentTexture; + } + public static ComputeTextureScalar GenerateTextureScalar(string sourceTextureFile, TextureCoordinate textureUVSetIndex, Vector2 textureUVscaling, TextureAddressMode addressModeU = TextureAddressMode.Wrap, TextureAddressMode addressModeV = TextureAddressMode.Wrap, string vfsOutputPath = "") + { + var textureFileName = Path.GetFileNameWithoutExtension(sourceTextureFile); + var url = vfsOutputPath + "_" + textureFileName; + + if (File.Exists(sourceTextureFile)) + { + //if (logger != nullptr) + //{ + // logger->Warning(String::Format("The texture '{0}' referenced in the mesh material can not be found on the system. Loading will probably fail at run time.", sourceTextureFile), + // nullptr, CallerInfo::Get(__FILEW__, __FUNCTIONW__, __LINE__)); + //} + } + + var uvScaling = textureUVscaling; + var textureName = textureFileName; + + var texture = AttachedReferenceManager.CreateProxyObject(AssetId.Empty, textureName); + + var currentTexture = + new ComputeTextureScalar(texture, (TextureCoordinate)textureUVSetIndex, uvScaling, Vector2.Zero) + { + AddressModeU = addressModeU, + AddressModeV = addressModeV + }; + + return currentTexture; + } + } } From d46164d513480a8cb1568a3d115d3414b059982a Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Mon, 29 Mar 2021 01:31:58 +0200 Subject: [PATCH 06/29] Mesh + Animation constraints --- .../GltfAnimationParser.cs | 32 ++++++++++++++++++- .../Stride.Importer.Gltf/GltfMeshParser.cs | 19 ++++++----- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs index 81bd8eb9fa..98a4519530 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs @@ -15,6 +15,10 @@ public static Skeleton ConvertSkeleton(SharpGLTF.Schema2.ModelRoot root) { Skeleton result = new Skeleton(); var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes.First()).Skin; + + // If there is no corresponding skins return null + if (skin == null) return null; + var jointList = Enumerable.Range(0, skin.JointsCount).Select(x => skin.GetJoint(x).Joint).ToList(); var mnd = jointList @@ -62,8 +66,34 @@ public static KeyFrameData CreateKeyFrame(float keyTime, T value) public static Dictionary ConvertCurves(IReadOnlyList channels, SharpGLTF.Schema2.ModelRoot root) { var result = new Dictionary(); - string baseString = "[ModelComponent.Key].Skeleton.NodeTransformations[index].Transform.type"; var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes.First()).Skin; + + // In case there is no skin joints/bones, + if (skin == null) + { + string base2 = "[TransformComponent].type"; + foreach (var chan in channels) + { + switch (chan.TargetNodePath) + { + case SharpGLTF.Schema2.PropertyPath.translation: + result.Add(base2.Replace("type", "Position"), ConvertCurve(chan.GetTranslationSampler())); + break; + case SharpGLTF.Schema2.PropertyPath.rotation: + result.Add(base2.Replace("type", "Rotation"), ConvertCurve(chan.GetRotationSampler())); + break; + case SharpGLTF.Schema2.PropertyPath.scale: + result.Add(base2.Replace("type", "Scale"), ConvertCurve(chan.GetScaleSampler())); + break; + }; + } + return result; + } + + string baseString = "[ModelComponent.Key].Skeleton.NodeTransformations[index].Transform.type"; + + + var jointList = Enumerable.Range(0, skin.JointsCount).Select(x => skin.GetJoint(x).Joint).ToList(); foreach (var chan in channels) { diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index dc63337092..1af4231aea 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -22,13 +22,11 @@ public class GltfMeshParser public static Model LoadFirstModel(SharpGLTF.Schema2.ModelRoot root) { - var result = new Model + var result = new Model(); + foreach (var model in root.LogicalMeshes) { - Meshes = - root.LogicalMeshes[0].Primitives - .Select(x => LoadMesh(x)) - .ToList() - }; + result.Meshes.AddRange(model.Primitives.Select(x => LoadMesh(x)).ToList()); + } result.Skeleton = ConvertSkeleton(root); return result; @@ -106,22 +104,23 @@ public static MeshSkinningDefinition ConvertInverseBindMatrices(SharpGLTF.Schema public static Dictionary ConvertAnimations(SharpGLTF.Schema2.ModelRoot root) { var animations = root.LogicalAnimations; + var meshName = root.LogicalMeshes[0].Name; var clips = animations - .Select(x => + .Select((x,i) => { //Create animation clip with var clip = new AnimationClip { Duration = TimeSpan.FromSeconds(x.Duration) }; clip.RepeatMode = AnimationRepeatMode.LoopInfinite; // Add Curve ConvertCurves(x.Channels, root).ToList().ForEach(v => clip.AddCurve(v.Key, v.Value)); - - return (x.Name, clip); + string name = x.Name ?? meshName + "_Animation_" + i; + return (name, clip); } ) .ToList() - .ToDictionary(x => x.Name, x => x.clip); + .ToDictionary(x => x.name, x => x.clip); return clips; } From 9fe612c34c3e1fed41f3363f748d48b2b3bb0841 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Mon, 29 Mar 2021 17:15:35 +0200 Subject: [PATCH 07/29] Some changes --- .../GltfAnimationParser.cs | 35 ++++++++- .../Stride.Importer.Gltf/GltfMeshParser.cs | 75 +++++++++++++------ .../tools/Stride.Importer.Gltf/GltfUtils.cs | 4 +- 3 files changed, 87 insertions(+), 27 deletions(-) diff --git a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs index 98a4519530..34e8127a8d 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs @@ -15,9 +15,37 @@ public static Skeleton ConvertSkeleton(SharpGLTF.Schema2.ModelRoot root) { Skeleton result = new Skeleton(); var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes.First()).Skin; - // If there is no corresponding skins return null - if (skin == null) return null; + if (skin == null) + { + result.Nodes = new List() { + new ModelNodeDefinition + { + Name = "root", + Flags = ModelNodeFlags.EnableRender, + ParentIndex = -1, + Transform = new TransformTRS + { + Position = Vector3.Zero, + Rotation = Quaternion.Identity, + Scale = Vector3.Zero + } + }, + new ModelNodeDefinition + { + Name = "Mesh", + Flags = ModelNodeFlags.EnableRender, + ParentIndex = -1, + Transform = new TransformTRS + { + Position = Vector3.Zero, + Rotation = Quaternion.Identity, + Scale = Vector3.Zero + } + }, + }.ToArray(); + return result; + } var jointList = Enumerable.Range(0, skin.JointsCount).Select(x => skin.GetJoint(x).Joint).ToList(); var mnd = @@ -66,9 +94,10 @@ public static KeyFrameData CreateKeyFrame(float keyTime, T value) public static Dictionary ConvertCurves(IReadOnlyList channels, SharpGLTF.Schema2.ModelRoot root) { var result = new Dictionary(); + if (root.LogicalAnimations.Count == 0) return result; var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes.First()).Skin; - // In case there is no skin joints/bones, + // In case there is no skin joints/bones, animate transform component if (skin == null) { string base2 = "[TransformComponent].type"; diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 1af4231aea..a581aaa12c 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -19,15 +19,29 @@ namespace Stride.Importer.Gltf { public class GltfMeshParser { - - public static Model LoadFirstModel(SharpGLTF.Schema2.ModelRoot root) + public static SharpGLTF.Schema2.ModelRoot LoadGltf(Stride.Core.IO.UFile sourcePath) { - var result = new Model(); - foreach (var model in root.LogicalMeshes) + + switch (sourcePath.GetFileExtension()) { - result.Meshes.AddRange(model.Primitives.Select(x => LoadMesh(x)).ToList()); + case ".gltf": + return SharpGLTF.Schema2.ModelRoot.Load(sourcePath); + case null: + return SharpGLTF.Schema2.ModelRoot.Load(sourcePath); + case ".glb": + var fs = new FileStream(sourcePath.FullPath, FileMode.Open); + return SharpGLTF.Schema2.ModelRoot.ReadGLB(fs); + default: + return null; } + } + public static Model LoadFirstModel(SharpGLTF.Schema2.ModelRoot root) + { + var result = new Model() + { + Meshes = root.LogicalMeshes[0].Primitives.Select(x => LoadMesh(x)).ToList() + }; result.Skeleton = ConvertSkeleton(root); return result; } @@ -40,30 +54,43 @@ public static TimeSpan GetAnimationDuration(SharpGLTF.Schema2.ModelRoot root) public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot, UFile sourcePath) { + SharpGLTF.Schema2.Skin skin = null; + HashSet boneNames = new HashSet(); + List nodes = new List(); + if (modelRoot.LogicalSkins.Where(x => x.Skeleton.Mesh == modelRoot.LogicalMeshes[0]).Count() > 0) + skin = + modelRoot.LogicalSkins + .Where(x => x.Skeleton.Mesh == modelRoot.LogicalMeshes[0]) + .First(); + + if (skin != null) + { + boneNames = + Enumerable.Range(0, skin.JointsCount) + .Select(x => skin.GetJoint(x).Joint.Name) + .ToHashSet(); + nodes = Enumerable.Range(0, skin.JointsCount) + .Select(x => new NodeInfo() { Name = skin.GetJoint(x).Joint.Name, Depth = skin.GetJoint(x).Joint.LogicalIndex, Preserve = true }) + .ToList(); + } - var skin = - modelRoot.LogicalSkins - //.Where(x => x.Skeleton.Mesh == modelRoot.LogicalMeshes[0]) - .First(); - var boneNames = - Enumerable.Range(0, skin.JointsCount) - .Select(x => skin.GetJoint(x).Joint.Name); var meshes = modelRoot .LogicalMeshes[0].Primitives - .Select((x, i) => modelRoot.LogicalMeshes[0].Name + "_" + i) + .Select((x, i) => (modelRoot.LogicalMeshes[0].Name + "_" + i, x.Material.Name)) .Select( x => new MeshParameters() { - MeshName = x, - BoneNodes = boneNames.ToHashSet(), - MaterialName = "", + MeshName = x.Item1, + BoneNodes = boneNames, + MaterialName = x.Name, NodeName = "" } ) .ToList(); - var animNodes = + + List animNodes = modelRoot.LogicalAnimations.Select(x => x.Name).ToList(); var entityInfo = new EntityInfo @@ -71,18 +98,19 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot Models = meshes, AnimationNodes = animNodes, Materials = LoadMaterials(modelRoot, sourcePath), - Nodes = new List(), + Nodes = nodes, TextureDependencies = GenerateTextureFullPaths(modelRoot, sourcePath) }; return entityInfo; } - + public static MeshSkinningDefinition ConvertInverseBindMatrices(SharpGLTF.Schema2.ModelRoot root) { var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes[0]).Skin; + if (skin == null) return null; var jointList = Enumerable.Range(0, skin.JointsCount).Select(skin.GetJoint); var mnt = new MeshSkinningDefinition @@ -108,7 +136,7 @@ public static Dictionary ConvertAnimations(SharpGLTF.Sche var clips = animations - .Select((x,i) => + .Select((x, i) => { //Create animation clip with var clip = new AnimationClip { Duration = TimeSpan.FromSeconds(x.Duration) }; @@ -150,7 +178,7 @@ public static Dictionary LoadMaterials(SharpGLTF.Schema2. { imgPath = gltfImg.Content.SourcePath; } - + switch (chan.Key) { case "BaseColor": @@ -171,7 +199,7 @@ public static Dictionary LoadMaterials(SharpGLTF.Schema2. break; } } - else if(chan.Texture == null && !chan.HasDefaultContent) + else if (chan.Texture == null && !chan.HasDefaultContent) { var vt = new ComputeColor(new Color(ConvertNumerics(chan.Parameter))); var x = new ComputeFloat(chan.Parameter.X); @@ -277,7 +305,8 @@ public static VertexBufferBinding[] ConvertSerializedVertexBufferBinding(SharpGL .Replace("BLENDINDICES", "JOINTS_0") .Replace("BLENDWEIGHT", "WEIGHTS_0") ) - .Select(y => mesh.GetVertexAccessor(y).TryGetVertexBytes(x).ToArray()) + .Select(y => mesh.GetVertexAccessor(y)?.TryGetVertexBytes(x).ToArray()) + .Where(x => x != null) ) .SelectMany(x => x) .SelectMany(x => x) diff --git a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs index 1b0d3121db..adf04cef07 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs @@ -34,13 +34,15 @@ public static Matrix ConvertNumerics(System.Numerics.Matrix4x4 mat) public static (VertexElement, int) ConvertVertexElement(KeyValuePair accessor, int offset) { - + // TODO : Simplify this part return (accessor.Key, accessor.Value.Format.ByteSize) switch { ("POSITION", 12) => (VertexElement.Position(0, offset), Vector3.SizeInBytes), ("NORMAL", 12) => (VertexElement.Normal(0, offset), Vector3.SizeInBytes), ("TANGENT", 12) => (VertexElement.Tangent(0, offset), Vector3.SizeInBytes), + ("TANGENT", 16) => (VertexElement.Tangent(0, offset), Vector4.SizeInBytes), ("COLOR", 16) => (VertexElement.Color(0, offset), Vector4.SizeInBytes), + ("COLOR_0", 16) => (VertexElement.Color(0, offset), Vector4.SizeInBytes), ("TEXCOORD_0", 8) => (VertexElement.TextureCoordinate(0, offset), Vector2.SizeInBytes), ("TEXCOORD_1", 8) => (VertexElement.TextureCoordinate(1, offset), Vector2.SizeInBytes), ("TEXCOORD_2", 8) => (VertexElement.TextureCoordinate(2, offset), Vector2.SizeInBytes), From 5c9cc9df4f94ead772cbed2219f0560257dad30f Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Mon, 29 Mar 2021 20:00:23 +0200 Subject: [PATCH 08/29] Little Hack to import multiple animations --- .../Stride.Assets.Models/GltfAssetImporter.cs | 36 +++++-------------- .../ImportModelCommand.Animation.cs | 7 ++++ .../ImportModelCommand.cs | 15 ++++++-- .../ModelAssetImporter.cs | 24 +++++++++---- .../Stride.Importer.Gltf/GltfMeshParser.cs | 3 +- 5 files changed, 48 insertions(+), 37 deletions(-) diff --git a/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs b/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs index 84f0d826f8..72f6e95ac2 100644 --- a/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs +++ b/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs @@ -10,6 +10,7 @@ using Stride.Assets.Textures; using Stride.Importer.Common; using Stride.Importer.Gltf; +using System.Linq; namespace Stride.Assets.Models { @@ -37,43 +38,24 @@ public override EntityInfo GetEntityInfo(UFile localPath, Logger logger, AssetIm /// public override void GetAnimationDuration(UFile localPath, Logger logger, AssetImporterParameters importParameters, out TimeSpan startTime, out TimeSpan endTime) { + var gltfFile = SharpGLTF.Schema2.ModelRoot.Load(localPath); + var animations = GltfMeshParser.ConvertAnimations(gltfFile); - var sceneData = GltfMeshParser.ConvertAnimations(SharpGLTF.Schema2.ModelRoot.Load(localPath)); + endTime = GltfMeshParser.GetAnimationDuration(gltfFile); // This will go down, so we start from positive infinity + startTime = CompressedTimeSpan.Zero; // This will go up, so we start from negative infinity - startTime = CompressedTimeSpan.MaxValue; // This will go down, so we start from positive infinity - endTime = CompressedTimeSpan.MinValue; // This will go up, so we start from negative infinity - - foreach (var animationClip in sceneData.Values) - { - foreach (var animationCurve in animationClip.Curves) - { - foreach (var compressedTimeSpan in animationCurve.Keys) - { - if (compressedTimeSpan < startTime) - startTime = compressedTimeSpan; - if (compressedTimeSpan > endTime) - endTime = compressedTimeSpan; - } - } - } - - if (startTime == CompressedTimeSpan.MaxValue) - startTime = CompressedTimeSpan.Zero; - if (endTime == CompressedTimeSpan.MinValue) - endTime = CompressedTimeSpan.Zero; + } public override IEnumerable Import(UFile localPath, AssetImporterParameters importParameters) { var assetItems = base.Import(localPath, importParameters); - // Need to remember the DeduplicateMaterials setting per ModelAsset since the importer may be rerun on this asset - if (!importParameters.InputParameters.TryGet(DeduplicateMaterialsKey, out var deduplicateMaterials)) - deduplicateMaterials = true; // Dedupe is the default value + foreach (var item in assetItems) { - if (item.Asset is ModelAsset modelAsset) + if (item.Asset is AnimationAsset animAsset) { - modelAsset.DeduplicateMaterials = deduplicateMaterials; + } } return assetItems; diff --git a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs index 79b2fddaa9..6dd371ec68 100644 --- a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs +++ b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs @@ -26,6 +26,13 @@ public partial class ImportModelCommand private unsafe object ExportAnimation(ICommandContext commandContext, ContentManager contentManager, bool failOnEmptyAnimation) { + + if (commandContext.CurrentCommand.Title.ToLower().Contains("gltf")) + { + var ts = TimeSpan.FromSeconds(1); + return LoadAnimation(commandContext, contentManager, out ts); + } + // Read from model file var modelSkeleton = LoadSkeleton(commandContext, contentManager); // we get model skeleton to compare it to real skeleton we need to map to AdjustSkeleton(modelSkeleton); diff --git a/sources/engine/Stride.Assets.Models/ImportModelCommand.cs b/sources/engine/Stride.Assets.Models/ImportModelCommand.cs index 40d1206d99..514b41a065 100644 --- a/sources/engine/Stride.Assets.Models/ImportModelCommand.cs +++ b/sources/engine/Stride.Assets.Models/ImportModelCommand.cs @@ -92,9 +92,18 @@ protected override async Task DoCommandOverride(ICommandContext co commandContext.Logger.Error($"Failed to import file {ContextAsString}."); return ResultStatus.Failed; } - - assetManager.Save(Location, exportedObject); - + if(exportedObject is Dictionary) + { + var exportedObjects = exportedObject as Dictionary; + foreach(var obj in exportedObjects) + assetManager.Save(obj.Key , obj.Value); + } + else + { + assetManager.Save(Location, exportedObject); + } + + commandContext.Logger.Verbose($"The {ContextAsString} has been successfully imported."); return ResultStatus.Successful; diff --git a/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs b/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs index ead4bacd32..7b50dc5c74 100644 --- a/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs +++ b/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs @@ -143,17 +143,29 @@ private static AssetItem ImportSkeleton(List assetReferences, UFile a private static void ImportAnimation(List assetReferences, UFile localPath, List animationNodes, bool shouldPostFixName, AssetItem skeletonAsset, TimeSpan animationStartTime, TimeSpan animationEndTime) { + bool shouldReplaceName = false; if (animationNodes != null && animationNodes.Count > 0) { - var assetSource = localPath; + if (animationNodes.Count > 1) + { + shouldReplaceName = true; + } + foreach(var anim in animationNodes) + { + var assetSource = localPath; - var asset = new AnimationAsset { Source = assetSource, AnimationTimeMaximum = animationEndTime, AnimationTimeMinimum = animationStartTime }; - var animUrl = localPath.GetFileNameWithoutExtension() + (shouldPostFixName ? " Animation" : ""); + var asset = new AnimationAsset { Source = assetSource, AnimationTimeMaximum = animationEndTime, AnimationTimeMinimum = animationStartTime }; + string animUrl; + if (!shouldReplaceName) + animUrl = localPath.GetFileNameWithoutExtension() + (shouldPostFixName ? " Animation " + anim : ""); + else + animUrl = localPath.GetFileNameWithoutExtension() + " " + anim; - if (skeletonAsset != null) - asset.Skeleton = AttachedReferenceManager.CreateProxyObject(skeletonAsset.Id, skeletonAsset.Location); + if (skeletonAsset != null) + asset.Skeleton = AttachedReferenceManager.CreateProxyObject(skeletonAsset.Id, skeletonAsset.Location); - assetReferences.Add(new AssetItem(animUrl, asset)); + assetReferences.Add(new AssetItem(animUrl, asset)); + } } } diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index a581aaa12c..4c1bde48e0 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -57,7 +57,7 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot SharpGLTF.Schema2.Skin skin = null; HashSet boneNames = new HashSet(); List nodes = new List(); - if (modelRoot.LogicalSkins.Where(x => x.Skeleton.Mesh == modelRoot.LogicalMeshes[0]).Count() > 0) + if (modelRoot.LogicalSkins.Where(x=>x.Skeleton != null).Where(x => x.Skeleton.Mesh == modelRoot.LogicalMeshes[0]).Count() > 0) skin = modelRoot.LogicalSkins .Where(x => x.Skeleton.Mesh == modelRoot.LogicalMeshes[0]) @@ -144,6 +144,7 @@ public static Dictionary ConvertAnimations(SharpGLTF.Sche // Add Curve ConvertCurves(x.Channels, root).ToList().ForEach(v => clip.AddCurve(v.Key, v.Value)); string name = x.Name ?? meshName + "_Animation_" + i; + clip.Optimize(); return (name, clip); } ) From 67aa25b3f4355de3de60d5973d40407c242f0aea Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Mon, 29 Mar 2021 20:17:02 +0200 Subject: [PATCH 09/29] Correct animation naming --- sources/engine/Stride.Assets.Models/ModelAssetImporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs b/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs index 7b50dc5c74..364d06384a 100644 --- a/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs +++ b/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs @@ -159,7 +159,7 @@ private static void ImportAnimation(List assetReferences, UFile local if (!shouldReplaceName) animUrl = localPath.GetFileNameWithoutExtension() + (shouldPostFixName ? " Animation " + anim : ""); else - animUrl = localPath.GetFileNameWithoutExtension() + " " + anim; + animUrl = anim; if (skeletonAsset != null) asset.Skeleton = AttachedReferenceManager.CreateProxyObject(skeletonAsset.Id, skeletonAsset.Location); From 7a6069bac6dd848d4368c5d8779563040f42bb79 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Tue, 30 Mar 2021 15:15:11 +0200 Subject: [PATCH 10/29] Animation asset name changes --- sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 4c1bde48e0..d58ac7bb5f 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -45,6 +45,10 @@ public static Model LoadFirstModel(SharpGLTF.Schema2.ModelRoot root) result.Skeleton = ConvertSkeleton(root); return result; } + public static string FirstModelName(SharpGLTF.Schema2.ModelRoot root) + { + return root.LogicalMeshes.First()?.Name; + } public static TimeSpan GetAnimationDuration(SharpGLTF.Schema2.ModelRoot root) { @@ -89,9 +93,9 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot } ) .ToList(); - + var meshName = FirstModelName(modelRoot); List animNodes = - modelRoot.LogicalAnimations.Select(x => x.Name).ToList(); + modelRoot.LogicalAnimations.Select(x => meshName + "_" + x.Name).ToList(); var entityInfo = new EntityInfo { @@ -143,7 +147,7 @@ public static Dictionary ConvertAnimations(SharpGLTF.Sche clip.RepeatMode = AnimationRepeatMode.LoopInfinite; // Add Curve ConvertCurves(x.Channels, root).ToList().ForEach(v => clip.AddCurve(v.Key, v.Value)); - string name = x.Name ?? meshName + "_Animation_" + i; + string name = x.Name == null ? meshName + "_Animation_" + i : meshName + "_" + x.Name; clip.Optimize(); return (name, clip); } From d0825a06fef786802c9258d9d0dd4e63c2ada56e Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Tue, 30 Mar 2021 16:47:09 +0200 Subject: [PATCH 11/29] Fixed glb texture import issue --- .../Stride.Assets.Models/ImportGltfCommand.cs | 4 +-- .../Stride.Importer.Gltf/GltfMeshParser.cs | 6 ++-- .../tools/Stride.Importer.Gltf/GltfUtils.cs | 31 +++++++++++++------ 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/sources/engine/Stride.Assets.Models/ImportGltfCommand.cs b/sources/engine/Stride.Assets.Models/ImportGltfCommand.cs index cc06646f27..1c53550763 100644 --- a/sources/engine/Stride.Assets.Models/ImportGltfCommand.cs +++ b/sources/engine/Stride.Assets.Models/ImportGltfCommand.cs @@ -33,8 +33,8 @@ public static bool IsSupportingExtensions(string ext) protected override Model LoadModel(ICommandContext commandContext, ContentManager contentManager) { - var materialMapping = Materials.Select((s, i) => new { Value = s, Index = i }).ToDictionary(x => x.Value.Name, x => x.Index); - var sceneData = GltfMeshParser.LoadFirstModel(SharpGLTF.Schema2.ModelRoot.Load(SourcePath)); + var model = GltfMeshParser.LoadGltf(SourcePath); + var sceneData = GltfMeshParser.LoadFirstModel(model); return sceneData; } diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index d58ac7bb5f..764b751245 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -176,8 +176,10 @@ public static Dictionary LoadMaterials(SharpGLTF.Schema2. string imgPath; if (gltfImg.Content.SourcePath == null) { - imgPath = Path.Join(sourcePath.GetFullDirectory(), gltfImg.Name + "." + gltfImg.Content.FileExtension); - gltfImg.Content.SaveToFile(imgPath); + var textureName = gltfImg.Name ?? FirstModelName(root) + "_" + (mat.Name ?? mat.LogicalIndex.ToString()) + "_" + chan.Key; + imgPath = Path.Join(sourcePath.GetFullDirectory(), textureName + "." + gltfImg.Content.FileExtension); + if(!File.Exists(imgPath)) + gltfImg.Content.SaveToFile(imgPath); } else { diff --git a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs index adf04cef07..4521624cea 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs @@ -77,19 +77,30 @@ public static PrimitiveType ConvertPrimitiveType(SharpGLTF.Schema2.PrimitiveType public static List GenerateTextureFullPaths(SharpGLTF.Schema2.ModelRoot root, UFile sourcePath) { - return root.LogicalTextures.Select(tex => + var result = new List(); + foreach (var mat in root.LogicalMaterials) { - var gltfImg = tex.PrimaryImage; - string imgPath; - if (gltfImg.Content.SourcePath == null) + foreach (var chan in mat.Channels) { - return imgPath = Path.Join(sourcePath.GetFullDirectory(), gltfImg.Name + "." + gltfImg.Content.FileExtension); - } - else - { - return imgPath = gltfImg.Content.SourcePath; + + if (chan.Texture != null && !chan.HasDefaultContent) + { + + var gltfImg = chan.Texture.PrimaryImage; + if (gltfImg.Content.SourcePath == null) + { + var textureName = gltfImg.Name ?? GltfMeshParser.FirstModelName(root) + "_" + (mat.Name??mat.LogicalIndex.ToString()) + "_" + chan.Key; + result.Add( Path.Join(sourcePath.GetFullDirectory(), textureName + "." + gltfImg.Content.FileExtension)); + + } + else + { + result.Add(gltfImg.Content.SourcePath); + } + } } - }).ToList(); + } + return result; } public static ComputeTextureColor GenerateTextureColor(string sourceTextureFile, TextureCoordinate textureUVSetIndex, Vector2 textureUVscaling, TextureAddressMode addressModeU = TextureAddressMode.Wrap, TextureAddressMode addressModeV = TextureAddressMode.Wrap, string vfsOutputPath = "") From 81f9fefb165d9d64d8014a735d0d46c7c59ef8b3 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Fri, 2 Apr 2021 01:16:44 +0200 Subject: [PATCH 12/29] Corrected bone null name + animation name --- .../ModelAssetImporter.cs | 14 +++---- .../GltfAnimationParser.cs | 2 +- .../Stride.Importer.Gltf/GltfMeshParser.cs | 38 ++++++++++++------- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs b/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs index 364d06384a..6281192c08 100644 --- a/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs +++ b/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs @@ -100,8 +100,8 @@ public override IEnumerable Import(UFile localPath, AssetImporterPara { TimeSpan startTime, endTime; GetAnimationDuration(localPath, importParameters.Logger, importParameters, out startTime, out endTime); - - ImportAnimation(rawAssetReferences, localPath, entityInfo.AnimationNodes, isImportingModel, skeletonAsset, startTime, endTime); + bool isGltfAsset = localPath.GetFileExtension().ToLower().Contains("glb") || localPath.GetFileExtension().ToLower().Contains("gltf"); + ImportAnimation(rawAssetReferences, localPath, entityInfo.AnimationNodes, isImportingModel, isGltfAsset, skeletonAsset, startTime, endTime); } // 4. Materials @@ -141,22 +141,18 @@ private static AssetItem ImportSkeleton(List assetReferences, UFile a return assetItem; } - private static void ImportAnimation(List assetReferences, UFile localPath, List animationNodes, bool shouldPostFixName, AssetItem skeletonAsset, TimeSpan animationStartTime, TimeSpan animationEndTime) + private static void ImportAnimation(List assetReferences, UFile localPath, List animationNodes, bool shouldPostFixName, bool isGltfAsset, AssetItem skeletonAsset, TimeSpan animationStartTime, TimeSpan animationEndTime) { - bool shouldReplaceName = false; + if (animationNodes != null && animationNodes.Count > 0) { - if (animationNodes.Count > 1) - { - shouldReplaceName = true; - } foreach(var anim in animationNodes) { var assetSource = localPath; var asset = new AnimationAsset { Source = assetSource, AnimationTimeMaximum = animationEndTime, AnimationTimeMinimum = animationStartTime }; string animUrl; - if (!shouldReplaceName) + if (!isGltfAsset) animUrl = localPath.GetFileNameWithoutExtension() + (shouldPostFixName ? " Animation " + anim : ""); else animUrl = anim; diff --git a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs index 34e8127a8d..718a042af1 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs @@ -54,7 +54,7 @@ public static Skeleton ConvertSkeleton(SharpGLTF.Schema2.ModelRoot root) x => new ModelNodeDefinition { - Name = x.Name, + Name = x.Name ?? "Joint_" + x.LogicalIndex, Flags = ModelNodeFlags.Default, ParentIndex = jointList.IndexOf(x.VisualParent) + 1, Transform = new TransformTRS diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 764b751245..87e5e47194 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -47,7 +47,7 @@ public static Model LoadFirstModel(SharpGLTF.Schema2.ModelRoot root) } public static string FirstModelName(SharpGLTF.Schema2.ModelRoot root) { - return root.LogicalMeshes.First()?.Name; + return root.LogicalMeshes.First()?.Name ?? "Mesh"; } public static TimeSpan GetAnimationDuration(SharpGLTF.Schema2.ModelRoot root) @@ -61,41 +61,51 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot SharpGLTF.Schema2.Skin skin = null; HashSet boneNames = new HashSet(); List nodes = new List(); - if (modelRoot.LogicalSkins.Where(x=>x.Skeleton != null).Where(x => x.Skeleton.Mesh == modelRoot.LogicalMeshes[0]).Count() > 0) + + var meshName = FirstModelName(modelRoot); + var modelName = modelRoot.LogicalMeshes[0].Name ?? "Mesh"; + var parent = modelRoot.LogicalSkins[0].VisualParents.First().Mesh; + if (modelRoot.LogicalSkins.Where(x => x.VisualParents.First()?.Mesh == modelRoot.LogicalMeshes[0]).Count() > 0) skin = modelRoot.LogicalSkins - .Where(x => x.Skeleton.Mesh == modelRoot.LogicalMeshes[0]) + .Where(x => x.VisualParents.First().Mesh == modelRoot.LogicalMeshes[0]) .First(); if (skin != null) { boneNames = Enumerable.Range(0, skin.JointsCount) - .Select(x => skin.GetJoint(x).Joint.Name) + .Select(x => skin.GetJoint(x).Joint.Name ?? "Joint_" + skin.GetJoint(x).Joint.LogicalIndex) .ToHashSet(); nodes = Enumerable.Range(0, skin.JointsCount) - .Select(x => new NodeInfo() { Name = skin.GetJoint(x).Joint.Name, Depth = skin.GetJoint(x).Joint.LogicalIndex, Preserve = true }) + .Select(x => new NodeInfo() { Name = skin.GetJoint(x).Joint.Name ?? "Joint_" + skin.GetJoint(x).Joint.LogicalIndex, Depth = skin.GetJoint(x).Joint.LogicalIndex, Preserve = true }) .ToList(); } - + var meshes = modelRoot .LogicalMeshes[0].Primitives - .Select((x, i) => (modelRoot.LogicalMeshes[0].Name + "_" + i, x.Material.Name)) + .Select( + x => + { + var materialName = x.Material == null ? FirstModelName(modelRoot) + "_" + (x.Material.Name ?? "Material" + x.Material.LogicalIndex) : ""; + return (meshName + "_" + x.LogicalIndex, materialName); + } + ) .Select( x => new MeshParameters() { MeshName = x.Item1, BoneNodes = boneNames, - MaterialName = x.Name, + MaterialName = x.materialName, NodeName = "" } ) .ToList(); - var meshName = FirstModelName(modelRoot); + List animNodes = - modelRoot.LogicalAnimations.Select(x => meshName + "_" + x.Name).ToList(); + modelRoot.LogicalAnimations.Select(x => x.Name == null ? meshName + "_Animation_" + x.LogicalIndex : meshName + "_" + x.Name).ToList(); var entityInfo = new EntityInfo { @@ -140,14 +150,14 @@ public static Dictionary ConvertAnimations(SharpGLTF.Sche var clips = animations - .Select((x, i) => + .Select(x => { //Create animation clip with var clip = new AnimationClip { Duration = TimeSpan.FromSeconds(x.Duration) }; clip.RepeatMode = AnimationRepeatMode.LoopInfinite; // Add Curve ConvertCurves(x.Channels, root).ToList().ForEach(v => clip.AddCurve(v.Key, v.Value)); - string name = x.Name == null ? meshName + "_Animation_" + i : meshName + "_" + x.Name; + string name = x.Name == null ? meshName + "_Animation_" + x.LogicalIndex : meshName + "_" + x.Name; clip.Optimize(); return (name, clip); } @@ -233,7 +243,9 @@ public static Dictionary LoadMaterials(SharpGLTF.Schema2. } material.Attributes.CullMode = CullMode.Back; - result.Add(mat.Name, material); + var materialName = FirstModelName(root) + "_" + (mat.Name ?? "Material" + mat.LogicalIndex); + + result.Add(materialName, material); } return result; } From 02df563d33e80bc317b66db238e6e83fa456b17a Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Fri, 2 Apr 2021 01:19:19 +0200 Subject: [PATCH 13/29] removed unused line of code --- sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 87e5e47194..42ce37a98b 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -64,7 +64,6 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot var meshName = FirstModelName(modelRoot); var modelName = modelRoot.LogicalMeshes[0].Name ?? "Mesh"; - var parent = modelRoot.LogicalSkins[0].VisualParents.First().Mesh; if (modelRoot.LogicalSkins.Where(x => x.VisualParents.First()?.Mesh == modelRoot.LogicalMeshes[0]).Count() > 0) skin = modelRoot.LogicalSkins From e0d98f8223f85e1000f2098a9519f589aacf5c35 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Fri, 2 Apr 2021 01:34:58 +0200 Subject: [PATCH 14/29] Corrected name of mesh for animations --- sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 42ce37a98b..50d207b327 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -87,7 +87,7 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot .Select( x => { - var materialName = x.Material == null ? FirstModelName(modelRoot) + "_" + (x.Material.Name ?? "Material" + x.Material.LogicalIndex) : ""; + var materialName = x.Material != null ? FirstModelName(modelRoot) + "_" + (x.Material.Name ?? "Material" + x.Material.LogicalIndex) : ""; return (meshName + "_" + x.LogicalIndex, materialName); } ) @@ -145,7 +145,7 @@ public static MeshSkinningDefinition ConvertInverseBindMatrices(SharpGLTF.Schema public static Dictionary ConvertAnimations(SharpGLTF.Schema2.ModelRoot root) { var animations = root.LogicalAnimations; - var meshName = root.LogicalMeshes[0].Name; + var meshName = FirstModelName(root); var clips = animations From e7342f031724d8d29c921bb5467591de751e3359 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Fri, 2 Apr 2021 13:47:18 +0200 Subject: [PATCH 15/29] Doc comments --- .../GltfAnimationParser.cs | 37 +++++++- .../Stride.Importer.Gltf/GltfMeshParser.cs | 84 +++++++++++++++-- .../tools/Stride.Importer.Gltf/GltfUtils.cs | 89 +++++++++++++------ 3 files changed, 175 insertions(+), 35 deletions(-) diff --git a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs index 718a042af1..dc112a8e8a 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs @@ -11,11 +11,16 @@ namespace Stride.Importer.Gltf { public class GltfAnimationParser { + /// + /// Converts GLTF Skeleton into a Stride Skeleton + /// + /// + /// skeleton public static Skeleton ConvertSkeleton(SharpGLTF.Schema2.ModelRoot root) { Skeleton result = new Skeleton(); var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes.First()).Skin; - // If there is no corresponding skins return null + // If there is no corresponding skins return a skeleton with 2 bones (an empty skeleton would make the editor crash) if (skin == null) { result.Nodes = new List() { @@ -46,7 +51,7 @@ public static Skeleton ConvertSkeleton(SharpGLTF.Schema2.ModelRoot root) }.ToArray(); return result; } - + // for each joints we create a ModelNodeDefinition var jointList = Enumerable.Range(0, skin.JointsCount).Select(x => skin.GetJoint(x).Joint).ToList(); var mnd = jointList @@ -67,6 +72,7 @@ public static Skeleton ConvertSkeleton(SharpGLTF.Schema2.ModelRoot root) } ) .ToList(); + // And insert a parent one not caught by the above function (GLTF does not consider the parent bone as a bone) mnd.Insert( 0, new ModelNodeDefinition @@ -85,12 +91,25 @@ public static Skeleton ConvertSkeleton(SharpGLTF.Schema2.ModelRoot root) return result; } - + /// + /// Helper function to create a keyframe from values + /// + /// + /// + /// + /// keyframe public static KeyFrameData CreateKeyFrame(float keyTime, T value) { return new KeyFrameData((CompressedTimeSpan)TimeSpan.FromSeconds(keyTime), value); } + /// + /// Convert a GLTF animation channel into a Stride AnimationCurve. + /// If the model has no skin, root motion should be enabled + /// + /// + /// + /// animationCurves public static Dictionary ConvertCurves(IReadOnlyList channels, SharpGLTF.Schema2.ModelRoot root) { var result = new Dictionary(); @@ -119,6 +138,7 @@ public static Dictionary ConvertCurves(IReadOnlyList ConvertCurves(IReadOnlyList + /// Converts a GLTF AnimationSampler into a Stride AnimationCurve + /// + /// + /// public static AnimationCurve ConvertCurve(SharpGLTF.Schema2.IAnimationSampler sampler) { var interpolationType = @@ -175,6 +200,12 @@ public static AnimationCurve ConvertCurve(SharpGLTF.Schema2.IAnimati KeyFrames = new FastList>(keyframes) }; } + + /// + /// Converts a GLTF AnimationSampler into a Stride AnimationCurve + /// + /// + /// public static AnimationCurve ConvertCurve(SharpGLTF.Schema2.IAnimationSampler sampler) { var interpolationType = diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 50d207b327..4f47eac6b4 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -19,6 +19,11 @@ namespace Stride.Importer.Gltf { public class GltfMeshParser { + /// + /// Loads the gltf file depending its extension. + /// + /// path of the gltf file + /// ModelRoot public static SharpGLTF.Schema2.ModelRoot LoadGltf(Stride.Core.IO.UFile sourcePath) { @@ -36,8 +41,15 @@ public static SharpGLTF.Schema2.ModelRoot LoadGltf(Stride.Core.IO.UFile sourcePa } } + + /// + /// Converts the first mesh in the GLTF file into a stride Model + /// + /// + /// public static Model LoadFirstModel(SharpGLTF.Schema2.ModelRoot root) { + // We load every primitives of the first mesh var result = new Model() { Meshes = root.LogicalMeshes[0].Primitives.Select(x => LoadMesh(x)).ToList() @@ -45,17 +57,36 @@ public static Model LoadFirstModel(SharpGLTF.Schema2.ModelRoot root) result.Skeleton = ConvertSkeleton(root); return result; } + + /// + /// Gets the first model name + /// + /// + /// public static string FirstModelName(SharpGLTF.Schema2.ModelRoot root) { + // TODO : Get the file name instead of `Mesh` return root.LogicalMeshes.First()?.Name ?? "Mesh"; } + /// + /// Gets the sum of all animation duration + /// + /// + /// public static TimeSpan GetAnimationDuration(SharpGLTF.Schema2.ModelRoot root) { var time = root.LogicalAnimations.Select(x => x.Duration).Sum(); return TimeSpan.FromSeconds(time); } + /// + /// Extract the entity info. This function tells the editor informations about any assets. + /// If any info is missing/wrong the assests won't be correctly imported. + /// + /// + /// + /// public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot, UFile sourcePath) { SharpGLTF.Schema2.Skin skin = null; @@ -63,13 +94,15 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot List nodes = new List(); var meshName = FirstModelName(modelRoot); - var modelName = modelRoot.LogicalMeshes[0].Name ?? "Mesh"; + + if (modelRoot.LogicalSkins.Where(x => x.VisualParents.First()?.Mesh == modelRoot.LogicalMeshes[0]).Count() > 0) skin = modelRoot.LogicalSkins .Where(x => x.VisualParents.First().Mesh == modelRoot.LogicalMeshes[0]) .First(); + // If there is a skin, we can load the bone names and instantiate the node informations. if (skin != null) { boneNames = @@ -81,6 +114,7 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot .ToList(); } + // Loading Mesh parameters, this will link the materials with the meshes var meshes = modelRoot .LogicalMeshes[0].Primitives @@ -103,10 +137,11 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot ) .ToList(); + // Loading the animation names (should be the same as the keys used in animations List animNodes = - modelRoot.LogicalAnimations.Select(x => x.Name == null ? meshName + "_Animation_" + x.LogicalIndex : meshName + "_" + x.Name).ToList(); + ConvertAnimations(modelRoot).Keys.ToList(); - var entityInfo = new EntityInfo + return new EntityInfo { Models = meshes, AnimationNodes = animNodes, @@ -114,12 +149,15 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot Nodes = nodes, TextureDependencies = GenerateTextureFullPaths(modelRoot, sourcePath) }; - return entityInfo; } - + /// + /// Converts GLTF joints into MeshSkinningDefinition, defining the Mesh to World matrix useful for skinning and animations. + /// + /// + /// public static MeshSkinningDefinition ConvertInverseBindMatrices(SharpGLTF.Schema2.ModelRoot root) { var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes[0]).Skin; @@ -142,6 +180,11 @@ public static MeshSkinningDefinition ConvertInverseBindMatrices(SharpGLTF.Schema return mnt; } + /// + /// Converts GLTF animations into Stride AnimationClips. + /// + /// + /// public static Dictionary ConvertAnimations(SharpGLTF.Schema2.ModelRoot root) { var animations = root.LogicalAnimations; @@ -166,8 +209,15 @@ public static Dictionary ConvertAnimations(SharpGLTF.Sche return clips; } + /// + /// Convert GLTF materials to Stride materials + /// + /// + /// + /// public static Dictionary LoadMaterials(SharpGLTF.Schema2.ModelRoot root, UFile sourcePath) { + // TODO : Handle official ClearCoat extension var result = new Dictionary(); foreach (var mat in root.LogicalMaterials) { @@ -249,6 +299,11 @@ public static Dictionary LoadMaterials(SharpGLTF.Schema2. return result; } + /// + /// Convert a GLTF Primitive into a Stride Mesh + /// + /// + /// public static Mesh LoadMesh(SharpGLTF.Schema2.MeshPrimitive mesh) { @@ -270,18 +325,28 @@ public static Mesh LoadMesh(SharpGLTF.Schema2.MeshPrimitive mesh) }; - //TODO : Add parameter collection only after checking if it has + // TODO : Add parameter collection only after checking if it has result.Parameters.Set(MaterialKeys.HasSkinningPosition, true); result.Parameters.Set(MaterialKeys.HasSkinningNormal, true); return result; } + /// + /// Gets the number of triangle indices. + /// + /// + /// private static int GetDrawCount(SharpGLTF.Schema2.MeshPrimitive mesh) { - var tmp = mesh.GetTriangleIndices().Select(x => new int[] { x.A, x.C, x.B }).SelectMany(x => x).Select(x => (uint)x).ToArray(); + // TODO : Check if every meshes has triangle indices return mesh.GetTriangleIndices().Select(x => new int[] { x.A, x.C, x.B }).SelectMany(x => x).Select(x => (uint)x).ToArray().Length; } + /// + /// Converts an index buffer into a serialized index buffer binding for asset creation. + /// + /// + /// public static IndexBufferBinding ConvertSerializedIndexBufferBinding(SharpGLTF.Schema2.MeshPrimitive mesh) { var indices = @@ -294,6 +359,11 @@ public static IndexBufferBinding ConvertSerializedIndexBufferBinding(SharpGLTF.S return new IndexBufferBinding(buf, true, indices.Length); } + /// + /// Converts a vertex buffer into a serialized vertex buffer for asset creation. + /// + /// + /// public static VertexBufferBinding[] ConvertSerializedVertexBufferBinding(SharpGLTF.Schema2.MeshPrimitive mesh) { var offset = 0; diff --git a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs index 4521624cea..04d5dab0c5 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs @@ -15,7 +15,11 @@ namespace Stride.Importer.Gltf { class GltfUtils { - + /// + /// Converts a System.Numerics value into a Stride value + /// + /// + /// public static Matrix ConvertNumerics(System.Numerics.Matrix4x4 mat) { return new Matrix( @@ -25,13 +29,37 @@ public static Matrix ConvertNumerics(System.Numerics.Matrix4x4 mat) mat.M41, mat.M42, mat.M43, mat.M44 ); } - + /// + /// Converts a System.Numerics value into a Stride value + /// + /// + /// public static Quaternion ConvertNumerics(System.Numerics.Quaternion v) => new Quaternion(v.X, v.Y, v.Z, v.W); + /// + /// Converts a System.Numerics value into a Stride value + /// + /// + /// public static Vector4 ConvertNumerics(System.Numerics.Vector4 v) => new Vector4(v.X, v.Y, v.Z, v.W); + /// + /// Converts a System.Numerics value into a Stride value + /// + /// + /// public static Vector3 ConvertNumerics(System.Numerics.Vector3 v) => new Vector3(v.X, v.Y, v.Z); + /// + /// Converts a System.Numerics value into a Stride value + /// + /// + /// public static Vector2 ConvertNumerics(System.Numerics.Vector2 v) => new Vector2(v.X, v.Y); - + /// + /// Gets the Stride VertexElement equivalent of a GLTF vertex + /// + /// + /// + /// public static (VertexElement, int) ConvertVertexElement(KeyValuePair accessor, int offset) { // TODO : Simplify this part @@ -59,7 +87,11 @@ public static (VertexElement, int) ConvertVertexElement(KeyValuePair throw new NotImplementedException(), }; } - + /// + /// Gets the GLTF primitive's PrimitiveType + /// + /// + /// public static PrimitiveType ConvertPrimitiveType(SharpGLTF.Schema2.PrimitiveType gltfType) { return gltfType switch @@ -75,6 +107,12 @@ public static PrimitiveType ConvertPrimitiveType(SharpGLTF.Schema2.PrimitiveType }; } + /// + /// Generates the texture files paths. + /// + /// + /// + /// public static List GenerateTextureFullPaths(SharpGLTF.Schema2.ModelRoot root, UFile sourcePath) { var result = new List(); @@ -103,19 +141,19 @@ public static List GenerateTextureFullPaths(SharpGLTF.Schema2.ModelRoot return result; } + /// + /// Generate a ComputeTextureColor for asset creation + /// + /// + /// + /// + /// + /// + /// + /// public static ComputeTextureColor GenerateTextureColor(string sourceTextureFile, TextureCoordinate textureUVSetIndex, Vector2 textureUVscaling, TextureAddressMode addressModeU = TextureAddressMode.Wrap, TextureAddressMode addressModeV = TextureAddressMode.Wrap, string vfsOutputPath = "") { var textureFileName = Path.GetFileNameWithoutExtension(sourceTextureFile); - var url = vfsOutputPath + "_" + textureFileName; - - if (File.Exists(sourceTextureFile)) - { - //if (logger != nullptr) - //{ - // logger->Warning(String::Format("The texture '{0}' referenced in the mesh material can not be found on the system. Loading will probably fail at run time.", sourceTextureFile), - // nullptr, CallerInfo::Get(__FILEW__, __FUNCTIONW__, __LINE__)); - //} - } var uvScaling = textureUVscaling; var textureName = textureFileName; @@ -131,20 +169,21 @@ public static ComputeTextureColor GenerateTextureColor(string sourceTextureFile, return currentTexture; } + + /// + /// Generate a ComputeTextureScalar for asset creation. + /// + /// + /// + /// + /// + /// + /// + /// public static ComputeTextureScalar GenerateTextureScalar(string sourceTextureFile, TextureCoordinate textureUVSetIndex, Vector2 textureUVscaling, TextureAddressMode addressModeU = TextureAddressMode.Wrap, TextureAddressMode addressModeV = TextureAddressMode.Wrap, string vfsOutputPath = "") { var textureFileName = Path.GetFileNameWithoutExtension(sourceTextureFile); - var url = vfsOutputPath + "_" + textureFileName; - - if (File.Exists(sourceTextureFile)) - { - //if (logger != nullptr) - //{ - // logger->Warning(String::Format("The texture '{0}' referenced in the mesh material can not be found on the system. Loading will probably fail at run time.", sourceTextureFile), - // nullptr, CallerInfo::Get(__FILEW__, __FUNCTIONW__, __LINE__)); - //} - } - + var uvScaling = textureUVscaling; var textureName = textureFileName; From bf7c38c2a383f16bbd5141df5ec115899f2adf49 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Tue, 6 Apr 2021 12:59:35 +0200 Subject: [PATCH 16/29] static classes --- sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs | 2 +- sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs | 6 +++--- sources/tools/Stride.Importer.Gltf/GltfUtils.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs index dc112a8e8a..7718f835e7 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs @@ -9,7 +9,7 @@ namespace Stride.Importer.Gltf { - public class GltfAnimationParser + public static class GltfAnimationParser { /// /// Converts GLTF Skeleton into a Stride Skeleton diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 4f47eac6b4..38cf92bc2f 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -17,7 +17,7 @@ namespace Stride.Importer.Gltf { - public class GltfMeshParser + public static class GltfMeshParser { /// /// Loads the gltf file depending its extension. @@ -292,9 +292,9 @@ public static Dictionary LoadMaterials(SharpGLTF.Schema2. } material.Attributes.CullMode = CullMode.Back; - var materialName = FirstModelName(root) + "_" + (mat.Name ?? "Material" + mat.LogicalIndex); + var materialName = FirstModelName(root) + "_" + (mat.Name ?? "Material") + "_" + mat.LogicalIndex; - result.Add(materialName, material); + result.TryAdd(materialName, material); } return result; } diff --git a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs index 04d5dab0c5..d2fe95d659 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs @@ -13,7 +13,7 @@ namespace Stride.Importer.Gltf { - class GltfUtils + public static class GltfUtils { /// /// Converts a System.Numerics value into a Stride value From 42ffc247ba0e5fae6f9002df7232460a138b7bd4 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Tue, 20 Apr 2021 14:24:28 +0200 Subject: [PATCH 17/29] Correct naming of texture --- .../Stride.Importer.Gltf/GltfMeshParser.cs | 6 +-- .../tools/Stride.Importer.Gltf/GltfUtils.cs | 41 ++++++++++--------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 38cf92bc2f..77811f94da 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -232,10 +232,11 @@ public static Dictionary LoadMaterials(SharpGLTF.Schema2. { var gltfImg = chan.Texture.PrimaryImage; + var textureName = gltfImg.Name ?? FirstModelName(root) + "_" + (mat.Name ?? mat.LogicalIndex.ToString()) + "_" + chan.Key; + string imgPath; if (gltfImg.Content.SourcePath == null) { - var textureName = gltfImg.Name ?? FirstModelName(root) + "_" + (mat.Name ?? mat.LogicalIndex.ToString()) + "_" + chan.Key; imgPath = Path.Join(sourcePath.GetFullDirectory(), textureName + "." + gltfImg.Content.FileExtension); if(!File.Exists(imgPath)) gltfImg.Content.SaveToFile(imgPath); @@ -339,7 +340,7 @@ public static Mesh LoadMesh(SharpGLTF.Schema2.MeshPrimitive mesh) private static int GetDrawCount(SharpGLTF.Schema2.MeshPrimitive mesh) { // TODO : Check if every meshes has triangle indices - return mesh.GetTriangleIndices().Select(x => new int[] { x.A, x.C, x.B }).SelectMany(x => x).Select(x => (uint)x).ToArray().Length; + return mesh.GetTriangleIndices().Select(x => new int[] { x.A, x.B, x.C }).SelectMany(x => x).Select(x => (uint)x).ToArray().Length; } /// @@ -377,7 +378,6 @@ public static VertexBufferBinding[] ConvertSerializedVertexBufferBinding(SharpGL return y.Item1; }) .ToList(); - var declaration = new VertexDeclaration( vertElem.ToArray() diff --git a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs index d2fe95d659..5587618685 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs @@ -7,6 +7,7 @@ using Stride.Core.IO; using Stride.Core.Mathematics; using Stride.Core.Serialization; +using Stride.Core.Serialization.Contents; using Stride.Graphics; using Stride.Rendering.Materials; using Stride.Rendering.Materials.ComputeColors; @@ -65,25 +66,26 @@ public static (VertexElement, int) ConvertVertexElement(KeyValuePair (VertexElement.Position(0, offset), Vector3.SizeInBytes), - ("NORMAL", 12) => (VertexElement.Normal(0, offset), Vector3.SizeInBytes), - ("TANGENT", 12) => (VertexElement.Tangent(0, offset), Vector3.SizeInBytes), - ("TANGENT", 16) => (VertexElement.Tangent(0, offset), Vector4.SizeInBytes), - ("COLOR", 16) => (VertexElement.Color(0, offset), Vector4.SizeInBytes), - ("COLOR_0", 16) => (VertexElement.Color(0, offset), Vector4.SizeInBytes), - ("TEXCOORD_0", 8) => (VertexElement.TextureCoordinate(0, offset), Vector2.SizeInBytes), - ("TEXCOORD_1", 8) => (VertexElement.TextureCoordinate(1, offset), Vector2.SizeInBytes), - ("TEXCOORD_2", 8) => (VertexElement.TextureCoordinate(2, offset), Vector2.SizeInBytes), - ("TEXCOORD_3", 8) => (VertexElement.TextureCoordinate(3, offset), Vector2.SizeInBytes), - ("TEXCOORD_4", 8) => (VertexElement.TextureCoordinate(4, offset), Vector2.SizeInBytes), - ("TEXCOORD_5", 8) => (VertexElement.TextureCoordinate(5, offset), Vector2.SizeInBytes), - ("TEXCOORD_6", 8) => (VertexElement.TextureCoordinate(6, offset), Vector2.SizeInBytes), - ("TEXCOORD_7", 8) => (VertexElement.TextureCoordinate(7, offset), Vector2.SizeInBytes), - ("TEXCOORD_8", 8) => (VertexElement.TextureCoordinate(8, offset), Vector2.SizeInBytes), - ("TEXCOORD_9", 8) => (VertexElement.TextureCoordinate(9, offset), Vector2.SizeInBytes), + ("POSITION", 12) => (VertexElement.Position(0, offset), 12), + ("NORMAL", 12) => (VertexElement.Normal(0, offset), 12), + ("TANGENT", 16) => (VertexElement.Tangent(0, offset), 16), + ("COLOR", 16) => (VertexElement.Color(0, offset), 16), + ("COLOR_0", 16) => (VertexElement.Color(0, offset), 16), + ("TEXCOORD_0", 8) => (VertexElement.TextureCoordinate(0, offset), 8), + ("TEXCOORD_1", 8) => (VertexElement.TextureCoordinate(1, offset), 8), + ("TEXCOORD_2", 8) => (VertexElement.TextureCoordinate(2, offset), 8), + ("TEXCOORD_3", 8) => (VertexElement.TextureCoordinate(3, offset), 8), + ("TEXCOORD_4", 8) => (VertexElement.TextureCoordinate(4, offset), 8), + ("TEXCOORD_5", 8) => (VertexElement.TextureCoordinate(5, offset), 8), + ("TEXCOORD_6", 8) => (VertexElement.TextureCoordinate(6, offset), 8), + ("TEXCOORD_7", 8) => (VertexElement.TextureCoordinate(7, offset), 8), + ("TEXCOORD_8", 8) => (VertexElement.TextureCoordinate(8, offset), 8), + ("TEXCOORD_9", 8) => (VertexElement.TextureCoordinate(9, offset), 8), ("JOINTS_0", 8) => (new VertexElement(VertexElementUsage.BlendIndices, 0, PixelFormat.R16G16B16A16_UInt, offset), 8), ("JOINTS_0", 4) => (new VertexElement(VertexElementUsage.BlendIndices, 0, PixelFormat.R8G8B8A8_UInt, offset), 4), - ("WEIGHTS_0", 16) => (new VertexElement(VertexElementUsage.BlendWeight, 0, PixelFormat.R32G32B32A32_Float, offset), Vector4.SizeInBytes), + ("WEIGHTS_0", 4) => (new VertexElement(VertexElementUsage.BlendWeight, 0, PixelFormat.R8G8B8A8_UInt, offset), 4), + ("WEIGHTS_0", 8) => (new VertexElement(VertexElementUsage.BlendWeight, 0, PixelFormat.R16G16B16A16_Float, offset), 8), + ("WEIGHTS_0", 16) => (new VertexElement(VertexElementUsage.BlendWeight, 0, PixelFormat.R32G32B32A32_Float, offset), 16), _ => throw new NotImplementedException(), }; } @@ -159,9 +161,10 @@ public static ComputeTextureColor GenerateTextureColor(string sourceTextureFile, var textureName = textureFileName; var texture = AttachedReferenceManager.CreateProxyObject(AssetId.Empty, textureName); + var currentTexture = - new ComputeTextureColor(texture, (TextureCoordinate)textureUVSetIndex, uvScaling, Vector2.Zero) + new ComputeTextureColor(texture, textureUVSetIndex, uvScaling, Vector2.Zero) { AddressModeU = addressModeU, AddressModeV = addressModeV @@ -190,7 +193,7 @@ public static ComputeTextureScalar GenerateTextureScalar(string sourceTextureFil var texture = AttachedReferenceManager.CreateProxyObject(AssetId.Empty, textureName); var currentTexture = - new ComputeTextureScalar(texture, (TextureCoordinate)textureUVSetIndex, uvScaling, Vector2.Zero) + new ComputeTextureScalar(texture, textureUVSetIndex, uvScaling, Vector2.Zero) { AddressModeU = addressModeU, AddressModeV = addressModeV From 516d12e3e57a2d4cfa5b4d805382450fb3624d1c Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Sun, 20 Jun 2021 13:03:37 +0200 Subject: [PATCH 18/29] Corrected breaking change on FBX animation importer --- .../ModelAssetImporter.cs | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs b/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs index 6281192c08..a6c58d4518 100644 --- a/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs +++ b/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs @@ -143,26 +143,42 @@ private static AssetItem ImportSkeleton(List assetReferences, UFile a private static void ImportAnimation(List assetReferences, UFile localPath, List animationNodes, bool shouldPostFixName, bool isGltfAsset, AssetItem skeletonAsset, TimeSpan animationStartTime, TimeSpan animationEndTime) { - if (animationNodes != null && animationNodes.Count > 0) { - foreach(var anim in animationNodes) + if (isGltfAsset) { + // Animation nodes in GLTF correspond to animations inside the gltf files, not the bone names in fbx files. + foreach (var anim in animationNodes) + { + var assetSource = localPath; + + var asset = new AnimationAsset { Source = assetSource, AnimationTimeMaximum = animationEndTime, AnimationTimeMinimum = animationStartTime }; + string animUrl = localPath.GetFileNameWithoutExtension() + (shouldPostFixName ? " Animation " + anim : ""); + + if (skeletonAsset != null) + asset.Skeleton = AttachedReferenceManager.CreateProxyObject(skeletonAsset.Id, skeletonAsset.Location); + + assetReferences.Add(new AssetItem(animUrl, asset)); + } + + } + else + { + var assetSource = localPath; var asset = new AnimationAsset { Source = assetSource, AnimationTimeMaximum = animationEndTime, AnimationTimeMinimum = animationStartTime }; - string animUrl; - if (!isGltfAsset) - animUrl = localPath.GetFileNameWithoutExtension() + (shouldPostFixName ? " Animation " + anim : ""); - else - animUrl = anim; + var animUrl = localPath.GetFileNameWithoutExtension() + (shouldPostFixName ? " Animation" : ""); if (skeletonAsset != null) asset.Skeleton = AttachedReferenceManager.CreateProxyObject(skeletonAsset.Id, skeletonAsset.Location); assetReferences.Add(new AssetItem(animUrl, asset)); + } } + + } private static void ImportModel(List assetReferences, UFile assetSource, UFile localPath, EntityInfo entityInfo, bool shouldPostFixName, AssetItem skeletonAsset) @@ -198,7 +214,7 @@ private static void ImportModel(List assetReferences, UFile assetSour if (skeletonAsset != null) asset.Skeleton = AttachedReferenceManager.CreateProxyObject(skeletonAsset.Id, skeletonAsset.Location); - var modelUrl = new UFile(localPath.GetFileNameWithoutExtension() + (shouldPostFixName?" Model": "")); + var modelUrl = new UFile(localPath.GetFileNameWithoutExtension() + (shouldPostFixName ? " Model" : "")); var assetItem = new AssetItem(modelUrl, asset); assetReferences.Add(assetItem); } @@ -277,7 +293,7 @@ private static void AdjustForTransparency(MaterialAsset material) // var isTransparent = false; // if (material.Parameters.ContainsKey(MaterialParameters.HasTransparency)) // isTransparent = (bool)material.Parameters[MaterialParameters.HasTransparency]; - + // if (!isTransparent) // { // // remove the diffuse node From c0a91feb55dc42cd87545fb7d1a0de3a05f1282a Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Mon, 21 Jun 2021 15:16:06 +0200 Subject: [PATCH 19/29] bump versions + simpler vertex buff creation + minor corrections --- .../Stride.Importer.Gltf/GltfMeshParser.cs | 31 +++++++++---------- .../tools/Stride.Importer.Gltf/GltfUtils.cs | 2 +- .../Stride.Importer.Gltf.csproj | 6 ++-- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 77811f94da..6714327303 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -90,7 +90,7 @@ public static TimeSpan GetAnimationDuration(SharpGLTF.Schema2.ModelRoot root) public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot, UFile sourcePath) { SharpGLTF.Schema2.Skin skin = null; - HashSet boneNames = new HashSet(); + HashSet boneNames = new HashSet(); List nodes = new List(); var meshName = FirstModelName(modelRoot); @@ -138,7 +138,7 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot .ToList(); // Loading the animation names (should be the same as the keys used in animations - List animNodes = + List animNodes = ConvertAnimations(modelRoot).Keys.ToList(); return new EntityInfo @@ -384,21 +384,18 @@ public static VertexBufferBinding[] ConvertSerializedVertexBufferBinding(SharpGL ); var size = mesh.VertexAccessors.First().Value.Count; - var byteBuffer = Enumerable.Range(0, size) - .Select( - x => - declaration.EnumerateWithOffsets() - .Select(y => y.VertexElement.SemanticName - .Replace("ORD", "ORD_" + y.VertexElement.SemanticIndex) - .Replace("BLENDINDICES", "JOINTS_0") - .Replace("BLENDWEIGHT", "WEIGHTS_0") - ) - .Select(y => mesh.GetVertexAccessor(y)?.TryGetVertexBytes(x).ToArray()) - .Where(x => x != null) - ) - .SelectMany(x => x) - .SelectMany(x => x) - .ToArray(); + + List bytelst = new(); + + for(int i=0; i< size; i++) + { + foreach(var e in mesh.VertexAccessors.Keys) + { + var bytes = mesh.GetVertexAccessor(e).TryGetVertexBytes(i).ToArray(); + bytelst.AddRange(bytes); + } + } + var byteBuffer = bytelst.ToArray(); var buffer = GraphicsSerializerExtensions.ToSerializableVersion( diff --git a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs index 5587618685..44c6c37c18 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs @@ -117,7 +117,7 @@ public static PrimitiveType ConvertPrimitiveType(SharpGLTF.Schema2.PrimitiveType /// public static List GenerateTextureFullPaths(SharpGLTF.Schema2.ModelRoot root, UFile sourcePath) { - var result = new List(); + var result = new List(); foreach (var mat in root.LogicalMaterials) { foreach (var chan in mat.Channels) diff --git a/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj b/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj index 6af727ecb0..0e0fdccfe3 100644 --- a/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj +++ b/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj @@ -3,12 +3,12 @@ $(StrideEditorTargetFramework) - 8.0 + 9.0 - - + + From b9c587ab384fa7b41bd6a6e4bf0eb1fee016b2a8 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Sat, 26 Jun 2021 20:30:37 +0200 Subject: [PATCH 20/29] Corrections + cubic animation import --- .../Stride.Assets.Models/GltfAssetImporter.cs | 22 +-- .../ImportModelCommand.Animation.cs | 19 +-- .../ImportModelCommand.cs | 14 +- .../GltfAnimationParser.cs | 125 ++++++++++++++++-- .../Stride.Importer.Gltf/GltfMeshParser.cs | 15 ++- .../tools/Stride.Importer.Gltf/GltfUtils.cs | 45 +++++-- 6 files changed, 173 insertions(+), 67 deletions(-) diff --git a/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs b/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs index 72f6e95ac2..e195e8a60d 100644 --- a/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs +++ b/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs @@ -39,26 +39,8 @@ public override EntityInfo GetEntityInfo(UFile localPath, Logger logger, AssetIm public override void GetAnimationDuration(UFile localPath, Logger logger, AssetImporterParameters importParameters, out TimeSpan startTime, out TimeSpan endTime) { var gltfFile = SharpGLTF.Schema2.ModelRoot.Load(localPath); - var animations = GltfMeshParser.ConvertAnimations(gltfFile); - - endTime = GltfMeshParser.GetAnimationDuration(gltfFile); // This will go down, so we start from positive infinity - startTime = CompressedTimeSpan.Zero; // This will go up, so we start from negative infinity - - - } - - public override IEnumerable Import(UFile localPath, AssetImporterParameters importParameters) - { - var assetItems = base.Import(localPath, importParameters); - - foreach (var item in assetItems) - { - if (item.Asset is AnimationAsset animAsset) - { - - } - } - return assetItems; + endTime = GltfMeshParser.GetAnimationDuration(gltfFile); + startTime = CompressedTimeSpan.Zero; } } } diff --git a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs index 6dd371ec68..41c2016ee9 100644 --- a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs +++ b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs @@ -26,13 +26,6 @@ public partial class ImportModelCommand private unsafe object ExportAnimation(ICommandContext commandContext, ContentManager contentManager, bool failOnEmptyAnimation) { - - if (commandContext.CurrentCommand.Title.ToLower().Contains("gltf")) - { - var ts = TimeSpan.FromSeconds(1); - return LoadAnimation(commandContext, contentManager, out ts); - } - // Read from model file var modelSkeleton = LoadSkeleton(commandContext, contentManager); // we get model skeleton to compare it to real skeleton we need to map to AdjustSkeleton(modelSkeleton); @@ -147,13 +140,13 @@ private unsafe object ExportAnimation(ICommandContext commandContext, ContentMan foreach (var node in nodesToMerge) { if (node.Item3 != null) - foreach (var curve in node.Item3.Clip.Curves) - { - foreach (CompressedTimeSpan time in curve.Keys) + foreach (var curve in node.Item3.Clip.Curves) { - animationKeysSet.Add(time); + foreach (CompressedTimeSpan time in curve.Keys) + { + animationKeysSet.Add(time); + } } - } } // Sort key times @@ -177,7 +170,7 @@ private unsafe object ExportAnimation(ICommandContext commandContext, ContentMan foreach (var node in nodesToMerge) { // Needs to be an array in order for it to be modified by the UpdateEngine, otherwise it would get passed by value - var modelNodeDefinitions = new ModelNodeDefinition[1] {node.Item1}; + var modelNodeDefinitions = new ModelNodeDefinition[1] { node.Item1 }; if (node.Item2 != null && node.Item3 != null) { diff --git a/sources/engine/Stride.Assets.Models/ImportModelCommand.cs b/sources/engine/Stride.Assets.Models/ImportModelCommand.cs index 514b41a065..f4418009c4 100644 --- a/sources/engine/Stride.Assets.Models/ImportModelCommand.cs +++ b/sources/engine/Stride.Assets.Models/ImportModelCommand.cs @@ -92,18 +92,18 @@ protected override async Task DoCommandOverride(ICommandContext co commandContext.Logger.Error($"Failed to import file {ContextAsString}."); return ResultStatus.Failed; } - if(exportedObject is Dictionary) + if (exportedObject is Dictionary) { var exportedObjects = exportedObject as Dictionary; - foreach(var obj in exportedObjects) - assetManager.Save(obj.Key , obj.Value); + foreach (var obj in exportedObjects) + assetManager.Save(obj.Key, obj.Value); } else { assetManager.Save(Location, exportedObject); } - - + + commandContext.Logger.Verbose($"The {ContextAsString} has been successfully imported."); return ResultStatus.Successful; @@ -141,7 +141,7 @@ private Matrix CombineMatricesFromNodeIndices(ModelNodeTransformation[] nodes, i return result; } - + protected abstract Model LoadModel(ICommandContext commandContext, ContentManager contentManager); protected abstract Dictionary LoadAnimation(ICommandContext commandContext, ContentManager contentManager, out TimeSpan duration); @@ -183,7 +183,7 @@ private static bool CompareParameters(Model model, Mesh baseMesh, Mesh newMesh) return false; return IsSubsetOf(localParams, newMesh.Parameters) && IsSubsetOf(newMesh.Parameters, localParams); } - + /// /// Compares the shadow options between the two meshes. /// diff --git a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs index 7718f835e7..39008f0745 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs @@ -1,3 +1,6 @@ +// Copyright (c) Stride contributors (https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. + using System; using System.Collections.Generic; using System.Linq; @@ -90,7 +93,7 @@ public static Skeleton ConvertSkeleton(SharpGLTF.Schema2.ModelRoot root) result.Nodes = mnd.ToArray(); return result; } - + /// /// Helper function to create a keyframe from values /// @@ -140,9 +143,9 @@ public static Dictionary ConvertCurves(IReadOnlyList skin.GetJoint(x).Joint).ToList(); foreach (var chan in channels) { @@ -188,10 +191,10 @@ public static AnimationCurve ConvertCurve(SharpGLTF.Schema2.IAnimati sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), AnimationCurveInterpolationType.Linear => sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), + // Cubic might be broken AnimationCurveInterpolationType.Cubic => - sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), - //sampler.GetCubicKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), - _ => throw new NotImplementedException() + sampler.GetCubicKeys().Select(x => ConvertNumerics(x.Value).Select(y => CreateKeyFrame(x.Key, y))).SelectMany(x => x), + _ => throw new NotImplementedException() }; return new AnimationCurve @@ -224,10 +227,10 @@ public static AnimationCurve ConvertCurve(SharpGLTF.Schema2.IAnimationS sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), AnimationCurveInterpolationType.Linear => sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), + // TODO : Cubic can be broken AnimationCurveInterpolationType.Cubic => - sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), - //sampler.GetCubicKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), - _ => throw new NotImplementedException() + sampler.GetCubicKeys().Select(x => ConvertNumerics(x.Value).Select(y => CreateKeyFrame(x.Key, y))).SelectMany(x => x), + _ => throw new NotImplementedException() }; return new AnimationCurve @@ -236,5 +239,107 @@ public static AnimationCurve ConvertCurve(SharpGLTF.Schema2.IAnimationS KeyFrames = new FastList>(keyframes) }; } + /// + /// Converts a GLTF AnimationSampler into a Stride AnimationCurve + /// + /// + /// + public static AnimationCurve ConvertCurve(SharpGLTF.Schema2.IAnimationSampler sampler) + { + var interpolationType = + sampler.InterpolationMode switch + { + SharpGLTF.Schema2.AnimationInterpolationMode.LINEAR => AnimationCurveInterpolationType.Linear, + SharpGLTF.Schema2.AnimationInterpolationMode.STEP => AnimationCurveInterpolationType.Constant, + SharpGLTF.Schema2.AnimationInterpolationMode.CUBICSPLINE => AnimationCurveInterpolationType.Cubic, + _ => throw new NotImplementedException(), + }; + + var keyframes = + interpolationType switch + { + AnimationCurveInterpolationType.Constant => + sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), + AnimationCurveInterpolationType.Linear => + sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), + // TODO : Cubic can be broken + AnimationCurveInterpolationType.Cubic => + sampler.GetCubicKeys().Select(x => ConvertNumerics(x.Value).Select(y => CreateKeyFrame(x.Key, y))).SelectMany(x => x), + _ => throw new NotImplementedException() + }; + + return new AnimationCurve + { + InterpolationType = interpolationType, + KeyFrames = new FastList>(keyframes) + }; + } + /// + /// Converts a GLTF AnimationSampler into a Stride AnimationCurve + /// + /// + /// + public static AnimationCurve ConvertCurve(SharpGLTF.Schema2.IAnimationSampler sampler) + { + var interpolationType = + sampler.InterpolationMode switch + { + SharpGLTF.Schema2.AnimationInterpolationMode.LINEAR => AnimationCurveInterpolationType.Linear, + SharpGLTF.Schema2.AnimationInterpolationMode.STEP => AnimationCurveInterpolationType.Constant, + SharpGLTF.Schema2.AnimationInterpolationMode.CUBICSPLINE => AnimationCurveInterpolationType.Cubic, + _ => throw new NotImplementedException(), + }; + + var keyframes = + interpolationType switch + { + AnimationCurveInterpolationType.Constant => + sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), + AnimationCurveInterpolationType.Linear => + sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, ConvertNumerics(x.Value))), + // TODO : Cubic can be broken + AnimationCurveInterpolationType.Cubic => + sampler.GetCubicKeys().Select(x => ConvertNumerics(x.Value).Select(y => CreateKeyFrame(x.Key, y))).SelectMany(x => x), + _ => throw new NotImplementedException() + }; + + return new AnimationCurve + { + InterpolationType = interpolationType, + KeyFrames = new FastList>(keyframes) + }; + } + /// + /// Converts a GLTF AnimationSampler into a Stride AnimationCurve + /// + /// + /// + public static AnimationCurve ConvertCurve(SharpGLTF.Schema2.IAnimationSampler sampler) + { + var interpolationType = + sampler.InterpolationMode switch + { + SharpGLTF.Schema2.AnimationInterpolationMode.LINEAR => AnimationCurveInterpolationType.Linear, + SharpGLTF.Schema2.AnimationInterpolationMode.STEP => AnimationCurveInterpolationType.Constant, + SharpGLTF.Schema2.AnimationInterpolationMode.CUBICSPLINE => AnimationCurveInterpolationType.Cubic, + _ => throw new NotImplementedException(), + }; + + var keyframes = + interpolationType switch + { + AnimationCurveInterpolationType.Constant => + sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, x.Value)), + AnimationCurveInterpolationType.Linear => + sampler.GetLinearKeys().Select(x => CreateKeyFrame(x.Key, x.Value)), + _ => throw new NotImplementedException() + }; + + return new AnimationCurve + { + InterpolationType = interpolationType, + KeyFrames = new FastList>(keyframes) + }; + } } } diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 6714327303..1f6d30f1bb 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -1,3 +1,6 @@ +// Copyright (c) Stride contributors (https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. + using System; using System.Collections.Generic; using System.IO; @@ -95,7 +98,7 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot var meshName = FirstModelName(modelRoot); - + if (modelRoot.LogicalSkins.Where(x => x.VisualParents.First()?.Mesh == modelRoot.LogicalMeshes[0]).Count() > 0) skin = modelRoot.LogicalSkins @@ -113,13 +116,13 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot .Select(x => new NodeInfo() { Name = skin.GetJoint(x).Joint.Name ?? "Joint_" + skin.GetJoint(x).Joint.LogicalIndex, Depth = skin.GetJoint(x).Joint.LogicalIndex, Preserve = true }) .ToList(); } - + // Loading Mesh parameters, this will link the materials with the meshes var meshes = modelRoot .LogicalMeshes[0].Primitives .Select( - x => + x => { var materialName = x.Material != null ? FirstModelName(modelRoot) + "_" + (x.Material.Name ?? "Material" + x.Material.LogicalIndex) : ""; return (meshName + "_" + x.LogicalIndex, materialName); @@ -238,7 +241,7 @@ public static Dictionary LoadMaterials(SharpGLTF.Schema2. if (gltfImg.Content.SourcePath == null) { imgPath = Path.Join(sourcePath.GetFullDirectory(), textureName + "." + gltfImg.Content.FileExtension); - if(!File.Exists(imgPath)) + if (!File.Exists(imgPath)) gltfImg.Content.SaveToFile(imgPath); } else @@ -387,9 +390,9 @@ public static VertexBufferBinding[] ConvertSerializedVertexBufferBinding(SharpGL List bytelst = new(); - for(int i=0; i< size; i++) + for (int i = 0; i < size; i++) { - foreach(var e in mesh.VertexAccessors.Keys) + foreach (var e in mesh.VertexAccessors.Keys) { var bytes = mesh.GetVertexAccessor(e).TryGetVertexBytes(i).ToArray(); bytelst.AddRange(bytes); diff --git a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs index 44c6c37c18..2503e9f0d1 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs @@ -1,13 +1,13 @@ +// Copyright (c) Stride contributors (https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. + using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; using Stride.Core.Assets; using Stride.Core.IO; using Stride.Core.Mathematics; using Stride.Core.Serialization; -using Stride.Core.Serialization.Contents; using Stride.Graphics; using Stride.Rendering.Materials; using Stride.Rendering.Materials.ComputeColors; @@ -36,18 +36,21 @@ public static Matrix ConvertNumerics(System.Numerics.Matrix4x4 mat) /// /// public static Quaternion ConvertNumerics(System.Numerics.Quaternion v) => new Quaternion(v.X, v.Y, v.Z, v.W); + /// /// Converts a System.Numerics value into a Stride value /// /// /// public static Vector4 ConvertNumerics(System.Numerics.Vector4 v) => new Vector4(v.X, v.Y, v.Z, v.W); + /// /// Converts a System.Numerics value into a Stride value /// /// /// public static Vector3 ConvertNumerics(System.Numerics.Vector3 v) => new Vector3(v.X, v.Y, v.Z); + /// /// Converts a System.Numerics value into a Stride value /// @@ -55,6 +58,27 @@ public static Matrix ConvertNumerics(System.Numerics.Matrix4x4 mat) /// public static Vector2 ConvertNumerics(System.Numerics.Vector2 v) => new Vector2(v.X, v.Y); + + public static List ConvertNumerics((System.Numerics.Vector2, System.Numerics.Vector2, System.Numerics.Vector2) value) + { + return new List { ConvertNumerics(value.Item1), ConvertNumerics(value.Item2), ConvertNumerics(value.Item3) }; + } + + public static List ConvertNumerics((System.Numerics.Vector3, System.Numerics.Vector3, System.Numerics.Vector3) value) + { + return new List { ConvertNumerics(value.Item1), ConvertNumerics(value.Item2), ConvertNumerics(value.Item3) }; + } + + public static List ConvertNumerics((System.Numerics.Vector4, System.Numerics.Vector4, System.Numerics.Vector4) value) + { + return new List { ConvertNumerics(value.Item1), ConvertNumerics(value.Item2), ConvertNumerics(value.Item3) }; + } + + public static List ConvertNumerics((System.Numerics.Quaternion, System.Numerics.Quaternion, System.Numerics.Quaternion) value) + { + return new List { ConvertNumerics(value.Item1), ConvertNumerics(value.Item2), ConvertNumerics(value.Item3) }; + } + /// /// Gets the Stride VertexElement equivalent of a GLTF vertex /// @@ -63,7 +87,7 @@ public static Matrix ConvertNumerics(System.Numerics.Matrix4x4 mat) /// public static (VertexElement, int) ConvertVertexElement(KeyValuePair accessor, int offset) { - // TODO : Simplify this part + // TODO : Can be simplified but this gives better control over what's implemented or not return (accessor.Key, accessor.Value.Format.ByteSize) switch { ("POSITION", 12) => (VertexElement.Position(0, offset), 12), @@ -104,8 +128,7 @@ public static PrimitiveType ConvertPrimitiveType(SharpGLTF.Schema2.PrimitiveType SharpGLTF.Schema2.PrimitiveType.LINE_STRIP => PrimitiveType.LineStrip, SharpGLTF.Schema2.PrimitiveType.TRIANGLES => PrimitiveType.TriangleList, SharpGLTF.Schema2.PrimitiveType.TRIANGLE_STRIP => PrimitiveType.TriangleStrip, - SharpGLTF.Schema2.PrimitiveType.TRIANGLE_FAN => PrimitiveType.Undefined, - _ => throw new NotImplementedException() + _ => PrimitiveType.Undefined }; } @@ -129,9 +152,9 @@ public static List GenerateTextureFullPaths(SharpGLTF.Schema2.ModelRoot var gltfImg = chan.Texture.PrimaryImage; if (gltfImg.Content.SourcePath == null) { - var textureName = gltfImg.Name ?? GltfMeshParser.FirstModelName(root) + "_" + (mat.Name??mat.LogicalIndex.ToString()) + "_" + chan.Key; - result.Add( Path.Join(sourcePath.GetFullDirectory(), textureName + "." + gltfImg.Content.FileExtension)); - + var textureName = gltfImg.Name ?? GltfMeshParser.FirstModelName(root) + "_" + (mat.Name ?? mat.LogicalIndex.ToString()) + "_" + chan.Key; + result.Add(Path.Join(sourcePath.GetFullDirectory(), textureName + "." + gltfImg.Content.FileExtension)); + } else { @@ -161,7 +184,7 @@ public static ComputeTextureColor GenerateTextureColor(string sourceTextureFile, var textureName = textureFileName; var texture = AttachedReferenceManager.CreateProxyObject(AssetId.Empty, textureName); - + var currentTexture = new ComputeTextureColor(texture, textureUVSetIndex, uvScaling, Vector2.Zero) @@ -186,7 +209,7 @@ public static ComputeTextureColor GenerateTextureColor(string sourceTextureFile, public static ComputeTextureScalar GenerateTextureScalar(string sourceTextureFile, TextureCoordinate textureUVSetIndex, Vector2 textureUVscaling, TextureAddressMode addressModeU = TextureAddressMode.Wrap, TextureAddressMode addressModeV = TextureAddressMode.Wrap, string vfsOutputPath = "") { var textureFileName = Path.GetFileNameWithoutExtension(sourceTextureFile); - + var uvScaling = textureUVscaling; var textureName = textureFileName; From 6dcfc298c798605b1c8691bdfcff4b866f741eb8 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Sat, 24 Jul 2021 11:17:12 +0200 Subject: [PATCH 21/29] Added 8byte vertex color --- sources/tools/Stride.Importer.Gltf/GltfUtils.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs index 2503e9f0d1..558fd7734b 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs @@ -94,6 +94,7 @@ public static (VertexElement, int) ConvertVertexElement(KeyValuePair (VertexElement.Normal(0, offset), 12), ("TANGENT", 16) => (VertexElement.Tangent(0, offset), 16), ("COLOR", 16) => (VertexElement.Color(0, offset), 16), + ("COLOR_0", 8) => (new VertexElement(VertexElementUsage.Color, 0, PixelFormat.R16G16B16A16_Float, offset), 8), ("COLOR_0", 16) => (VertexElement.Color(0, offset), 16), ("TEXCOORD_0", 8) => (VertexElement.TextureCoordinate(0, offset), 8), ("TEXCOORD_1", 8) => (VertexElement.TextureCoordinate(1, offset), 8), From fc64315133cfca2cbcf3cff9bb794a6747b7b310 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Sat, 7 Aug 2021 18:27:50 +0200 Subject: [PATCH 22/29] Merge all meshes --- .../Stride.Importer.Gltf/GltfMeshParser.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 1f6d30f1bb..e9659e8ae7 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -9,6 +9,7 @@ using Stride.Assets.Materials; using Stride.Core.IO; using Stride.Core.Mathematics; +using Stride.Extensions; using Stride.Graphics; using Stride.Graphics.Data; using Stride.Importer.Common; @@ -53,9 +54,23 @@ public static SharpGLTF.Schema2.ModelRoot LoadGltf(Stride.Core.IO.UFile sourcePa public static Model LoadFirstModel(SharpGLTF.Schema2.ModelRoot root) { // We load every primitives of the first mesh + var draws = + root.LogicalMeshes + .Select(x => (x.Primitives.Select(x => LoadMesh(x)).ToList(), ConvertNumerics(x.VisualParents.First().WorldMatrix))).ToList(); + for (int i = 0; i < draws.Count; i++) + { + var mat = draws[i].Item2; + for (int j = 0; j < draws[i].Item1.Count; j++) + { + for (int k = 0; k < draws[i].Item1[j].Draw.VertexBuffers.Count(); k++) + { + draws[i].Item1[j].Draw.VertexBuffers[k].TransformBuffer(ref mat); + } + } + } var result = new Model() { - Meshes = root.LogicalMeshes[0].Primitives.Select(x => LoadMesh(x)).ToList() + Meshes = draws.SelectMany(x => x.Item1).ToList() }; result.Skeleton = ConvertSkeleton(root); return result; From 7f541266ed0b3f74fffd7b6d3d6e82c1912fa4bc Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Sun, 22 Aug 2021 01:07:05 +0200 Subject: [PATCH 23/29] Fusing animations and meshes --- .../Stride.Assets.Models/ImportGltfCommand.cs | 2 +- .../GltfAnimationParser.cs | 160 ++++++++---------- .../Stride.Importer.Gltf/GltfMeshParser.cs | 51 +++--- .../tools/Stride.Importer.Gltf/GltfUtils.cs | 2 +- 4 files changed, 97 insertions(+), 118 deletions(-) diff --git a/sources/engine/Stride.Assets.Models/ImportGltfCommand.cs b/sources/engine/Stride.Assets.Models/ImportGltfCommand.cs index 1c53550763..a3ae5dbec3 100644 --- a/sources/engine/Stride.Assets.Models/ImportGltfCommand.cs +++ b/sources/engine/Stride.Assets.Models/ImportGltfCommand.cs @@ -41,7 +41,7 @@ protected override Model LoadModel(ICommandContext commandContext, ContentManage protected override Dictionary LoadAnimation(ICommandContext commandContext, ContentManager contentManager, out TimeSpan duration) { var file = SharpGLTF.Schema2.ModelRoot.Load(SourcePath); - var sceneData = GltfMeshParser.ConvertAnimations(file); + var sceneData = GltfMeshParser.ConvertAnimations(file,SourcePath.GetFileNameWithoutExtension()); duration = GltfMeshParser.GetAnimationDuration(file); return sceneData; } diff --git a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs index 39008f0745..07c3d96b83 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs @@ -22,75 +22,50 @@ public static class GltfAnimationParser public static Skeleton ConvertSkeleton(SharpGLTF.Schema2.ModelRoot root) { Skeleton result = new Skeleton(); - var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes.First()).Skin; + var nodes = new List(); + var skins = root.LogicalSkins; + //var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes.First()).Skin; // If there is no corresponding skins return a skeleton with 2 bones (an empty skeleton would make the editor crash) - if (skin == null) + foreach (var skin in skins) { - result.Nodes = new List() { - new ModelNodeDefinition - { - Name = "root", - Flags = ModelNodeFlags.EnableRender, - ParentIndex = -1, - Transform = new TransformTRS - { - Position = Vector3.Zero, - Rotation = Quaternion.Identity, - Scale = Vector3.Zero - } - }, - new ModelNodeDefinition - { - Name = "Mesh", - Flags = ModelNodeFlags.EnableRender, - ParentIndex = -1, - Transform = new TransformTRS + var jointList = Enumerable.Range(0, skin.JointsCount).Select(x => skin.GetJoint(x).Joint).ToList(); + nodes.AddRange( + jointList + .Select( + x => + new ModelNodeDefinition { - Position = Vector3.Zero, - Rotation = Quaternion.Identity, - Scale = Vector3.Zero + Name = x.Name ?? "Joint_" + x.LogicalIndex, + Flags = ModelNodeFlags.Default, + ParentIndex = jointList.IndexOf(x.VisualParent) + 1, + Transform = new TransformTRS + { + Position = ConvertNumerics(x.LocalTransform.Translation), + Rotation = ConvertNumerics(x.LocalTransform.Rotation), + Scale = ConvertNumerics(x.LocalTransform.Scale) + } + } - }, - }.ToArray(); - return result; + ) + ); + // And insert a parent one not caught by the above function (GLTF does not consider the parent bone as a bone) + } - // for each joints we create a ModelNodeDefinition - var jointList = Enumerable.Range(0, skin.JointsCount).Select(x => skin.GetJoint(x).Joint).ToList(); - var mnd = - jointList - .Select( - x => - new ModelNodeDefinition - { - Name = x.Name ?? "Joint_" + x.LogicalIndex, - Flags = ModelNodeFlags.Default, - ParentIndex = jointList.IndexOf(x.VisualParent) + 1, - Transform = new TransformTRS + nodes.Insert( + 0, + new ModelNodeDefinition { - Position = ConvertNumerics(x.LocalTransform.Translation), - Rotation = ConvertNumerics(x.LocalTransform.Rotation), - Scale = ConvertNumerics(x.LocalTransform.Scale) - } - - } - ) - .ToList(); - // And insert a parent one not caught by the above function (GLTF does not consider the parent bone as a bone) - mnd.Insert( - 0, - new ModelNodeDefinition - { - Name = "Armature", - Flags = ModelNodeFlags.EnableRender, - ParentIndex = -1, - Transform = new TransformTRS - { - Position = Vector3.Zero, - Rotation = Quaternion.Identity, - Scale = Vector3.Zero - } - }); - result.Nodes = mnd.ToArray(); + Name = "Armature", + Flags = ModelNodeFlags.EnableRender, + ParentIndex = -1, + Transform = new TransformTRS + { + Position = Vector3.Zero, + Rotation = Quaternion.Identity, + Scale = Vector3.Zero + } + }); + result.Nodes = nodes.ToArray(); return result; } @@ -117,52 +92,63 @@ public static Dictionary ConvertCurves(IReadOnlyList(); if (root.LogicalAnimations.Count == 0) return result; - var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes.First()).Skin; + var skins = root.LogicalSkins; + var skNodes = ConvertSkeleton(root).Nodes.ToList(); + //var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes.First()).Skin; // In case there is no skin joints/bones, animate transform component - if (skin == null) + if (skins.Count() == 0) { - string base2 = "[TransformComponent].type"; + string basestring = "[TransformComponent].type"; foreach (var chan in channels) { switch (chan.TargetNodePath) { case SharpGLTF.Schema2.PropertyPath.translation: - result.Add(base2.Replace("type", "Position"), ConvertCurve(chan.GetTranslationSampler())); + result.Add(basestring.Replace("type", "Position"), ConvertCurve(chan.GetTranslationSampler())); break; case SharpGLTF.Schema2.PropertyPath.rotation: - result.Add(base2.Replace("type", "Rotation"), ConvertCurve(chan.GetRotationSampler())); + result.Add(basestring.Replace("type", "Rotation"), ConvertCurve(chan.GetRotationSampler())); break; case SharpGLTF.Schema2.PropertyPath.scale: - result.Add(base2.Replace("type", "Scale"), ConvertCurve(chan.GetScaleSampler())); + result.Add(basestring.Replace("type", "Scale"), ConvertCurve(chan.GetScaleSampler())); break; }; } return result; } - // Else we animate the model. - string baseString = "[ModelComponent.Key].Skeleton.NodeTransformations[index].Transform.type"; - - - var jointList = Enumerable.Range(0, skin.JointsCount).Select(x => skin.GetJoint(x).Joint).ToList(); - foreach (var chan in channels) + foreach (var skin in skins) { - var index = jointList.IndexOf(chan.TargetNode) + 1; - switch (chan.TargetNodePath) + var jointList = Enumerable.Range(0, skin.JointsCount).Select(x => skin.GetJoint(x).Joint).ToList(); + foreach (var chan in channels) { - case SharpGLTF.Schema2.PropertyPath.translation: - result.Add(baseString.Replace("index", $"{index}").Replace("type", "Position"), ConvertCurve(chan.GetTranslationSampler())); - break; - case SharpGLTF.Schema2.PropertyPath.rotation: - result.Add(baseString.Replace("index", $"{index}").Replace("type", "Rotation"), ConvertCurve(chan.GetRotationSampler())); - break; - case SharpGLTF.Schema2.PropertyPath.scale: - result.Add(baseString.Replace("index", $"{index}").Replace("type", "Scale"), ConvertCurve(chan.GetScaleSampler())); - break; - }; + //var index0 = jointList.IndexOf(chan.TargetNode) + 1; + var index = skNodes.IndexOf(skNodes.First(x => x.Name == chan.TargetNode.Name)); + switch (chan.TargetNodePath) + { + case SharpGLTF.Schema2.PropertyPath.translation: + result.Add( + $"[ModelComponent.Key].Skeleton.NodeTransformations[{index}].Transform.Position", + ConvertCurve(chan.GetTranslationSampler()) + ); + break; + case SharpGLTF.Schema2.PropertyPath.rotation: + result.Add( + $"[ModelComponent.Key].Skeleton.NodeTransformations[{index}].Transform.Rotation", + ConvertCurve(chan.GetRotationSampler()) + ); + break; + case SharpGLTF.Schema2.PropertyPath.scale: + result.Add( + $"[ModelComponent.Key].Skeleton.NodeTransformations[{index}].Transform.Scale", + ConvertCurve(chan.GetScaleSampler()) + ); + break; + }; + } } return result; diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index e9659e8ae7..4e2d7e7bc2 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -54,9 +54,10 @@ public static SharpGLTF.Schema2.ModelRoot LoadGltf(Stride.Core.IO.UFile sourcePa public static Model LoadFirstModel(SharpGLTF.Schema2.ModelRoot root) { // We load every primitives of the first mesh + var sk = ConvertSkeleton(root); var draws = root.LogicalMeshes - .Select(x => (x.Primitives.Select(x => LoadMesh(x)).ToList(), ConvertNumerics(x.VisualParents.First().WorldMatrix))).ToList(); + .Select(x => (x.Primitives.Select(x => LoadMesh(x,sk)).ToList(), ConvertNumerics(x.VisualParents.First().WorldMatrix))).ToList(); for (int i = 0; i < draws.Count; i++) { var mat = draws[i].Item2; @@ -72,21 +73,10 @@ public static Model LoadFirstModel(SharpGLTF.Schema2.ModelRoot root) { Meshes = draws.SelectMany(x => x.Item1).ToList() }; - result.Skeleton = ConvertSkeleton(root); + result.Skeleton = sk; return result; } - /// - /// Gets the first model name - /// - /// - /// - public static string FirstModelName(SharpGLTF.Schema2.ModelRoot root) - { - // TODO : Get the file name instead of `Mesh` - return root.LogicalMeshes.First()?.Name ?? "Mesh"; - } - /// /// Gets the sum of all animation duration /// @@ -111,7 +101,7 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot HashSet boneNames = new HashSet(); List nodes = new List(); - var meshName = FirstModelName(modelRoot); + var meshName = sourcePath.GetFileNameWithoutExtension(); if (modelRoot.LogicalSkins.Where(x => x.VisualParents.First()?.Mesh == modelRoot.LogicalMeshes[0]).Count() > 0) @@ -139,7 +129,7 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot .Select( x => { - var materialName = x.Material != null ? FirstModelName(modelRoot) + "_" + (x.Material.Name ?? "Material" + x.Material.LogicalIndex) : ""; + var materialName = x.Material != null ? sourcePath.GetFileNameWithoutExtension() + "_" + (x.Material.Name ?? "Material" + x.Material.LogicalIndex) : ""; return (meshName + "_" + x.LogicalIndex, materialName); } ) @@ -157,7 +147,7 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot // Loading the animation names (should be the same as the keys used in animations List animNodes = - ConvertAnimations(modelRoot).Keys.ToList(); + ConvertAnimations(modelRoot, sourcePath.GetFileNameWithoutExtension()).Keys.ToList(); return new EntityInfo { @@ -176,20 +166,21 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot /// /// /// - public static MeshSkinningDefinition ConvertInverseBindMatrices(SharpGLTF.Schema2.ModelRoot root) + public static MeshSkinningDefinition ConvertInverseBindMatrices(SharpGLTF.Schema2.ModelRoot root, Skeleton sk) { var skin = root.LogicalNodes.First(x => x.Mesh == root.LogicalMeshes[0]).Skin; if (skin == null) return null; var jointList = Enumerable.Range(0, skin.JointsCount).Select(skin.GetJoint); + var nodeList = sk.Nodes.ToList(); var mnt = new MeshSkinningDefinition { Bones = jointList - .Select((x, i) => + .Select(x => new MeshBoneDefinition { - NodeIndex = i + 1, + NodeIndex = nodeList.IndexOf(nodeList.First(n => n.Name == x.Joint.Name)), LinkToMeshMatrix = ConvertNumerics(x.InverseBindMatrix) } ) @@ -203,10 +194,10 @@ public static MeshSkinningDefinition ConvertInverseBindMatrices(SharpGLTF.Schema /// /// /// - public static Dictionary ConvertAnimations(SharpGLTF.Schema2.ModelRoot root) + public static Dictionary ConvertAnimations(SharpGLTF.Schema2.ModelRoot root, string filename) { var animations = root.LogicalAnimations; - var meshName = FirstModelName(root); + var meshName = filename; var clips = animations @@ -218,7 +209,7 @@ public static Dictionary ConvertAnimations(SharpGLTF.Sche // Add Curve ConvertCurves(x.Channels, root).ToList().ForEach(v => clip.AddCurve(v.Key, v.Value)); string name = x.Name == null ? meshName + "_Animation_" + x.LogicalIndex : meshName + "_" + x.Name; - clip.Optimize(); + if(clip.Curves.Count > 1) clip.Optimize(); return (name, clip); } ) @@ -250,7 +241,7 @@ public static Dictionary LoadMaterials(SharpGLTF.Schema2. { var gltfImg = chan.Texture.PrimaryImage; - var textureName = gltfImg.Name ?? FirstModelName(root) + "_" + (mat.Name ?? mat.LogicalIndex.ToString()) + "_" + chan.Key; + var textureName = gltfImg.Name ?? sourcePath.GetFileNameWithoutExtension() + "_" + (mat.Name ?? mat.LogicalIndex.ToString()) + "_" + chan.Key; string imgPath; if (gltfImg.Content.SourcePath == null) @@ -288,20 +279,22 @@ public static Dictionary LoadMaterials(SharpGLTF.Schema2. { var vt = new ComputeColor(new Color(ConvertNumerics(chan.Parameter))); var x = new ComputeFloat(chan.Parameter.X); + switch (chan.Key) { case "BaseColor": material.Attributes.Diffuse = new MaterialDiffuseMapFeature(vt); material.Attributes.DiffuseModel = new MaterialDiffuseLambertModelFeature(); + //material.Attributes.Transparency = new MaterialTransparencyBlendFeature(); break; case "MetallicRoughness": - material.Attributes.MicroSurface = new MaterialGlossinessMapFeature(x); + material.Attributes.MicroSurface = new MaterialGlossinessMapFeature(x) { Invert = true }; break; case "Normal": - material.Attributes.Surface = new MaterialNormalMapFeature(vt); + material.Attributes.Surface = new MaterialNormalMapFeature(vt) { IsXYNormal = true }; break; case "Occlusion": - material.Attributes.Occlusion = new MaterialOcclusionMapFeature(); + material.Attributes.Occlusion = new MaterialOcclusionMapFeature() {CavityMap = vt as IComputeScalar }; break; case "Emissive": material.Attributes.Emissive = new MaterialEmissiveMapFeature(vt); @@ -311,7 +304,7 @@ public static Dictionary LoadMaterials(SharpGLTF.Schema2. } material.Attributes.CullMode = CullMode.Back; - var materialName = FirstModelName(root) + "_" + (mat.Name ?? "Material") + "_" + mat.LogicalIndex; + var materialName = sourcePath.GetFileNameWithoutExtension() + "_" + (mat.Name ?? "Material") + "_" + mat.LogicalIndex; result.TryAdd(materialName, material); } @@ -323,7 +316,7 @@ public static Dictionary LoadMaterials(SharpGLTF.Schema2. /// /// /// - public static Mesh LoadMesh(SharpGLTF.Schema2.MeshPrimitive mesh) + public static Mesh LoadMesh(SharpGLTF.Schema2.MeshPrimitive mesh, Skeleton sk) { var draw = new MeshDraw @@ -338,7 +331,7 @@ public static Mesh LoadMesh(SharpGLTF.Schema2.MeshPrimitive mesh) var result = new Mesh(draw, new ParameterCollection()) { - Skinning = ConvertInverseBindMatrices(mesh.LogicalParent.LogicalParent), + Skinning = ConvertInverseBindMatrices(mesh.LogicalParent.LogicalParent, sk), Name = mesh.LogicalParent.Name, MaterialIndex = mesh.LogicalParent.LogicalParent.LogicalMaterials.ToList().IndexOf(mesh.Material) }; diff --git a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs index 558fd7734b..388b5073cf 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfUtils.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfUtils.cs @@ -153,7 +153,7 @@ public static List GenerateTextureFullPaths(SharpGLTF.Schema2.ModelRoot var gltfImg = chan.Texture.PrimaryImage; if (gltfImg.Content.SourcePath == null) { - var textureName = gltfImg.Name ?? GltfMeshParser.FirstModelName(root) + "_" + (mat.Name ?? mat.LogicalIndex.ToString()) + "_" + chan.Key; + var textureName = gltfImg.Name ?? sourcePath.GetFileNameWithoutExtension() + "_" + (mat.Name ?? mat.LogicalIndex.ToString()) + "_" + chan.Key; result.Add(Path.Join(sourcePath.GetFullDirectory(), textureName + "." + gltfImg.Content.FileExtension)); } From 005d765bd1b44d11e8b1091c8490c994e2a268b6 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Fri, 12 Nov 2021 15:59:56 +0100 Subject: [PATCH 24/29] ignore configs --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 5adc34ed40..7b62c07e94 100644 --- a/.gitignore +++ b/.gitignore @@ -154,3 +154,5 @@ fastlane/report.xml fastlane/screenshots *.user project.lock.json +/hooks +/lfs/objects From f0d9a220e9f852659dba15655ce914ac580a0b92 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Fri, 26 Nov 2021 15:59:14 +0100 Subject: [PATCH 25/29] Correction root animation import --- build/Stride.sln | 15 +++++++++++++++ .../ImportModelCommand.Animation.cs | 4 ++++ .../ModelAssetImporter.cs | 2 +- .../GltfAnimationParser.cs | 2 +- .../Stride.Importer.Gltf/GltfMeshParser.cs | 19 +++++++++++++------ 5 files changed, 34 insertions(+), 8 deletions(-) diff --git a/build/Stride.sln b/build/Stride.sln index 6d9753b3af..e2422741d1 100644 --- a/build/Stride.sln +++ b/build/Stride.sln @@ -337,6 +337,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.Engine.NoAssets.Test EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.Importer.Gltf", "..\sources\tools\Stride.Importer.Gltf\Stride.Importer.Gltf.csproj", "{69CA1F59-5735-403C-8009-2CE282C72DE6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stride.Importer.Common.Extensions", "..\sources\tools\Stride.Importer.Common.Extensions\Stride.Importer.Common.Extensions.csproj", "{A32AA17A-588A-4B47-86B7-397C63B2DFD2}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution ..\sources\shared\Stride.NuGetResolver\Stride.NuGetResolver.projitems*{00b72ed7-00e9-47f7-868d-8162027cd068}*SharedItemsImports = 13 @@ -1541,6 +1543,18 @@ Global {69CA1F59-5735-403C-8009-2CE282C72DE6}.Release|Mixed Platforms.Build.0 = Release|Any CPU {69CA1F59-5735-403C-8009-2CE282C72DE6}.Release|Win32.ActiveCfg = Release|Any CPU {69CA1F59-5735-403C-8009-2CE282C72DE6}.Release|Win32.Build.0 = Release|Any CPU + {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Debug|Win32.ActiveCfg = Debug|Any CPU + {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Debug|Win32.Build.0 = Debug|Any CPU + {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Release|Any CPU.Build.0 = Release|Any CPU + {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Release|Win32.ActiveCfg = Release|Any CPU + {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Release|Win32.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1669,6 +1683,7 @@ Global {83E4E436-659A-4D82-90DD-0DC67BE38D17} = {E4508D15-6503-4A29-ADC4-27B3A5E99545} {1C94168A-3C0D-4C6B-883B-91627D2EF3A1} = {A7ED9F01-7D78-4381-90A6-D50E51C17250} {69CA1F59-5735-403C-8009-2CE282C72DE6} = {6F473FA6-4F8B-4FBA-AE33-EE5AF997D50C} + {A32AA17A-588A-4B47-86B7-397C63B2DFD2} = {6F473FA6-4F8B-4FBA-AE33-EE5AF997D50C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FF877973-604D-4EA7-B5F5-A129961F9EF2} diff --git a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs index 65adb3215c..d96ba8f18e 100644 --- a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs +++ b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs @@ -76,6 +76,10 @@ private unsafe object ExportAnimation(ICommandContext commandContext, ContentMan { animationClip.AddCurve($"[TransformComponent.Key]." + channelName.Replace("Transform.", string.Empty), curve); } + else if(channelName.StartsWith("[TransformComponent.Key]")) + { + animationClip.AddCurve(channelName, curve); + } // Also apply Camera curves // TODO: Add some other curves? diff --git a/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs b/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs index 6fbfd085aa..9701321968 100644 --- a/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs +++ b/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs @@ -153,7 +153,7 @@ private static void ImportAnimation(List assetReferences, UFile local var assetSource = localPath; var asset = new AnimationAsset { Source = assetSource, AnimationTimeMaximum = animationEndTime, AnimationTimeMinimum = animationStartTime }; - string animUrl = localPath.GetFileNameWithoutExtension() + (shouldPostFixName ? " Animation " + anim : ""); + string animUrl = anim + "_Animation"; if (skeletonAsset != null) asset.Skeleton = AttachedReferenceManager.CreateProxyObject(skeletonAsset.Id, skeletonAsset.Location); diff --git a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs index 07c3d96b83..6748ed9ad7 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfAnimationParser.cs @@ -99,7 +99,7 @@ public static Dictionary ConvertCurves(IReadOnlyList GetAnimatedNodes(SharpGLTF.Schema2.ModelRoot root) + { + return root.LogicalAnimations.SelectMany(x => x.Channels.SelectMany(y => x.Channels.Select(z => z.TargetNode.Name))).ToList(); + } + /// /// Extract the entity info. This function tells the editor informations about any assets. /// If any info is missing/wrong the assests won't be correctly imported. @@ -95,7 +101,7 @@ public static TimeSpan GetAnimationDuration(SharpGLTF.Schema2.ModelRoot root) /// /// /// - public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot, UFile sourcePath) + public static EntityInfoExtended ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot, UFile sourcePath) { SharpGLTF.Schema2.Skin skin = null; HashSet boneNames = new HashSet(); @@ -146,16 +152,16 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot .ToList(); // Loading the animation names (should be the same as the keys used in animations - List animNodes = - ConvertAnimations(modelRoot, sourcePath.GetFileNameWithoutExtension()).Keys.ToList(); + List animNodes = GetAnimatedNodes(modelRoot); - return new EntityInfo + return new EntityInfoExtended { Models = meshes, AnimationNodes = animNodes, Materials = LoadMaterials(modelRoot, sourcePath), Nodes = nodes, - TextureDependencies = GenerateTextureFullPaths(modelRoot, sourcePath) + TextureDependencies = GenerateTextureFullPaths(modelRoot, sourcePath), + AnimationNames = ConvertAnimations(modelRoot, sourcePath).Keys.ToList() }; } @@ -198,6 +204,7 @@ public static Dictionary ConvertAnimations(SharpGLTF.Sche { var animations = root.LogicalAnimations; var meshName = filename; + var sk = ConvertSkeleton(root).Nodes.Select(x => x.Name); var clips = animations @@ -208,7 +215,7 @@ public static Dictionary ConvertAnimations(SharpGLTF.Sche clip.RepeatMode = AnimationRepeatMode.LoopInfinite; // Add Curve ConvertCurves(x.Channels, root).ToList().ForEach(v => clip.AddCurve(v.Key, v.Value)); - string name = x.Name == null ? meshName + "_Animation_" + x.LogicalIndex : meshName + "_" + x.Name; + string name = "Armature";//x.Channels.Count() > 0 ? x.Channels.First().TargetNode.Name : null; if(clip.Curves.Count > 1) clip.Optimize(); return (name, clip); } From 6770a72e265862b8a82ec9040a98b118017e56a2 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Fri, 26 Nov 2021 16:00:20 +0100 Subject: [PATCH 26/29] Removed EntityInfoExtended --- sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 3f12531eb3..5d05d890b6 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -13,7 +13,6 @@ using Stride.Graphics; using Stride.Graphics.Data; using Stride.Importer.Common; -using Stride.Importer.Common.Extensions; using Stride.Rendering; using Stride.Rendering.Materials; using Stride.Rendering.Materials.ComputeColors; @@ -101,7 +100,7 @@ public static List GetAnimatedNodes(SharpGLTF.Schema2.ModelRoot root) /// /// /// - public static EntityInfoExtended ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot, UFile sourcePath) + public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot, UFile sourcePath) { SharpGLTF.Schema2.Skin skin = null; HashSet boneNames = new HashSet(); @@ -154,14 +153,13 @@ public static EntityInfoExtended ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot m // Loading the animation names (should be the same as the keys used in animations List animNodes = GetAnimatedNodes(modelRoot); - return new EntityInfoExtended + return new EntityInfo { Models = meshes, AnimationNodes = animNodes, Materials = LoadMaterials(modelRoot, sourcePath), Nodes = nodes, - TextureDependencies = GenerateTextureFullPaths(modelRoot, sourcePath), - AnimationNames = ConvertAnimations(modelRoot, sourcePath).Keys.ToList() + TextureDependencies = GenerateTextureFullPaths(modelRoot, sourcePath) }; } From 6e6db931b201189e9469c8877d2ed3b099d445d5 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Sun, 28 Nov 2021 12:34:59 +0100 Subject: [PATCH 27/29] Removed project from sln --- build/Stride.sln | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/build/Stride.sln b/build/Stride.sln index e2422741d1..6d9753b3af 100644 --- a/build/Stride.sln +++ b/build/Stride.sln @@ -337,8 +337,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.Engine.NoAssets.Test EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.Importer.Gltf", "..\sources\tools\Stride.Importer.Gltf\Stride.Importer.Gltf.csproj", "{69CA1F59-5735-403C-8009-2CE282C72DE6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stride.Importer.Common.Extensions", "..\sources\tools\Stride.Importer.Common.Extensions\Stride.Importer.Common.Extensions.csproj", "{A32AA17A-588A-4B47-86B7-397C63B2DFD2}" -EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution ..\sources\shared\Stride.NuGetResolver\Stride.NuGetResolver.projitems*{00b72ed7-00e9-47f7-868d-8162027cd068}*SharedItemsImports = 13 @@ -1543,18 +1541,6 @@ Global {69CA1F59-5735-403C-8009-2CE282C72DE6}.Release|Mixed Platforms.Build.0 = Release|Any CPU {69CA1F59-5735-403C-8009-2CE282C72DE6}.Release|Win32.ActiveCfg = Release|Any CPU {69CA1F59-5735-403C-8009-2CE282C72DE6}.Release|Win32.Build.0 = Release|Any CPU - {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Debug|Win32.ActiveCfg = Debug|Any CPU - {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Debug|Win32.Build.0 = Debug|Any CPU - {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Release|Any CPU.Build.0 = Release|Any CPU - {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Release|Win32.ActiveCfg = Release|Any CPU - {A32AA17A-588A-4B47-86B7-397C63B2DFD2}.Release|Win32.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1683,7 +1669,6 @@ Global {83E4E436-659A-4D82-90DD-0DC67BE38D17} = {E4508D15-6503-4A29-ADC4-27B3A5E99545} {1C94168A-3C0D-4C6B-883B-91627D2EF3A1} = {A7ED9F01-7D78-4381-90A6-D50E51C17250} {69CA1F59-5735-403C-8009-2CE282C72DE6} = {6F473FA6-4F8B-4FBA-AE33-EE5AF997D50C} - {A32AA17A-588A-4B47-86B7-397C63B2DFD2} = {6F473FA6-4F8B-4FBA-AE33-EE5AF997D50C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FF877973-604D-4EA7-B5F5-A129961F9EF2} From ddbc547f23b46c6b874559ea14972f8c5c958a02 Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Sun, 28 Nov 2021 13:59:31 +0100 Subject: [PATCH 28/29] update reference GLTF --- .../tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj b/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj index 0e0fdccfe3..841f6dcfd0 100644 --- a/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj +++ b/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj @@ -17,12 +17,7 @@ - - - - - ..\Stride.Importer.Common\bin\net5.0-windows\x64\Debug\Stride.Importer.Common.dll - + From 6a3482fb0e644e35cf583d3ba476aac63741757b Mon Sep 17 00:00:00 2001 From: Youness KAFIA Date: Sun, 26 Dec 2021 17:06:43 +0100 Subject: [PATCH 29/29] Minor changes --- .../Stride.Assets.Models/GltfAssetImporter.cs | 1 + .../Stride.Assets.Models/ImportGltfCommand.cs | 2 - .../ImportModelCommand.Animation.cs | 9 +++- .../ModelAssetImporter.cs | 2 +- .../Stride.Importer.Common.cpp | 1 + .../Stride.Importer.Gltf/GltfMeshParser.cs | 43 +++++++++++-------- .../Stride.Importer.Gltf.csproj | 6 ++- 7 files changed, 40 insertions(+), 24 deletions(-) diff --git a/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs b/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs index e195e8a60d..7da0dfa170 100644 --- a/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs +++ b/sources/engine/Stride.Assets.Models/GltfAssetImporter.cs @@ -42,5 +42,6 @@ public override void GetAnimationDuration(UFile localPath, Logger logger, AssetI endTime = GltfMeshParser.GetAnimationDuration(gltfFile); startTime = CompressedTimeSpan.Zero; } + } } diff --git a/sources/engine/Stride.Assets.Models/ImportGltfCommand.cs b/sources/engine/Stride.Assets.Models/ImportGltfCommand.cs index a3ae5dbec3..ae4f17162f 100644 --- a/sources/engine/Stride.Assets.Models/ImportGltfCommand.cs +++ b/sources/engine/Stride.Assets.Models/ImportGltfCommand.cs @@ -53,8 +53,6 @@ protected override Skeleton LoadSkeleton(ICommandContext commandContext, Content return sceneData; } - - public override bool ShouldSpawnNewProcess() { return true; diff --git a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs index d96ba8f18e..d354aa8d78 100644 --- a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs +++ b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs @@ -26,6 +26,13 @@ public partial class ImportModelCommand private unsafe object ExportAnimation(ICommandContext commandContext, ContentManager contentManager, bool failOnEmptyAnimation) { + // Gltf case + if (commandContext.CurrentCommand.Title.ToLower().Contains("gltf")) + { + var ts = TimeSpan.FromSeconds(1); + return LoadAnimation(commandContext, contentManager, out ts); + } + // Read from model file var modelSkeleton = LoadSkeleton(commandContext, contentManager); // we get model skeleton to compare it to real skeleton we need to map to AdjustSkeleton(modelSkeleton); @@ -42,7 +49,7 @@ private unsafe object ExportAnimation(ICommandContext commandContext, ContentMan { foreach (var animationCurve in clip.Value.Curves) { - animationCurve.ShiftKeys(startTime); + animationCurve?.ShiftKeys(startTime); } } diff --git a/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs b/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs index 9701321968..a892a0e06f 100644 --- a/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs +++ b/sources/engine/Stride.Assets.Models/ModelAssetImporter.cs @@ -101,7 +101,7 @@ public override IEnumerable Import(UFile localPath, AssetImporterPara TimeSpan startTime, endTime; GetAnimationDuration(localPath, importParameters.Logger, importParameters, out startTime, out endTime); bool isGltfAsset = localPath.GetFileExtension().ToLower().Contains("glb") || localPath.GetFileExtension().ToLower().Contains("gltf"); - ImportAnimation(rawAssetReferences, localPath, entityInfo.AnimationNodes, isImportingModel, isGltfAsset, skeletonAsset, startTime, endTime); + ImportAnimation(rawAssetReferences, localPath, isGltfAsset ? entityInfo.AnimationNames : entityInfo.AnimationNodes, isImportingModel, isGltfAsset, skeletonAsset, startTime, endTime); } // 4. Materials diff --git a/sources/tools/Stride.Importer.Common/Stride.Importer.Common.cpp b/sources/tools/Stride.Importer.Common/Stride.Importer.Common.cpp index 9fe4ea6ccc..6e3c4cd6ff 100644 --- a/sources/tools/Stride.Importer.Common/Stride.Importer.Common.cpp +++ b/sources/tools/Stride.Importer.Common/Stride.Importer.Common.cpp @@ -61,6 +61,7 @@ public ref class EntityInfo List^ AnimationNodes; List^ Models; List^ Nodes; + List^ AnimationNames; }; public ref class MeshMaterials diff --git a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs index 5d05d890b6..ac6c3170a5 100644 --- a/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs +++ b/sources/tools/Stride.Importer.Gltf/GltfMeshParser.cs @@ -55,9 +55,9 @@ public static Model LoadFirstModel(SharpGLTF.Schema2.ModelRoot root) { // We load every primitives of the first mesh var sk = ConvertSkeleton(root); - var draws = + var draws = root.LogicalMeshes - .Select(x => (x.Primitives.Select(x => LoadMesh(x,sk)).ToList(), ConvertNumerics(x.VisualParents.First().WorldMatrix))).ToList(); + .Select(x => (x.Primitives.Select(x => LoadMesh(x, sk)).ToList(), ConvertNumerics(x.VisualParents.First().WorldMatrix))).ToList(); for (int i = 0; i < draws.Count; i++) { var mat = draws[i].Item2; @@ -152,11 +152,13 @@ public static EntityInfo ExtractEntityInfo(SharpGLTF.Schema2.ModelRoot modelRoot // Loading the animation names (should be the same as the keys used in animations List animNodes = GetAnimatedNodes(modelRoot); + List animNames = ConvertAnimations(modelRoot, sourcePath.GetFileNameWithoutExtension()).Keys.ToList(); return new EntityInfo { Models = meshes, AnimationNodes = animNodes, + AnimationNames = animNames, Materials = LoadMaterials(modelRoot, sourcePath), Nodes = nodes, TextureDependencies = GenerateTextureFullPaths(modelRoot, sourcePath) @@ -205,21 +207,24 @@ public static Dictionary ConvertAnimations(SharpGLTF.Sche var sk = ConvertSkeleton(root).Nodes.Select(x => x.Name); var clips = - animations - .Select(x => - { - //Create animation clip with - var clip = new AnimationClip { Duration = TimeSpan.FromSeconds(x.Duration) }; - clip.RepeatMode = AnimationRepeatMode.LoopInfinite; - // Add Curve - ConvertCurves(x.Channels, root).ToList().ForEach(v => clip.AddCurve(v.Key, v.Value)); - string name = "Armature";//x.Channels.Count() > 0 ? x.Channels.First().TargetNode.Name : null; - if(clip.Curves.Count > 1) clip.Optimize(); - return (name, clip); - } - ) - .ToList() - .ToDictionary(x => x.name, x => x.clip); + animations + .Select(x => + { + //Create animation clip with + var clip = new AnimationClip { Duration = TimeSpan.FromSeconds(x.Duration) }; + clip.RepeatMode = AnimationRepeatMode.LoopInfinite; + // Add Curve + foreach (var c in ConvertCurves(x.Channels, root)) + { + clip.AddCurve(c.Key, c.Value); + } + + if (clip.Curves.Count > 1) clip.Optimize(); + return (x.Name ?? "Animation_" + animations.ToList().IndexOf(x), clip); + } + ) + .ToList() + .ToDictionary(x => x.Item1, x => x.clip); return clips; } @@ -284,7 +289,7 @@ public static Dictionary LoadMaterials(SharpGLTF.Schema2. { var vt = new ComputeColor(new Color(ConvertNumerics(chan.Parameter))); var x = new ComputeFloat(chan.Parameter.X); - + switch (chan.Key) { case "BaseColor": @@ -299,7 +304,7 @@ public static Dictionary LoadMaterials(SharpGLTF.Schema2. material.Attributes.Surface = new MaterialNormalMapFeature(vt) { IsXYNormal = true }; break; case "Occlusion": - material.Attributes.Occlusion = new MaterialOcclusionMapFeature() {CavityMap = vt as IComputeScalar }; + material.Attributes.Occlusion = new MaterialOcclusionMapFeature() { CavityMap = vt as IComputeScalar }; break; case "Emissive": material.Attributes.Emissive = new MaterialEmissiveMapFeature(vt); diff --git a/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj b/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj index 841f6dcfd0..c625ff15fe 100644 --- a/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj +++ b/sources/tools/Stride.Importer.Gltf/Stride.Importer.Gltf.csproj @@ -17,9 +17,13 @@ - + + + ..\Stride.Importer.Common\bin\net6.0-windows7.0\x64\Debug\Stride.Importer.Common.dll + +