[SOLVED] Orbiting camera

Hey folks,

I’m new a this promising community and therefore sure u guys can answer this:

We have a sphere and lots of points around this sphere which we would like to move our camera to. Each point has the same distance to the planets surface. Is there any way to get the camera (smooth moving) from one point to another? This should be accomplished while having in mind that we have an orbiting camera which has exactly the same distance to the surface as the points we want to move to.

I know there are AsyncTasks in the PandaFramework as well as the CLerpNodePathInterval class which should help. But I don’t really get how I would design a non linear movement with those two.

Appreciating your help :slight_smile:
Philipp

Hey, welcome to the forums!

You can make smooth movement by passing CLerpInterval::BT_ease_in_out to the blend_type parameter of the CLerp*Interval constructor.

Thanks for the quick response.

I’m sorry I think i didn’t make myself clear enough. I think I know how to use the CLerp kinda things. But I don’t really get the point when telling it to make a non linear movement. What I want is some kind of curve like movement.

For example if we have points distributed around the sphere (each with the exact same distance to the surface) one at the north pole, another one at the south pole. How would I manage to move the camera from the one point to the other without flying all the way through the sphere.

If you say there is no way in doing that with a “out of the box” mechanism I’d be fine as well. I just wanted to know whether I’m missing something or not.

You could use motion paths for that purpose.

Also, if your goal is just to sweep the camera through a circular arc, that sounds like a rotation lerp to me. Put the camera on the end of an arm, then rotate that arm. This is similar to what is demonstrated in the “solar system” sample program, if you don’t mind reading the code in Python.

David

Thanks, I’ll have a look at both.

I was unable to find anything on motion paths and assumed you meant “motion trails”. I tried to include the “cMotionTrail.h” header but my compiler complains that it cannot find the file. In fact I couldn’t find the file either letting windows search in the corresponding folders. How can I find the header to give those motion trails a try? (libp3direct.lib is included)

Edit: I have a precompiled header as suggested somewhere here in the forum. For including I used the exact same lines as described here http://www.panda3d.org/reference/cxx/class!_c_motion_trail

Actually, rdb was indeed referring to “motion paths” or “mopath” and not “motion trails”, which is something different. A mopath is an arbitrarily-shaped curve that you create externally to Panda, for instance in a modeling package like Blender, and then use in Panda to describe at runtime the path along which an object should move.

However, the high-level interfaces for mopaths are written in Python (using just a few lines of Python code).

You can use a mopath in C++, but it’s more low-level: you load your path as a NurbsCurve or NurbsCurveEvaluator, and then use an interval or task to evaluate the position along your path at time t each frame, and set your object to that position. It only requires a few lines of code to do this, but you do have to understand how the individual pieces fit together.

I wouldn’t use mopaths for this purpose unless you really do need your object to move along an arbitrary-shaped curve which you create externally to Panda.

David

It took me a while to create the above thing in panda. My first attempt using the cross product to get the sense of rotation almost made me crazy because of the case differentiations. A colleague gave me the advice to convert my point to the polar coordinate system and it worked like a charm.

If somebody is interested in the exact same thing: Here is the way I did it:

void rotateToPoint(LVecBase3f & _oldRot, LVecBase3f &_newPos)
{
	LVecBase3f rotation = LVecBase3f(0.0f);
	float polarDistHead = 0.0f, polarDistRotation = 0.0f;

	// Convert heading to polar
	polarDistHead = sqrtf(_newPos.get_x() * _newPos.get_x() + _newPos.get_y() * _newPos.get_y());
	if (polarDistHead != 0)
		rotation.set_x(acosf(_newPos.get_x() / polarDistHead) * (180/M_PI));

	if (_newPos.get_y() < 0)
		rotation.set_x((rotation.get_x() * (-1.0f)) + 360.0f);

	// Project x value of new position into quadrant I or IV if not already in
	if (_newPos.get_x() < 0)
		_newPos.set_x(_newPos.get_x() * (-1));

	// Convert rotation to polar
	if (_newPos.get_x() == 0)
	{
		rotation.set_z(_oldRot.get_z());
	}
	else
	{
		polarDistRotation = sqrtf(_newPos.get_x() * _newPos.get_x() + _newPos.get_z() * _newPos.get_z());
		if (polarDistRotation != 0)
			rotation.set_z(acosf(_newPos.get_x() / polarDistRotation) * (180/M_PI));
	}

	if (_newPos.get_z() > 0)
		rotation.set_z((rotation.get_z() * (-1.0f)));

	if (fmod(_oldRot.get_x(), 360.0f) >= 0.0f)
		if (fmod(_oldRot.get_x(), 360.0f) <= rotation.get_x())
			if (rotation.get_x() - fmod(_oldRot.get_x(), 360.0f) <= 180.0f)
				rotation.set_x(_oldRot.get_x() - fmod(_oldRot.get_x(), 360.0f) + rotation.get_x()); 
			else
				rotation.set_x(_oldRot.get_x() - fmod(_oldRot.get_x(), 360.0f) + (rotation.get_x() - 360.0f)); 
		else
			if (fmod(_oldRot.get_x(), 360.0f) - rotation.get_x() <= 180.0f)
				rotation.set_x(_oldRot.get_x() - fmod(_oldRot.get_x(), 360.0f) + rotation.get_x()); 
			else
				rotation.set_x(_oldRot.get_x() - fmod(_oldRot.get_x(), 360.0f) + (360.0f + rotation.get_x()));
	else
		if (rotation.get_x() - fmod(_oldRot.get_x(), 360.0f) <= 180.0f)
			rotation.set_x(_oldRot.get_x() - fmod(_oldRot.get_x(), 360.0f) + rotation.get_x());
		else
			rotation.set_x(_oldRot.get_x() - fmod(_oldRot.get_x(), 360.0f) - 360.0f + rotation.get_x());

	rotJoint = new CLerpNodePathInterval(
		"rotate", 
		1.0f, // take any speed you like
		CLerpNodePathInterval::BT_ease_in_out, 
		false,
		true,
		this->cameraJoint, // the joint of your arm with the camera mounted on top of it
		NodePath());

	rotJoint->set_end_hpr(rotation);	
	rotJoint->start();
}

I took it out of the context so I had to apply some small fixes so you won’t get confused by any other calls to methods I cannot post. I hope it still works.

One more thing: We wanted the rotation interval to be something like [-90°,90°] so we projected _newPos x coordinate to quadrants I or IV. Hope you guys get what I mean.

Thanks for your help. This thread is solved!

Congratulations! But I think you did it the hard way. The easy way involves no trigonometry and would be something more like this:

NodePath arm = render.attach_new_node("arm");
model.reparent_to(arm);
model.setPos(0, radius, 0);
PT(CLerpNodePathInterval) i = new CLerpNodePathInterval("rotate", 5.0, CLerpInterval::BT_no_blend, true, false, arm, NodePath());
i->set_start_hpr(LVecBas3f(0, -90, 0));
i->set_end_hpr(LVecBas3f(0, 90, 0));
i->start();

David

If you don’t know your end_hpr but have vectors pointing from the center of the planet into random directions you first have to compute the hpr.

However your post made me realize I forgot about posting the CLerp part. I edited my sample code above and included the corresponding code.

Panda can do this for you too:

i->set_start_hpr(arm.get_hpr());
arm.look_at(look_vector);
i->set_end_hpr(arm.get_hpr());

David

Hmm, good point. Thanks :smiley:

Edit:
One more thing: Lets say the current hpr is something like (350, 0, 0) and the target hpr is (10, 0, 0). Would we take the short or the long way? It has to be the short way (that exactly is what took me so long to come up with an acceptable solution. In Panda you go from 360° to 361° instead of 1°)

If you use set_start_quat() and set_end_quat() instead of set_start_hpr() and set_end_hpr() (the quat variations also accept a HPR triple as input, but the intervening calculation is performed via quaternions), then the lerp will always follow the great-circle shortest distance.

Or, you can do the math for HPR ahead of time to ensure that each of the three components are within 180 degrees of each other. That just takes a couple of lines of code.

David

OK that’s what I did. Good to know there is an easier way to accomplish this. Thanks again!