Browse Source

AnimationPointer++ [WIP]

Vicente Penades Armengot 9 months ago
parent
commit
500c20e676

+ 6 - 1
src/SharpGLTF.Core/Schema2/gltf.AnimationChannel.cs

@@ -119,7 +119,12 @@ namespace SharpGLTF.Schema2
             new Collections.ChildSetter<AnimationChannel>(this).SetProperty(ref _target, target);
             new Collections.ChildSetter<AnimationChannel>(this).SetProperty(ref _target, target);
         }
         }
 
 
-        internal AnimationSampler _GetSampler() { return this.LogicalParent._Samplers[this._sampler]; }
+        public IAnimationSampler<T> GetSamplerOrNull<T>()
+        {
+            return _GetSampler() as IAnimationSampler<T>;
+        }
+
+        internal AnimationSampler _GetSampler() { return this.LogicalParent._Samplers[this._sampler]; }        
 
 
         public IAnimationSampler<Vector3> GetScaleSampler()
         public IAnimationSampler<Vector3> GetScaleSampler()
         {
         {

+ 5 - 0
src/SharpGLTF.Core/Schema2/gltf.Material.cs

@@ -125,6 +125,11 @@ namespace SharpGLTF.Schema2
             return null;
             return null;
         }
         }
 
 
+        public string GetAnimationPointer()
+        {
+            return $"/materials/{LogicalIndex}/pbrMetallicRoughness";
+        }
+
         #endregion
         #endregion
 
 
         #region validation
         #region validation

+ 8 - 1
src/SharpGLTF.Core/Schema2/gltf.MaterialChannel.cs

@@ -76,7 +76,7 @@ namespace SharpGLTF.Schema2
 
 
         public Material LogicalParent => _Material;
         public Material LogicalParent => _Material;
 
 
-        public String Key => _Key;
+        public String Key => _Key;        
 
 
         public Boolean HasDefaultContent => _CheckHasDefaultContent();
         public Boolean HasDefaultContent => _CheckHasDefaultContent();
 
 
@@ -137,6 +137,13 @@ namespace SharpGLTF.Schema2
 
 
         #region API
         #region API
 
 
+        public String GetAnimationPointer()
+        {
+            var ptr = _Material.GetAnimationPointer();
+
+            return ptr + "/" + Key;
+        }
+
         public float GetFactor(string key)
         public float GetFactor(string key)
         {
         {
             return _Parameters
             return _Parameters

+ 22 - 125
src/SharpGLTF.Core/Schema2/gltf.TextureInfo.cs

@@ -1,4 +1,5 @@
 using System;
 using System;
+using System.Linq;
 using System.Numerics;
 using System.Numerics;
 
 
 namespace SharpGLTF.Schema2
 namespace SharpGLTF.Schema2
@@ -9,32 +10,20 @@ namespace SharpGLTF.Schema2
     /// - <see cref="MaterialOcclusionTextureInfo"/>
     /// - <see cref="MaterialOcclusionTextureInfo"/>
     /// </remarks>
     /// </remarks>
     [System.Diagnostics.DebuggerDisplay("LogicalTexture[{_LogicalTextureIndex}]")]
     [System.Diagnostics.DebuggerDisplay("LogicalTexture[{_LogicalTextureIndex}]")]
-    public partial class TextureInfo
+    public partial class TextureInfo : Collections.IChildOf<Material>
     {
     {
         #region lifecycle
         #region lifecycle
 
 
-        public TextureInfo() { }
+        public TextureInfo() { }        
 
 
-        public TextureInfo(TextureInfo other)
+        void Collections.IChildOf<Material>.SetLogicalParent(Material parent)
         {
         {
-            if (other == null) throw new ArgumentNullException(nameof(other));
-
-            _index = other._index;
-            _texCoord = other._texCoord;
-
-            this.Extras = other.Extras;
-
-            // TODO: should copy all extensions, not only TextureTransform.
-
-            var otherXform = other.GetExtension<TextureTransform>();
-
-            if (otherXform != null && !otherXform.IsDefault)
-            {
-                var thisXform = other.UseExtension<TextureTransform>();
-                otherXform.CopyTo(thisXform);
-            }
+            this.LogicalParent = parent;
         }
         }
 
 
+        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
+        public Material LogicalParent { get; private set; }
+
         #endregion
         #endregion
 
 
         #region properties
         #region properties
@@ -46,17 +35,7 @@ namespace SharpGLTF.Schema2
         {
         {
             get => _index;
             get => _index;
             internal protected set => _index = value;
             internal protected set => _index = value;
-        }
-
-        /// <summary>
-        /// Index into <see cref="ModelRoot.LogicalTextures"/>
-        /// </summary>
-        [Obsolete("Use LogicalTextureIndex instead", true)]
-        public int _LogicalTextureIndex
-        {
-            get => _index;
-            internal protected set => _index = value;
-        }
+        }        
 
 
         /// <summary>
         /// <summary>
         /// Gets or sets the index of texture's TEXCOORD_[index] attribute used for texture coordinate mapping.
         /// Gets or sets the index of texture's TEXCOORD_[index] attribute used for texture coordinate mapping.
@@ -73,8 +52,16 @@ namespace SharpGLTF.Schema2
 
 
         #region API
         #region API
 
 
+        internal string _GetAnimationPointer()
+        {
+            throw new NotImplementedException();
+        }
+
         public void SetTransform(Vector2 offset, Vector2 scale, float rotation, int? texCoordOverride = null)
         public void SetTransform(Vector2 offset, Vector2 scale, float rotation, int? texCoordOverride = null)
         {
         {
+            var oldXform = this.GetExtension<TextureTransform>();
+            if (oldXform != null) oldXform._Parent = null;
+
             var xform = new TextureTransform(this)
             var xform = new TextureTransform(this)
             {
             {
                 TextureCoordinateOverride = texCoordOverride,
                 TextureCoordinateOverride = texCoordOverride,
@@ -83,7 +70,7 @@ namespace SharpGLTF.Schema2
                 Rotation = rotation
                 Rotation = rotation
             };
             };
 
 
-            if (xform.IsDefault) this.RemoveExtensions<TextureTransform>();
+            if (xform.IsDefault) this.RemoveExtensions<TextureTransform>(); // TODO: this may no longer be valid because KHR_animation_pointer requires the object to exist.
             else this.SetExtension(xform);
             else this.SetExtension(xform);
         }
         }
 
 
@@ -96,87 +83,7 @@ namespace SharpGLTF.Schema2
             validate.IsNullOrIndex("Index", _index, validate.Root.LogicalTextures);
             validate.IsNullOrIndex("Index", _index, validate.Root.LogicalTextures);
 
 
             base.OnValidateReferences(validate);
             base.OnValidateReferences(validate);
-        }
-
-        #endregion
-    }
-
-    [System.Diagnostics.DebuggerDisplay("TextureTransform {Offset} {Scale} {Rotation} {TextureCoordinate}")]
-    public sealed partial class TextureTransform
-    {
-        #region lifecycle
-
-        #pragma warning disable CA1801 // Review unused parameters
-        internal TextureTransform(TextureInfo parent) { }
-        #pragma warning restore CA1801 // Review unused parameters
-
-        #endregion
-
-        #region properties
-
-        public Vector2 Offset
-        {
-            get => _offset.AsValue(_offsetDefault);
-            set => _offset = value.AsNullable(_offsetDefault);
-        }
-
-        public Vector2 Scale
-        {
-            get => _scale.AsValue(_scaleDefault);
-            set => _scale = value.AsNullable(_scaleDefault);
-        }
-
-        public float Rotation
-        {
-            get => (float)_rotation.AsValue(_rotationDefault);
-            set => _rotation = ((double)value).AsNullable(_rotationDefault);
-        }
-
-        /// <summary>
-        /// Gets or sets a value that overrides <see cref="TextureInfo.TextureCoordinate"/> if supplied, and if this extension is supported.
-        /// </summary>
-        public int? TextureCoordinateOverride
-        {
-            get => _texCoord;
-            set => _texCoord = value;
-        }
-
-        internal bool IsDefault
-        {
-            get
-            {
-                if (_texCoord.HasValue) return false;
-                if (_offset.HasValue) return false;
-                if (_scale.HasValue) return false;
-                if (_rotation.HasValue) return false;
-                return true;
-            }
-        }
-
-        public Matrix3x2 Matrix
-        {
-            get
-            {
-                var s = Matrix3x2.CreateScale(Scale);
-                var r = Matrix3x2.CreateRotation(-Rotation);
-                var t = Matrix3x2.CreateTranslation(Offset);
-
-                return s * r * t;
-            }
-        }
-
-        #endregion
-
-        #region API
-
-        internal void CopyTo(TextureTransform other)
-        {
-            if (other == null) throw new ArgumentNullException(nameof(other));
-            other.TextureCoordinateOverride = this.TextureCoordinateOverride;
-            other.Rotation = this.Rotation;
-            other.Offset = this.Offset;
-            other.Scale = this.Scale;            
-        }
+        }        
 
 
         #endregion
         #endregion
     }
     }
@@ -186,12 +93,7 @@ namespace SharpGLTF.Schema2
     {
     {
         #region lifecycle
         #region lifecycle
 
 
-        public MaterialNormalTextureInfo() { }
-
-        public MaterialNormalTextureInfo(MaterialNormalTextureInfo other) :base(other)
-        {
-            _scale = other._scale;
-        }
+        public MaterialNormalTextureInfo() { }        
 
 
         #endregion
         #endregion
 
 
@@ -213,12 +115,7 @@ namespace SharpGLTF.Schema2
     {
     {
         #region lifecycle
         #region lifecycle
 
 
-        public MaterialOcclusionTextureInfo() { }
-
-        public MaterialOcclusionTextureInfo(MaterialOcclusionTextureInfo other) : base(other)
-        {
-            _strength = other._strength;
-        }
+        public MaterialOcclusionTextureInfo() { }        
 
 
         #endregion
         #endregion
 
 
@@ -233,5 +130,5 @@ namespace SharpGLTF.Schema2
         }
         }
 
 
         #endregion
         #endregion
-    }
+    }    
 }
 }

+ 137 - 0
src/SharpGLTF.Core/Schema2/gltf.TextureTransform.cs

@@ -0,0 +1,137 @@
+using System;
+using System.Linq;
+using System.Numerics;
+
+namespace SharpGLTF.Schema2
+{
+    /// <summary>
+    /// Represents a 2D transform applied to the UV coordinates of a material.
+    /// </summary>
+    /// <remarks>
+    /// Child of <see cref="TextureInfo.Transform"/>
+    /// </remarks>
+    [System.Diagnostics.DebuggerDisplay("TextureTransform {Offset} {Scale} {Rotation} {TextureCoordinate}")]
+    public sealed partial class TextureTransform
+    {
+        #region lifecycle
+
+        #pragma warning disable CA1801 // Review unused parameters
+        internal TextureTransform(TextureInfo parent) { _Parent = parent; }
+        #pragma warning restore CA1801 // Review unused parameters
+
+        #endregion
+
+        #region properties
+
+        internal TextureInfo _Parent;
+
+        public Vector2 Offset
+        {
+            get => _offset.AsValue(_offsetDefault);
+            set => _offset = value.AsNullable(_offsetDefault);
+        }
+
+        public Vector2 Scale
+        {
+            get => _scale.AsValue(_scaleDefault);
+            set => _scale = value.AsNullable(_scaleDefault);
+        }
+
+        public float Rotation
+        {
+            get => (float)_rotation.AsValue(_rotationDefault);
+            set => _rotation = ((double)value).AsNullable(_rotationDefault);
+        }
+
+        /// <summary>
+        /// Gets or sets a value that overrides <see cref="TextureInfo.TextureCoordinate"/> if supplied, and if this extension is supported.
+        /// </summary>
+        public int? TextureCoordinateOverride
+        {
+            get => _texCoord;
+            set => _texCoord = value;
+        }
+
+        internal bool IsDefault
+        {
+            get
+            {
+                if (_texCoord.HasValue) return false;
+                if (_offset.HasValue) return false;
+                if (_scale.HasValue) return false;
+                if (_rotation.HasValue) return false;
+                return true;
+            }
+        }
+
+        public Matrix3x2 Matrix
+        {
+            get
+            {
+                var s = Matrix3x2.CreateScale(Scale);
+                var r = Matrix3x2.CreateRotation(-Rotation);
+                var t = Matrix3x2.CreateTranslation(Offset);
+
+                return s * r * t;
+            }
+        }
+
+        #endregion
+
+        #region API
+
+        private string _GetAnimationPointer(string propertyName)
+        {
+            switch (propertyName)
+            {
+                case "offset": break;
+                case "rotation": break;
+                case "scale": break;
+                default: throw new ArgumentException("invalid property", nameof(propertyName));
+            }
+
+            var pointerPath = _Parent._GetAnimationPointer() + $"/extensions/KHR_texture_transform/{propertyName}";
+
+            return pointerPath;
+        }
+
+        public Matrix3x2 GetMatrix(Animation track, float time)
+        {
+            if (track == null) return this.Matrix;
+
+            var scale = track
+                .FindChannels(_GetAnimationPointer("scale"))
+                .FirstOrDefault()?.GetSamplerOrNull<Vector2>()?.CreateCurveSampler()?.GetPoint(time)
+                ?? this.Scale;
+
+            var rotation = track
+                .FindChannels(_GetAnimationPointer("rotation"))
+                .FirstOrDefault()?.GetSamplerOrNull<Single>()?.CreateCurveSampler()?.GetPoint(time)
+                ?? this.Rotation;
+
+            var offset = track
+                .FindChannels(_GetAnimationPointer("offset"))
+                .FirstOrDefault()?.GetSamplerOrNull<Vector2>()?.CreateCurveSampler()?.GetPoint(time)
+                ?? this.Offset;
+
+            var s = Matrix3x2.CreateScale(scale);
+            var r = Matrix3x2.CreateRotation(-rotation);
+            var t = Matrix3x2.CreateTranslation(offset);
+
+            return s * r * t;
+        }
+
+        internal void CopyTo(TextureTransform other)
+        {
+            if (other == null) throw new ArgumentNullException(nameof(other));
+            other.TextureCoordinateOverride = this.TextureCoordinateOverride;
+            other.Rotation = this.Rotation;
+            other.Offset = this.Offset;
+            other.Scale = this.Scale;
+        }
+
+        #endregion
+    }
+
+
+}

+ 5 - 0
src/SharpGLTF.Runtime/Runtime/ArmatureInstance.cs

@@ -32,6 +32,8 @@ namespace SharpGLTF.Runtime
 
 
             _NodeInstances = ni;
             _NodeInstances = ni;
 
 
+            _MaterialTemplates = template.Materials;
+
             _AnimationTracks = template.Tracks;
             _AnimationTracks = template.Tracks;
         }
         }
 
 
@@ -45,6 +47,9 @@ namespace SharpGLTF.Runtime
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         private readonly IReadOnlyList<NodeInstance> _NodeInstances;
         private readonly IReadOnlyList<NodeInstance> _NodeInstances;
 
 
+        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
+        private readonly IReadOnlyList<MaterialTemplate> _MaterialTemplates;
+
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         private readonly IReadOnlyList<AnimationTrackInfo> _AnimationTracks;
         private readonly IReadOnlyList<AnimationTrackInfo> _AnimationTracks;
 
 

+ 23 - 2
src/SharpGLTF.Runtime/Runtime/ArmatureTemplate.cs

@@ -59,6 +59,18 @@ namespace SharpGLTF.Runtime
                 dstNodes[nidx] = new NodeTemplate(srcNode.Key, pidx, cidx, options);
                 dstNodes[nidx] = new NodeTemplate(srcNode.Key, pidx, cidx, options);
             }
             }
 
 
+            // gather materials
+
+            var dstMaterials = srcNodes
+                .Keys
+                .Select(item => item.Mesh)
+                .Where(item => item != null)
+                .SelectMany(mesh => mesh.Primitives)
+                .Select(prim => prim.Material)
+                .Where(mat => mat != null)
+                .Select(mat => new MaterialTemplate(mat, options))
+                .ToArray();
+
             // gather animation durations.
             // gather animation durations.
 
 
             var dstTracks = srcScene.LogicalParent
             var dstTracks = srcScene.LogicalParent
@@ -66,10 +78,10 @@ namespace SharpGLTF.Runtime
                 .Select(item => new AnimationTrackInfo(item.Name, RuntimeOptions.ConvertExtras(item, options), item.Duration))
                 .Select(item => new AnimationTrackInfo(item.Name, RuntimeOptions.ConvertExtras(item, options), item.Duration))
                 .ToArray();
                 .ToArray();
 
 
-            return new ArmatureTemplate(dstNodes, dstTracks);
+            return new ArmatureTemplate(dstNodes, dstMaterials, dstTracks);
         }
         }
 
 
-        private ArmatureTemplate(NodeTemplate[] nodes, AnimationTrackInfo[] animTracks)
+        private ArmatureTemplate(NodeTemplate[] nodes, MaterialTemplate[] materials, AnimationTrackInfo[] animTracks)
         {
         {
             // check that child nodes always follow parent nodes
             // check that child nodes always follow parent nodes
 
 
@@ -89,6 +101,7 @@ namespace SharpGLTF.Runtime
             }
             }
 
 
             _NodeTemplates = nodes;
             _NodeTemplates = nodes;
+            _MaterialTemplates = materials;
             _AnimationTracks = animTracks;
             _AnimationTracks = animTracks;
         }
         }
 
 
@@ -99,6 +112,9 @@ namespace SharpGLTF.Runtime
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         private readonly NodeTemplate[] _NodeTemplates;
         private readonly NodeTemplate[] _NodeTemplates;
 
 
+        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
+        private readonly MaterialTemplate[] _MaterialTemplates;
+
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         private readonly AnimationTrackInfo[] _AnimationTracks;
         private readonly AnimationTrackInfo[] _AnimationTracks;
 
 
@@ -111,6 +127,11 @@ namespace SharpGLTF.Runtime
         /// </summary>
         /// </summary>
         public IReadOnlyList<NodeTemplate> Nodes => _NodeTemplates;
         public IReadOnlyList<NodeTemplate> Nodes => _NodeTemplates;
 
 
+        /// <summary>
+        /// Gets the list of materials used by the meshes of this model
+        /// </summary>
+        public IReadOnlyList<MaterialTemplate> Materials => _MaterialTemplates;
+
         /// <summary>
         /// <summary>
         /// Gets the animations tracks info.
         /// Gets the animations tracks info.
         /// </summary>
         /// </summary>

+ 10 - 0
src/SharpGLTF.Runtime/Runtime/MaterialInstance.cs

@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SharpGLTF.Runtime
+{
+    class MaterialInstance
+    {
+    }
+}

+ 99 - 0
src/SharpGLTF.Runtime/Runtime/MaterialTemplate.cs

@@ -0,0 +1,99 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Text;
+using System.Xml.Linq;
+
+namespace SharpGLTF.Runtime
+{
+    class MaterialTemplate
+    {
+        #region lifecycle
+
+        internal MaterialTemplate(Schema2.Material srcMaterial, RuntimeOptions options)
+        {
+            _LogicalSourceIndex = srcMaterial.LogicalIndex;            
+
+            Name = srcMaterial.Name;
+            Extras = RuntimeOptions.ConvertExtras(srcMaterial, options);
+
+            var isolateMemory = options?.IsolateMemory ?? false;
+
+            foreach (var srcChannel in srcMaterial.Channels)
+            {
+                var key = srcChannel.Key;
+
+                var txform = srcChannel.TextureTransform;                
+                var scale = txform == null ? null : new AnimatableProperty<Vector2>(txform.Scale);
+                var rotation = txform == null ? null : new AnimatableProperty<float>(txform.Rotation);
+                var offset = txform == null ? null : new AnimatableProperty<Vector2>(txform.Offset);                
+
+                foreach (var srcAnim in srcMaterial.LogicalParent.LogicalAnimations)
+                {
+                    var lidx = srcAnim.LogicalIndex;
+
+                    // paths look like:
+                    // "/materials/14/pbrMetallicRoughness/metallicRoughnessTexture/extensions/KHR_texture_transform/scale"
+
+                    var pointerPath = srcChannel.GetAnimationPointer();
+
+                    foreach (var c in srcAnim.FindChannels(pointerPath))
+                    {
+                        System.Diagnostics.Trace.WriteLine(c.TargetPointerPath);
+
+                        if (c.TargetPointerPath.EndsWith("/extensions/KHR_texture_transform/scale"))
+                        {
+                            if (scale == null) throw new InvalidOperationException("found pointer to a TextureTransform, but target was not found");
+                            var sampler = c.GetSamplerOrNull<Vector2>().CreateCurveSampler(isolateMemory);
+                            scale.SetCurve(lidx, sampler);
+                        }
+
+                        if (c.TargetPointerPath.EndsWith("/extensions/KHR_texture_transform/rotation"))
+                        {
+                            if (rotation == null) throw new InvalidOperationException("found pointer to a TextureTransform, but target was not found");
+                            var sampler = c.GetSamplerOrNull<float>().CreateCurveSampler(isolateMemory);
+                            rotation.SetCurve(lidx, sampler);
+                        }
+
+                        if (c.TargetPointerPath.EndsWith("/extensions/KHR_texture_transform/offset"))
+                        {
+                            if (offset == null) throw new InvalidOperationException("found pointer to a TextureTransform, but target was not found");
+                            var sampler = c.GetSamplerOrNull<Vector2>().CreateCurveSampler(isolateMemory);
+                            offset.SetCurve(lidx, sampler);
+                        }
+                    }
+                }
+            }
+        }
+
+        #endregion
+
+        #region data
+        
+        private readonly int _LogicalSourceIndex;       
+
+        #endregion
+
+        #region properties
+
+        public string Name { get; set; }
+
+        public Object Extras { get; set; }
+
+        /// <summary>
+        /// Gets the index of the source <see cref="Schema2.Material"/> in <see cref="Schema2.ModelRoot.LogicalMaterials"/>
+        /// </summary>
+        public int LogicalNodeIndex => _LogicalSourceIndex;
+
+        #endregion
+
+        #region API
+
+        public Matrix3x2 GetTextureTransform(int trackLogicalIndex, float time)
+        {
+            throw new NotImplementedException();
+        }
+
+        #endregion
+    }
+}

+ 1 - 1
src/SharpGLTF.Toolkit/IO/WavefrontWriter.cs

@@ -286,7 +286,7 @@ namespace SharpGLTF.IO
             var triangles = Toolkit.EvaluateTriangles<VGEOMETRY, VMATERIAL>(model.DefaultScene, options, animation, time);
             var triangles = Toolkit.EvaluateTriangles<VGEOMETRY, VMATERIAL>(model.DefaultScene, options, animation, time);
 
 
             // bake the material transforms into the UV coordinates
             // bake the material transforms into the UV coordinates
-            triangles = EvaluatedTriangle<VGEOMETRY, VMATERIAL, VEMPTY>.TransformTextureCoordsByMaterial(triangles);
+            triangles = EvaluatedTriangle<VGEOMETRY, VMATERIAL, VEMPTY>.TransformTextureCoordsByMaterial(triangles, animation, time);
 
 
             foreach (var triangle in triangles)
             foreach (var triangle in triangles)
             {
             {

+ 13 - 10
src/SharpGLTF.Toolkit/Schema2/EvaluatedTriangle.cs

@@ -121,27 +121,30 @@ namespace SharpGLTF.Schema2
 
 
         #region API
         #region API
 
 
-        public static IEnumerable<EvaluatedTriangle<TvG, TvM, TvS>> TransformTextureCoordsByMaterial(IEnumerable<EvaluatedTriangle<TvG, TvM, TvS>> triangles)
+        /// <summary>
+        /// Materials may include a <see cref="TextureTransform"/> which affects the texture coordinates.
+        /// </summary>
+        /// <param name="triangles">the triangles to be updated.</param>
+        /// <returns>A new collection fo triangles with transformed uv coordinates (if required)</returns>
+        public static IEnumerable<EvaluatedTriangle<TvG, TvM, TvS>> TransformTextureCoordsByMaterial(IEnumerable<EvaluatedTriangle<TvG, TvM, TvS>> triangles, Animation track = null, float time = -1)
         {
         {
             // cache transform for speed up
             // cache transform for speed up
-            var xformDict = new Dictionary<Material, Matrix3x2>();
+            var diffuseTextureXformDict = new Dictionary<Material, Matrix3x2>();
 
 
             EvaluatedTriangle<TvG, TvM, TvS> _getTransformedTriangle(EvaluatedTriangle<TvG, TvM, TvS> triangle)
             EvaluatedTriangle<TvG, TvM, TvS> _getTransformedTriangle(EvaluatedTriangle<TvG, TvM, TvS> triangle)
             {
             {
                 if (triangle.Material == null) return triangle;
                 if (triangle.Material == null) return triangle;
 
 
-                if (!xformDict.TryGetValue(triangle.Material, out var xform))
+                if (!diffuseTextureXformDict.TryGetValue(triangle.Material, out var textureXform))
                 {
                 {
-                    xform = triangle.Material.GetDiffuseTextureTransform()
-                        ?.Matrix
-                        ?? Matrix3x2.Identity;
+                    textureXform = triangle.Material.GetDiffuseTextureMatrix(track, time) ?? Matrix3x2.Identity;
 
 
-                    xformDict[triangle.Material] = xform;
+                    diffuseTextureXformDict[triangle.Material] = textureXform;
                 }
                 }
 
 
-                if (xform.IsIdentity) return triangle;
+                if (textureXform.IsIdentity) return triangle;
 
 
-                return triangle._TransformTextureBy(xform);
+                return triangle._TransformTextureBy(textureXform);
             }
             }
 
 
             return triangles.Select(tri => _getTransformedTriangle(tri));
             return triangles.Select(tri => _getTransformedTriangle(tri));
@@ -151,7 +154,7 @@ namespace SharpGLTF.Schema2
         {
         {
             var a = this.A;
             var a = this.A;
             var b = this.B;
             var b = this.B;
-            var c = this.C;
+            var c = this.C;            
 
 
             a.Material.SetTexCoord(0, Vector2.Transform(a.Material.GetTexCoord(0), xform));
             a.Material.SetTexCoord(0, Vector2.Transform(a.Material.GetTexCoord(0), xform));
             b.Material.SetTexCoord(0, Vector2.Transform(b.Material.GetTexCoord(0), xform));
             b.Material.SetTexCoord(0, Vector2.Transform(b.Material.GetTexCoord(0), xform));

+ 9 - 0
src/SharpGLTF.Toolkit/Schema2/MaterialExtensions.cs

@@ -577,6 +577,15 @@ namespace SharpGLTF.Schema2
             return null;
             return null;
         }
         }
 
 
+        public static Matrix3x2? GetDiffuseTextureMatrix(this Material material, Animation track, float time)
+        {
+            var xform = material.GetDiffuseTextureTransform();
+
+            return xform?.Matrix;
+
+            // return xform.GetMatrix(track, time);
+        }
+
         #endregion
         #endregion
     }
     }
 }
 }

+ 4 - 3
tests/SharpGLTF.Core.Tests/Schema2/LoadAndSave/LoadSampleTests.cs

@@ -274,12 +274,13 @@ namespace SharpGLTF.Schema2.LoadAndSave
             model.AttachToCurrentTest(System.IO.Path.ChangeExtension(System.IO.Path.GetFileName(path), ".glb"));
             model.AttachToCurrentTest(System.IO.Path.ChangeExtension(System.IO.Path.GetFileName(path), ".glb"));
         }
         }
 
 
+        [TestCase("AnimationPointerUVs.glb")]
+        [TestCase("RiggedFigure.glb")]
         [TestCase("RiggedFigure.glb")]
         [TestCase("RiggedFigure.glb")]
         [TestCase("RiggedSimple.glb")]
         [TestCase("RiggedSimple.glb")]
         [TestCase("BoxAnimated.glb")]
         [TestCase("BoxAnimated.glb")]
         [TestCase("AnimatedMorphCube.glb")]        
         [TestCase("AnimatedMorphCube.glb")]        
-        [TestCase("CesiumMan.glb")]
-        //[TestCase("Monster.glb")] // temporarily removed from khronos repo
+        [TestCase("CesiumMan.glb")]        
         [TestCase("BrainStem.glb")]
         [TestCase("BrainStem.glb")]
         [TestCase("Fox.glb")]
         [TestCase("Fox.glb")]
         public void LoadModelsWithAnimations(string path)
         public void LoadModelsWithAnimations(string path)
@@ -307,7 +308,7 @@ namespace SharpGLTF.Schema2.LoadAndSave
                 var t = duration * i / 10;
                 var t = duration * i / 10;
                 int tt = (int)(t * 1000.0f);
                 int tt = (int)(t * 1000.0f);
 
 
-                model.AttachToCurrentTest($"{path} at {tt}.obj",anim, t);
+                model.AttachToCurrentTest($"{path} at {tt}.obj", anim, t);
             }            
             }            
         }
         }
 
 

+ 1 - 0
tests/SharpGLTF.Core.Tests/Validation/InvalidFilesTests.cs

@@ -70,6 +70,7 @@ namespace SharpGLTF.Validation
             {
             {
                 if (f.EndsWith("invalid_image_data.gltf")) continue; // we're not checking images data (yet)
                 if (f.EndsWith("invalid_image_data.gltf")) continue; // we're not checking images data (yet)
                 if (f.EndsWith("png_eos.gltf")) continue; // we're not checking images data (yet)
                 if (f.EndsWith("png_eos.gltf")) continue; // we're not checking images data (yet)
+                if (f.EndsWith("custom_property.gltf")) continue; // Accessor[0] _bufferView: must be defined.
 
 
                 // https://github.com/KhronosGroup/glTF-Validator/issues/189
                 // https://github.com/KhronosGroup/glTF-Validator/issues/189
                 if (f.EndsWith("node_weights_override.gltf")) continue;
                 if (f.EndsWith("node_weights_override.gltf")) continue;

+ 12 - 0
tests/SharpGLTF.Runtime.Tests/Runtime/SceneTemplateTests.cs

@@ -124,5 +124,17 @@ namespace SharpGLTF.Runtime
             }
             }
         }
         }
 
 
+        [Test]
+        public static void TestAnimationPointer()
+        {
+            var modelPath = TestFiles.GetSampleModelsPaths()
+                                .FirstOrDefault(item => item.Contains("AnimationPointerUVs.glb"));
+
+            var model = Schema2.ModelRoot.Load(modelPath);            
+
+            var sceneTemplate = SceneTemplate.Create(model.DefaultScene);
+            var sceneInstance = sceneTemplate.CreateInstance();
+            sceneInstance.Armature.SetAnimationFrame(0, 0.1f);            
+        }
     }
     }
 }
 }