Modified third person camera troubles

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
maddog39
Posts: 10
Joined: Sat Aug 23, 2008 1:23 am
Location: Pennsylvania, US
Contact:

Modified third person camera troubles

Post by maddog39 »

Hey everyone,

I recently wanted a third person camera and attempted an update version of keless' camera based on ICameraSceneNode and IEventReceiver. However for the life of me I cannot get this thing to link. I keep getting undefined reference to ICameraSceneNode::setTarget() errors. Any ideas?

Source code: http://pastebin.com/f4351bca3

Thanks!
- Alec
koto
Posts: 7
Joined: Sun Aug 24, 2008 3:06 pm

Modified third person camera troubles

Post by koto »

Hi,

The problem is that setTarget method is "pure virtual method" one, declared in the ICameraSceneNode class. Thus you can not call it from your's derived class.

Also my compiler issued such a warning :
warning C4584: 'ThirdPersonCamera' : base-class 'irr::IEventReceiver' is already a base-class of 'irr::scene::ICameraSceneNode'.
maddog39
Posts: 10
Joined: Sat Aug 23, 2008 1:23 am
Location: Pennsylvania, US
Contact:

Post by maddog39 »

So I suppose I should have to reimplement that function; and I didnt realize ICameraSceneNode was already doing multiple-inheritance with IEventReceiver, so it should be safe to just overload OnEvent.

[Edit]
I have no idea how I would ago about rewriting that function and frustrated by the fact that I cant use it from a derived class. Looking through CCameraSceneNode.cpp/h in the irrlicht source code didnt give me much insight.
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

You can just override the method by implementing the OnEvent method with a properly specified member function. You have to make that method public (in the class definition) in order to override the publically available function. That will also make it available from within a derived class.
maddog39
Posts: 10
Joined: Sat Aug 23, 2008 1:23 am
Location: Pennsylvania, US
Contact:

Post by maddog39 »

I got that, was already solved as I dont need to extend IEventReceiver anymore, thats the only code modification I had to make. I was talking about setTarget() which is required by the code but cant be called and does not allow the class to link.
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

The same holds for setTarget, besides coming from ICameraSceneNode instead of IEventReceiver.
maddog39
Posts: 10
Joined: Sat Aug 23, 2008 1:23 am
Location: Pennsylvania, US
Contact:

Post by maddog39 »

I'm still not quite clear on what your suggesting. I attempt this:

Code: Select all

void ThirdPersonCamera::setTarget(const vector3df &pos)
{
	ICameraSceneNode::setTarget(pos);
}
But that didnt solve the problem.
rogerborg
Admin
Posts: 3590
Joined: Mon Oct 09, 2006 9:36 am
Location: Scotland - gonnae no slag aff mah Engleesh
Contact:

Post by rogerborg »

Uh...
koto wrote:The problem is that setTarget method is "pure virtual method" one, declared in the ICameraSceneNode class. Thus you can not call it from your's derived class.
I don't know what vintage that code it, but it's certainly not valid with Irrlicht 1.4+.

It appears to assume that ICameraSceneNode provides a full camera implementation. That's no longer correct (was it ever?). If you want to do that now, you'll either have to base your camera on CCameraSceneNode, or encapsulate one.
Please upload candidate patches to the tracker.
Need help now? IRC to #irrlicht on irc.freenode.net
How To Ask Questions The Smart Way
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Hmm, which code? koto's reply is correct, the setTarget is pure virtual, and IMHO was all the time. You can copy the code from CCameraSceneNode.cpp or just do your own.
rogerborg
Admin
Posts: 3590
Joined: Mon Oct 09, 2006 9:36 am
Location: Scotland - gonnae no slag aff mah Engleesh
Contact:

Post by rogerborg »

But you'd still have to implement all the other pure virtual methods of ICameraSceneNode. Easier to just encapsulate a CCameraSceneNode.

Actually, I wouldn't bother doing it as a scene node at all: I'd just call the body of ThirdPersonCamera::OnRegisterSceneNode() from my main loop, but since we're 'fixing' ThirdPersonCamera...

Code: Select all

#include <irrlicht.h>
#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#endif


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

// Note: This is based on ISceneNode, NOT ICameraSceneNode, since we don't want to have to
// implement all the methods in ICameraSceneNode
class ThirdPersonCamera : public ISceneNode
{
public:
    ThirdPersonCamera(
        ISceneNode* child,
        ISceneManager* scene,
        s32 id = -1,
        vector3df position = vector3df(0, 0, 0),
        f32 camHeight = 80.0f,
        f32 camLeash = 100.0f,
        f32 camSpeed = 1.0f
    ) : ISceneNode(scene->getRootSceneNode(), scene, id)
    {
        realCamera = scene->addCameraSceneNode(this);
        speed = camSpeed;
        height = camHeight;
        leash = camLeash;
        target = child;

        // Setup camera
        lastTargetPos = target->getAbsolutePosition();
        setPosition(lastTargetPos + vector3df(0, 20, 20));
        realCamera->setTarget(lastTargetPos);
        target->grab();
    }

    ~ThirdPersonCamera()
    {
        target->drop();
    }

    void OnRegisterSceneNode()
    {
        vector3df myPosition = getAbsolutePosition();
        const vector3df currTargetPos = target->getAbsolutePosition();
        vector3df camToTarg = currTargetPos - myPosition;
       
        // Leash is only in the X-Z plane, so only count distance using X and Z
        vector2df xzDist(camToTarg.X, camToTarg.Z);
       
        // If too far away, move camera closer
        if (xzDist.getLength() > leash)
        {
            // Set X-Z position, move closer
            vector3df camToTargOrig = vector3df( xzDist.X, 0, xzDist.Y);
            camToTarg = camToTargOrig.normalize() * (camToTargOrig.getLength() - leash);
            myPosition += camToTarg;
        }

        // Set Y position
        myPosition.Y = currTargetPos.Y + height;

        setPosition(myPosition);
       
        // Look at target position
        realCamera->setTarget(currTargetPos);
        lastTargetPos = currTargetPos;
       
        // Register scene node for rendering
        if (IsVisible)
            SceneManager->registerNodeForRendering(this);
       
        ISceneNode::OnRegisterSceneNode();
    }

    void render(void) { /* noop */ }

    const core::aabbox3d<f32>& ThirdPersonCamera::getBoundingBox() const
    {
        return realCamera->getBoundingBox();
    }

    ICameraSceneNode * getRealCamera(void)
    {
        return realCamera;
    }
 
private:
    ICameraSceneNode * realCamera;
    ISceneNode* target;
    vector3df lastTargetPos;
    f32 height;
    f32 leash;
    f32 speed;
};


// I'm going to use a separate event receiver, since there's little logical reason to
// put the functionality into a camera.
class MyEventReceiver : public IEventReceiver
{
public:
    // This is the one method that we have to implement
    virtual bool OnEvent(const SEvent& event)
    {
        // Remember whether each key is down or up
        if (event.EventType == irr::EET_KEY_INPUT_EVENT)
            KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;

        return false;
    }

    // This is used to check whether a key is being held down
    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:
    // We use this array to store the current state of each key
    bool KeyIsDown[KEY_KEY_CODES_COUNT];
};


int main()
{
    IrrlichtDevice *device =
        createDevice( video::EDT_OPENGL, dimension2d<s32>(640, 480), 16,
            false, false, false, 0);

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

    IAnimatedMesh* mesh = smgr->getMesh("../../media/sydney.md2");
    IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );
    if (node)
    {
        node->setMaterialFlag(EMF_LIGHTING, false);
        node->setMD2Animation ( scene::EMAT_STAND );
        node->setMaterialTexture( 0, driver->getTexture("../../media/sydney.bmp") );
    }

    const f32 heightOffset = node->getBoundingBox().getExtent().Y - node->getBoundingBox().getCenter().Y;

    node->setPosition(vector3df(1000, 0, 1000));

    ThirdPersonCamera * camera = new ThirdPersonCamera(node, smgr);
    MyEventReceiver eventReceiver;
    device->setEventReceiver(&eventReceiver);


    scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode(
        "../../media/terrain-heightmap.bmp",
        0,                              // parent node
        -1,                              // node id
        core::vector3df(0.f, 0.f, 0.f),         // position
        core::vector3df(0.f, 0.f, 0.f),         // rotation
        core::vector3df(40.f, 4.4f, 40.f),      // scale
        video::SColor ( 255, 255, 255, 255 ),   // vertexColor,
        5,                              // maxLOD
        scene::ETPS_17,                     // patchSize
        4                              // smoothFactor
        );

    terrain->setMaterialFlag(video::EMF_LIGHTING, false);
    terrain->setMaterialTexture(0, driver->getTexture("../../media/terrain-texture.jpg"));
    terrain->setMaterialTexture(1, driver->getTexture("../../media/detailmap3.jpg"));
    terrain->setMaterialType(video::EMT_DETAIL_MAP);
    terrain->scaleTexture(1.0f, 20.0f);


    u32 then = device->getTimer()->getTime();

    while(device->run())
    {
        u32 now = device->getTimer()->getTime();
        f32 delta = (f32)(now - then) / 1000.f;
        then = now;

        if(eventReceiver.IsKeyDown(KEY_KEY_A))
            node->setRotation(node->getRotation() + (vector3df(0, -180, 0) * delta));

        if(eventReceiver.IsKeyDown(KEY_KEY_D))
            node->setRotation(node->getRotation() + (vector3df(0, 180, 0) * delta));

        if(eventReceiver.IsKeyDown(KEY_KEY_W))
        {
            // vector3df direction = node->getRotation().rotationToDirection(vector3df(1, 0, 0)); // Will be available in 1.5
            vector3df direction(1, 0, 0); // Sydney faces +X
            direction.rotateXZBy(-node->getRotation().Y); // rotateXZBy() seems bass ackwards
            node->setPosition(node->getAbsolutePosition() + (direction * 100.f * delta));
        }

        if(eventReceiver.IsKeyDown(KEY_KEY_S))
        {
            // vector3df direction = node->getRotation().rotationToDirection(vector3df(1, 0, 0)); // Will be available in 1.5
            vector3df direction(1, 0, 0); // Sydney faces +X
            direction.rotateXZBy(-node->getRotation().Y); // rotateXZBy() seems bass ackwards
            node->setPosition(node->getAbsolutePosition() + (direction * -100.f * delta));
        }

        node->updateAbsolutePosition();
        vector3df position = node->getAbsolutePosition();
        position.Y = terrain->getHeight(position.X, position.Z) + heightOffset;
        node->setPosition(position);

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

    camera->drop();
    device->drop();
    return 0;
}
Last edited by rogerborg on Wed Aug 27, 2008 9:20 am, edited 1 time in total.
Please upload candidate patches to the tracker.
Need help now? IRC to #irrlicht on irc.freenode.net
How To Ask Questions The Smart Way
maddog39
Posts: 10
Joined: Sat Aug 23, 2008 1:23 am
Location: Pennsylvania, US
Contact:

Post by maddog39 »

rogerborg wrote: It appears to assume that ICameraSceneNode provides a full camera implementation. That's no longer correct (was it ever?). If you want to do that now, you'll either have to base your camera on CCameraSceneNode, or encapsulate one.
Yes I was assuming when I tried to write my implementation that it was a full implementation. Although it seems that it was intended to be more of an abstract class, am I right? But if thats the case then why would we be instantiating it for example with addCameraSceneNode()? I dont know, just a thought.
rogerborg wrote: Actually, I wouldn't bother doing it as a scene node at all: I'd just call the body of ThirdPersonCamera::OnRegisterSceneNode() from my main loop, but since we're 'fixing' ThirdPersonCamera...
Yea, the original code for this class actually did it that way. Although people were suggesting that it might make more sense to make it a scene node so it could automatically update itself. Thanks a lot for your help and your code, I will be trying this out later. Perhaps it does make more sense to simply have an update() function or something and call it from the mainloop.
dlangdev
Posts: 1324
Joined: Tue Aug 07, 2007 7:28 pm
Location: Beaverton OR
Contact:

Post by dlangdev »

Nice code, I never thought about that implementation, tho. Thanks.
Image
maddog39
Posts: 10
Joined: Sat Aug 23, 2008 1:23 am
Location: Pennsylvania, US
Contact:

Post by maddog39 »

rogerborg, I tried compiling your example today and quickly discovered that it wasnt compiling. Apparently because vector3df does not have a function rotationToDirection() and I even checked the docs to confirm this.

Code: Select all

[maddog39@desktop ThirdPersonCamera]$ make
g++ main.cpp -O3 -march=core2 -I/usr/include/irrlicht -lIrrlicht -lX11 -lXxf86vm -lXext -lXrandr -lGL -lGLU -o tpc
main.cpp:83: error: extra qualification ‘ThirdPersonCamera::’ on member ‘getBoundingBox’
main.cpp: In function ‘int main()’:
main.cpp:199: error: ‘const class irr::core::vector3d<float>’ has no member named ‘rotationToDirection’
main.cpp:205: error: ‘const class irr::core::vector3d<float>’ has no member named ‘rotationToDirection’
make: *** [all] Error 1
I figured it might have been because I was running an older version of Irrlicht but that doesnt seem to be the case.

Code: Select all

[maddog39@desktop ThirdPersonCamera]$ pacman -Qi irrlicht
Name           : irrlicht
Version        : 1.4.1-0
URL            : http://irrlicht.sourceforge.net/index.html
Licenses       : zlib/libpng  
Groups         : None
Provides       : None
Depends On     : x-server  zlib  libpng  libjpeg  
Optional Deps  : None
Required By    : None
Conflicts With : None
Replaces       : None
Installed Size : 38359.13 K
Packager       : Unknown Packager
Architecture   : i686
Build Date     : Thu 24 Jul 2008 11:10:00 AM EDT
Install Date   : Thu 24 Jul 2008 11:11:26 AM EDT
Install Reason : Explicitly installed
Install Script : No
Description    : The Irrlicht Engine is an open source high performance realtime 3D engine.
[Edit]
Well I found an SVN log entry on July 17 for revision 1416 where it's noted that your rotationToDirection() function was added. So I am assuming run this with an SVN build, and I would assume it will be availible in 1.5?
rogerborg
Admin
Posts: 3590
Joined: Mon Oct 09, 2006 9:36 am
Location: Scotland - gonnae no slag aff mah Engleesh
Contact:

Post by rogerborg »

maddog39 wrote:Yes I was assuming when I tried to write my implementation that [ICameraSceneNode] was a full implementation. Although it seems that it was intended to be more of an abstract class, am I right? But if thats the case then why would we be instantiating it for example with addCameraSceneNode()? I dont know, just a thought.
All of the Irrlicht "I" classes are interfaces. They all contain at least one pure virtual method that needs to be implemented in a concrete class. None of them should be directly instantiatable.

So when you do ISceneManager::addCameraSceneNode(), it returns an ICameraSceneNode *, but it's actually a CCameraSceneNode object that's created.
maddog39 wrote:rogerborg, I tried compiling your example today and quickly discovered that it wasnt compiling. Apparently because vector3df does not have a function rotationToDirection() and I even checked the docs to confirm this.
Ah, I'm sorry, you're quite right. I always use the SVN trunk, and forget that most users are still on 1.4 / 1.4.1. I've corrected the sample to use rotateXZBy() instead.
Please upload candidate patches to the tracker.
Need help now? IRC to #irrlicht on irc.freenode.net
How To Ask Questions The Smart Way
Post Reply