Page 1 of 2

Rotating around an arbitrary axis

Posted: Mon Oct 20, 2008 11:47 pm
by Tubby2877
So what i want is for the camera to rotate a full 360 degrees completely around the origin in the game.

the problem i was having with doing it by the camera rotation based of a parent dummy node was the gimble lock it would get at the top.

to combat this, i decided to leave the camera at the centre and move all the world objects around it. now there is no gimble lock and the y rotation is fine

the problem is the x rotation!!!!

at the start it's rotation is fine and the objects pass over and under the camera, when you move the y axis however and then rotate, it still rotates around the x axis and the objects just spin there to the left or the right at varying radius's depending on how far around the y axis i am instead of going under you.

i think what i want to do is get the difference of the two nodes (the root node and the camera) and create an arbitrary X axis for the objects to rotate around i think???

How do i do this, if you guys understand please help!!!

Posted: Tue Oct 21, 2008 3:28 am
by wyrmmage
Why not just use quaternions? :)
-wyrmmage

Posted: Tue Oct 21, 2008 4:50 am
by Dark_Kilauea
To add, irrlicht has an implementation here

Posted: Tue Oct 21, 2008 5:28 am
by Tubby2877
tried the quaternions and they are a no go for me anyway,

this is a snippet of code that is meant to rotate around the centre but
all it does is snap the items back to the front and then does nothing, not very helpful.

Code: Select all

core::quaternion rotation;
core::vector3df axis(0,0,0); axis.normalize();
core::vector3df final;

rotation.fromAngleAxis( 45*core::PI/180.0, axis );
rotation.toEuler(final);

CentreNode->setRotation(final);
i tried quaternions before and that is why i decided to move the whole world instead of the camera because irrlicht quaternion class just didn't seem to have what i needed to actually rotate the bugger!!!! just a whole lot of snapping the camera and being stubborn.

if anyone know how to actually ROTATE the camera instead of just snapping it. please feel free to give me a kick up the butt for winging!!!!

Posted: Tue Oct 21, 2008 7:04 am
by bitplane
You can't normalize a vector of (0,0,0), it has no direction.

Posted: Tue Oct 21, 2008 9:22 am
by Tubby2877
so should i just make it 0,0,1 then or what???

and if i can't then how will i be able to get it to rotate around the origin???

Posted: Tue Oct 21, 2008 11:36 am
by rogerborg
Image

Breathe into the bag. In. Out. In. Out. Now, sloooowly.

Tubby2877 wrote: i tried quaternions before and that is why i decided to move the whole world instead of the camera because irrlicht quaternion class just didn't seem to have what i needed to actually rotate the bugger!!!! just a whole lot of snapping the camera and being stubborn.
It's stubbornly doing exactly what you're telling it to do. Can we back up a bit and examine the requirements, rather than the current solution?

Tubby2877 wrote:So what i want is for the camera to rotate a full 360 degrees completely around the origin in the game.
Do you want the camera at the origin looking out, or do you want it orbiting around the origin, looking in at the origin? I think you want the former, but I can't be sure.


If so, then here's a quaternion based solution that rotates both the forward and up axes of the camera to remove gimbal lock. (CAVEAT: this is usually where Vitek appears to explain a 2 line replacement for this method).

If this doesn't do what you want, then let's go back to your requirements so that we can be sure that we're solving the same problem.

Code: Select all

#include <irrlicht.h>

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#pragma comment(lib, "Irrlicht.lib")

// Standard event receiver
class MyEventReceiver : public IEventReceiver
{
public:
	virtual bool OnEvent(const SEvent& event)
	{
		if (event.EventType == irr::EET_KEY_INPUT_EVENT)
			KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;

		return false;
	}

	virtual bool IsKeyDown(EKEY_CODE keyCode) const	{ return KeyIsDown[keyCode]; }
	
	MyEventReceiver()
	{
		for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
			KeyIsDown[i] = false;
	}

private:
	bool KeyIsDown[KEY_KEY_CODES_COUNT];
};


void rotateVectorAroundAxis(vector3df & vector, const vector3df & axis, f32 radians)
{
	quaternion MrQuaternion;
	matrix4 MrMatrix;
	(void)MrQuaternion.fromAngleAxis(radians, axis);
	MrQuaternion.getMatrix(MrMatrix);
	MrMatrix.rotateVect(vector);
}


int main()
{
	MyEventReceiver receiver;
	IrrlichtDevice *device =
		createDevice( video::EDT_OPENGL, dimension2d<s32>(640, 480), 32,
			false, false, false, &receiver);

	IVideoDriver* driver = device->getVideoDriver();
	ISceneManager* smgr = device->getSceneManager();

	// Place some random billboards to give us something to look at.
	for(int i = 0; i < 100; ++i)
	{
		scene::IBillboardSceneNode * bill = smgr->addBillboardSceneNode();
		bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
		bill->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
		bill->setMaterialFlag(video::EMF_LIGHTING, false);
		bill->setMaterialFlag(video::EMF_ZBUFFER, false);
		bill->setPosition(vector3df((f32)(rand()%500) - 250.f, (f32)(rand()%500) - 250.f, (f32)(rand()%500) - 250.f));
	}

	ICameraSceneNode * camera = smgr->addCameraSceneNode();

	u32 then = device->getTimer()->getTime();
	while(device->run())
	{
		u32 const now = device->getTimer()->getTime();
		const f32 moveSpeed = (f32)(now - then) / 500.f; // Let's rotate at a framerate independent speed
		then = now;

		// Work out the 3 axes for the camera.
		vector3df forward = (camera->getTarget() - camera->getAbsolutePosition()).normalize();
		vector3df up = camera->getUpVector();
		vector3df right = forward.crossProduct(up);

		// yaw around the up axis
		if(receiver.IsKeyDown(KEY_KEY_D))
			rotateVectorAroundAxis(forward, up, moveSpeed);
		else if(receiver.IsKeyDown(KEY_KEY_A))
			rotateVectorAroundAxis(forward, up, -moveSpeed);

		// pitch around the right axis (we need to change both forward AND up)
		if(receiver.IsKeyDown(KEY_KEY_S)) // S == pitch up, i.e. we're simulating moving a joystick.
		{
			rotateVectorAroundAxis(forward, right, moveSpeed);
			rotateVectorAroundAxis(up, right, moveSpeed);
		}
		else if(receiver.IsKeyDown(KEY_KEY_W))
		{
			rotateVectorAroundAxis(forward, right, -moveSpeed);
			rotateVectorAroundAxis(up, right, -moveSpeed);
		}

		// roll around the forward axis
		if(receiver.IsKeyDown(KEY_KEY_Q))
			rotateVectorAroundAxis(up, forward, moveSpeed);
		else if(receiver.IsKeyDown(KEY_KEY_E))
			rotateVectorAroundAxis(up, forward, -moveSpeed);

		// And re-orient the camera to face along the foward and up axes.
		camera->setTarget(camera->getAbsolutePosition() + forward);
		camera->setUpVector(up);

		driver->beginScene(true, true, SColor(255,100,101,140));
		smgr->drawAll();
		driver->endScene();
	}

	device->drop();

	return 0;
}

Posted: Wed Oct 22, 2008 1:08 am
by Tubby2877
ok so that does the camera moving in a 360 degrees looking mode,
but i want to be able to MOVE the camera in a 360 degree motion as well.
i'm sorry if that wasn't clear.

what i am havning is an object in the centre, at the origin

from there i want to be able to move the camera in and out from it and around in a sphere like shape so it is always a certain zoom radius from the centre, the camera will have an independent settarget function like the one that rogerborg described, but that only allows to LOOK 360 degrees which is helpful but only half the problem.

the idea now is to move in 360 degrees around the centre in a sphere like mode. and that is what is stuffing up.

because the xaxis isn't changing when we are not on the x = 0 for the rotation, the rotation is smaller as the y gets further and further from x = 0;

Hope this helps you figure it out lol

Posted: Wed Oct 22, 2008 1:25 am
by vitek
You might have a look at this topic. It discusses spherical coordinates and has simple camera manipulation code that uses them.

Travis

Posted: Wed Oct 22, 2008 11:53 am
by rogerborg
Then just position the camera by moving it back along its view axis. This isn't rocket science, although we could add some rockets if you like.

Code: Select all


#include <irrlicht.h>

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#pragma comment(lib, "Irrlicht.lib")

// Standard event receiver
class MyEventReceiver : public IEventReceiver
{
public:
   virtual bool OnEvent(const SEvent& event)
   {
      if (event.EventType == irr::EET_KEY_INPUT_EVENT)
         KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;

      return false;
   }

   virtual bool IsKeyDown(EKEY_CODE keyCode) const   { return KeyIsDown[keyCode]; }
   
   MyEventReceiver()
   {
      for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
         KeyIsDown[i] = false;
   }

private:
   bool KeyIsDown[KEY_KEY_CODES_COUNT];
};


void rotateVectorAroundAxis(vector3df & vector, const vector3df & axis, f32 radians)
{
   quaternion MrQuaternion;
   matrix4 MrMatrix;
   (void)MrQuaternion.fromAngleAxis(radians, axis);
   MrQuaternion.getMatrix(MrMatrix);
   MrMatrix.rotateVect(vector);
}


int main()
{
   MyEventReceiver receiver;
   IrrlichtDevice *device =
      createDevice( video::EDT_OPENGL, dimension2d<s32>(640, 480), 32,
         false, false, false, &receiver);

   IVideoDriver* driver = device->getVideoDriver();
   ISceneManager* smgr = device->getSceneManager();

   // Place some random billboards to give us something to look at.
   for(int i = 0; i < 100; ++i)
   {
      IBillboardSceneNode * bill = smgr->addBillboardSceneNode();
      bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
      bill->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
      bill->setMaterialFlag(video::EMF_LIGHTING, false);
      bill->setMaterialFlag(video::EMF_ZBUFFER, false);
      bill->setPosition(vector3df((f32)(rand()%500) - 250.f, (f32)(rand()%500) - 250.f, (f32)(rand()%500) - 250.f));
   }

   // Place a target node that orbits around 10, 20, 30 to demonstrate that we can 
   // orbit around any position, even a moving one.
	IBillboardSceneNode * targetNode = smgr->addBillboardSceneNode();
	targetNode->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
	targetNode->setMaterialTexture(0, driver->getTexture("../../media/portal1.bmp"));
	targetNode->setMaterialFlag(video::EMF_LIGHTING, false);
	targetNode->setMaterialFlag(video::EMF_ZBUFFER, false);
	targetNode->setPosition(vector3df(10, 20, 30));

	ISceneNodeAnimator * animator = smgr->createFlyCircleAnimator(vector3df(10, 20, 30), 40, .001f, vector3df(1, 2, 3).normalize());
	targetNode->addAnimator(animator);
	animator->drop();
	animator = 0;

   ICameraSceneNode * camera = smgr->addCameraSceneNode();

	f32 orbitDistance = 100.f;

   u32 then = device->getTimer()->getTime();
   while(device->run())
   {
      u32 const now = device->getTimer()->getTime();
      const f32 moveSpeed = (f32)(now - then) / 500.f; // Let's rotate at a framerate independent speed
      then = now;

      // Work out the 3 axes for the camera.
      vector3df forward = (camera->getTarget() - camera->getAbsolutePosition()).normalize();
      vector3df up = camera->getUpVector();
      vector3df right = forward.crossProduct(up);

      // yaw around the up axis
      if(receiver.IsKeyDown(KEY_KEY_D))
         rotateVectorAroundAxis(forward, up, -moveSpeed);
      else if(receiver.IsKeyDown(KEY_KEY_A))
         rotateVectorAroundAxis(forward, up, +moveSpeed);

      // pitch around the right axis (we need to change both forward AND up)
      if(receiver.IsKeyDown(KEY_KEY_W))
      {
         rotateVectorAroundAxis(forward, right, -moveSpeed);
         rotateVectorAroundAxis(up, right, -moveSpeed);
      }
      else if(receiver.IsKeyDown(KEY_KEY_S))
      {
         rotateVectorAroundAxis(forward, right, +moveSpeed);
         rotateVectorAroundAxis(up, right, +moveSpeed);
      }

      // roll around the forward axis
      if(receiver.IsKeyDown(KEY_KEY_Q))
         rotateVectorAroundAxis(up, forward, +moveSpeed);
      else if(receiver.IsKeyDown(KEY_KEY_E))
         rotateVectorAroundAxis(up, forward, -moveSpeed);

      if(receiver.IsKeyDown(KEY_KEY_Z))
         orbitDistance = max_(10.f, orbitDistance - moveSpeed * 100.f);
      else if(receiver.IsKeyDown(KEY_KEY_X))
         orbitDistance = min_(200.f, orbitDistance + moveSpeed * 100.f);

	  // Move BACK up the forward axis of the camera to place it in its orbit.
	  camera->setPosition(targetNode->getAbsolutePosition() - (forward * orbitDistance));

      // Point the camera at the target node, and align its up axis correctly.
      camera->setTarget(targetNode->getAbsolutePosition());
      camera->setUpVector(up);

      driver->beginScene(true, true, SColor(255,100,101,140));
      smgr->drawAll();
      driver->endScene();
   }

   device->drop();

   return 0;
} 

Posted: Mon Nov 03, 2008 1:57 am
by Tubby2877
Again this only helps for one of the axis

now the camera will roll over the top all fine (pressing w and s)

but pressing a and d, the camera will do that it was doing before

So what i want is for both axis to use 0,0,0 as there centre and for them both to rotate around that centre point, at the moment i can either get one doing it or the other.

the solution vitek put up was helpful but just switched the problem from one axis to the other

i have the camera's look at all fine, it is positioning the camera the is causing the problem at the moment

like i am walking along the surface of the sphere from point to point.

Posted: Mon Nov 03, 2008 11:50 am
by rogerborg
Tubby2877 wrote:but pressing a and d, the camera will do that it was doing before

[...]

like i am walking along the surface of the sphere from point to point.
I honestly have no idea what functionality you require. The 2nd code example above provides roll (Q and E), and orbitting using the camera's right and up vectors. This allows you to move it to any point on the surface of a sphere centred on its its look-at position.

Can you explain your requirements using commonly understood idioms? Are there any existing well known applications that display the behaviour that you require? Can you express it in an example, using terms like latitude and longitude, or polar coordinates?

Posted: Mon Nov 03, 2008 4:39 pm
by vitek
It sounds like he wants continuous movement in all directions. So if he presses buttons to go up/down on the sphere, he doesn't want it to stop at the top, but to start going back down the other side of the sphere.

I don't see where this issue would come up in your code, but the code that I posted would definitely do it. I intentionally limit Phi to prevent looking straight up/down. But recalculating the up vector (as described in my comments and done in your example) should resolve the problem.

Travis

Posted: Mon Nov 03, 2008 5:16 pm
by rogerborg
Yup, there's no gimbal lock in my code; all 3 axes are free.

I'm wondering if he wants A and D to produce rotation in XZ around a circle of latitude (i.e. decreasing to nil at the +/-Y 'poles'), rather than to orbit around the camera's right vector.

Posted: Tue Nov 04, 2008 8:52 pm
by asparagusx
Maybe what he is looking for is an orbiting camera, using the mouse input rather than keyboard presses.

Something like this maybe ?

http://au.youtube.com/watch?v=BkT3fzxlfUw


Anton