Animation Blending (continued)

Started by
5 comments, last by haegarr 14 years, 11 months ago
Hello, haegarr, godmodder and others: I was just working on improving my C++ knowledge to understand every code you wrote me before in that thread: Animation Blending Now i want to continue that thread. Thanks, Kasya
Advertisement
I thought it would be better to use blending externally. Not inside AnimationTrack.

This is my code i wrote:

Bone:

class Bone : public Resource {public:	Bone(Skeleton * skel, Bone * bone) : creator(skel), parent(bone) { }	~Bone();//didn't wrote the setters and getters	void AddChild(Bone * bone);	Bone * getBone(const String &str);		void createNode(Node * root);private:	typedef std::list<Bone *> BoneList;	BoneList bones;	Skeleton * creator;	Bone * parent;	Vector3 position;	Quat orientation;	};


Skeleton:

class Skeleton : public Resource {private:	Bone * root;public:	Skeleton() { root = new Bone(this, 0); root->SetName("RootBone"); }	~Skeleton() {}	void AddBone(Vector3 pos, Quat rot, Bone * parent, String name);	Bone * getRootBone() const { return root; }	};


Node:

class Node : public SceneNode {public:	Node() {}	~Node() {}	void update(real time);//Didn't wrote the setters and gettersprotected:	Vector3 position;	Quat orientation;};


Instance:

template<class NodeVar> class Instance {public:	Instance() : node(new NodeVar) { }	~Instance() { node->Destroy(); }	NodeVar * getNode() const { return node; }	virtual void Render(real time)=0;protected:	NodeVar * node;};


Skeleton Instance:

class SkeletonInstance : public Instance<Node> {public:	SkeletonInstance(Skeleton * skel);	~SkeletonInstance() { }	Skeleton * getSkeleton() const { return skeleton; }	void Render(real time) { /* Render Code Here */ }private:	Skeleton * skeleton;};


AnimationKeyframe:

class Keyframe {public:	real time;};class TransformKeyframe : public Keyframe {public:	Vector3 position;	Quat rotation;};


AnimationTrack

class AnimationTrack : public Resource {protected:	typedef std::list<Keyframe *> KeyList;	KeyList keys;public:	AnimationTrack() {}	~AnimationTrack();		void addKeyframe(Keyframe * key);protected:	KeyList::iterator findKeyframe(real time);protected:	static bool sortByTime(Keyframe * key1, Keyframe * key2);	friend class NodeAnimationBinding;};


AnimationBinding:

class AnimationBinding {protected:	AnimationTrack * track;public:	AnimationBinding(AnimationTrack * trk) : track(trk) {}	~AnimationBinding() {}	virtual void contribute(real time)=0;};class NodeAnimationBinding : public AnimationBinding {private:	Node * node;	public:	NodeAnimationBinding(Node * n, AnimationTrack * t) : node(n), AnimationBinding(t) {}	~NodeAnimationBinding() {}	void contribute(real time) {}	};


I didn't wrote the Animation and AnimationInstance class. Because i get confused in track and binding class. Thats what i was thinking:

- Tracks will be inside Animations
- Animations will be in my "Resource Library"
- Binding will have pointer to track and to the node (will be numeric value too) which it will be applied.
- Bindings will be inside AnimationInstances
- AnimationInstances will be inside SkeletonInstances
- SkeletonInstance will animate AnimationInstances

Why i get confused:
thats what i wanted to write:
AnimationInstance * Animation::newInstance(SkeletonInstance * skel) {AnimationInstance * instance = new AnimationInstance(this);for(TrackList::iterator i = tracks.begin(); i != tracks.end(); ++i) {Node * node = getTrackForNode((*i), skel); //This will find the which track needs to apply to which node AnimationBinding * binding = new AnimationBinding(node, (*i));instance->addBinding(binding);}return instance;}



What to write inside getTrackForNodeInSkeleton(AnimationTrack * track, SkeletonInstance * skelInstance);? Or maybe there are some other ways to do? (But now with numeric value, because i don't want to use numeric values for now, i'll use them later)

Tell every bad thing i have in this system.

Thanks,
Kasya
Hi Kasya.

The first things that coming to my mind are as follows.

(1) Bones and Resources

I'm not sure how you've defined the Resource class. Normally a Resource object is
(a) an essential part of the game (often called an "asset"),
(b) can be loaded on request at runtime,
(c) are named,
(d) is an entity that is useable in a scene.
Usually that means also that a Resource is not a light-weight implementation. Looking at the enumeration above, I personally would not make Bone be a Resource: E.g. it makes no sense to load a single bone from mass storage, and its cannot be used sensefully by its own in the scene. Furthermore, it is better to implement them light weighted.

(2) Class naming

Just a matter of personal taste, but I would not choose a generalized name for a specialized class. Although you haven't shown us the declaration of the SceneNode class, the inheritance of Node seems me to be done to create a specialized version of SceneNode by adding geometric information to it. I personally prefer in usch cases to choose a name that expresses the specialization, too. E.g. "AnchorSceneNode" if it defines an axis in space, or "AnimatableSceneNode" if it adds animation possibility. Something like that.

Maybe that I have enforced such a naming due to some unheedful explanations in the former thread. My apologize if that is the case.

(3) Instancing a skeleton

The class Instance refers to a single Node object, which itself stores a single axis definition.

As such, a Node can represent the current state of a single bone only. But a skeleton deals with a couple of bones, and hence a SkeletonInstance needs to manage a couple of current states of bones. How is this implemented?


Quote:Original post by Kasya
- Tracks will be inside Animations
- Animations will be in my "Resource Library"
- Binding will have pointer to track and to the node (will be numeric value too) which it will be applied.
- Bindings will be inside AnimationInstances
- AnimationInstances will be inside SkeletonInstances
- SkeletonInstance will animate AnimationInstances

Of course, there are many ways of doing such a complex thing. The following is just an explanation of the way I've chosen. You can choose another if you want.

- Tracks will be inside Animations

Yes.

- Animations will be in my "Resource Library"

Yes: Animations are a kind resource, since they (a) are assets and (b) can be re-used, both sequentially and concurrently.

- Binding will have pointer to track and to the node (will be numeric value too) which it will be applied.
- Bindings will be inside AnimationInstances

That depends on what you mean with "track". Looking in the former thread on page 5 beginning with my post "/4/2008 1:18:03 PM", you can see that I have both AnimationPrototype::Track as well as AnimationInstance::Track classes. And the latter one are actually bindings. As such they refer to its prototype (AnimationPrototype::Track) as the source and the variable (Node in your case) as the target. From the code snippet the post above, I assume your Binding class is my AnimationInstance::Track class. So you're presumbly right.

- AnimationInstances will be inside SkeletonInstances
- SkeletonInstance will animate AnimationInstances

Perhaps. SkeletonInstances are part of the scene graph, and as such of the "logical description" of the scene. AnimationInstances are part of the timeline, and as such of the "temporal description" of the scene. If you look back at the former thread then you can see that I told that an animation works on variables without the need to know that they belong to bones or whatever else. That means that an animation can drive a bone, a wall-clock hand, a pleul, or whatever. You can re-read this in the former thread on page 5 beginning with my post "9/4/2008 7:25:50 AM" (simply search for the keyword AnimationInstance).
Hence, in my engine AnimationInstances will not be inside SkeletonInstance. Further, the Timeline drives AnimationInstances.


Quote:Original post by Kasya
What to write inside getTrackForNodeInSkeleton(AnimationTrack * track, SkeletonInstance * skelInstance);? Or maybe there are some other ways to do? (But now with numeric value, because i don't want to use numeric values for now, i'll use them later)

Several ways are declared in the former thread in post "9/5/2008 8:25:01 AM".

In principal, what I do is to get a key from the current track, and to let the scene node (skel in your case) solve for the key. Something similar to this:
AnimationInstance *Animation::newInstance(SkeletonInstance* skel) {   AnimationInstance * instance = new AnimationInstance(this);   for(TrackList::iterator i = tracks.begin(); i != tracks.end(); ++i) {      key_t key = i->getKey();      Node * node = skel->resolve(key);      if(node) {         AnimationBinding * binding = new AnimationBinding(node, (*i));         instance->addBinding(binding);      }   }   return instance;}

where key_t is the type of key you use (e.g. a string, integer number, whatever). See the aforementioned post in the former thread of the caveats of this method, please.

Hello haegarr,

(1) Bones and resources
That's my resource class:

/* There is not resource manager for now. Because first is to finish the Animation. Then the other stuff whic i don't need now */class Resource {protected:	String name;public:	Resource() {}	Resource(const String &str) : name(str) { }	~Resource() {}	void SetName(const String &str);		String getName() const { return name; }};


I used the bone with resource because i was a little bit lazy to write setBoneName() and getBoneName() :) But you are right. I changed it into setName and getName instead of deriving it from resource.

(2) and (3): I am sorry. I forgot to add the SceneNode:

class SceneNode {public:	SceneNode() {}	~SceneNode() {}	SceneNode * AddChild(SceneNode * node);	void Destroy();	void Release();	virtual void update(real time);	std::list<SceneNode *> getNodes() { return nodes; }protected:	typedef std::list<SceneNode *> NodeList;	NodeList nodes;	SceneNode * parent;};/* Transform Node: Just call it Node because don't have time to write TransformNode or DOFNode :) */class Node : public SceneNode {public:	Node() {}	~Node() {}	void update(real time);	void setPosition(Vector3 pos);	void setOrientation(Quat rot);	Vector3 getPosition() const { return position; }	Quat getOrientation() const { return orientation; }protected:	Vector3 position;	Quat orientation;};


I added a comment for myself to not to forget what the node was. I am using it very often so i thought it would be better to write just node instead of writing TransformNode or something else. :)

Quote:
If you look back at the former thread then you can see that I told that an animation works on variables without the need to know that they belong to bones or whatever else. That means that an animation can drive a bone, a wall-clock hand, a pleul, or whatever.


I think it is what i call NumericValue. It is a Vector3, Quat, Color or something else and i just do transformations into it without knowing where it belongs. Am i right?

I understand the code you wrote. Just want to make sure about some things:
+ i->getKey()
| - Is that the id or name of the object in SkeletonInstance or somewhere?
+ skel->resolve(key)
| - Does it search inside the bones for a bone whose id or name is the key? And if i don't have the bone id (as you see i use nodes in my SkeletonInstance class), can i do it? or i need to add at least a unique id (uint_t for fast search operation) into my node class to get the key from the node.

EDIT: I thought of this:
clas BoneNode : public Node {private:String * name; /*I added the pointer for just taking the name of the bone from the Bone class. Not initializing a new string */public:String getName()const { return &name; }void SetName(const String &str) { name = str; }BoneNode * getBoneByName(const String &str) const; };class SkeletonInstance : public Instance<BoneNode> {/* The same code of SkeletonInstance as i posted earlier + */String getBoneByName(const String &name) const { return node->getBoneByName(name); }};


Do i need AnimationTrack names? And does AnimationTrack needs to be resource?

Thanks,
Kasya

[Edited by - Kasya on May 3, 2009 9:27:19 AM]
Quote:Original post by Kasya
I added a comment for myself to not to forget what the node was. I am using it very often so i thought it would be better to write just node instead of writing TransformNode or something else. :)

No problem, you can name your classes like you want. I only hinted at a convention I use. Consistency in naming classes and methods may help co-workes and code reviewers to find one's way more easily.

Quote:Original post by Kasya
I think it is what i call NumericValue. It is a Vector3, Quat, Color or something else and i just do transformations into it without knowing where it belongs. Am i right?

Yes.

Quote:Original post by Kasya
I understand the code you wrote. Just want to make sure about some things:
+ i->getKey()
| - Is that the id or name of the object in SkeletonInstance or somewhere?

It is, but it is also more than that: It is (a) an identifier of a bone in SkeletonPrototype. Since those bones have one-to-one counterparts in SkeletonInstance (namely where the current bone state is stored), it is (b) an identifier of a bone instance. And it is (c) a key in the tracks of AnimationPrototype to define to which values the Bindings have to refer later.

Assume for example that each bone has a numeric ID or a string that stores its name like the "RootBone" in the OP. This is the said identifier. It is stored with the bones in the SkeletonPrototype. After instancing SkeletonPrototype to a SkeletonInstance object, each bone in the instance refers to its prototype bone and hence indirectly has the same identifier. Of course, the identifier has to be unique in the context of a skeleton prototype.

Now, when the animation designer builds a new animation prototype, s/he has to create AnimationPrototype::Track objects and populate the AnimationPrototype object with them. Each of those tracks has to be parametrized with a key (which has to be of the same type as the identifier of the bones).

Quote:Original post by Kasya
+ skel->resolve(key)
| - Does it search inside the bones for a bone whose id or name is the key?...

In principle it does. To be exact: It lets the skeleton instance search its bone instance for those bone which refers to a bone prototype that stores an identifier matching the specified key (ugh ;)). But keep on reading...

Quote:Original post by Kasya
... And if i don't have the bone id (as you see i use nodes in my SkeletonInstance class), can i do it? or i need to add at least a unique id (uint_t for fast search operation) into my node class to get the key from the node.

The animation should not know about skeletons and bones. That was the reason why I said in the former thread that already the basic SceneNode should provide the method SceneNode::resolve(...). Going a step further, IMHO it should also not know about (Transform)Node as well. IMHO the SceneNode::resolve method (or methods) should return what you've named NumericValue.

Doing so allows us the following: Assume that the key stored with an AnimationPrototype::Track may be "LeftUpperLeg.POSITION" and obviously stores an Vector3 for the position of the left upper leg. The track has no clue what a leg is, but it has a clue what a position is. This is because you have a special type of track. If I remember correctly, we had something like
template<typename value_g> class ConcreteTrack<value_g> : public AnimationPrototype::Track;
in the former thread. Setting value_g to Vector3 makes the type of track I'm speaking from here. Perhaps we had another scheme, like non-templated classes like
class Vector3Track : public AnimationPrototype::Track;
However, such a track uses Vector3 for its keys, and hence Vector3 is the result of its interpolation.

Now, during instancing the AnimationInstance, a SceneNode object is overhanded. Whether this is a SkeletonInstance is not of interest for the animation. The only thing of interest is that the overhanded SceneNode has a SceneNode::resolve(key_t) method that returns a NumericVariable<Vector3>. Since this example uses a string ("LeftUpperLeg.POSITION") as key, key_t is obviously a string. Now, the SceneNode parses the key and divides it into "LeftUpperLeg" and "POSITION". In reality the SceneNode is a SkeletonInstance, hence the implementation of SkeletonInstance:: resolve(...) is invoked. As such, the routine can be implemented with access to the bone instances. And hence yes, the bone instance can be searched for a bone which refers to an original bone which stores "LeftUpperLeg" as its identifer. If not found, then nil is returned. If found, then the 2nd part of the key ("POSITION") is used to detect that the position member (and not the orientation member or something else) is of interest. Hence the NumericValue<Vector3> of the position is returned. Voila.

BTW: You can, of course, also uses routines like
SceneNode:.resolve(key_t key,semantic_t semantic)
as I've explained in the former thread.

Quote:Original post by Kasya
Do i need AnimationTrack names?

Not necessarily. However, notice that the tracks of the animation prototype have keys stored with them. The keys can be used as names if needed (e.g. for logs or so).

Quote:Original post by Kasya
And does AnimationTrack needs to be resource?

Nope. They are components of AnimationPrototype similarly to bones which are components of SkeletonPrototype.
From your post i understand that i have an animation track for only one transformation. Then there will be 2 times more animation tracks than usual: 1 for orientation and 1 for position. Is that efficient?

And which of them is more efficient
1. 2 Tracks (position+orientation) and 1 keyframe variable (value) for one Bone
2. 2 Keyframe variable (position+orientation) and 1 track for one Bone
3. Both of them. (If the option is this, then i'll just create both of them: NodeAnimationTrack and (Vector3+Quat)AnimationTrack)

Thanks,
Kasya
Quote:Original post by Kasya
From your post i understand that i have an animation track for only one transformation. Then there will be 2 times more animation tracks than usual: 1 for orientation and 1 for position. Is that efficient?

And which of them is more efficient
1. 2 Tracks (position+orientation) and 1 keyframe variable (value) for one Bone
2. 2 Keyframe variable (position+orientation) and 1 track for one Bone
3. Both of them. (If the option is this, then i'll just create both of them: NodeAnimationTrack and (Vector3+Quat)AnimationTrack)

That depends on the use cases. If you ask a user of a DCC package like Maya or the like, s/he may ask you why there is no possibility in your animation to access just the x component of the position.

Do you need the position change in principle? E.g. if we are speaking of skeleton animation of e.g. human like avatars. Do they need to lengthen their bones? Maybe some bones may need to do so just to avoid artifacts when deforming the mesh too heavily, but in general they need not. If you combine position and orientation into single keyframes then both are ever to be computed simultaneously. It would be a waste in both memory and runtime if only a few positions are altered ever.

On the other side, you may use animation data originating from motion capturing. That is usually all-in-one and sampled simultaneously. Hence combined keyframe attributes will probably be more efficient.

This topic is closed to new replies.

Advertisement