Manual event driven Camera

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
thanhle
Posts: 325
Joined: Wed Jun 12, 2013 8:09 am

Manual event driven Camera

Post by thanhle »

Hi all,

Below is the camera driven by user generated mouse and keyboard events. Useful for creating editor using different gui interface.
The camera has pan, zoom, rotate and keyboard input.
There are some constants in the code. Ideally I want to change those value dynamically at run time, to speed up the desired effect according to the distance between the location of interest with the camera. I will update if I have time.

MyEventReceiver.h

Code: Select all

 
#pragma once
#ifndef MYEVENTRECEIVER_H
#define MYEVENTRECEIVER_H
 
 
#include <irrlicht.h>
#include <iostream>
using namespace irr;
using namespace std;
 
class MyEventReceiver // : public IEventReceiver
{
public:
   MyEventReceiver()
   {
      u32 k;
      for (k = 0; k < sizeof(Keys) / sizeof(*Keys); ++k)
         Keys[k] = false;
   }
 
   virtual ~MyEventReceiver()
   {
   }
 
   // you may need to change the parameter type depending on your Irrlicht version
 /*  virtual bool OnEvent(const SEvent& event)
   {
      cout <<   "event!/n";
      if (event.EventType == EET_KEY_INPUT_EVENT)
      {
         Keys[event.KeyInput.Key] = event.KeyInput.PressedDown;
         return true;
      }
      return false;
   }*/
 
 
public: bool ManualEvent(const SEvent& event)
   {
      //Register keydown events
      if (event.EventType == EET_KEY_INPUT_EVENT)
      {
         Keys[event.KeyInput.Key] = event.KeyInput.PressedDown;
         return true;
      }
      //Set receiver event to mouse
      if(event.EventType == EET_MOUSE_INPUT_EVENT)
      {  
        this->mEvent = event;
        return true;
      }
      return false;
   }
 
      // This is used to check whether a key is being held down
    virtual bool IsKeyDown(EKEY_CODE keyCode) const
    {
        return Keys[keyCode];
    }
 
public:
   bool Keys[KEY_KEY_CODES_COUNT];
   core::position2d <s32> lastMousePosition;
   SEvent mEvent;
};
 
#endif
 
CameraOrbit.h

Code: Select all

 
#pragma once
#ifndef ORBITCAMERA_H
#define ORBITCAMERA_H
 
#include <irrlicht.h>
#include "MyEventReceiver.h"
 
namespace NVuDu {
 
    using namespace irr;
    using namespace core;
    using namespace scene;
    using namespace video;
    using namespace io;
    using namespace gui;
 
     class OrbitCamera
    {
        
        private:  float phyTolerance ;
        IrrlichtDevice *device;
        public: scene::ICameraSceneNode *camera;
        scene::ISceneManager *smgr;
        vector3df target;
        core::position2d <s32> mouseLastPosition;
        double radius;
        vector3df rot, pos;
 
        public: OrbitCamera(IrrlichtDevice* device, ICameraSceneNode *cam, vector3df target)
        {
            phyTolerance = 0.001f;
            this->device = device;
            this->camera = cam; 
            this->target = target;
            this->smgr = device->getSceneManager();
            this->pos = camera->getPosition();
 
            this->radius = vector3df(this->target - this->pos).getLength(); //  initRadius;
            vector3df dir = target - pos ;
            float len = dir.getLength();
            dir.normalize();
            rot = dir.getSphericalCoordinateAngles()*PI/180;
    }
 
      vector3df getPositionShericalCoordinate( f32 rotX, f32 rotY, f32 radius )
    {
        float   phy = rotX;
        float theta = rotY;
 
        vector3df camPos( sin(phy)*sin(theta) , cos(phy), sin(phy)*cos(theta));
        camPos*=radius;
 
        return camPos;
    }
 
      public:  vector3df getSphericalAngleFromDirection( vector3df dir )
    {
       
        float r = dir.getLength();
        float phy = acos(dir.Y);
        float theta = atan2f(dir.X, dir.Z);
 
        return vector3df(phy, theta,0);
    }
 
       void updateCameraByKeys(MyEventReceiver &receiver,  f32 Elapsed )
       {
            UpdateByKeys( receiver,  Elapsed);  
        }
 
         void UpdateByKeys(MyEventReceiver &receiver, f32 Elapsed)
         {
                
            f32 Speed = 10;
            
            vector3df up =  camera->getUpVector();
            vector3df tpos = camera->getPosition();
            vector3df tlookat =  camera->getTarget();
            vector3df dir = tlookat - tpos;
            float len = dir.getLength();
            dir.normalize();
            vector3df right = dir.crossProduct(up);
            right.normalize();
            vector3df newTarget;
            vector3df nNUp = right.crossProduct(dir);
 
            vector3df forward1 = dir;
            forward1 = forward1*len;
        
            float len2 = forward1.getLength();
            vector3df nup = right.crossProduct(dir);
    
            // adjust camera position based on current key input
             if (receiver.IsKeyDown(KEY_KEY_W))
             {
                tpos = tpos + dir*Speed;
                newTarget = tpos + forward1;
            
             }
             if (receiver.Keys[KEY_KEY_S])
              {
                 tpos = tpos - dir*Speed;
                 newTarget = tpos + forward1;
                
             }
             if (receiver.Keys[KEY_KEY_A])
              {   
                 tpos = tpos + right*Speed;
                newTarget = tpos + forward1;
            
             }
             if (receiver.Keys[KEY_KEY_D])
               {
                 tpos = tpos - right*Speed;
                newTarget = tpos + forward1;
            
             }
             this->radius = len;
             this->target = newTarget;
             camera->setPosition(tpos);
             camera->setTarget(newTarget);
         }
 
         void PerformPan(MyEventReceiver &receiver)
         {
            f32 speed = 5;
            vector3df up =  camera->getUpVector();
            vector3df pos1 = camera->getPosition();
            vector3df lookat =  camera->getTarget();
            vector3df dir = lookat - pos1;
            float mradius = dir.getLength();
            dir.normalize();
            vector3df right = dir.crossProduct(up);
            right.normalize();
            vector3df newUp = right.crossProduct(dir);
            newUp.normalize();
                        
            core::position2d <s32> m = device->getCursorControl()->getPosition();
            vector3df mousePos(m.X, m.Y, 0);
            vector3df lastPos(receiver.lastMousePosition.X,  receiver.lastMousePosition.Y, 0);
            vector3df offset =  mousePos - lastPos;                         
            offset.normalize();
    
            pos1 = pos1 + right*offset.X*speed;
            pos1 = pos1 + newUp *offset.Y*speed;
                        
            vector3df forward = newUp.crossProduct(right); 
            forward.normalize();
            forward = forward*mradius;
                        
            camera->setPosition(pos1);
            ////camera->setUpVector(newUp);  //Correct for vector. Maybe other mosue mode??? 3rd person??? or FPS
            camera->setTarget(forward + pos1);
            receiver.lastMousePosition = m;
                        
            this->pos = pos1;
            this->target = forward + pos1;
            this->radius = mradius;
        }
 
         void PerformZoomByMiddleMouse(MyEventReceiver &receiver)
         {
                f32 Speed = 5;
                //System::Diagnostics::Debug::WriteLine("target " + target.X + "," +  target.Y + "," +  target.Z  + " pos " + pos.X + "," + pos.Y + "," + pos.Z);
                core::position2d <s32> current = device->getCursorControl()->getPosition();
                core::position2d <s32> delta =  receiver.lastMousePosition - current;
                        
                vector3df tpos = camera->getPosition();
                vector3df oldpos = tpos;
                vector3df tlookat =  camera->getTarget();
                        
                vector3df dir = tlookat - tpos;
                float len = dir.getLength();
                dir.normalize();
                    
                vector3df forward1 = dir;
                forward1 = forward1*len;
                    
                if(delta.Y < 0)
                {
                    tpos = tpos + dir*Speed;
                }else if(delta.Y > 0)
                {
                    tpos = tpos - dir*Speed;
                }
                        
                if( (tpos - target).getLength() < Speed)  //Prevent further zoom.
                {
                    tpos = oldpos;
                }
 
                this->radius = len;
                camera->setPosition(tpos);
            
                receiver.lastMousePosition = current;
            }
 
         void UpdateByMouse(MyEventReceiver &receiver, f32 Elapsed)
         {
                if(receiver.mEvent.MouseInput.Event == EMOUSE_INPUT_EVENT::EMIE_MOUSE_MOVED)
                {
                    if(receiver.mEvent.MouseInput.isMiddlePressed()) //Zoom by middle mouse pressed...
                    {
                        PerformZoomByMiddleMouse(receiver);
                        return;
                    }
 
                    if(receiver.mEvent.MouseInput.isRightPressed())  //Pan...
                    {
                        PerformPan(receiver);
                        return;
                    }
 
 
                    //Orbit
                    if(receiver.mEvent.MouseInput.isLeftPressed())
                    {
                         core::position2d <s32> current = device->getCursorControl()->getPosition();
                         core::position2d <s32> delta =   current - receiver.lastMousePosition;
 
                         vector3df oldRot = this->rot;
                     
                         this->rot.X = this->rot.X + (float)delta.Y/100;
 
                          
                         this->rot.Y =   this->rot.Y + (float)delta.X/100 ;
                    
                         this->rot.Y = this->rot.Y > 2*PI? this->rot.Y-2*PI: this->rot.Y;
                         this->rot.Y = this->rot.Y < -2*PI? this->rot.Y+2*PI: this->rot.Y;
                         receiver.lastMousePosition = current;
 
                        if(this->rot.X >= (PI - phyTolerance))
                        {
                            this->rot.X = oldRot.X; // -=  abs(rotVal);
                        
                        }
                        else if(this->rot.X <= phyTolerance)
                        {
                            this->rot.X = oldRot.X ;
                        
                        //  this->rot.X += abs(rotVal);
                        }
                          
                    }
                }
 
                //Zoom code below...
                if(receiver.mEvent.EventType ==  irr::EET_MOUSE_INPUT_EVENT)  //Zoom to target...
                {
                    if(receiver.mEvent.MouseInput.Event == irr::EMIE_MOUSE_WHEEL)
                    {
                        if(receiver.mEvent.MouseInput.Wheel > 0)
                        {
                            //deltaLen = 5;
                            radius *= 1.2;
                            radius = min<float>( radius, 100000);
                        }
                        else
                        {
                            //deltaLen = -5;
                            radius /= 1.2;
                            radius = max<float>( radius, 0.05);
                        }
                    }
                }
 
            //Update for the orbit -> Zoom camera...
            vector3df lookat =  camera->getTarget();
            vector3df pos2 = target - getPositionShericalCoordinate(this->rot.X, this->rot.Y, radius) ;
            this->pos = pos2;
            this->camera->setPosition(pos2);
 
            vector3df forward = target - this->pos;
 
            forward = forward.normalize()*radius;
 
            target = this->pos  + forward;
                this->camera->setTarget(target);
            
            //camera->setUpVector(newUp);  //If we want to have continuous rotation. Update upvector.
        }
     
       void updateCameraByMouse(MyEventReceiver &receiver,  f32 Elapsed )
       {
            UpdateByMouse( receiver,  Elapsed); 
        }
 
    };
}
#endif // ORBITCAMERA_H
 
Fix/Update
* Update to be compatible with existing getSphericalCoordinateAngles() function.
* Fix to direction sign.


Usage:
OrbitCamera *cam = new OrbitCamera(device, myCAm, myCAm->getTarget());
MyEventReceiver * receiver = new MyEventReceiver();


Inside On mouse move event

if(mouseLeftDown) //This button down while mouse move will do the orbit.
{
core::position2d <s32> pos = device->getCursorControl()->getPosition();

SEvent evt;
evt.EventType = EET_MOUSE_INPUT_EVENT;
evt.MouseInput.Event = irr::EMOUSE_INPUT_EVENT::EMIE_MOUSE_MOVED;
evt.MouseInput.ButtonStates = irr::EMBSM_LEFT;

evt.MouseInput.X = pos.X;
evt.MouseInput.Y = pos.Y;
receiver->ManualEvent(evt);

cam ->updateCameraByMouse(* receiver, 0.1);

}


if(mouseRightDown) //This button down and mouse move will doe a pan in X and Y direction
{

core::position2d <s32> pos = device->getCursorControl()->getPosition();
SEvent evt;
evt.EventType = EET_MOUSE_INPUT_EVENT;
evt.MouseInput.Event = irr::EMOUSE_INPUT_EVENT::EMIE_MOUSE_MOVED;
evt.MouseInput.ButtonStates = irr::EMBSM_RIGHT;

evt.MouseInput.X = pos.X;
evt.MouseInput.Y = pos.Y;

receiver->ManualEvent(evt);
myCam->updateCameraByMouse(* receiver, 0.1);

}


if(mouseMiddleDown) //This button mouse down with mouse move will do a zoom in/out
{
core::position2d <s32> pos = device->getCursorControl()->getPosition();
SEvent evt;
evt.EventType = EET_MOUSE_INPUT_EVENT;
evt.MouseInput.Event = irr::EMOUSE_INPUT_EVENT::EMIE_MOUSE_MOVED;
evt.MouseInput.ButtonStates = irr::EMBSM_MIDDLE ;// // BSM_MIDDLE;// event.KeyInput.PressedDown

evt.MouseInput.X = pos.X;
evt.MouseInput.Y = pos.Y;

receiver->ManualEvent(evt);
myCam->updateCameraByMouse(* receiver, 0.1);
}


//Key events to do a move forward and pan left/right
SEvent evt;
evt.EventType = EET_KEY_INPUT_EVENT;
evt.KeyInput.PressedDown = true;
evt.KeyInput.Key = (EKEY_CODE)e->KeyCode;
receiver->ManualEvent(evt);
myCam->updateCameraByKeys(* receiver, 0.1);


//Mouse scroll event for also zoom.
SEvent evt;
evt.EventType = irr::EET_MOUSE_INPUT_EVENT;
evt.MouseInput.Event = irr::EMIE_MOUSE_WHEEL;
evt.MouseInput.ButtonStates = irr::EMBSM_EXTRA1;
evt.MouseInput.Wheel = e->Delta;
receiver->ManualEvent(evt);
myCam->updateCameraByMouse(* receiver, 0.1);
Post Reply