Rotating around an arbitrary axis

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Tubby2877
Posts: 12
Joined: Fri Sep 12, 2008 1:54 pm

Post by Tubby2877 »

That video is a very good example except i do want it with the keyboard

So what is happening is the camera's look at is being controlled by the mouse and that is all fine and done, all working good and don't need to touch it.

The problem is moving the position of the camera.
What i would like to happen is for the camera to free roam around the whole area of the sphere like you were walking on it.

at the moment what will happen is that if you press one way (say a and d), it will circle the centre all fine but then when you want to go up and down it will depend on there it is.

if it is right in front of the objects then it's all good and will rotate properly over and create a full circle.

But if you move 90 degrees to that then the camera's position just rotates around itself and not over the objects. because it is locked to the axises.

it's really hard to explain in text and i don't really know anything else that uses something like it, my friend pointed out that it would be simillar to the start of populus when you are looking at the globe.

SO what is happening at the moment, if you are looking at a sphere, in an essence if you are on the equator you do a full circle of the radius but if you move a bit further down, say to the antartic, then if you hit left and right again then it will still go around in that direction only it is a much smaller circle. as it is still thinking it is using the axis for the equator as it's rotation.

What i want is that if you hit right then you would move right, then when it stopped it would in a sence, reset the axises so then if we hit up it would roll over the top of the globe, and then if i hit left anywhere it would still perform a full circle the size of the radius rather then just looking around the centre axis and doing little circle.

i don't know if this helps you to understand my problem or just makes it more confusing

anyway enjoy
rogerborg
Admin
Posts: 3590
Joined: Mon Oct 09, 2006 9:36 am
Location: Scotland - gonnae no slag aff mah Engleesh
Contact:

Post by rogerborg »

I am absolutely baffled as to how the behaviour that you are describing is different from that in the compilable, runnable sample app that I have already posted.

Can you please do yourself a favour and actually compile and run the following code. You can just replace one of the SDK examples with this source and it should build and run.

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->setPosition(vector3df((f32)(rand()%500) - 250.f, (f32)(rand()%500) - 250.f, (f32)(rand()%500) - 250.f));
   }

   ISceneNode * targetNode = smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/dwarf.x"));
   targetNode->setMaterialFlag(video::EMF_LIGHTING, false);

   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;
}
Please upload candidate patches to the tracker.
Need help now? IRC to #irrlicht on irc.freenode.net
How To Ask Questions The Smart Way
jddevnet
Posts: 17
Joined: Mon Oct 06, 2008 8:27 pm

Post by jddevnet »

I second that, rogerborg. When I tested it a week ago, it seemed to work as described. On a side note, I just wanted to say that the code was extremely helpful to my research as well.
lymantok
Posts: 67
Joined: Mon Dec 31, 2007 6:13 am

Post by lymantok »

Rogerborg, thanks very much for posting this code. I had just finished my first example skydome program using irrlicht, and was looking for exactly this type of 360 all axis rotation camera about the origin. It works perfectly.

One thing I have not investigated yet is using mouse/joystick control in addition to keyboard control for rotation. Is that a straightforward change to the code or do I need to check for the mouse/joystick for each axis and direction?

Thx!
afecelis
Admin
Posts: 3075
Joined: Sun Feb 22, 2004 10:44 pm
Location: Colombia
Contact:

Post by afecelis »

nice example Rogerborg :) First time I fiddle around with a quaternion example. Thanks for posting it.
Inverting the camera's axis, would then just be a matter of reassigning the keys, or using negative values to invert its direction?

regards,
Alvaro
zillion42
Posts: 324
Joined: Wed Aug 29, 2007 12:32 am
Location: Hamburg, Germany

Post by zillion42 »

I guess there's probably a thousand examples showing this behaviour for mouseinput aswell, but why not stick to this example... and that is essentially what I'm trying to do...
I made a FPS camera, orienting my code on the irrlicht FPS camera, but soon I had to realize that it gives me the gimbal lock behaviour, so I remembered this thread and thought I give it a try with quaternions... to be honest, I heard the term a lot, but I haven't even checked a wiki for its meaning, thus I have no understanding of what I'm doing... (<-The usual case)... ok... unacceptable actually... I do some reading now... WhoOo... Question N°2 Can anyone try to explain what an extension to real numbers is supposed to mean ?
What I have so far is the cursor delta for each frame:
pseudo code:

Code: Select all

gui::ICursorControl* cursor;
...
cursor = device->getCursorControl();
...
event Receiver:
...
if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
{
	CursorPos = cursor->getRelativePosition();
}
...
...
core::vector2df delta = CursorPos - core::vector2df(0.5,0.5);
cursor->setPosition(core::vector2df(0.5,0.5));
...
core::vector3df forwardD = cam->getTarget() - camPos;
forwardD.normalize();
core::vector3df upD = cam->getUpVector();
core::vector3df rightD = forwardD.crossProduct(upD);
...
...
//here it starts getting really confusing for me
...
...
rotateVectorAroundAxis(forwardD, upD, delta.X);
//rotateVectorAroundAxis(forwardD, rightD, delta.Y*-1);
//rotateVectorAroundAxis(upD, rightD, delta.Y*-1);
//rotateVectorAroundAxis(upD, forwardD, 0.0);
// And re-orient the camera to face along the foward and up axes.
camera->setTarget(camPos + forwardD);
camera->setUpVector(upD);
one of the obvious problems is, that I dont have a keyInput to wait for, instead I have a mouse delta... I compiled rogerborgs example and tested what happens if I hold up + right same time, and it did what it's supposed to do, so I thought since I have no event seperating each axis I'll have to do it all at the same time... as you can see, I uncommented a few to see what's going on with each of them seperatly... anyway it's not really working, best I got was rotation on X and Y as expected but strange & undesired banking... :?:
Well basically ... help !!! and a big thank you...
I'd need a few more years or a lot more patience...
zillion42
Posts: 324
Joined: Wed Aug 29, 2007 12:32 am
Location: Hamburg, Germany

Post by zillion42 »

strange... when I'm doing it in rogerborgs example it works just fine... couldn't say aynthing else... It feels like those puzzles with 2 pictures that look exactly the same on the first look and you have to find the 5 detail differences... :evil:

anyway, the following seems to work perfect and does the described with mouse input...

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")

gui::ICursorControl* cursor;
core::position2d<f32> CursorPos;

// 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;
	   }
	   if (event.EventType == EET_MOUSE_INPUT_EVENT)
	   {
			if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
			{
				CursorPos = cursor->getRelativePosition();
			}
	   }

      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,core::vector3df(0,0,0));
   MrMatrix.rotateVect(vector);
}


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

   IVideoDriver* driver = device->getVideoDriver();
   ISceneManager* smgr = device->getSceneManager();
   cursor = device->getCursorControl();
   // 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();
   camera->setPosition(core::vector3df(5,0,1));
   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);

      core::vector2df delta = CursorPos - core::vector2df(0.5,0.5);
	  cursor->setPosition(core::vector2df(0.5,0.5));
	  rotateVectorAroundAxis(forward, up, delta.X);
   	  rotateVectorAroundAxis(forward, right, delta.Y*-1);
	  rotateVectorAroundAxis(up, right, delta.Y*-1);

      // 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;
} 
...und sie wussten nicht was sie taten...
zillion42
Posts: 324
Joined: Wed Aug 29, 2007 12:32 am
Location: Hamburg, Germany

Post by zillion42 »

well, I still havent't solved the puzzle... I'm already googling "Quaternions for dummies"...
I animated the camera in the above example with a fly circle animator and the result was a pole flipping camera again... Do I have to move the target in a flycircle aswell ?
Seems unlogical because the forward axis and up axis is worked out on each frame and the target is set to camera position + forward before being alligned to forward and up, so it should work in a flycircle aswell, shouldn't it ?

well, to phrase it, it works fine as long as the camera isn't moving, and to make myself more clear I am not trying to get an orbiting camera but a camera that rotates around itself...

thanks again

EDIT:
I think I got it right & the example actually covers it all... :oops:
well, at least I added mouseinput... 8)
zillion42 wrote:...undesired banking...
is actually perfectly right and just my inability to think 3 dimensional at times. Remembering my days with the flightsim I figured it's the way it's supposed to be... This thread has it covered aswell btw:
http://irrlicht.sourceforge.net/phpBB2/ ... highlight=
Post Reply