Custom Camera OR Event Receiver for a Noob

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
android_808
Posts: 13
Joined: Mon Feb 28, 2005 4:12 pm

Custom Camera OR Event Receiver for a Noob

Post by android_808 »

I know this question has been asked several times before, but whenever I try one of the suggested methods I encounter numerous problems. So Far I have, essentially the code from the API page with a few alterations, like hiding the cursor and adding FPS to title bar.:

main.cpp

Code: Select all

#include <Irrlicht.h>

 using namespace irr;

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

 int main()
 {
        // start up the engine
        IrrlichtDevice *device = createDevice(video::EDT_OPENGL,
                core::dimension2d<s32>(640,480), false);

        video::IVideoDriver* driver = device->getVideoDriver();
        scene::ISceneManager* scenemgr = device->getSceneManager();
        gui::ICursorControl * cursorcontrol = device->getCursorControl();
        cursorcontrol->setVisible(false);
        
        // set initial window title shown whilst loading
        device->setWindowCaption(L"Cameleon Engine");

        // add .pk3 archive to the file system
        device->getFileSystem()->addZipFileArchive("maps/map-20kdm2.pk3");

        // load .bsp file and show it using an octtree
        scenemgr->addOctTreeSceneNode(scenemgr->getMesh("20kdm2.bsp"));

        // add a first person shooter style user controlled camera
        scenemgr->addCameraSceneNodeFPS();
 
        wchar_t tmp[255];
        
        // draw everything
        while(device->run() && driver)
        {
                driver->beginScene(true, true, video::SColor(255,0,0,255));
                scenemgr->drawAll();
                driver->endScene();
                // get driver name and fps to display in window title
		    	swprintf(tmp, 255, L"Cameleon Engine (%s fps:%d)", driver->getName(),	driver->getFPS());
		    	device->setWindowCaption(tmp);
        }

        // delete device
        device->drop();
        return 0;
 }
What I want to do is create a camera like the included FPS camera with a few alterations. I figured I could either take the standard camera class and build upon it to reach similar functionality to the FPS cam or just add an event receiver to camera.

I have a few questions which may help me choose one of the two possible methods:

Should I be inheriting from CCameraSceneNode or ICameraSceneNode if I make a custom camera class?

If using an event receiver, how to I stop it from stuttering? When I press a key, the camera moves forward, stutters and then carries on OK.
Fraza
Posts: 113
Joined: Sat Feb 26, 2005 11:28 am
Location: Leeds

Post by Fraza »

To stop the stuttering you can use keydown and key up events. For example:

bMovingForwards is initialised as false.
If bMoving forwards, then you move forwards (d'uh!)
1. On KeyDown forwards key, bMovingForwards is assigned to true.
2. On KeyUp forwards key, bMovingForwards is assigned to flase.

This is repeated for each direction. This will elliminate 'stuttering'.

Your camera scene node should be a pointer to the Camera.

For example

ICameraSceneNode* Camera = NULL;

(I recommend you use a global variable or a static variable in camera handling routines to store the camera. I don't recommend you put the camera variable into the main function or directly call it in the main game loop)

Another thing to bare in mind, the mathematics for the camera can be very tricky! Don't let yourself fall asleep when working it out!

Code: Select all

     ____  _______
    / ___\/ \__ \ \
   / /__ / ^ \/ /  \
  / ___// ___ \/| \ \
 / / /\ \/  /\ \|_|\ \
/_/_/ /\_\  \_\_\_\ \_\
android_808
Posts: 13
Joined: Mon Feb 28, 2005 4:12 pm

Post by android_808 »

Okay, I think I get what you mean about the bMovingForwards example. I could use the input receiver to record the events add them to a buffer, then at the start of the loop call a function to update the position rotation of the camera, something like:.

Code: Select all

        // draw everything
        while(device->run() && driver)
        {
                driver->beginScene(true, true, video::SColor(255,0,0,255));
                eventreceiver.update();
                scenemgr->drawAll();
                driver->endScene();
                // get driver name and fps to display in window title
             swprintf(tmp, 255, L"Cameleon Engine (%s fps:%d)", driver->getName(),   driver->getFPS());
             device->setWindowCaption(tmp);
        }
I have also discovered that the input receiver needs to allow multiple inputs. A previous test would stop when more than one key was pressed or if the mouse was moved at the same time. I have an example for this though from the forum.
Your camera scene node should be a pointer to the Camera.

For example

ICameraSceneNode* Camera = NULL;
Could you qualify this a little. I'm guessing I wouldn't need to create a new camera class, only the input reciever, as I am only concerned with the movement of the camera for now. So could I simply add:

Code: Select all

ICameraSceneNode* Camera = NULL;
camera = sm->addCameraSceneNode(0, core::vector3df(0,0,0), core::vector3df(-586,708,52));
to replace the existing FPS camera for now. When I start to introduce more features I could then move the definition to the header file and set it during the constructor.
Fraza
Posts: 113
Joined: Sat Feb 26, 2005 11:28 am
Location: Leeds

Post by Fraza »

at runtime, you can change the cameraSceneNode's position easily using Camera->setTarget(vector3df(fX, fY, fZ)); and Camera->setPosition(vector3df(fX, fY, fZ));

Becuase you can do this, all you need to do is work out the angles, which goes something like...

Code: Select all

// example taken from my personal camera scene node

    vector3df CameraPos = Camera->getPosition(), CameraTarget = Camera->getTarget();
    float fXZDistance= fDist*cos(PI*(fAngleY/180));
    CameraTarget.Y = fDist*sin(PI*(fAY/180))+fPosY;
    CameraTarget.X = cos(PI*(fAX/180))*fXZDistance+fPosX;
    CameraTarget.Z = sin(PI*(fAX/180))*fXZDistance+fPosZ;
    CameraPosition.X = fPosX;
    CameraPosition.Y = fPosY;
    CameraPosition.Z = fPosZ;
    Camera->setPosition(CameraPos);
    Camera->setTarget(CameraTarget);

what the variables are

fPosX, fPosY, fPosZ all mark the current position. Originally they will probably be set to 0, 0, 0. If you want to make the camera move forwards you can assign these to the Camera->getTarget()->X, Camera->getTarget()->Y and Camera->getTarget()->Z values. If you also want to move backwards, the difference between Camera->getPosition() and Camera->getTarget() values can be utilised.

fDist is the distance between the camera position and the camera look at.

fAX is the angle on the XZ plane that the camera faces in. (this may be a little complex, I will make a diagram)

Code: Select all

Z
^
|
|   ^
|  /
| /
|/ Angle here is fAX angle.
+--------------->X
fAY is a slightly more complex angle. it is the angle between the Y axis and XZ plane. (again a diagram will help get this across)

Code: Select all

Y
^
|
|   ^
|  /
| /
|/ Angle here is fAY angle.
+--------------->XZ Plane
angle fAY cannot be = to 90 or -90 but can be any value imbetween. Also, getting too close to 90 (e.g. 89.9) isn't necessary and should be avoided. 89 is a perfectly good angle. If you set fAY to 90 or -90 you will see exactly why these are undesirable values to use.

You won't need to create anew camera class. It's fairly pointless. My advice is create a procedure that handles all the camera events (e.g. moving), if you wish to reuse it then you can copy the procedure to a new project.

Also, I don't know about the keys jamming with the event receiver. Maybe you should consider making your own event receiver?

I hope this is of some help.
Post Reply