Ok, so I'm REAL new at C++ and Irrlicht, but not so new to programming in general (PHP, javascript, actionscript, maxscript). Anyways, I spent the whole day (17hrs) putting together this block of code to do a 3rd person camera the way I like them done. Could you guys who know what your doing and how things should be done have a look at it for me and see if you can see any nasty stupid mistakes? (It works for me here, I mean stuff like "you really shouldn't be doing that that way").
Controls are pretty easy :
right click and drag to orbit
mouse wheel to zoom
W key to move forward
Code: Select all
#include <iostream>
#include <Irrlicht.h>
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
#pragma comment(lib, "Irrlicht.lib")
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SETTINGS /////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// setup key engine objects
IrrlichtDevice* device = createDevice(EDT_OPENGL,dimension2d<s32>(800,600),32,false,false,false);
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
IGUIEnvironment* guienv = device->getGUIEnvironment();
ICursorControl* CursorControl;
// keyboard registry
bool keys[irr::KEY_KEY_CODES_COUNT];
// mouse registry
bool mouseDownL;
bool mouseDownM;
bool mouseDownR;
f32 lastWheelMovement;
position2d<f32> cursor;
position2d<f32> cursorOld;
position2d<f32> cursorDelta;
// camera registry
f32 cameraOrbit = 45;
f32 cameraAngle = 0;
f32 cameraDistance = 30;
f32 cameraOrbitOld = 0;
f32 cameraAngleOld = 0;
// player registry
f32 playerX = 0;
f32 playerY = 0;
f32 playerZ = 0;
f32 playerCompass = 30;
f32 playerTurnTo = 0;
f32 playerTurnSpeed = 1;
f32 playerMoveSpeed = 0.01;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CLASS : MyEventReceiver //////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class MyEventReceiver : public IEventReceiver
{
public:
virtual bool OnEvent(const SEvent& event)
{
if(event.EventType == irr::EET_KEY_INPUT_EVENT)
{
keys[event.KeyInput.Key] = event.KeyInput.PressedDown;
return false;
}
if (event.EventType == EET_MOUSE_INPUT_EVENT)
{
// left mouse button state check
if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) mouseDownL = true;
if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) mouseDownL = false;
// middle mouse button state check
if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN) mouseDownM = true;
if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP) mouseDownM = false;
if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
{
cameraDistance -= event.MouseInput.Wheel * (cameraDistance / 20) * 2;
if(cameraDistance < 10) cameraDistance = 10;
if(cameraDistance > 100) cameraDistance = 100;
}
// right mouse button state check
if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
{
mouseDownR = true;
cursorOld.X = event.MouseInput.X;
cursorOld.Y = event.MouseInput.Y;
cursorDelta.X = 0;
cursorDelta.Y = 0;
cameraOrbitOld = cameraOrbit;
cameraAngleOld = cameraAngle;
}
if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) mouseDownR = false;
// mouse move check
if(event.MouseInput.Event == EMIE_MOUSE_MOVED)
{
// add condition that right mouse button be down
if(mouseDownR == true){
cursor.X = event.MouseInput.X;
cursor.Y = event.MouseInput.Y;
cursorDelta.X = cursor.X - cursorOld.X;
cursorDelta.Y = cursor.Y - cursorOld.Y;
if(cursorDelta.Y > 100) cursorDelta.Y = 100;
if(cursorDelta.Y < -100) cursorDelta.Y = -100;
cameraOrbit = (int)(cameraOrbitOld + cursorDelta.X) % 360;
cameraAngle = (int)(cameraAngleOld + cursorDelta.Y) % 360;
if(cameraAngle > 88) cameraAngle = 88;
if(cameraAngle < -88) cameraAngle = -88;
}
}
return false;
}
return false;
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CLASS : CMySampleSceneNode ///////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class CMySampleSceneNode : public ISceneNode
{
private:
static const s32 tileResolution = 32; // how many segments to have down each edge of the tile
static const s32 cellSize = 2; // how many units square will each cell of the tile be?
aabbox3d<f32> Box; // [A]xis [A]ligned [B]ounding [B]ox in [3D] space
S3DVertex Vertices[((tileResolution+1) * (tileResolution+1))]; // An array where each cell is of type S3DVertex
u16 indices[(tileResolution * tileResolution) * 6]; // number of cells * 6 (each cell has two tris, each tri has 3 indices)
SMaterial Material; // A material object of type SMaterial
IVideoDriver *driver; // A link through to the driver object
public:
/////////////////
// constructor //
/////////////////
CMySampleSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id) : ISceneNode(parent, mgr, id)
{
// put link to videoDriver into already prepared var of type IVideoDriver
driver = SceneManager->getVideoDriver();
// set material settings
Material.Wireframe = true;
Material.Lighting = false;
Material.BackfaceCulling = false;
// create vertex objects in vertex array
s32 vIndex = 0;
for (f32 i=0; i<(tileResolution+1); i++){
for (f32 j=0; j<(tileResolution+1); j++){
Vertices[vIndex] = S3DVertex((j*cellSize),0,(i*cellSize), 0,0,1, SColor(255,0,255,255),j,i);
// std :: cout << vIndex << ":" << j << "," << i << "\n";
vIndex++;
}
}
// reposition the bounding box to the first vertex
Box.reset(Vertices[0].Pos);
// add the defined vertices to Box
for (s32 i=0; i<vIndex; i++)
Box.addInternalPoint(Vertices[i].Pos);
// setup indices
s32 iIndex = 0;
for (s32 i=0; i<tileResolution; i++){
for (s32 j=0; j<tileResolution; j++){
s32 currentVertex = (i*(tileResolution+1)) + j;
indices[iIndex + 0] = currentVertex;
indices[iIndex + 1] = currentVertex+1;
indices[iIndex + 2] = currentVertex+tileResolution+1;
indices[iIndex + 3] = currentVertex+1;
indices[iIndex + 4] = currentVertex+tileResolution+2;
indices[iIndex + 5] = currentVertex+tileResolution+1;
iIndex = iIndex + 6;
}
}
}
/////////////////////////
// OnRegisterSceneNode //
/////////////////////////
virtual void OnRegisterSceneNode()
{
if (IsVisible)
ISceneNode::OnRegisterSceneNode();
}
///////////////
// OnAnimate //
///////////////
virtual void OnAnimate(u32 timeMs)
{
ISceneNode::OnAnimate(timeMs);
}
////////////
// render //
////////////
virtual void render()
{
driver->setMaterial(Material);
driver->setTransform(ETS_WORLD, AbsoluteTransformation);
driver->drawIndexedTriangleList(&Vertices[0],(tileResolution+1) * (tileResolution+1),&indices[0],((tileResolution*tileResolution)*2));
}
////////////////////
// getBoundingBox //
////////////////////
virtual const aabbox3d<f32>& getBoundingBox() const
{
return Box;
}
//////////////////////
// getMaterialCount //
//////////////////////
virtual u32 getMaterialCount()
{
return 1;
}
/////////////////
// getMaterial //
/////////////////
virtual SMaterial& getMaterial(s32 i)
{
return Material;
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION : sphericalXYZ //////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
vector3df sphericalXYZ(f32 compassAngle, f32 elevationAngle, f32 radius){
compassAngle = compassAngle * -1;
elevationAngle = elevationAngle * -1;
elevationAngle = elevationAngle + 90;
f32 x = radius * cos(compassAngle * PI/180.0f ) * sin(elevationAngle * PI/180.0f );
f32 z = radius * sin(compassAngle * PI/180.0f ) * sin(elevationAngle * PI/180.0f );
f32 y = radius * cos(elevationAngle * PI/180.0f );
vector3df result;
result.X = x;
result.Y = y;
result.Z = z;
return result;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION : int2string ////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
stringw int2string(int intToConvert){
char temp[50];
sprintf(temp,"%d",intToConvert);
return temp;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//// /////////////////////////////////////////////////////////////////////////////////////////////////////////////
//// MAIN /////////////////////////////////////////////////////////////////////////////////////////////////////////////
//// /////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int main()
{
// setup event receiver
MyEventReceiver rv;
device->setEventReceiver(&rv);
for(int x=0; x<irr::KEY_KEY_CODES_COUNT; x++) keys[x] = false;
// setup screen display elements
device->setWindowCaption(L"Irrlicht Custom Scene Node!");
IGUIStaticText* debug_panel = guienv->addStaticText(L"Hello World",rect<s32>(5, 5, 200, 200),false,true,0,-1,false);
// setup the "player"
ISceneNode* cPivot1 = smgr->addCubeSceneNode(0.5,0,-1,vector3df(playerX,playerY,playerZ));
cPivot1->setMaterialTexture(0,driver->getTexture("media/wall.bmp"));
cPivot1->setMaterialFlag(video::EMF_LIGHTING, false);
ISceneNode* cPivot2 = smgr->addCubeSceneNode(2,cPivot1,-1,vector3df(0,1,0));
cPivot2->setMaterialTexture(0,driver->getTexture("media/wall.bmp"));
cPivot2->setMaterialFlag(video::EMF_LIGHTING, false);
ISceneNode* cPivot3 = smgr->addCubeSceneNode(1,cPivot2,-1,vector3df(0,0,2));
cPivot3->setMaterialTexture(0,driver->getTexture("media/wall.bmp"));
cPivot3->setMaterialFlag(video::EMF_LIGHTING, false);
// setup the camera
ICameraSceneNode* myCamera = smgr->addCameraSceneNode(cPivot1, sphericalXYZ(cameraOrbit,cameraAngle,cameraDistance), cPivot1->getPosition());
// create references to our custom nodes
CMySampleSceneNode* myNode = new CMySampleSceneNode(smgr->getRootSceneNode(),smgr,1);
myNode->setPosition(vector3df(0,0,0));
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DEVICE : main loop ///////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
while(device->run() && device->isWindowActive()) {
/////////////////////////
// keyboard management //
/////////////////////////
// system keys
if(keys[KEY_ESCAPE]){
device->closeDevice();
}
// player keys
if(keys[KEY_KEY_W]){
// player movement vector
vector3df playerStep = sphericalXYZ(cameraOrbit,0,playerMoveSpeed);
playerX -= playerStep.X;
playerY -= playerStep.Y;
playerZ -= playerStep.Z;
// player rotation brain torture
playerTurnTo = cameraOrbit - 90;
if(playerTurnTo < 0) playerTurnTo += 360;
if(playerTurnTo >= 360) playerTurnTo -= 360;
if(playerCompass < 0) playerCompass = playerCompass + 360;
if(playerCompass >= 360) playerCompass = playerCompass - 360;
f32 playerTurnDir = 0;
f32 playerTurnDelta = 0;
if(playerCompass > playerTurnTo)
{
if(playerCompass - playerTurnTo < 180){
playerTurnDir = -1;
playerTurnDelta = playerCompass - playerTurnTo;
} else {
playerTurnDir = 1;
playerTurnDelta = (360 - playerCompass) + playerTurnTo;
}
}
if(playerCompass < playerTurnTo)
{
if(playerTurnTo - playerCompass < 180){
playerTurnDir = 1;
playerTurnDelta = playerTurnTo - playerCompass;
} else {
playerTurnDir = -1;
playerTurnDelta = (360 - playerTurnTo) + playerCompass;
}
}
f32 playerTurnAmount;
if(playerTurnDelta < playerTurnSpeed){
playerTurnAmount = playerTurnDelta;
} else {
playerTurnAmount = playerTurnSpeed;
}
playerCompass += (playerTurnAmount * playerTurnDir);
cPivot1->setPosition(vector3df(playerX,playerY,playerZ));
cPivot2->setRotation(vector3df(0,playerCompass,0));
myCamera->setTarget(vector3df(playerX,playerY,playerZ));
}
// orient camera
myCamera->setPosition(sphericalXYZ(cameraOrbit,cameraAngle,cameraDistance));
// debug display
stringw displayFPS = (stringw)"FPS : " + int2string(driver->getFPS());
stringw displayLmouse = (stringw)"left mouse button : " + mouseDownL;
stringw displayMmouse = (stringw)"middle mouse button : " + mouseDownM;
stringw displayRmouse = (stringw)"right mouse button : " + mouseDownR;
stringw displayWmouse = (stringw)"last wheel movement : " + lastWheelMovement;
stringw displayCursorX = (stringw)"cursor x : " + cursor.X;
stringw displayCursorY = (stringw)"cursor y : " + cursor.Y;
stringw displayCursorOldX = (stringw)"cursorOld x : " + cursorOld.X;
stringw displayCursorOldY = (stringw)"cursorOld y : " + cursorOld.Y;
stringw displayCursorDeltaX = (stringw)"cursorDelta x : " + cursorDelta.X;
stringw displayCursorDeltaY = (stringw)"cursorDelta y : " + cursorDelta.Y;
stringw displayCameraOrbit = (stringw)"camera orbit : " + cameraOrbit;
stringw displayCameraAngle = (stringw)"camera angle : " + cameraAngle;
stringw displayPlayerCompass = (stringw)"player compass : " + playerCompass;
stringw displayPlayerTurnTo = (stringw)"player turn to : " + playerTurnTo;
stringw displayDebug = displayFPS + "\n" +
displayLmouse + "\n" +
displayMmouse + "\n" +
displayRmouse + "\n" +
displayWmouse + "\n" +
displayCursorX + "\n" +
displayCursorY + "\n" +
displayCursorOldX + "\n" +
displayCursorOldY + "\n" +
displayCursorDeltaX + "\n" +
displayCursorDeltaY + "\n" +
displayCameraOrbit + "\n" +
displayCameraAngle + "\n" +
displayPlayerCompass + "\n" +
displayPlayerTurnTo;
debug_panel->setText(stringw(displayDebug).c_str());
//////////////////
// render scene //
//////////////////
driver->beginScene(true, true, SColor(0,120,120,120));
myNode->render();
smgr->drawAll();
guienv->drawAll();
driver->endScene();
}
device->drop();
return 0;
}