Bunkerbewohner / ColladaXna

Collada Importer for XNA (Work in Progress)
https://github.com/Bunkerbewohner/ColladaXna/wiki
MIT License
15 stars 3 forks source link

AnimationImporter.cs #16

Open Niittis opened 12 years ago

Niittis commented 12 years ago

Hi

In the Titles file function: static bool DoesAnimationAffectJoints(Dictionary<string, Joint> joints, XmlNode xmlAnimation)

Lines: XmlNode xmlChannel = xmlAnimation.SelectSingleNode("//channel"); ----- 8<---------------- string target = xmlChannel.Attributes["target"].Value;

Seems always to ALWAYS inspect the first animations channels target, instead of the parameters one..

It started to work more nicely with following line: XmlNode xmlChannel = xmlAnimation.SelectSingleNode("animation/channel");

Does this make any sense... ?

I found this when Animators Max spitted out some animated joints without any frames.

Best, Niittis

ps. Thanks for nice piece of Code.

Niittis commented 12 years ago

Hi just realized that its the Max who adds this extra Animation tag layer per bone... and therefore it was not working right...

Bunkerbewohner commented 12 years ago

Hello Niittis!

Thanks for reporting this issue. But I'm a bit confused: Shouldn't xmlAnimation.SelectSingleNode("//channel"); yield the same result as xmlAnimation.SelectSingleNode("animation/channel");? At least assuming that the XML structure is as expected (<animation><channel>...</channel></animation>). Or does your last comment mean that your file was just erroneous?

The typical animation I was expecting looks like the following example:

<animation>
    <source id="input">
        <float_array id="input-array" count="61">
            0 0.03333333 0.06666667 0.1 [...] 1.9 1.933333 1.966667 2
        </float_array>
        <technique_common>
            <accessor source="#input-array" count="61" stride="1">
                <param name="TIME" type="float"/>
            </accessor>
        </technique_common>
    </source>
    <source id="output">
        <float_array id="output-array" count="976">
            0 0 -1 0 0.9999995 -0.001040518 0 -0.001343131 ... 32.87532 0 0 0 1
        </float_array>
        <technique_common>
            <accessor source="#output-array" count="61" stride="16">
                <param name="TRANSFORM" type="float4x4"/>
            </accessor>
        </technique_common>
    </source>
    <source id="interpolation">
        <Name_array id="interpolation-array" count="61">LINEAR ...</Name_array>
        <technique_common>
            <accessor source="#interpolation-array" count="61" stride="1">
                <param name="INTERPOLATION" type="name"/>
            </accessor>
        </technique_common>
    </source>
    <sampler id="sampler">
        <input semantic="INPUT" source="#input"/>
        <input semantic="OUTPUT" source="#output"/>
        <input semantic="INTERPOLATION" source="#interpolation"/>
    </sampler>
    <channel source="#sampler" target="somenode/matrix"/>
</animation>

If your COLLADA files differ greatly I would like to see the relevant code fragment, if you don't mind. In any case I'd gladly pull changes you made to the code that helped making it work into this repository. :)

Niittis commented 12 years ago

The problem lies on the way 3DMax exports the animations.

<library_animations>
    *<animation id="Bip01_R_Forearm-anim" name="Bip01_R_Forearm">
      <animation>
        <source id="Bip01_R_Forearm-Matrix-animation-input">
        <source id="Bip01_R_Forearm-Matrix-animation-output-transform">
        <source id="Bip01_R_Forearm-Interpolations">         
        <sampler id="Bip01_R_Forearm-Matrix-animation-transform">         
        <channel source="#Bip01_R_Forearm-Matrix-animation-transform" target="Bip01_R_Forearm/matrix"/>
    *  </animation>
    *</animation>
    *<animation id="Bip01_L_Forearm-anim" name="Bip01_L_Forearm">
    *  <animation>
        <source id="Bip01_L_Forearm-Matrix-animation-input">
        <source id="Bip01_L_Forearm-Matrix-animation-output-transform">
        <source id="Bip01_L_Forearm-Interpolations">         
        <sampler id="Bip01_L_Forearm-Matrix-animation-transform">         
        <channel source="#Bip01_L_Forearm-Matrix-animation-transform" target="Bip01_L_Forearm/matrix"/>
    *  </animation>
    </animation>

The structure is more nested in 3DMax, however if the lines marked with *""** would be removed the result should be the same.

There was a piece of code in AnimationImporter.cs trying to Merge the animations, but it did not work.

            // Try to merge all animations (must be extended to consider animation clips)
            // NOTE: Didn't work for Maya animations, something's still of with them
            if (animations.Count  > 1 )//&& false)
            {
                try
                {
                    var animation = MergeAnimations(animations);
                    animations.Clear();
                    animations.Add(animation);
                }
                catch (Exception)
                {
                    // didn't work, ignore
                }
            }

I am thinking of trying to identify the case before parsing and then move/feed the inner animation nodes to the first sibling, that should end up into expected syntax and therefore could be handled like the others.

Niittis commented 12 years ago

During the XML reorganizer testing I started to think that the static bool DoesAnimationAffectJoints(Dictionary<string, Joint> joints, XmlNode xmlAnimation) only looks always for the First channel and ignores the animation if that particular channel does not effect the animation.

            XmlNode xmlChannel = xmlAnimation.SelectSingleNode("//channel");
            if (xmlChannel == null)
            {
                throw new Exception("Animation '" + xmlAnimation.Attributes["id"].Value + 
                    "' does not contain a channel:" + xmlAnimation.ChildNodes.Count);
            }

            string target = xmlChannel.Attributes["target"].Value;

            return joints.ContainsKey(ExtractNodeIdFromTarget(target));

It should check if even one of the channels uses existing joints before ignoring the whole animation

Something like this:

            // If any of the channels affects the joints, this is a valid animation.
            XmlNodeList xmlChannels = xmlAnimation.SelectNodes("//channel");
            if (xmlChannels == null)
            {
                throw new Exception("Animation '" + xmlAnimation.Attributes["id"].Value +
                    "' does not contain a channel:" + xmlAnimation.ChildNodes.Count);
            }

            foreach ( XmlNode xmlChannel in xmlChannels)
            {
                string target = xmlChannel.Attributes["target"].Value;

                if (joints.ContainsKey(ExtractNodeIdFromTarget(target)))
                {
                    return true;
                }
            }

            return false;
Bunkerbewohner commented 12 years ago

Yes you are right, I guess I assumed that was always only one channel present in an animation, just as I assumed that animations wouldn't be nested, because my test models met these assumptions. However, obviously and as stated in the specification animation elements can actually be nested and more than one channel (or none at all) can be present. It's both a blessing and a curse that there are so many different ways to represent essentially the same data in COLLADA. My importer is a very non-generic implementation that just presumes the most simple cases.

By the way I always used the option "bake animation" in the FBX/DAE exporter of 3ds max, which could possibly help you here, too, because this should merge everything into one simple animation.

As for multiple channels, I don't know if they are also joined by "baking" the animation, but your altered code would fix that particular issue anyway. I just commited the equivalently changed method to github in hopes that it doesn't break anything (my test cases still worked, but who knows).