vpenades / SharpGLTF

glTF reader and writer for .NET Standard
MIT License
467 stars 75 forks source link

Any way to access the animation channels array without knowing the nodes? #81

Closed jhm-ciberman closed 3 years ago

jhm-ciberman commented 3 years ago

I want to load an animation, but when I access the SharpGLTF.Schema2.Animation object, the only way to access the channels is knowing the target nodes. So for example supose the following case where I have a lot of nodes but only the node 0 is being animated, and all the other nodes are not affected by animation:

"nodes": [ ... LOT of nodes], 

"animations": [
    {
      "samplers" : [
        {
          "input" : 2,
          "interpolation" : "LINEAR",
          "output" : 3
        }
      ],
      "channels" : [ {
        "sampler" : 0,
        "target" : {
          "node" : 0,  
          "path" : "rotation"
        }
      } ]
    }
  ],

To access the first animation channel I should iterate over the whole list of nodes and try to find all the possible combinations or target paths (rotation, scale or translation) for every node in the scene. For example:

foreach (var animation in gltf.LogicalAnimations) 
{
    var customAnimation = new MyCustomAnimationControllerForMyGameCharacter(); 
    foreach (var node in gltf.LogicalNodes) 
    {  
        var rot = animation.FindRotationSampler(node);
        if (rot != null) 
        {
           customAnimation.AddSampler(rot);
        }
        var trans= animation.FindTranslationSampler(node);
        if (trans!= null) 
        {
           customAnimation.AddSampler(trans);
        }
        var scale = animation.FindScaleSampler(node);
        if (scale != null) 
        {
           customAnimation.AddSampler(scale);
        }
    }
}

As you can see in a small hierarchy this is no problem, but with a lot of nodes this becomes inefficient and also looks like a hack and not a real solution. It would be super helpful if the _channels and _samplers private member variables could be made public. In this wat the code could be more elegant.

Edit: I just saw that there is a Node.IsTransformAnimated property, but looking at the implementation It looks that is exactly the same:

        /// <summary>
        /// Gets a value indicating whether this transform is affected by any animation.
        /// </summary>
        public bool IsTransformAnimated
        {
            get
            {
                var root = this.LogicalParent;

                if (root.LogicalAnimations.Count == 0) return false;

                // check if it's affected by animations.
                if (root.LogicalAnimations.Any(anim => anim.FindScaleSampler(this) != null)) return true;
                if (root.LogicalAnimations.Any(anim => anim.FindRotationSampler(this) != null)) return true;
                if (root.LogicalAnimations.Any(anim => anim.FindTranslationSampler(this) != null)) return true;

                return false;
            }
        }

EDIT2: Note that my intention is to develop if possible my own custom animation samplers, so my objective is to access the raw time (float[]) and value arrays (Vector3[]/Quaternion[]) and then use those values in my own custom AnimationController. I don't know if the api offers an easy way to access those values.