Talk:Internal Animation Format

From Second Life Wiki
Revision as of 01:19, 12 October 2011 by Coaldust Numbers (talk | contribs)
Jump to navigation Jump to search

Funny this page showed up, I was just thinking about this stuff the other night. This documentation is only good for version 2 and not version 1. -- Strife Onizuka 23:37, 10 December 2007 (PST)

Joint Scale Keys

I noticed that the format is missing Joint Scale Keys, which the client seems to fully support internally.

What are the thoughts of upgrading the animation asset format to support Scale keys? Should I prematurely add support for Xscale Yscale and Zscale to my upgraded BVH parser and add Scale keys ("num_scale_keys")? Although, even if I did that, I am not sure what the final quantizations are to be for serialization. I probably should best attempt to contact Nyx Linden directly (He was the one who worked on this format, right?) --Nexii Malthus 00:40, 30 January 2010 (UTC)

You are talking about scaling the joints during the animation. I'm pretty sure you can do this without modding the client. I just forget how to do it. -- Strife (talk|contribs) 03:28, 30 January 2010 (UTC)
You sure about that? I checked the sourcecode paths myself and the LLKeyFrameMotion class during deserialization does not take into account any scale keys. Neither does it do on serialization. I am using 1.23 as a base though, so this might be different in the latest snowglobe clients, but I think all the lindens had been too busy with work Viewer 2.0, bug fixing or other assorted projects.
Unless you mean way the avatar mesh is artificially stretched with position values, that isn't true scaling, that is only an artifact/side effect. --Nexii Malthus 12:35, 30 January 2010 (UTC)

Joint Rotation Keys

I've spotted an inaccuracy in this article.

The x, y, and z values of the joint rotation keys are the x, y, and z values of a normalized quaternion, not degrees or anything to do with Euler angles. The w value of the quaternion is reconstructed when the animation is read, and being normalized makes this possible. The x, y, and z values range from -1 to 1 instead of -180 to 180. I don't think the non-linear comment is helpful either.

Coaldust Numbers 20:17, 8 October 2011 (PDT)

There were two animation versions: 0.1 and 1.0.
  • 0.1 uses a quaternion with a w value that needs to be regenerated as you describe.
  • 1.0 uses 3 floats representing a Euler.
Here is a subset of my notes from when I reverse engineered the client cache (with the aid of client strings) before LL open sourced the client.
They are in a shorthand that is relatively straight forward. I don't think I ever checked and corrected them against the client source.
   {<type = 0x4100> INVENTORY_ANIMATION
        <data> = <version><priority><length><emote><loop_in_point><loop_out_point>
                    <subversion?><ease_in_time><ease_out_time><hand_pose><NoJ>(<Joint>*<NoJ>)<NoC>(<Constraint>*<NoC>)
        <version>               =   word
        <subversion>            =   word
        {<version> == 0x0001 && <subversion> == 0x0000
            <priority>          =   0x40000000
            <length>            =   float, length in seconds
            {<emote>            =   null terminated string, if empty just a null
                Aaaaah
                Afraid
                Angry
                Big Smile
                Bored
                Cry
                Disdain
                Embarrassed
                Frown
                Kiss
                Laugh
                Plllppt
                Repulsed
                Sad
                Shrug
                Smile
                Surprise
                Wink
                Worry
                Express_Anger_Emote
            }
            <loop_in_point>     =   float, loop start
            <loop_out_point>    =   float, loop end
            <loop>              =   0x10000000
            <ease_in_time>      =   float, lead in
            <ease_out_time>     =   float, lead out
            {<hand_pose>        =   unsigned long, hand pose
                <Hand = 0x00000000> Spread
                <Hand = 0x01000000> Relaxed
                <Hand = 0x02000000> Point Both
                <Hand = 0x03000000> Fist
                <Hand = 0x04000000> Relaxed Left
                <Hand = 0x05000000> Point Left
                <Hand = 0x06000000> Fist Left
                <Hand = 0x07000000> Relaxed Right
                <Hand = 0x08000000> Point Right
                <Hand = 0x09000000> Fist Right
                <Hand = 0x0A000000> Salute Right
                <Hand = 0x0B000000> Typing
                <Hand = 0x0C000000> Peace Right
            }
            <NoJ>               =   unsigned long, Number of Joints
            {<Joint>            =   <name><JPriority><NoP R>(<R>*<NoR>)<NoP>(<P>*<NoP>)
                <name>          =   null terminated string
                <JPriority>     =   unsigned long, Joint Priority (same as Priority unless you use voodoo)
                <NoR>           =   unsigned long, Number Of Paramaters in section A, Angles
                <NoP>           =   unsigned long, Number Of Paramaters in section B, Positions
                {<R>            =   <FN><Y><X><Z>
                    <FN>        =   short, Frame Number
                    <X>         =   unsigned short
                    <Y>         =   unsigned short
                    <Z>         =   unsigned short
                    {How to read:
                        rot.x = ((double)d.x - 32767.0)/32767.0;
                        rot.y = ((double)d.y - 32767.0)/32767.0;
                        rot.z = ((double)d.z - 32767.0)/32767.0;
                        rot.w = 0.0;
                        double tw = 1.0 - rot.norm();
                        if(tw > 0.0) rot.w = sqrt(tw);
                    }
                }
                {<P>            =   <FN><Y><X><Z>
                    <FN>        =   short, Frame Number
                    <X>         =   unsigned short
                    <Y>         =   unsigned short
                    <Z>         =   unsigned short
                    {How to read:
                        (((double)a - 32767.0) * 0.3125)
                    }
                }
            }
            <NoC>               =   signed long
            {<Constraint>       =   <chain_length><constraint_type><source_volume><source_offset><target_volume>
                                    <target_offset><target_dir><ease_in_start><ease_in_stop><ease_out_start><ease_out_stop>
                <chain_length>      =   unsigned byte
                <constraint_type>   =   unsigned byte
                <source_volume>     =   unsigned byte[16]
                {<source_offset>    =   LLVector3 = <x><y><z>
                    <x>             =   float
                    <y>             =   float
                    <z>             =   float
                }
                {<target_volume>    =   unsigned byte[16], null terminated string
                    "GROUND"
                    "PELVIS"
                    "BELLY"
                    "CHEST"
                    "NECK"
                    "HEAD"
                    "L_CLAVICLE"
                    "L_UPPER_ARM"
                    "L_LOWER_ARM"
                    "L_HAND"
                    "R_CLAVICLE"
                    "R_UPPER_ARM"
                    "R_LOWER_ARM"
                    "R_HAND"
                    "R_UPPER_LEG"
                    "R_LOWER_LEG"
                    "R_FOOT"
                    "L_UPPER_LEG"
                    "L_LOWER_LEG"
                    "L_FOOT"
                }
                {<target_offset>    =   LLVector3 = <x><y><z>
                    <x>             =   float
                    <y>             =   float
                    <z>             =   float
                }
                {<target_dir>       =   LLVector3 = <x><y><z>
                    <x>             =   float
                    <y>             =   float
                    <z>             =   float
                }
                <ease_in_start>     =   float
                <ease_in_stop>      =   float
                <ease_out_start>    =   float
                <ease_out_stop>     =   float
            }
        }
        {<version> == 0x0000 && <subversion> == 0x0001
            <priority>          =   0x40000000
            <length>            =   float, length in seconds
            {<emote>            =   null terminated string, if empty just a null
                Aaaaah
                Afraid
                Angry
                Big Smile
                Bored
                Cry
                Disdain
                Embarrassed
                Frown
                Kiss
                Laugh
                Plllppt
                Repulsed
                Sad
                Shrug
                Smile
                Surprise
                Wink
                Worry
                Express_Anger_Emote
            }
            <loop_in_point>     =   float, loop start
            <loop_out_point>    =   float, loop end
            <loop>              =   0x10000000
            <ease_in_time>      =   float, lead in
            <ease_out_time>     =   float, lead out
            {<hand_pose>        =   unsigned long, hand pose
                <Hand = 0x00000000> Spread
                <Hand = 0x01000000> Relaxed
                <Hand = 0x02000000> Point Both
                <Hand = 0x03000000> Fist
                <Hand = 0x04000000> Relaxed Left
                <Hand = 0x05000000> Point Left
                <Hand = 0x06000000> Fist Left
                <Hand = 0x07000000> Relaxed Right
                <Hand = 0x08000000> Point Right
                <Hand = 0x09000000> Fist Right
                <Hand = 0x0A000000> Salute Right
                <Hand = 0x0B000000> Typing
                <Hand = 0x0C000000> Peace Right
            }
            <NoJ>               =   unsigned long, Number of Joints
            {<Joint>            =   <name><JPriority><NoP R>(<R>*<NoR>)<NoP>(<P>*<NoP>)
                <name>          =   null terminated string
                <JPriority>     =   unsigned long, Joint Priority (same as Priority unless you use voodoo)
                <NoR>           =   unsigned long, Number Of Paramaters in section A, Angles
                <NoP>           =   unsigned long, Number Of Paramaters in section B, Positions
                {<R>            =   <FN><Y><X><Z>
                    <FN>        =   float, frame time
                    <X>         =   float
                    <Y>         =   float
                    <Z>         =   float
                }
                {<P>            =   <FN><Y><X><Z>
                    <FN>        =   float, frame time
                    <X>         =   float
                    <Y>         =   float
                    <Z>         =   float
                }
            }
            <NoC>               =   signed long, number of constraints, no more then 10
            {<Constraint>       =   <chain_length><constraint_type><source_volume><source_offset><target_volume>
                                    <target_offset><target_dir><ease_in_start><ease_in_stop><ease_out_start><ease_out_stop>
                <chain_length>      =   unsigned byte
                <constraint_type>   =   unsigned byte
                <source_volume>     =   unsigned byte[16]
                {<source_offset>    =   LLVector3 = <x><y><z>
                    <x>             =   float
                    <y>             =   float
                    <z>             =   float
                }
                {<target_volume>    =   unsigned byte[16], null terminated string
                    "GROUND"
                    "PELVIS"
                    "BELLY"
                    "CHEST"
                    "NECK"
                    "HEAD"
                    "L_CLAVICLE"
                    "L_UPPER_ARM"
                    "L_LOWER_ARM"
                    "L_HAND"
                    "R_CLAVICLE"
                    "R_UPPER_ARM"
                    "R_LOWER_ARM"
                    "R_HAND"
                    "R_UPPER_LEG"
                    "R_LOWER_LEG"
                    "R_FOOT"
                    "L_UPPER_LEG"
                    "L_LOWER_LEG"
                    "L_FOOT"
                }
                {<target_offset>    =   LLVector3 = <x><y><z>
                    <x>             =   float
                    <y>             =   float
                    <z>             =   float
                }
                {<target_dir>       =   LLVector3 = <x><y><z>
                    <x>             =   float
                    <y>             =   float
                    <z>             =   float
                }
                <ease_in_start>     =   float
                <ease_in_stop>      =   float
                <ease_out_start>    =   float
                <ease_out_stop>     =   float
            }
        }
    }
I also at one point wrote an animation decoder that would (in two steps) give you an bvh again but heck if I know what I did with it.
-- Strife (talk|contribs) 10:57, 11 October 2011 (PDT)
I am currently in the process of writing a tool to work with .anim files (dumps of the internal format). I ran into strange behavior when trying to edit it as if it was a Euler angle. I found this in the source code of indra\llcharacter\llkeyframemotion.cpp in the latest viewer:
			RotationKey rot_key;
			rot_key.mTime = time;
			LLVector3 rot_angles;
			U16 x, y, z;

			BOOL success = TRUE;

			if (old_version)
			{
				success = dp.unpackVector3(rot_angles, "rot_angles") && rot_angles.isFinite();

				LLQuaternion::Order ro = StringToOrder("ZYX");
				rot_key.mRotation = mayaQ(rot_angles.mV[VX], rot_angles.mV[VY], rot_angles.mV[VZ], ro);
			}
			else
			{
				success &= dp.unpackU16(x, "rot_angle_x");
				success &= dp.unpackU16(y, "rot_angle_y");
				success &= dp.unpackU16(z, "rot_angle_z");

				LLVector3 rot_vec;
				rot_vec.mV[VX] = U16_to_F32(x, -1.f, 1.f);
				rot_vec.mV[VY] = U16_to_F32(y, -1.f, 1.f);
				rot_vec.mV[VZ] = U16_to_F32(z, -1.f, 1.f);
				rot_key.mRotation.unpackFromVector3(rot_vec);
			}
It does at least seem to have a range of -1 to 1 as I said, and I know editing the angles in AnimMaker as if they were Euler angles doesn't get you the results you would expect. I'd like to hear the explanation.
I've also determined, and I'm quite positive of this, as I ran into a bug in my program due to believing the documentation on this page, that the vectors for the constraint source_offset, target_offset, and target_dir are stored as sets of 3 F32. I'm not certain of the order yet. To be specific, when I believed these to be strings it caused the alignment for everything after the first source_offset to be wrong. A vector definitely takes 12 bytes. If you're wondering how I'm so sure about the proper formatting of a constraint, it's because I'm looking at the official "ground sit constrained" animation.

Coaldust Numbers 01:11, 12 October 2011 (PDT)