CharacterMaker in c++

I’m trying to load in a Character in C++.
Yes, python is not an option.

Anyway… After gazing at the Character class for 15 minutes and not having any clue how to use it I found the CharacterMaker class… However that has just caused me more troubles…
I just want to load in an .egg file and get an Actor like object.

CharacterMaker(EggGroup *root, EggLoader &loader) : _loader(loader), _egg_root(root)

This is the constructor… I can’t figure out what to put in as attributes… EggLoader doesn’t seem to have a usefull constructor and neither does EggData which EggLoader takes in as an attribute. It’s all very confusing :stuck_out_tongue:

Ps… Search doesn’t turn up a single result for charactermaker :frowning:

Yeah, I’ve tried looking into that as well. Its all very confusing.
Though, how I solve it: I don’t use an actor class, just normal models, which I animate by loading the animation egg files into the scene graph as well. (see the Roaming Ralph in C++ example how to do that.)

Thanks, but htat’s not an option for me, I need to have access to the skeleton/joint structure that is only present in the Character class.

Hmm, I guess you’d need to be digging in Panda’s EggXXX interface. It’s actually not very much documented. I guess you will need to load an egg file that way then supplying something of that interface to the CharacterMaker.

Don’t mess with the egg loader; that’s only for reading an egg file. Once you have it loaded, you don’t need the egg interface any more, unless you want to know what it literally in the egg file instead of the semantics of the structure that was built from it.

But in your case, you want to know more about the structure of the character. I believe there happens to be a video in the videos section in which I talked about the relationship between characters, Actors, and egg files; you might find it instructive.

The short answer is: load the model and the animation, and use auto_bind() to bind them together.

To find a particular joint within the character, use Character::find_joint(). To walk the entire hierarchy of joints, use Character::get_bundle() to get the root of the hierarchy, then use AnimGroup::get_num_children() and AnimGroup::get_child(), recursively, on that bundle.

David

But the ONLY constructor for CharacterMaker is:

CharacterMaker(EggGroup *root, EggLoader &loader);
Still trying to figure out how to create an EggGroup and an EggLoader.

I went over to the video section and could only find “showoff” videos, nothing instructional. Or at least the description didn’t sound instrictional. If it is indeed there could you point me to it?

This just gives me access to playing animations… I don’t see how I use this to create a character and get access to joints?

Thanks… I’m sure it’ll be a breeze once I actually get a working instance of the Character class :slight_smile:

Btw… I’m not interested in animations at all… I need to get access to the skeleton/joints to transform them in code (I get quanternion rotations sent over network from annother component that controls the character)

You don’t want the CharacterMaker class. That’s used by the egg loader for the purpose of constructing a Character object out of an egg file.

You need to understand a little of the structure of a Character internally, and how it relates to the higher-level Python class, Actor, which is the principle interface documented here.

Actor is the high-level interface to animations from the Python level. An Actor typically contains one Character node (though it might contain several, in the case of multipart actors).

Character is the front-facing interface to controlling animations at the C++ level. When the egg loader finds an animated model in an egg file, it creates a Character object, which is a kind of PandaNode, and attaches it to the scene graph. Then it constructs the geometry corresponding to that animated model and attaches it to the Character (in the form of one or more GeomNodes). It also fills in the contents of the Character object with the model’s hierarchy of joints and morphs.

The Character object itself is really just a mechanism to attach the joint hierarchy into the scene graph. The actual joint hierarchy is called a bundle; it is a hierarchy of PartGroup nodes (not related to PandaNode), whose root is a node of type PartBundle. This bundle can be retrieved with Character.getBundle().

Most of the interfaces that control the animation are made through the bundle. For instance, you can bind an animation here, or control the blending and/or weight of different animations. You can also walk through the list of children of the bundle, using get_num_children()/get_child(); and if you do this recursively, you can walk through the entire hierarchy of joints. Or, you can simply call PartBundle::find_child() to immediately return the joint with a particular name.

But to control the joints of a character programmatically, you don’t need to get a pointer to the joint. Instead, you set it up directly from the PartBundle, using PartBundle::control_joint(). You pass it a new PandaNode that you have attached to the Character node. Henceforth, any transform that you assign to this PandaNode will be automatically applied to the joint you specified.

In general, you might find it useful to read through the documentation on this website about using the Actor class, and then reading the Actor.py file itself, to see how it uses the low-level C++ class to achieve its magic. You will find that Actor is in many cases just a thin wrapper around Character and PartBundle.

For instance, the manual here recommends calling Actor.controlJoint() to control a joint programmatically. If you read Actor.py, you can see that Actor.controlJoint() simply calls PartBundle::control_joint().

David

Ah, I see the instructional videos have been buried in the manual. Sorry about that. Here are the links I was referring to:

http://panda3d.org/manual/index.php/Disney_Video_Lectures

David

Thanks… Your posts are very helpful!

After about 10 hours (you must think i’m a horrible programmer! :slight_smile: ) I finally got it working.
I had actually given up, and was writing a very, very long post here explaining what I had done and what was working and what wasn’t, and then I finally figured out the problem. It had to do with the fact that that I was using the combine_with() function in the Character class to create my character instead of typecasting the node retrieved from the loaded model to a Character object.

The weird thing is, that using my combine_with() method, I actually got access to all the joints, and control_joint worked, so I thought i was on the right path, but none of my transformations had any effect.

Here is the code…

		NodePath eveNP = window->load_model(framework.get_models(), "models/eve/eve.egg");		
		eveNP.reparent_to(window->get_render());		
		eveNP.set_pos(2, 0, 0);

		ModelRoot* eveN = (ModelRoot*)eveNP.node();
		NodePath eveChNP = eveNP.find("**/+Character");		
		Character* eveCH = (Character*)eveChNP.node();
	
		if(!eveN->is_of_type(eveCH->get_type()))
			printf("Model is not Character?\n");
		else
			printf("Character loaded\n"); 

		CharacterJointBundle* eveBundle = eveCH->get_bundle(0);
		NodePath eveNeckNP = eveNP.attach_new_node("EveNeck");

		if (eveBundle->control_joint("Neck", eveNeckNP.node()))
			printf("Neck control success!\n"); // This prints out
		else
			printf("**** Neck control FAILED ****\n");

		eveNeckNP.set_pos(eveNeckNP, 1, 1, -2);

that took me probably 10+ hours to figure out!
these typecasts and inheritances are pretty tricky!

Panda3d could really use a good class diagram (hope there isn’t one already, or i’ll jump off a cliff :slight_smile: )

And to control joints:

Member variables:
	const NodePath& m_parent;
	
	NodePath m_nodePath;
	Character* m_character;
	CharacterJointBundle* m_jointBundle;

	NodePath ControlJoint(std::string _jointName, NodePath* _parent)
	{
		NodePath jointNP = _parent->attach_new_node(_jointName);

		if (m_jointBundle->control_joint(_jointName, jointNP.node()))
		{
			CharacterJoint* joint = (CharacterJoint*)m_jointBundle->find_child(_jointName);
			jointNP.set_mat(joint->get_initial_value());

			printf("Joint control request for '%s' success!\n", _jointName.c_str()); 
			return jointNP;
		}
		else
		{
			printf("Joint control request for '%s' FAILED!\n", _jointName.c_str()); 
			return NULL;
		}		
	}

And to “expose” all joints into a nodepath hierarcy:

	void ExposeAllJoints()
	{
		for (int i = 0; i < m_jointBundle->get_num_children(); i++)
			subExposeJoint(0, m_nodePath, m_jointBundle->get_child(i));			
	}

	void subExposeJoint(int _level, NodePath _parent, PartGroup* _bundle)
	{
		NodePath np = _parent;

		if (_level > 0)
			np = ControlJoint(_bundle->get_name(), &_parent);

		for (int i = 0; i < _bundle->get_num_children(); i++)
			subExposeJoint(_level+1, np, _bundle->get_child(i));
	}

Excellent work! I’m sorry for all the frustration. I know how it is–when I’m trying to write code using, for instance, the Maya API, I feel like I’m just repeatedly banging my head into a wall. I can see that the API has lots and lots of power, but internally everything’s connected to each other in mysterious ways, and it’s not at all obvious how I’m expected to get from point A to point B. There are lots of things to try and none of them work the way you’d think they would.

I’m sure trying to program in Panda, without having a solid familiarity with the underlying philosophy, feels pretty similar. Hang in, though, the API really does make an elegant sense to it when you get the pattern.

A couple of comments. We have the DCAST macro, short for downcast, which provides a type-safe version of the static cast operation. This works like this:

      ModelRoot* eveN = DCAST(ModelRoot, eveNP.node());
      NodePath eveChNP = eveNP.find("**/+Character");      
      Character* eveCH = DCAST(Character, eveChNP.node());

DCAST will perform the cast if the source object is, in fact, an instance of the requested type. If it is not, it will print an error message and return NULL. Its test can be compiled out for a production build, similar to assert().

Also, I find this a little curious:

      if(!eveN->is_of_type(eveCH->get_type()))
         printf("Model is not Character?\n");
      else
         printf("Character loaded\n"); 

I predict this should always print the line “Model is not Character?”, because, in fact, a ModelNode is not a Character, and you shouldn’t expect it to be.

David

Yeah… It’s all starting to make sense now, it was mainly the typecasts that were confusing me, because that’s the biggest difference from python.

DCAST will be helpfull, I’ll use that in the future.

That code that checks if it’s a character probably doesn’t work like you said, it did at one point but I must’ve broken it when refactoring.

Related to that, is it possible to do the type check without creating an instance of the class that you want to check for first?
I know there is a static variable in the class that stores the typehandle, but it’s private and I didn’t find a public static function to access it.

Sure.

      if(!eveCH->is_of_type(Character::get_class_type()))
         printf("Node is not Character?\n");

This is the kind of thing that DCAST does internally.

David

Hey, sorry to wake up this very old thread, but I have a question about this downcasting.
It appears DCAST works only if the two classes are both reference counted, right?
Look, I have two classes, DaeElement and TiXmlElement. DaeElement is reference counted while TiXmlElement is not. (it belongs to TinyXml).
DaeElement inherits from TiXmlElement and TypedReferenceCount. I’m trying to cast a TiXmlElement* into a PT(DaeElement). This is what I’m using:

PT(DaeElement) rootElement;
rootElement = dynamic_cast<DaeElement*> (myTiXmlElementPointer);

This works, though I don’t know if this is a safe and correct way to do it. DCAST didn’t work at all.
…or this could be using an incredibly stupid approach.
Can anyone comment on this?

DCAST isn’t related to reference counting, but it does work only if both classes inherit from Panda’s TypedObject class.

In your case, just perform a standard static cast. There’s no need to go through the additional overhead (and portability limitations) of C++'s fancy new dynamic cast feature. You can just write:

rootElement = (DaeElement*)(myTiXmlElementPointer);

David

Ah, thanks a bunch David. I somehow drew a direct line between TypedObject and ReferenceCount upon seeing TypedReferenceCount.
What I tried was casting it directly into a PT(DaeElement), which didn’t work. Your suggestion worked perfectly though, thanks.

An unrelated question: I noticed Panda has a convenient wrapper function for almost everything; does it also have something convenient instead of the standard FILE (also with case-insensitive paths), or can I just use the standard directly after conversion of my paths using the Filename class?

FILE? You mean, the FILE * type from stdio.h? We generally use iostreams instead within Panda code, and the VirtualFileSystem will create and return an ifstream for you that is routed through the vfs-mount system. For writing, you can create your own ofstream and use the Filename class to open it.

David

Ah, right. Works great, thanks again for your help.

Hi, im new to panda3d and I need some help. What is the best way to get manual control of joint transformations? I see its easier in python but I have to do this in C++. I tried using some code from this topic but most of it didn’t work as I expected. Here is the first part I used:

  NodePath actor = window->load_model(framework.get_models(),"/c/panda stuff/models/eve/eve");
  actor.reparent_to(window->get_render());
  NodePath actorNP = actor.find("**/+Character");
  Character* actorCH = DCAST(Character, actorNP.node());   
  CharacterJointBundle* actorBundle = actorCH->get_bundle(0);
  NodePath part = actor.attach_new_node("RightShoulder");
  actorBundle->control_joint("RightShoulder", part.node());

With control_joint I get access to the joint and I can modify it but it moves from its place (when I access shoulder the hand moves to the middle of the model). Do I have to get control of all the joints and set them in a hierarchy somehow or is there a better way to get control of single joint? I tried to use ExposeAllJoints() and ControlJoint from this topic but it doesn’t work for me:/

No, that’s the right idea, but you do have to load the original transform from the joint back onto the node you’ve created, otherwise it will move the joint to (0, 0, 0).

Use something like this:

MovingPartMatrix *joint = DCAST(MovingPartMatrix, actorBundle->find_child("RightShoulder");
part.set_mat(joint->get_initial_value());

David