(C++) RTSCamera

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
CmdKewin
Posts: 29
Joined: Thu Aug 17, 2006 1:49 pm

(C++) RTSCamera

Post by CmdKewin »

[EDIT 22.05.2007] Latest version http://irrlicht.sourceforge.net/phpBB2/ ... 066#117579

[EDIT 12.04.2007] Latest version http://irrlicht.sourceforge.net/phpBB2/ ... 066#112891

I felt like it was about time to give back something to Irrlicht's users...

A nice and shiny Camera Class. Ideal for RTS's. It's basically a reimplementation of the Maya Camera, with a few additions.
Most notably:

-No vertical translation
-Translates with both WASD keys, right mouse button and arrow keys
-Translates with mouse movement on border screen. Left, Right, Up and Down
-Zooms with right-left mouse buttons and Wheel.
-Ability to recenter itself around a node. Useful for unit selection.

On my todo list:
-Make transaction from current node to next non-linear (interpolation anyone? :))
-Make keys configurable via xml file
-I need to exose a few parameters: min and max zoom levels are already there (setMaxZoom and setMinZoom), but i want to have the selected node (when clicking on one) to be public, in order for a main StateManager to check for node type and react accordingly.
-Expore current zoom level.

Feel free to use it in your projects and modify/correct it. Just let me know of any useful improvement :)

Compiled with SVN revision 301. Shoul work on 1.1 too. But then 1.2 is coming in a few days/weeks :)

Code follows.

RTSCamera.h

Code: Select all

#ifndef __RTSCamera__
#define __RTSCamera__

#include <irrlicht.h>

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

class RTSCamera : public ICameraSceneNode
{
	public:
		RTSCamera(IrrlichtDevice* devicepointer,ISceneNode* parent,ISceneManager* smgr,s32 id,
			f32 rotateSpeed = -1000.0f,f32 zoomSpeed = 1000.0f,f32 translationSpeed = 1000.0f);

		virtual ~RTSCamera();

		//Events
		virtual bool OnEvent(SEvent event);
		virtual void OnPreRender();
		virtual void render();
		virtual void OnPostRender(u32 timeMs);

		//Setup
		virtual void setInputReceiverEnabled(bool enabled);
		virtual bool isInputReceiverEnabled();

		//Gets
		virtual const aabbox3d<f32>& getBoundingBox() const;
		virtual const matrix4& getProjectionMatrix();
		virtual const SViewFrustrum* getViewFrustrum();
		virtual vector3df getTarget() const;
		virtual const matrix4& getViewMatrix();
		virtual vector3df getUpVector() const;
		virtual f32 getNearValue();
		virtual f32 getFarValue();
		virtual f32 getAspectRatio();
		virtual f32 getFOV();

		//Sets
		virtual void setNearValue(f32 zn);
		virtual void setFarValue(f32 zf);
		virtual void setAspectRatio(f32 aspect);
		virtual void setFOV(f32 fovy);
		virtual void setUpVector(const vector3df& pos);
		virtual void setProjectionMatrix(const matrix4& projection);
		virtual void setPosition(const vector3df& newpos);
		virtual void setTarget(const vector3df& newpos);

		//Helper Functions
		void pointCameraAtNode(ISceneNode* selectednode);
		void setMinZoom(f32 amount);
		void setMaxZoom(f32 amount);

		//Type Return
		virtual ESCENE_NODE_TYPE getType() { return ESNT_CAMERA; }

	protected:
		//Properties
		vector3df Target;
		vector3df UpVector;
		matrix4 Projection;
		matrix4 View;
		SViewFrustrum ViewArea;
		aabbox3d<f32> BBox;
		bool InputReceiverEnabled;
		dimension2d<f32> screenDim;
		f32 Fovy;		//Field of view, in radians.
		f32 Aspect;		//Aspect ratio.
		f32 ZNear;		//Value of the near view-plane.
		f32 ZFar;		//Z-value of the far view-plane.

		void recalculateProjectionMatrix();
		void recalculateViewArea();

	private:
		IrrlichtDevice* device;
		vector3df Pos;
		bool zooming,rotating,moving,translating;
		f32 zoomSpeed;
		f32 translateSpeed;
		f32 rotateSpeed;
		f32 rotateStartX, rotateStartY;
		f32 zoomStartX, zoomStartY;
		f32 translateStartX, translateStartY;
		f32 currentZoom;
		f32 rotX, rotY;
		vector3df oldTarget;
		vector2df MousePos;
		bool Keys[KEY_KEY_CODES_COUNT];
		bool MouseKeys[3];
		f32 targetMinDistance;
		f32 targetMaxDistance;

		enum MOUSE_BUTTON
		{
			MOUSE_BUTTON_LEFT,
			MOUSE_BUTTON_MIDDLE,
			MOUSE_BUTTON_RIGHT
		};

		void allKeysUp();
		void allMouseKeysUp();
		bool isKeyDown(s32 key);
		bool isMouseKeyDown(s32 key);
		void animate();
		void updateAnimationState();
};

#endif
RTSCamera.cpp

Code: Select all

#include "RTSCamera.h"

RTSCamera::RTSCamera(IrrlichtDevice* devicepointer,ISceneNode* parent,ISceneManager* smgr,s32 id,
	 f32 rs,f32 zs,f32 ts)
: ICameraSceneNode(parent,smgr,id,vector3df(1.0f,1.0f,1.0f),vector3df(0.0f,0.0f,0.0f),
					vector3df(1.0f,1.0f,1.0f)),InputReceiverEnabled(true)
{
	device = devicepointer;
	BBox.reset(0,0,0);

	UpVector.set(0.0f,1.0f,0.0f);

	Fovy = core::PI / 2.5f;
	Aspect = 4.0f / 3.0f;
	ZNear = 1.0f;
	ZFar = 3000.0f;

	IVideoDriver* d = smgr->getVideoDriver();
	if (d)
	{
		screenDim.Width = (f32)d->getCurrentRenderTargetSize().Width;
		screenDim.Height = (f32)d->getCurrentRenderTargetSize().Height;
		Aspect = screenDim.Width / screenDim.Height;
	}

	zooming = false;
	rotating = false;
	moving = false;
	translating = false;
	zoomSpeed = zs;
	rotateSpeed = rs;
	translateSpeed = ts;
	currentZoom = 100.0f;
	targetMinDistance = 1.0f;
	targetMaxDistance = 2000.0f;
	Target.set(0.0f,0.0f,0.0f);
	rotX = 0;
	rotY = 0;
	oldTarget = Target;

	allKeysUp();
	allMouseKeysUp();

	recalculateProjectionMatrix();
	recalculateViewArea();

	smgr->setActiveCamera(this);
}

RTSCamera::~RTSCamera()
{
}

bool RTSCamera::OnEvent(SEvent event)
{
	if (!InputReceiverEnabled)
		return false;

	ISceneNode* selectednode;
	dimension2d<s32> ssize = SceneManager->getVideoDriver()->getScreenSize();

	if(event.EventType == EET_MOUSE_INPUT_EVENT)
	{
		switch(event.MouseInput.Event)
		{
			case EMIE_LMOUSE_PRESSED_DOWN:
				selectednode = SceneManager->getSceneCollisionManager()->getSceneNodeFromScreenCoordinatesBB(device->getCursorControl()->getPosition(),0xFF,false);

				if(selectednode)
					pointCameraAtNode(selectednode);
				else
					MouseKeys[0] = true;
				break;
			case EMIE_RMOUSE_PRESSED_DOWN:
				MouseKeys[2] = true;
				break;
			case EMIE_MMOUSE_PRESSED_DOWN:
				MouseKeys[1] = true;
				break;
			case EMIE_LMOUSE_LEFT_UP:
				MouseKeys[0] = false;
				break;
			case EMIE_RMOUSE_LEFT_UP:
				MouseKeys[2] = false;
				break;
			case EMIE_MMOUSE_LEFT_UP:
				MouseKeys[1] = false;
				break;
			case EMIE_MOUSE_MOVED:
				MousePos.X = event.MouseInput.X / (f32)ssize.Width;
				MousePos.Y = event.MouseInput.Y / (f32)ssize.Height;
				break;
			case EMIE_MOUSE_WHEEL:
				currentZoom -= event.MouseInput.Wheel * zoomSpeed;
				break;
		}
		return true;
	}

	if(event.EventType == EET_KEY_INPUT_EVENT)
	{
		Keys[event.KeyInput.Key] = event.KeyInput.PressedDown;
		return true;
	}

	return false;
}

void RTSCamera::OnPreRender()
{
	IVideoDriver* driver = SceneManager->getVideoDriver();
	if (!driver)
		return;

	if (SceneManager->getActiveCamera() == this)
	{
		screenDim.Width = (f32)driver->getCurrentRenderTargetSize().Width;
		screenDim.Height = (f32)driver->getCurrentRenderTargetSize().Height;

		driver->setTransform(ETS_PROJECTION,Projection);

		//If UpVector and Vector to Target are the same, we have a problem.
		//Correct it.
		vector3df pos = getAbsolutePosition();
		vector3df tgtv = Target - pos;
		tgtv.normalize();

		vector3df up = UpVector;
		up.normalize();

		f32 dp = tgtv.dotProduct(up);
		if ((dp > -1.0001f && dp < -0.9999f) || (dp < 1.0001f && dp > 0.9999f))
			up.X += 1.0f;

		View.buildCameraLookAtMatrixLH(pos,Target,up);
		recalculateViewArea();

		SceneManager->registerNodeForRendering(this,ESNRP_CAMERA);
	}

	if (IsVisible)
		ISceneNode::OnPreRender();
}

void RTSCamera::render()
{
	IVideoDriver* driver = SceneManager->getVideoDriver();
	if (!driver)
		return;

	driver->setTransform(ETS_VIEW,View);
}

void RTSCamera::OnPostRender(u32 timeMs)
{
	animate();

	ISceneNode::setPosition(Pos);
	updateAbsolutePosition();

	//TODO Add Animators
}

void RTSCamera::setInputReceiverEnabled(bool enabled)
{
	InputReceiverEnabled = enabled;
}

bool RTSCamera::isInputReceiverEnabled()
{
	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
	return InputReceiverEnabled;
}

const aabbox3d<f32>& RTSCamera::getBoundingBox() const
{
	return BBox;
}

const matrix4& RTSCamera::getProjectionMatrix()
{
	return Projection;
}

const SViewFrustrum* RTSCamera::getViewFrustrum()
{
	return &ViewArea;
}

vector3df RTSCamera::getTarget() const
{
	return Target;
}

const matrix4& RTSCamera::getViewMatrix()
{
	return View;
}

core::vector3df RTSCamera::getUpVector() const
{
	return UpVector;
}

f32 RTSCamera::getNearValue()
{
	return ZNear;
}

f32 RTSCamera::getFarValue()
{
	return ZFar;
}

f32 RTSCamera::getAspectRatio()
{
	return Aspect;
}

f32 RTSCamera::getFOV()
{
	return Fovy;
}

void RTSCamera::setNearValue(f32 f)
{
	ZNear = f;
	recalculateProjectionMatrix();
}

void RTSCamera::setFarValue(f32 f)
{
	ZFar = f;
	recalculateProjectionMatrix();
}

void RTSCamera::setAspectRatio(f32 f)
{
	Aspect = f;
	recalculateProjectionMatrix();
}

void RTSCamera::setFOV(f32 f)
{
	Fovy = f;
	recalculateProjectionMatrix();
}

void RTSCamera::setUpVector(const vector3df& pos)
{
	UpVector = pos;
}

void RTSCamera::setProjectionMatrix(const core::matrix4& projection)
{
	Projection = projection;
}

void RTSCamera::setPosition(const vector3df& pos)
{
	Pos = pos;
	updateAnimationState();

	ISceneNode::setPosition(pos);
}

void RTSCamera::setTarget(const core::vector3df& pos)
{
	Target = oldTarget = pos;
	updateAnimationState();
}

void RTSCamera::pointCameraAtNode(ISceneNode* selectednode)
{
	vector3df totarget = getPosition() - getTarget();
	setPosition(selectednode->getPosition() + (totarget.normalize() * 100));
	setTarget(selectednode->getPosition());
	updateAnimationState();
}

void RTSCamera::setMinZoom(f32 amount)
{
	targetMinDistance = amount;
}

void RTSCamera::setMaxZoom(f32 amount)
{
	targetMaxDistance = amount;
}

void RTSCamera::recalculateProjectionMatrix()
{
	Projection.buildProjectionMatrixPerspectiveFovLH(Fovy,Aspect,ZNear,ZFar);
}

void RTSCamera::recalculateViewArea()
{
	matrix4 mat = Projection * View;
	ViewArea = SViewFrustrum(mat);

	ViewArea.cameraPosition = getAbsolutePosition();
	ViewArea.recalculateBoundingBox();
}

void RTSCamera::allKeysUp()
{
	for(int i = 0;i < KEY_KEY_CODES_COUNT;i++)
		Keys[KEY_KEY_CODES_COUNT] = false;
}

void RTSCamera::allMouseKeysUp()
{
	for (s32 i=0; i<3; ++i)
		MouseKeys[i] = false;
}

bool RTSCamera::isKeyDown(s32 key)
{
	return Keys[key];
}

bool RTSCamera::isMouseKeyDown(s32 key)
{
	return MouseKeys[key];
}

void RTSCamera::animate()
{
	//Rotation Vals
	f32 nRotX = rotX;
	f32 nRotY = rotY;
	f32 nZoom = currentZoom;

	//Translation Vals
	vector3df translate(oldTarget);
	vector3df tvectX = Pos - Target;
	tvectX = tvectX.crossProduct(UpVector);
	tvectX.normalize();

	//Zoom
	if (isMouseKeyDown(MOUSE_BUTTON_RIGHT) && isMouseKeyDown(MOUSE_BUTTON_LEFT))
	{
		if (!zooming)
		{
			zoomStartX = MousePos.X;
			zoomStartY = MousePos.Y;
			zooming = true;
			nZoom = currentZoom;
		}
		else
		{
			f32 old = nZoom;
			nZoom += (zoomStartX - MousePos.X) * zoomSpeed * 100;

			if (nZoom < targetMinDistance)
				nZoom = targetMinDistance;
			else if (nZoom > targetMaxDistance)
				nZoom = targetMaxDistance;

			if (nZoom < 0)
				nZoom = old;
		}
	}
	else
	{
		if (zooming)
		{
			f32 old = currentZoom;
			currentZoom = currentZoom + (zoomStartX - MousePos.X ) * zoomSpeed;
			nZoom = currentZoom;

			if (nZoom < 0)
				nZoom = currentZoom = old;
		}

		zooming = false;
	}

	//Rotation
	if (isMouseKeyDown(MOUSE_BUTTON_LEFT) && !zooming)
	{
		if (!rotating)
		{
			rotateStartX = MousePos.X;
			rotateStartY = MousePos.Y;
			rotating = true;
			nRotX = rotX;
			nRotY = rotY;
		}
		else
		{
			nRotX += (rotateStartX - MousePos.X) * rotateSpeed;
			nRotY += (rotateStartY - MousePos.Y) * rotateSpeed;
		}
	}
	else
	{
		if (rotating)
		{
			rotX = rotX + (rotateStartX - MousePos.X) * rotateSpeed;
			rotY = rotY + (rotateStartY - MousePos.Y) * rotateSpeed;
			nRotX = rotX;
			nRotY = rotY;
		}

		rotating = false;
	}

	//Translate
	if (isMouseKeyDown(MOUSE_BUTTON_RIGHT) && !zooming)
	{
		if (!translating)
		{
			translateStartX = MousePos.X;
			translateStartY = MousePos.Y;
			translating = true;
		}
		else
		{
			translate += tvectX * (translateStartX - MousePos.X) * translateSpeed;
			translate.X += tvectX.Z * (translateStartY - MousePos.Y) * translateSpeed;
			translate.Z -= tvectX.X * (translateStartY - MousePos.Y) * translateSpeed;

			oldTarget = translate;
		}
	}
	else if (isKeyDown(KEY_KEY_W) || isKeyDown(KEY_UP) && !zooming)
	{
		if (!translating)
			translating = true;
		else
		{
			vector3df movevector = getPosition() - getTarget();
			movevector.Y = 0;
			movevector.normalize();

			setPosition(getPosition() - movevector * translateSpeed);
			setTarget(getTarget() - movevector * translateSpeed);
			updateAbsolutePosition();
		}
	}
	else if (isKeyDown(KEY_KEY_S) || isKeyDown(KEY_DOWN) && !zooming)
	{
		if (!translating)
			translating = true;
		else
		{
			vector3df movevector = getPosition() - getTarget();
			movevector.Y = 0;
			movevector.normalize();

			setPosition(getPosition() + movevector * translateSpeed);
			setTarget(getTarget() + movevector * translateSpeed);
			updateAbsolutePosition();
		}
	}
	else if (isKeyDown(KEY_KEY_A) || isKeyDown(KEY_LEFT) && !zooming)
	{
		if (!translating)
			translating = true;
		else
		{
			vector3df totargetvector = getPosition() - getTarget();
			totargetvector.normalize();
			vector3df crossvector = totargetvector.crossProduct(getUpVector());
			vector3df strafevector = crossvector.normalize();

			setPosition(getPosition() - strafevector * translateSpeed);
			setTarget(getTarget() - strafevector * translateSpeed);
			updateAbsolutePosition();
		}
	}
	else if (isKeyDown(KEY_KEY_D) || isKeyDown(KEY_RIGHT) && !zooming)
	{
		if (!translating)
			translating = true;
		else
		{
			vector3df totargetvector = getPosition() - getTarget();
			totargetvector.normalize();
			vector3df crossvector = totargetvector.crossProduct(getUpVector());
			vector3df strafevector = crossvector.normalize();

			setPosition(getPosition() + strafevector * translateSpeed);
			setTarget(getTarget() + strafevector * translateSpeed);
			updateAbsolutePosition();
		}
	}
	else //Add Code Here for Normal Mouse Movement
	{
		translating = false;

		if (!translating && !zooming && !rotating)
		{
			//Mouse Coordinates go from 0 to 1 on both axes
			if (MousePos.X < 0.05)	//Up
			{
				vector3df totargetvector = getPosition() - getTarget();
				totargetvector.normalize();
				vector3df crossvector = totargetvector.crossProduct(getUpVector());
				vector3df strafevector = crossvector.normalize();

				setPosition(getPosition() - strafevector * translateSpeed);
				setTarget(getTarget() - strafevector * translateSpeed);
				updateAbsolutePosition();
			}
			else if (MousePos.X > 0.95) //Down
			{
				vector3df totargetvector = getPosition() - getTarget();
				totargetvector.normalize();
				vector3df crossvector = totargetvector.crossProduct(getUpVector());
				vector3df strafevector = crossvector.normalize();

				setPosition(getPosition() + strafevector * translateSpeed);
				setTarget(getTarget() + strafevector * translateSpeed);
				updateAbsolutePosition();
			}
			else if (MousePos.Y < 0.05)	//Up
			{
				vector3df movevector = getPosition() - getTarget();
				movevector.Y = 0;
				movevector.normalize();

				setPosition(getPosition() - movevector * translateSpeed);
				setTarget(getTarget() - movevector * translateSpeed);
				updateAbsolutePosition();
			}
			else if (MousePos.Y > 0.95) //Down
			{
				vector3df movevector = getPosition() - getTarget();
				movevector.Y = 0;
				movevector.normalize();

				setPosition(getPosition() + movevector * translateSpeed);
				setTarget(getTarget() + movevector * translateSpeed);
				updateAbsolutePosition();
			}
		}
	}

	//Set Position
	Target = translate;

	Pos.X = nZoom + Target.X;
	Pos.Y = Target.Y;
	Pos.Z = Target.Z;

	Pos.rotateXYBy(nRotY,Target);
	Pos.rotateXZBy(-nRotX,Target);

	//Correct Rotation Error
	UpVector.set(0,1,0);
	UpVector.rotateXYBy(-nRotY,vector3df(0,0,0));
	UpVector.rotateXZBy(-nRotX+180.f,vector3df(0,0,0));
}

void RTSCamera::updateAnimationState()
{
	vector3df pos(Pos - Target);

	// X rotation
	vector2df vec2d(pos.X,pos.Z);
	rotX = (f32)vec2d.getAngle();

	// Y rotation
	pos.rotateXZBy(rotX,vector3df());
	vec2d.set(pos.X, pos.Y);
	rotY = -(f32)vec2d.getAngle();

	// Zoom
	currentZoom = (f32)Pos.getDistanceFrom(Target);
}

Then use it like this:

Code: Select all

	
RTSCamera* camera = new RTSCamera(device,smgr->getRootSceneNode(),smgr,-1,1000.0f,10.0f,10.0f);
camera->setPosition(vector3df(600,600,600));
Last edited by CmdKewin on Tue May 22, 2007 2:45 pm, edited 4 times in total.
Joey
Posts: 2
Joined: Thu Mar 22, 2007 2:29 am

Post by Joey »

a small typo:

void RTSCamera::allKeysUp()
{
for(int i = 0;i < KEY_KEY_CODES_COUNT;i++)
Keys[KEY_KEY_CODES_COUNT] = false;
}

change " Keys[KEY_KEY_CODES_COUNT] = false;" -> "Keys=false"

i use it and i like it with some small customizations, good work
CmdKewin
Posts: 29
Joined: Thu Aug 17, 2006 1:49 pm

Post by CmdKewin »

Oh, thanks :) That solved a nasty bug I was having.

Anyway, I'll soon (tm) upload a new version of the camera, with the XML configuration and the interpolated transition implemented.
MickeyKnox
Posts: 58
Joined: Tue Apr 10, 2007 7:49 pm
Location: Karlsruhe

Post by MickeyKnox »

I would like to use your camera, but it want compile. Im using irrlicht-1.3.
Iget this from the compiler:

g++ -I../lib/irrlicht-1.3/include -I/usr/X11R6/include -O3 -ffast-math main.cpp -o ../tai -L/usr/X11R6/lib -L../lib/irrlicht-1.3/lib/Linux -lIrrlicht -lGL -lGLU -lXxf86vm -lXext -lX11
RTSCamera.h:34: error: ISO C++ forbids declaration of 'SViewFrustrum' with no type
RTSCamera.h:34: error: 'SViewFrustrum' declared as a 'virtual' field
RTSCamera.h:34: error: expected ';' before '*' token
RTSCamera.h:67: error: 'SViewFrustrum' does not name a type
make: *** [all_linux] Error 1
garrittg
Posts: 117
Joined: Wed Apr 20, 2005 6:17 pm
Location: Iowa, USA
Contact:

Post by garrittg »

change all instances of SViewFrustrum to SViewFrustum and try to compile. SViewFrustrum was renamed in v1.3.

HTH
MickeyKnox
Posts: 58
Joined: Tue Apr 10, 2007 7:49 pm
Location: Karlsruhe

Post by MickeyKnox »

Thanks for the quick answer.
Unfortunately it still doesnt compile:

g++ -I../lib/irrlicht-1.3/include -I/usr/X11R6/include -O3 -ffast-math source/main.cpp source/RTSCamera.cpp -o tai -L/usr/X11R6/lib -L../lib/irrlicht-1.3/lib/Linux -lIrrlicht -lGL -lGLU -lXxf86vm -lXext -lX11
source/main.cpp: In function 'int main()':
source/main.cpp:122: error: cannot allocate an object of abstract type 'RTSCamera'
source/RTSCamera.h:14: note: because the following virtual functions are pure within 'RTSCamera':
../lib/irrlicht-1.3/include/ICameraSceneNode.h:112: note: virtual const irr::scene::SViewFrustum* irr::scene::ICameraSceneNode::getViewFrustum() const
source/RTSCamera.cpp: In member function 'virtual void RTSCamera::OnPreRender()':
source/RTSCamera.cpp:142: error: 'OnPreRender' is not a member of 'irr::scene::ISceneNode'
make: *** [all_linux] Error 1
garrittg
Posts: 117
Joined: Wed Apr 20, 2005 6:17 pm
Location: Iowa, USA
Contact:

Post by garrittg »

this are all the interface breaking changes in v1.3.

- change all instances of OnPreRender to OnRegisterSceneNode

- change all OnPostRender to OnAnimate

for the getViewFrustum, check the declaration and implementation match:

virtual const SViewFrustum* getViewFrustum() const;

const SViewFrustum* RTSCamera::getViewFrustum() const {
...
...
}
MickeyKnox
Posts: 58
Joined: Tue Apr 10, 2007 7:49 pm
Location: Karlsruhe

Post by MickeyKnox »

Halleluja, it works! :)
Thanks a lot!

By the way, there might be an error in changes.txt:
... it should be enough to rename OnPreRender() to OnAnimate() and OnPostRender() to OnRegisterSceneNode() in most cases.

Aniway, thanks again...
garrittg
Posts: 117
Joined: Wed Apr 20, 2005 6:17 pm
Location: Iowa, USA
Contact:

Post by garrittg »

you are very welcome :)
CmdKewin
Posts: 29
Joined: Thu Aug 17, 2006 1:49 pm

Post by CmdKewin »

Ok, here is the lastest version. No new features, just all bugfixes and API changes implemented.

Code: Select all

#ifndef __RTSCamera__
#define __RTSCamera__

#include <irrlicht.h>

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

class RTSCamera : public ICameraSceneNode
{
	public:
		RTSCamera(IrrlichtDevice* devicepointer,ISceneNode* parent,ISceneManager* smgr,s32 id,
			f32 rotateSpeed = -1000.0f,f32 zoomSpeed = 1000.0f,f32 translationSpeed = 1000.0f);

		virtual ~RTSCamera();

		//Events
		virtual void render();
		virtual bool OnEvent(SEvent event);
		virtual void OnRegisterSceneNode(); //old virtual void OnPreRender();
		virtual void OnAnimate(u32 timeMs);	//old virtual void OnPostRender(u32 timeMs);

		//Setup
		virtual void setInputReceiverEnabled(bool enabled);
		virtual bool isInputReceiverEnabled();

		//Gets
		virtual const aabbox3d<f32>& getBoundingBox() const;
		virtual const matrix4& getProjectionMatrix();
		virtual const SViewFrustum* getViewFrustum() const;
		virtual vector3df getTarget() const;
		virtual const matrix4& getViewMatrix();
		virtual vector3df getUpVector() const;
		virtual f32 getNearValue();
		virtual f32 getFarValue();
		virtual f32 getAspectRatio();
		virtual f32 getFOV();

		//Sets
		virtual void setNearValue(f32 zn);
		virtual void setFarValue(f32 zf);
		virtual void setAspectRatio(f32 aspect);
		virtual void setFOV(f32 fovy);
		virtual void setUpVector(const vector3df& pos);
		virtual void setProjectionMatrix(const matrix4& projection);
		virtual void setPosition(const vector3df& newpos);
		virtual void setTarget(const vector3df& newpos);

		virtual void setZoomSpeed(f32 value);
		virtual void setTranslateSpeed(f32 value);
		virtual void setRotationSpeed(f32 value);

		//Helper Functions
		void pointCameraAtNode(ISceneNode* selectednode);
		void setMinZoom(f32 amount);
		void setMaxZoom(f32 amount);

		//Type Return
		virtual ESCENE_NODE_TYPE getType() { return ESNT_CAMERA; }

		//Public Attributes
		bool atMinDistance;
		bool atMaxDistance;
		ISceneNode* selectednode;
	protected:
		//Properties
		vector3df Target;
		vector3df UpVector;
		matrix4 Projection;
		matrix4 View;
		SViewFrustum ViewArea;
		aabbox3d<f32> BBox;
		bool InputReceiverEnabled;
		dimension2d<f32> screenDim;
		f32 Fovy;		//Field of view, in radians.
		f32 Aspect;		//Aspect ratio.
		f32 ZNear;		//Value of the near view-plane.
		f32 ZFar;		//Z-value of the far view-plane.

		void recalculateProjectionMatrix();
		void recalculateViewArea();

	private:
		IrrlichtDevice* device;
		vector3df Pos;
		bool zooming,rotating,moving,translating;
		f32 zoomSpeed;
		f32 translateSpeed;
		f32 rotateSpeed;
		f32 rotateStartX, rotateStartY;
		f32 zoomStartX, zoomStartY;
		f32 translateStartX, translateStartY;
		f32 currentZoom;
		f32 rotX, rotY;
		vector3df oldTarget;
		vector2df MousePos;
		bool Keys[KEY_KEY_CODES_COUNT];
		bool MouseKeys[3];
		f32 targetMinDistance;
		f32 targetMaxDistance;

		enum MOUSE_BUTTON
		{
			MOUSE_BUTTON_LEFT,
			MOUSE_BUTTON_MIDDLE,
			MOUSE_BUTTON_RIGHT
		};

		void allKeysUp();
		void allMouseKeysUp();
		bool isKeyDown(s32 key);
		bool isMouseKeyDown(s32 key);
		void animate();
		void updateAnimationState();
};

#endif

Code: Select all

#include "RTSCamera.h"

RTSCamera::RTSCamera(IrrlichtDevice* devicepointer,ISceneNode* parent,ISceneManager* smgr,s32 id,
	 f32 rs,f32 zs,f32 ts)
: ICameraSceneNode(parent,smgr,id,vector3df(1.0f,1.0f,1.0f),vector3df(0.0f,0.0f,0.0f),
					vector3df(1.0f,1.0f,1.0f)),InputReceiverEnabled(true)
{
	device = devicepointer;
	BBox.reset(0,0,0);

	UpVector.set(0.0f,1.0f,0.0f);

	Fovy = core::PI / 2.5f;
	Aspect = 4.0f / 3.0f;
	ZNear = 1.0f;
	ZFar = 3000.0f;

	atMinDistance = false;

	IVideoDriver* d = smgr->getVideoDriver();
	if (d)
	{
		screenDim.Width = (f32)d->getCurrentRenderTargetSize().Width;
		screenDim.Height = (f32)d->getCurrentRenderTargetSize().Height;
		Aspect = screenDim.Width / screenDim.Height;
	}

	zooming = false;
	rotating = false;
	moving = false;
	translating = false;
	zoomSpeed = zs;
	rotateSpeed = rs;
	translateSpeed = ts;
	currentZoom = 100.0f;
	targetMinDistance = 1.0f;
	targetMaxDistance = 2000.0f;
	Target.set(0.0f,0.0f,0.0f);
	rotX = 0;
	rotY = 0;
	oldTarget = Target;

	atMinDistance = false;
	atMaxDistance = false;

	allKeysUp();
	allMouseKeysUp();

	recalculateProjectionMatrix();
	recalculateViewArea();

	smgr->setActiveCamera(this);
}

RTSCamera::~RTSCamera()
{
}

bool RTSCamera::OnEvent(SEvent event)
{
	if (!InputReceiverEnabled)
		return false;

	dimension2d<s32> ssize = SceneManager->getVideoDriver()->getScreenSize();

	if(event.EventType == EET_MOUSE_INPUT_EVENT)
	{
		switch(event.MouseInput.Event)
		{
			case EMIE_LMOUSE_PRESSED_DOWN:
				selectednode = SceneManager->getSceneCollisionManager()->getSceneNodeFromScreenCoordinatesBB(device->getCursorControl()->getPosition(),0xFF,false);

				if(selectednode)
					pointCameraAtNode(selectednode);
				else
				{
					selectednode = NULL;
					MouseKeys[0] = true;
				}
				break;
			case EMIE_RMOUSE_PRESSED_DOWN:
				MouseKeys[2] = true;
				break;
			case EMIE_MMOUSE_PRESSED_DOWN:
				MouseKeys[1] = true;
				break;
			case EMIE_LMOUSE_LEFT_UP:
				MouseKeys[0] = false;
				break;
			case EMIE_RMOUSE_LEFT_UP:
				MouseKeys[2] = false;
				break;
			case EMIE_MMOUSE_LEFT_UP:
				MouseKeys[1] = false;
				break;
			case EMIE_MOUSE_MOVED:
				MousePos.X = event.MouseInput.X / (f32)ssize.Width;
				MousePos.Y = event.MouseInput.Y / (f32)ssize.Height;
				break;
			case EMIE_MOUSE_WHEEL:
				currentZoom -= event.MouseInput.Wheel * zoomSpeed;

				printf("%f\n",currentZoom);

				if (currentZoom <= targetMinDistance)
					atMinDistance = true;
				else if (currentZoom >= targetMaxDistance)
					atMaxDistance = true;
				else
				{
					atMinDistance = false;
					atMaxDistance = false;
				}

				break;
		}
		return true;
	}

	if(event.EventType == EET_KEY_INPUT_EVENT)
	{
		Keys[event.KeyInput.Key] = event.KeyInput.PressedDown;
		return true;
	}

	return false;
}

void RTSCamera::OnRegisterSceneNode()
{
	IVideoDriver* driver = SceneManager->getVideoDriver();
	if (!driver)
		return;

	if (SceneManager->getActiveCamera() == this)
	{
		screenDim.Width = (f32)driver->getCurrentRenderTargetSize().Width;
		screenDim.Height = (f32)driver->getCurrentRenderTargetSize().Height;

		driver->setTransform(ETS_PROJECTION,Projection);

		//If UpVector and Vector to Target are the same, we have a problem.
		//Correct it.
		vector3df pos = getAbsolutePosition();
		vector3df tgtv = Target - pos;
		tgtv.normalize();

		vector3df up = UpVector;
		up.normalize();

		f32 dp = tgtv.dotProduct(up);
		if ((dp > -1.0001f && dp < -0.9999f) || (dp < 1.0001f && dp > 0.9999f))
			up.X += 1.0f;

		View.buildCameraLookAtMatrixLH(pos,Target,up);
		recalculateViewArea();

		SceneManager->registerNodeForRendering(this,ESNRP_CAMERA);
	}

	if (IsVisible)
		ISceneNode::OnRegisterSceneNode();
}

void RTSCamera::render()
{
	IVideoDriver* driver = SceneManager->getVideoDriver();
	if (!driver)
		return;

	driver->setTransform(ETS_VIEW,View);
}

void RTSCamera::OnAnimate(u32 timeMs)
{
	animate();

	ISceneNode::setPosition(Pos);
	updateAbsolutePosition();

	//TODO Add Animators
}

void RTSCamera::setInputReceiverEnabled(bool enabled)
{
	InputReceiverEnabled = enabled;
}

bool RTSCamera::isInputReceiverEnabled()
{
	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
	return InputReceiverEnabled;
}

const aabbox3d<f32>& RTSCamera::getBoundingBox() const
{
	return BBox;
}

const matrix4& RTSCamera::getProjectionMatrix()
{
	return Projection;
}

const SViewFrustum* RTSCamera::getViewFrustum() const
{
	return &ViewArea;
}

vector3df RTSCamera::getTarget() const
{
	return Target;
}

const matrix4& RTSCamera::getViewMatrix()
{
	return View;
}

core::vector3df RTSCamera::getUpVector() const
{
	return UpVector;
}

f32 RTSCamera::getNearValue()
{
	return ZNear;
}

f32 RTSCamera::getFarValue()
{
	return ZFar;
}

f32 RTSCamera::getAspectRatio()
{
	return Aspect;
}

f32 RTSCamera::getFOV()
{
	return Fovy;
}

void RTSCamera::setNearValue(f32 f)
{
	ZNear = f;
	recalculateProjectionMatrix();
}

void RTSCamera::setFarValue(f32 f)
{
	ZFar = f;
	recalculateProjectionMatrix();
}

void RTSCamera::setAspectRatio(f32 f)
{
	Aspect = f;
	recalculateProjectionMatrix();
}

void RTSCamera::setFOV(f32 f)
{
	Fovy = f;
	recalculateProjectionMatrix();
}

void RTSCamera::setUpVector(const vector3df& pos)
{
	UpVector = pos;
}

void RTSCamera::setProjectionMatrix(const core::matrix4& projection)
{
	Projection = projection;
}

void RTSCamera::setPosition(const vector3df& pos)
{
	Pos = pos;
	updateAnimationState();

	ISceneNode::setPosition(pos);
}

void RTSCamera::setTarget(const core::vector3df& pos)
{
	Target = oldTarget = pos;
	updateAnimationState();
}

void RTSCamera::setZoomSpeed(f32 value)
{
	zoomSpeed = value;
}

void RTSCamera::setTranslateSpeed(f32 value)
{
	translateSpeed = value;
}

void RTSCamera::setRotationSpeed(f32 value)
{
	rotateSpeed = value;
}

void RTSCamera::pointCameraAtNode(ISceneNode* selectednode)
{
	vector3df totarget = getPosition() - getTarget();
	setPosition(selectednode->getPosition() + (totarget.normalize() * 100));
	setTarget(selectednode->getPosition());
	updateAnimationState();
}

void RTSCamera::setMinZoom(f32 amount)
{
	targetMinDistance = amount;
}

void RTSCamera::setMaxZoom(f32 amount)
{
	targetMaxDistance = amount;
}

void RTSCamera::recalculateProjectionMatrix()
{
	Projection.buildProjectionMatrixPerspectiveFovLH(Fovy,Aspect,ZNear,ZFar);
}

void RTSCamera::recalculateViewArea()
{
	matrix4 mat = Projection * View;
	ViewArea = SViewFrustum(mat);

	ViewArea.cameraPosition = getAbsolutePosition();
	ViewArea.recalculateBoundingBox();
}

void RTSCamera::allKeysUp()
{
	for(int i = 0;i < KEY_KEY_CODES_COUNT;i++)
		Keys[KEY_KEY_CODES_COUNT] = false;
}

void RTSCamera::allMouseKeysUp()
{
	for (s32 i=0; i<3; ++i)
		MouseKeys[i] = false;
}

bool RTSCamera::isKeyDown(s32 key)
{
	return Keys[key];
}

bool RTSCamera::isMouseKeyDown(s32 key)
{
	return MouseKeys[key];
}

void RTSCamera::animate()
{
	//Rotation Vals
	f32 nRotX = rotX;
	f32 nRotY = rotY;
	f32 nZoom = currentZoom;

	//Translation Vals
	vector3df translate(oldTarget);
	vector3df tvectX = Pos - Target;
	tvectX = tvectX.crossProduct(UpVector);
	tvectX.normalize();

	//Zoom
	if (isMouseKeyDown(MOUSE_BUTTON_RIGHT) && isMouseKeyDown(MOUSE_BUTTON_LEFT))
	{
		if (!zooming)
		{
			zoomStartX = MousePos.X;
			zoomStartY = MousePos.Y;
			zooming = true;
			nZoom = currentZoom;
		}
		else
		{
			f32 old = nZoom;
			nZoom += (zoomStartX - MousePos.X) * zoomSpeed * 100;

			if (nZoom < targetMinDistance)
				nZoom = targetMinDistance;
			else if (nZoom > targetMaxDistance)
				nZoom = targetMaxDistance;

			if (nZoom < 0)
				nZoom = old;
		}
	}
	else
	{
		if (zooming)
		{
			f32 old = currentZoom;
			currentZoom = currentZoom + (zoomStartX - MousePos.X ) * zoomSpeed;
			nZoom = currentZoom;

			if (nZoom < 0)
				nZoom = currentZoom = old;
		}
		zooming = false;
	}

	//Rotation
	if (isMouseKeyDown(MOUSE_BUTTON_LEFT) && !zooming)
	{
		if (!rotating)
		{
			rotateStartX = MousePos.X;
			rotateStartY = MousePos.Y;
			rotating = true;
			nRotX = rotX;
			nRotY = rotY;
		}
		else
		{
			nRotX += (rotateStartX - MousePos.X) * rotateSpeed;
			nRotY += (rotateStartY - MousePos.Y) * rotateSpeed;
		}
	}
	else
	{
		if (rotating)
		{
			rotX = rotX + (rotateStartX - MousePos.X) * rotateSpeed;
			rotY = rotY + (rotateStartY - MousePos.Y) * rotateSpeed;
			nRotX = rotX;
			nRotY = rotY;
		}

		rotating = false;
	}

	//Translate
	if (isMouseKeyDown(MOUSE_BUTTON_RIGHT) && !zooming)
	{
		if (!translating)
		{
			translateStartX = MousePos.X;
			translateStartY = MousePos.Y;
			translating = true;
		}
		else
		{
			translate += tvectX * (translateStartX - MousePos.X) * translateSpeed;
			translate.X += tvectX.Z * (translateStartY - MousePos.Y) * translateSpeed;
			translate.Z -= tvectX.X * (translateStartY - MousePos.Y) * translateSpeed;

			oldTarget = translate;
		}
	}
	else if (isKeyDown(KEY_KEY_W) || isKeyDown(KEY_UP) && !zooming)
	{
		if (!translating)
			translating = true;
		else
		{
			vector3df movevector = getPosition() - getTarget();
			movevector.Y = 0;
			movevector.normalize();

			setPosition(getPosition() - movevector * translateSpeed);
			setTarget(getTarget() - movevector * translateSpeed);
			updateAbsolutePosition();
		}
	}
	else if (isKeyDown(KEY_KEY_S) || isKeyDown(KEY_DOWN) && !zooming)
	{
		if (!translating)
			translating = true;
		else
		{
			vector3df movevector = getPosition() - getTarget();
			movevector.Y = 0;
			movevector.normalize();

			setPosition(getPosition() + movevector * translateSpeed);
			setTarget(getTarget() + movevector * translateSpeed);
			updateAbsolutePosition();
		}
	}
	else if (isKeyDown(KEY_KEY_A) || isKeyDown(KEY_LEFT) && !zooming)
	{
		if (!translating)
			translating = true;
		else
		{
			vector3df totargetvector = getPosition() - getTarget();
			totargetvector.normalize();
			vector3df crossvector = totargetvector.crossProduct(getUpVector());
			vector3df strafevector = crossvector.normalize();

			setPosition(getPosition() - strafevector * translateSpeed);
			setTarget(getTarget() - strafevector * translateSpeed);
			updateAbsolutePosition();
		}
	}
	else if (isKeyDown(KEY_KEY_D) || isKeyDown(KEY_RIGHT) && !zooming)
	{
		if (!translating)
			translating = true;
		else
		{
			vector3df totargetvector = getPosition() - getTarget();
			totargetvector.normalize();
			vector3df crossvector = totargetvector.crossProduct(getUpVector());
			vector3df strafevector = crossvector.normalize();

			setPosition(getPosition() + strafevector * translateSpeed);
			setTarget(getTarget() + strafevector * translateSpeed);
			updateAbsolutePosition();
		}
	}
	else //Add Code Here for Normal Mouse Movement
	{
		translating = false;

		if (!translating && !zooming && !rotating)
		{
			//Mouse Coordinates go from 0 to 1 on both axes
			if (MousePos.X < 0.05)	//Up
			{
				vector3df totargetvector = getPosition() - getTarget();
				totargetvector.normalize();
				vector3df crossvector = totargetvector.crossProduct(getUpVector());
				vector3df strafevector = crossvector.normalize();

				setPosition(getPosition() - strafevector * translateSpeed);
				setTarget(getTarget() - strafevector * translateSpeed);
				updateAbsolutePosition();
			}
			else if (MousePos.X > 0.95) //Down
			{
				vector3df totargetvector = getPosition() - getTarget();
				totargetvector.normalize();
				vector3df crossvector = totargetvector.crossProduct(getUpVector());
				vector3df strafevector = crossvector.normalize();

				setPosition(getPosition() + strafevector * translateSpeed);
				setTarget(getTarget() + strafevector * translateSpeed);
				updateAbsolutePosition();
			}
			else if (MousePos.Y < 0.05)	//Up
			{
				vector3df movevector = getPosition() - getTarget();
				movevector.Y = 0;
				movevector.normalize();

				setPosition(getPosition() - movevector * translateSpeed);
				setTarget(getTarget() - movevector * translateSpeed);
				updateAbsolutePosition();
			}
			else if (MousePos.Y > 0.95) //Down
			{
				vector3df movevector = getPosition() - getTarget();
				movevector.Y = 0;
				movevector.normalize();

				setPosition(getPosition() + movevector * translateSpeed);
				setTarget(getTarget() + movevector * translateSpeed);
				updateAbsolutePosition();
			}
		}
	}

	//Set Position
	Target = translate;

	Pos.X = nZoom + Target.X;
	Pos.Y = Target.Y;
	Pos.Z = Target.Z;

	Pos.rotateXYBy(nRotY,Target);
	Pos.rotateXZBy(-nRotX,Target);

	//Correct Rotation Error
	UpVector.set(0,1,0);
	UpVector.rotateXYBy(-nRotY,vector3df(0,0,0));
	UpVector.rotateXZBy(-nRotX+180.f,vector3df(0,0,0));
}

void RTSCamera::updateAnimationState()
{
	vector3df pos(Pos - Target);

	// X rotation
	vector2df vec2d(pos.X,pos.Z);
	rotX = (f32)vec2d.getAngle();

	// Y rotation
	pos.rotateXZBy(rotX,vector3df());
	vec2d.set(pos.X, pos.Y);
	rotY = -(f32)vec2d.getAngle();

	// Zoom
	currentZoom = (f32)Pos.getDistanceFrom(Target);
}
Worteltaart
Posts: 17
Joined: Sat Mar 24, 2007 5:03 pm
Location: Holland

Post by Worteltaart »

Does anyone have a .NET port for this piece of work (C#)?
Common sense is what tells you the world is flat.
roxaz
Posts: 575
Joined: Tue Jan 23, 2007 8:35 pm
Location: LT

Post by roxaz »

Worteltaart wrote:Does anyone have a .NET port for this piece of work (C#)?
nope, but you can always write one :)
Worteltaart
Posts: 17
Joined: Sat Mar 24, 2007 5:03 pm
Location: Holland

Post by Worteltaart »

So I will, so I will ;)
Common sense is what tells you the world is flat.
CmdKewin
Posts: 29
Joined: Thu Aug 17, 2006 1:49 pm

Post by CmdKewin »

Don't do it yet :) I discovered a major Bug. Apparently, the particle system was not affected by the Camera. Meaning that, particles where not always facing the camera (as they should really, since they are billboards). I'm not sure why (maybe a leftover of some SVN changes I'm not aware of), but everything else works properly. After a few hours (and a complete rewrite), I did get rid of it though.

Here is the updated code (renamed a few things too).

RTSCamera.h

Code: Select all

#ifndef __RTSCAMERA__
#define __RTSCAMERA__

#include <irrlicht.h>

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

class RTSCamera : public ICameraSceneNode
{
	public:
		RTSCamera(IrrlichtDevice* devicepointer,ISceneNode* parent,ISceneManager* smgr,s32 id,
			f32 rotateSpeed = -1000.0f,f32 zoomSpeed = 1000.0f,f32 translationSpeed = 1000.0f);

		virtual ~RTSCamera();

		//Events
		virtual void render();
		virtual bool OnEvent(SEvent event);
		virtual void OnRegisterSceneNode();
		virtual void OnAnimate(u32 timeMs);

		//Setup
		virtual void setInputReceiverEnabled(bool enabled);
		virtual bool isInputReceiverEnabled();

		//Gets
		virtual const aabbox3d<f32>& getBoundingBox() const;
		virtual const matrix4& getProjectionMatrix();
		virtual const SViewFrustum* getViewFrustum() const;
		virtual vector3df getTarget() const;
		virtual const matrix4& getViewMatrix();
		virtual vector3df getUpVector() const;
		virtual f32 getNearValue();
		virtual f32 getFarValue();
		virtual f32 getAspectRatio();
		virtual f32 getFOV();

		//Sets
		virtual void setNearValue(f32 zn);
		virtual void setFarValue(f32 zf);
		virtual void setAspectRatio(f32 aspect);
		virtual void setFOV(f32 fovy);
		virtual void setUpVector(const vector3df& pos);
		virtual void setProjectionMatrix(const matrix4& projection);
		virtual void setPosition(const vector3df& newpos);
		virtual void setTarget(const vector3df& newpos);

		virtual void setZoomSpeed(f32 value);
		virtual void setTranslateSpeed(f32 value);
		virtual void setRotationSpeed(f32 value);


		//Helper Functions
		//virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0);
		//virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0);
		void pointCameraAtNode(ISceneNode* selectednode);
		void setMinZoom(f32 amount);
		void setMaxZoom(f32 amount);

		//Type Return
		virtual ESCENE_NODE_TYPE getType() const { return ESNT_CAMERA; }

		//Public Attributes
		bool atMinDistance;
		bool atMaxDistance;
		ISceneNode* selectednode;
	protected:
		//Properties
		vector3df Target;
		vector3df UpVector;
		matrix4 Projection;
		matrix4 View;
		SViewFrustum ViewArea;
		aabbox3d<f32> BBox;
		bool InputReceiverEnabled;
		dimension2d<f32> screenDim;
		f32 Fovy;	// Field of view, in radians.
		f32 Aspect;	// Aspect ratio.
		f32 ZNear;	// value of the near view-plane.
		f32 ZFar;	// Z-value of the far view-plane.

		void recalculateProjectionMatrix();
		void recalculateViewArea();

	private:
		IrrlichtDevice* device;
		vector3df Pos;
		bool zooming, rotating, moving, translating;
		f32 zoomSpeed;
		f32 translateSpeed;
		f32 rotateSpeed;
		f32 rotateStartX, rotateStartY;
		f32 zoomStartX, zoomStartY;
		f32 translateStartX, translateStartY;
		f32 currentZoom;
		f32 rotX, rotY;
		vector3df oldTarget;
		vector2df MousePos;
		bool Keys[KEY_KEY_CODES_COUNT];
		bool MouseKeys[3];
		f32 targetMinDistance;
		f32 targetMaxDistance;

		enum MOUSE_BUTTON
		{
			MOUSE_BUTTON_LEFT,
			MOUSE_BUTTON_MIDDLE,
			MOUSE_BUTTON_RIGHT
		};

		void allKeysUp();
		void allMouseButtonsUp();
		bool isKeyDown(s32 key);
		bool isMouseButtonDown(s32 key);
		void animate();
		void updateAnimationState();
};

#endif
RTSCamera.cpp

Code: Select all

#include "RTSCamera.h"

RTSCamera::RTSCamera(IrrlichtDevice* devicepointer,ISceneNode* parent,ISceneManager* smgr,s32 id,
	 f32 rs,f32 zs,f32 ts)
	: ICameraSceneNode(parent,smgr,id,vector3df(1.0f,1.0f,1.0f),vector3df(0.0f,0.0f,0.0f),
					vector3df(1.0f,1.0f,1.0f)),InputReceiverEnabled(true)
{
	device = devicepointer;
	BBox.reset(0,0,0);

	UpVector.set(0.0f, 1.0f, 0.0f);

	// set default projection
	Fovy = core::PI / 2.5f;	// Field of view, in radians.
	Aspect = 4.0f / 3.0f;	// Aspect ratio.
	ZNear = 1.0f;		// value of the near view-plane.
	ZFar = 100000.0f;		// Z-value of the far view-plane.

	IVideoDriver* d = smgr->getVideoDriver();
	if (d)
		Aspect = (f32)d->getCurrentRenderTargetSize().Width / (f32)d->getCurrentRenderTargetSize().Height;

	zooming = false;
	rotating = false;
	moving = false;
	translating = false;
	zoomSpeed = zs;
	rotateSpeed = rs;
	translateSpeed = ts;
	targetMinDistance = 1.0f;
	targetMaxDistance = 2000.0f;
	Target.set(0.0f,0.0f,0.0f);
	rotX = 0;
	rotY = 0;
	oldTarget = Target;

	atMinDistance = false;
	atMaxDistance = false;

	allKeysUp();
	allMouseButtonsUp();

	recalculateProjectionMatrix();
	recalculateViewArea();

	smgr->setActiveCamera(this);
}

RTSCamera::~RTSCamera()
{
}

bool RTSCamera::OnEvent(SEvent event)
{
	if (!InputReceiverEnabled)
		return false;

	dimension2d<s32> ssize = SceneManager->getVideoDriver()->getScreenSize();
	if(event.EventType == EET_MOUSE_INPUT_EVENT)
	{
		switch(event.MouseInput.Event)
		{
			case EMIE_LMOUSE_PRESSED_DOWN:
				selectednode = SceneManager->getSceneCollisionManager()->getSceneNodeFromScreenCoordinatesBB(device->getCursorControl()->getPosition());
				if(selectednode && selectednode->getType() == ESNT_BILLBOARD)
				{
					pointCameraAtNode(selectednode);
				}
				else
				{
					selectednode = NULL;
					MouseKeys[0] = true;
				}
				break;
			case EMIE_RMOUSE_PRESSED_DOWN:
				MouseKeys[2] = true;
				break;
			case EMIE_MMOUSE_PRESSED_DOWN:
				MouseKeys[1] = true;
				break;
			case EMIE_LMOUSE_LEFT_UP:
				MouseKeys[0] = false;
				break;
			case EMIE_RMOUSE_LEFT_UP:
				MouseKeys[2] = false;
				break;
			case EMIE_MMOUSE_LEFT_UP:
				MouseKeys[1] = false;
				break;
			case EMIE_MOUSE_MOVED:
				{
					IVideoDriver* driver = SceneManager->getVideoDriver();
					if (driver)
					{
						MousePos.X = event.MouseInput.X / (f32)ssize.Width;
						MousePos.Y = event.MouseInput.Y / (f32)ssize.Height;
					}
				}
				break;
			case EMIE_MOUSE_WHEEL:
				currentZoom -= event.MouseInput.Wheel * zoomSpeed;
				break;
			default:
				break;
		}

		return true;
	}

	if(event.EventType == EET_KEY_INPUT_EVENT)
	{
		Keys[event.KeyInput.Key] = event.KeyInput.PressedDown;
		return true;
	}

	return false;
}

void RTSCamera::OnRegisterSceneNode()
{
	vector3df pos = getAbsolutePosition();
	vector3df tgtv = Target - pos;
	tgtv.normalize();

	vector3df up = UpVector;
	up.normalize();

	f32 dp = tgtv.dotProduct(up);

	if ( core::equals ( fabs ( dp ), 1.f ) )
	{
		up.X += 0.5f;
	}

	ViewArea.Matrices [ ETS_VIEW ].buildCameraLookAtMatrixLH(pos, Target, up);
	ViewArea.setTransformState ( ETS_VIEW );
	recalculateViewArea();

	if( SceneManager->getActiveCamera () == this )
		SceneManager->registerNodeForRendering(this, ESNRP_CAMERA);

	if(IsVisible)
		ISceneNode::OnRegisterSceneNode();
}

void RTSCamera::render()
{
	IVideoDriver* driver = SceneManager->getVideoDriver();
	if ( driver)
	{
		driver->setTransform(ETS_PROJECTION, ViewArea.Matrices [ ETS_PROJECTION ] );
		driver->setTransform(ETS_VIEW, ViewArea.Matrices [ ETS_VIEW ] );
	}
}

void RTSCamera::OnAnimate(u32 timeMs)
{
	animate();

	ISceneNode::setPosition(Pos);
	updateAbsolutePosition();
}

void RTSCamera::setInputReceiverEnabled(bool enabled)
{
	InputReceiverEnabled = enabled;
}

bool RTSCamera::isInputReceiverEnabled()
{
	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
	return InputReceiverEnabled;
}

const aabbox3d<f32>& RTSCamera::getBoundingBox() const
{
	return ViewArea.getBoundingBox();
}

const matrix4& RTSCamera::getProjectionMatrix()
{
	return ViewArea.Matrices [ ETS_PROJECTION ];
}

const SViewFrustum* RTSCamera::getViewFrustum() const
{
	return &ViewArea;
}

vector3df RTSCamera::getTarget() const
{
	return Target;
}

const matrix4& RTSCamera::getViewMatrix()
{
	return ViewArea.Matrices [ video::ETS_VIEW ];
}

core::vector3df RTSCamera::getUpVector() const
{
	return UpVector;
}

f32 RTSCamera::getNearValue()
{
	return ZNear;
}

f32 RTSCamera::getFarValue()
{
	return ZFar;
}

f32 RTSCamera::getAspectRatio()
{
	return Aspect;
}

f32 RTSCamera::getFOV()
{
	return Fovy;
}

void RTSCamera::setNearValue(f32 f)
{
	ZNear = f;
	recalculateProjectionMatrix();
}

void RTSCamera::setFarValue(f32 f)
{
	ZFar = f;
	recalculateProjectionMatrix();
}

void RTSCamera::setAspectRatio(f32 f)
{
	Aspect = f;
	recalculateProjectionMatrix();
}

void RTSCamera::setFOV(f32 f)
{
	Fovy = f;
	recalculateProjectionMatrix();
}

void RTSCamera::setUpVector(const vector3df& pos)
{
	UpVector = pos;
}

void RTSCamera::setProjectionMatrix(const matrix4& projection)
{
	ViewArea.Matrices [ ETS_PROJECTION ] = projection;
	ViewArea.setTransformState ( ETS_PROJECTION );
}

void RTSCamera::setPosition(const core::vector3df& pos)
{
	Pos = pos;
	updateAnimationState();

	ISceneNode::setPosition(pos);
}

void RTSCamera::setTarget(const core::vector3df& pos)
{
	Target = oldTarget = pos;
	updateAnimationState();
}

void RTSCamera::setZoomSpeed(f32 value)
{
	zoomSpeed = value;
}

void RTSCamera::setTranslateSpeed(f32 value)
{
	translateSpeed = value;
}

void RTSCamera::setRotationSpeed(f32 value)
{
	rotateSpeed = value;
}

void RTSCamera::pointCameraAtNode(ISceneNode* selectednode)
{
	vector3df totarget = getPosition() - getTarget();
	setPosition(selectednode->getPosition() + (totarget.normalize() * 100));
	setTarget(selectednode->getPosition());
	updateAnimationState();
}

void RTSCamera::setMinZoom(f32 amount)
{
	targetMinDistance = amount;
}

void RTSCamera::setMaxZoom(f32 amount)
{
	targetMaxDistance = amount;
}

void RTSCamera::recalculateProjectionMatrix()
{
	ViewArea.Matrices [ ETS_PROJECTION ].buildProjectionMatrixPerspectiveFovLH(Fovy, Aspect, ZNear, ZFar);
	ViewArea.setTransformState ( ETS_PROJECTION );
}


void RTSCamera::recalculateViewArea()
{
	ViewArea.cameraPosition = getAbsolutePosition();
	ViewArea.setFrom ( ViewArea.Matrices [ SViewFrustum::ETS_VIEW_PROJECTION_3 ] );
}

void RTSCamera::allKeysUp()
{
	for(int i = 0;i < KEY_KEY_CODES_COUNT;i++)
		Keys[i] = false;
}

void RTSCamera::allMouseButtonsUp()
{
	for (s32 i=0; i<3; ++i)
		MouseKeys[i] = false;
}

bool RTSCamera::isKeyDown(s32 key)
{
	return Keys[key];
}

bool RTSCamera::isMouseButtonDown(s32 key)
{
	return MouseKeys[key];
}

void RTSCamera::animate()
{
	f32 nRotX = rotX;
	f32 nRotY = rotY;
	f32 nZoom = currentZoom;

	vector3df translate(oldTarget);
	vector3df tvectX = Pos - Target;
	tvectX = tvectX.crossProduct(UpVector);
	tvectX.normalize();

	//Zoom
	if (isMouseButtonDown(MOUSE_BUTTON_RIGHT) && isMouseButtonDown(MOUSE_BUTTON_LEFT))
	{
		if (!zooming)
		{
			zoomStartX = MousePos.X;
			zoomStartY = MousePos.Y;
			zooming = true;
			nZoom = currentZoom;
		}
		else
		{
			f32 old = nZoom;
			nZoom += (zoomStartX - MousePos.X) * zoomSpeed * 100;

//			f32 targetMinDistance = 0.1f;
//			if (nZoom < targetMinDistance)
//				nZoom = targetMinDistance;

			if (nZoom < targetMinDistance)
				nZoom = targetMinDistance;
			else if (nZoom > targetMaxDistance)
				nZoom = targetMaxDistance;


			if (nZoom < 0)
				nZoom = old;
		}
	}
	else
	{
		if (zooming)
		{
			f32 old = currentZoom;
			currentZoom = currentZoom + (zoomStartX - MousePos.X ) * zoomSpeed;
			nZoom = currentZoom;

			if (nZoom < 0)
				nZoom = currentZoom = old;
		}
		zooming = false;
	}

	//Rotate
	if(isMouseButtonDown(MOUSE_BUTTON_LEFT) && !zooming)
	{
		if (!rotating)
		{
			rotateStartX = MousePos.X;
			rotateStartY = MousePos.Y;
			rotating = true;
			nRotX = rotX;
			nRotY = rotY;
		}
		else
		{
			nRotX += (rotateStartX - MousePos.X) * rotateSpeed;
			nRotY += (rotateStartY - MousePos.Y) * rotateSpeed;
		}
	}
	else
	{
		if (rotating)
		{
			rotX = rotX + (rotateStartX - MousePos.X) * rotateSpeed;
			rotY = rotY + (rotateStartY - MousePos.Y) * rotateSpeed;
			nRotX = rotX;
			nRotY = rotY;
		}

		rotating = false;
	}

	//Translate
	if(isMouseButtonDown(MOUSE_BUTTON_RIGHT) && !zooming)
	{
		if (!translating)
		{
			translateStartX = MousePos.X;
			translateStartY = MousePos.Y;
			translating = true;
		}
		else
		{
			translate += tvectX * (translateStartX - MousePos.X) * translateSpeed;
			translate.X += tvectX.Z * (translateStartY - MousePos.Y) * translateSpeed;
			translate.Z -= tvectX.X * (translateStartY - MousePos.Y) * translateSpeed;

			oldTarget = translate;
		}
	}
	else if (isKeyDown(KEY_KEY_W) || isKeyDown(KEY_UP) && !zooming)
	{
		if (!translating)
			translating = true;
		else
		{
			vector3df movevector = getPosition() - getTarget();
			movevector.Y = 0;
			movevector.normalize();

			setPosition(getPosition() - movevector * translateSpeed);
			setTarget(getTarget() - movevector * translateSpeed);
			updateAbsolutePosition();
		}
	}
	else if (isKeyDown(KEY_KEY_S) || isKeyDown(KEY_DOWN) && !zooming)
	{
		if (!translating)
			translating = true;
		else
		{
			vector3df movevector = getPosition() - getTarget();
			movevector.Y = 0;
			movevector.normalize();

			setPosition(getPosition() + movevector * translateSpeed);
			setTarget(getTarget() + movevector * translateSpeed);
			updateAbsolutePosition();
		}
	}
	else if (isKeyDown(KEY_KEY_A) || isKeyDown(KEY_LEFT) && !zooming)
	{
		if (!translating)
			translating = true;
		else
		{
			vector3df totargetvector = getPosition() - getTarget();
			totargetvector.normalize();
			vector3df crossvector = totargetvector.crossProduct(getUpVector());
			vector3df strafevector = crossvector.normalize();

			setPosition(getPosition() - strafevector * translateSpeed);
			setTarget(getTarget() - strafevector * translateSpeed);
			updateAbsolutePosition();
		}
	}
	else if (isKeyDown(KEY_KEY_D) || isKeyDown(KEY_RIGHT) && !zooming)
	{
		if (!translating)
			translating = true;
		else
		{
			vector3df totargetvector = getPosition() - getTarget();
			totargetvector.normalize();
			vector3df crossvector = totargetvector.crossProduct(getUpVector());
			vector3df strafevector = crossvector.normalize();

			setPosition(getPosition() + strafevector * translateSpeed);
			setTarget(getTarget() + strafevector * translateSpeed);
			updateAbsolutePosition();
		}
	}
	else
	{
		translating = false;

		if (!translating && !zooming && !rotating)
		{
			//Mouse Coordinates go from 0 to 1 on both axes
			if (MousePos.X < 0.05)	//Up
			{
				vector3df totargetvector = getPosition() - getTarget();
				totargetvector.normalize();
				vector3df crossvector = totargetvector.crossProduct(getUpVector());
				vector3df strafevector = crossvector.normalize();

				setPosition(getPosition() - strafevector * translateSpeed);
				setTarget(getTarget() - strafevector * translateSpeed);
				updateAbsolutePosition();
			}
			else if (MousePos.X > 0.95) //Down
			{
				vector3df totargetvector = getPosition() - getTarget();
				totargetvector.normalize();
				vector3df crossvector = totargetvector.crossProduct(getUpVector());
				vector3df strafevector = crossvector.normalize();

				setPosition(getPosition() + strafevector * translateSpeed);
				setTarget(getTarget() + strafevector * translateSpeed);
				updateAbsolutePosition();
			}
			else if (MousePos.Y < 0.05)	//Up
			{
				vector3df movevector = getPosition() - getTarget();
				movevector.Y = 0;
				movevector.normalize();

				setPosition(getPosition() - movevector * translateSpeed);
				setTarget(getTarget() - movevector * translateSpeed);
				updateAbsolutePosition();
			}
			else if (MousePos.Y > 0.95) //Down
			{
				vector3df movevector = getPosition() - getTarget();
				movevector.Y = 0;
				movevector.normalize();

				setPosition(getPosition() + movevector * translateSpeed);
				setTarget(getTarget() + movevector * translateSpeed);
				updateAbsolutePosition();
			}
		}
	}

	//Set Position
	Target = translate;

	Pos.X = nZoom + Target.X;
	Pos.Y = Target.Y;
	Pos.Z = Target.Z;

	Pos.rotateXYBy(nRotY, Target);
	Pos.rotateXZBy(-nRotX, Target);

	//Correct Rotation Error
	UpVector.set(0,1,0);
	UpVector.rotateXYBy(-nRotY, core::vector3df(0,0,0));
	UpVector.rotateXZBy(-nRotX+180.f, core::vector3df(0,0,0));
}

void RTSCamera::updateAnimationState()
{
	vector3df pos(Pos - Target);

	// X rotation
	vector2df vec2d(pos.X, pos.Z);
	rotX = (f32)vec2d.getAngle();

	// Y rotation
	pos.rotateXZBy(rotX, vector3df());
	vec2d.set(pos.X, pos.Y);
	rotY = -(f32)vec2d.getAngle();

	// Zoom
	currentZoom = (f32)Pos.getDistanceFrom(Target);
}
Interface hasn't changed:

Code: Select all

RTSCamera* camera = new RTSCamera(device,smgr->getRootSceneNode(),smgr,-1,1000.0f,10.0f,10.0f);
camera->setPosition(vector3df(600,600,600)); 
Ico
Posts: 289
Joined: Tue Aug 22, 2006 11:35 pm

Post by Ico »

Quite interesting - maybe you could add three things (shouldn't be that hard to do at all :) ) so it's "ready out of the box":

Add a bounding box that limits the camera's movement (ignored if set to the default NULL; min/max zoom defined by bounding box too?).

Add members to control the keys/mousebutton config.

Maybe add the possibility to drag the map (or better the camera) by (i.e.) holding the right mouse button and dragging the cursor.
Post Reply