Showing posts with label How-To. Show all posts
Showing posts with label How-To. Show all posts

Thursday, December 17, 2009

Irrlicht – my third person camera

Currently I work on my own RPG game and I hope after gaining enough experience with it I’ll try to make my own MMORPG. Am I humble enough?

There are many MMORPG games on the market and all of them can work as an inspiration. My first task was to make a third person camera or more precisely third person camera animator. You don’t need to make an animator to use this logic. Creating an animator leads to some complications that I won’t discuss here.

We will use the right mouse button to orbit around the object and the scroller to zoom in and out. You need to add something like this in your event receiver or in OnEvent function of the animator:

 case EMIE_RMOUSE_PRESSED_DOWN:
  MouseKeys[2] = true;

  break;
 case EMIE_RMOUSE_LEFT_UP:
  MouseKeys[2] = false;

  break;
 case EMIE_MOUSE_MOVED:
  MousePos = CursorControl->getRelativePosition();

  break;
 case EMIE_MOUSE_WHEEL:
  Zoom = Zoom + event.MouseInput.Wheel*ZoomSpeed;

  if (Zoom < TargetMinDistance)
   Zoom = TargetMinDistance;

  if (Zoom > TargetMaxDistance)
      Zoom = TargetMaxDistance;

Now, when we have the input data, we must implement the logic. You must add this in animateNode function of your animator, or in some update method if you don’t use an animator.

 core::vector3df target = targetNode->getPosition();

 f32 nRotX = RotX;
 f32 nRotY = RotY;

 if (isMouseKeyDown(2))
 {
  if (!Rotating)

  {
   RotateStart = MousePos;
   Rotating = true;

   nRotX = RotX;
   nRotY = RotY;
  }

  else
  {
   nRotX += (RotateStart.X - MousePos.X) * RotateSpeed;

   nRotY += (RotateStart.Y - MousePos.Y) * RotateSpeed;

  }
 }
 else if(Rotating)
 {

  RotX += (RotateStart.X - MousePos.X) * RotateSpeed;

  RotY += (RotateStart.Y - MousePos.Y) * RotateSpeed;

  nRotX = RotX;
  nRotY = RotY;
  Rotating = false;

 }

 target = targetNode->getPosition();

 Pos.X = Zoom + target.X;
 Pos.Y = Zoom + target.Y;

 Pos.Z = target.Z;

 Pos.rotateXYBy(nRotY, target);

 Pos.rotateXZBy(-nRotX, target);

 camera->setPosition(Pos);

 camera->setTarget(target); 

This is very basic functionality but you will grasp the logic. I’ve used the Maya animator the get the idea. My code have a nasty flip on the top and the bottom, you can fix it if you want with restricting the camera movement or changing the up vector. And don’t make the camera parent or child of the target.

Wednesday, October 28, 2009

Irrlicht - How to get mouse pointer coordinates on a terrain?

You may need the coordinates where a user clicked on your terrain for many reasons. For example many MMORPG games use the mouse to move the character. Using point-and-click system is relatively easy for the players and not hard to implement as a code.

How to get mouse coordinates from a 3D place with Irrlicht? You need to use getCollisionPoint member function of the irr::scene::ISceneCollisionManager class. The current documentation at the official site is for version 1.5.1 but the current Irrlicht version is 1.6. getCollisionPoint function has one more parameter in the current version.
If you try to compile the project with only four parameters you’ll get an error message:

**** Build of configuration Debug for project GameNext ****

**** Internal Builder is used for build ****
g++ -ID:\irrlicht-1.6\include -O0 -g3 -Wall -c -fmessage-length=0 -osrc\GameNext.o ..\src\GameNext.cpp
..\src\GameNext.cpp: In function `int main()':
..\src\GameNext.cpp:59: error: no matching function for call to `irr::scene::ISceneCollisionManager::getCollisionPoint(const irr::core::line3d<irr::f32>&, irr::scene::ITriangleSelector*&, irr::core::vector3df&, irr::core::triangle3df&)'
D:/irrlicht-1.6/include/ISceneCollisionManager.h:43: note: candidates are: virtual bool irr::scene::ISceneCollisionManager::getCollisionPoint(const irr::core::line3d<irr::f32>&, irr::scene::ITriangleSelector*, irr::core::vector3df&, irr::core::triangle3df&, const irr::scene::ISceneNode*&)
..\src\GameNext.cpp:57: warning: unused variable 'node'
Build error occurred, build is stopped
Time consumed: 1873 ms.

The right syntax is:

bool getCollisionPoint(const core::line3d<f32>& ray,
        ITriangleSelector* selector, core::vector3df& outCollisionPoint,
        core::triangle3df& outTriangle, const ISceneNode*& outNode)


So how would our code look like?

First we need to get the cursor position in the 2D space.
core::position2d< s32 > pos = cursor->getPosition();
Now, we need to get the 3D vector from it.
const core::line3d<f32> ray = 
        collisionManager->getRayFromScreenCoordinates(pos);
And check if our line intersects with the terrain.
if(collisionManager->getCollisionPoint
        (ray, selector, point, triangle, node))

{
 // do something
}

Here is the full demo code:

#include <irrlicht.h>

using namespace irr;

using namespace core;
using namespace scene;
using namespace video;

using namespace io;
using namespace gui;

int main() {

 IrrlichtDevice *device = createDevice(video::EDT_OPENGL,
   dimension2d<u32> (800, 600),
   16, false, false, false, 0);

 if (!device)
  return 1;

 IVideoDriver* driver = device->getVideoDriver();

 ISceneManager* sceneManager = device->getSceneManager();
 IGUIEnvironment* guiEnvironment = device->getGUIEnvironment();

 // -------
 scene::ITerrainSceneNode* currentTerrain =
   sceneManager->addTerrainSceneNode(

     "media/poiheightmap.bmp", // height map
     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(100.f, 1.f, 100.f), // scale

     video::SColor(255, 255, 255, 255), // vertexColor

     5, // maxLOD
     scene::ETPS_17, // patchSize
     4);

 currentTerrain->setMaterialFlag(video::EMF_LIGHTING, false);
 currentTerrain->setMaterialTexture(0,
   driver->getTexture("media/terrain-texture.jpg"));

 currentTerrain->setMaterialTexture(1,
   driver->getTexture("media/detailmap3.jpg"));
 currentTerrain->scaleTexture(1.0f, 20.0f);

 ITriangleSelector* selector = sceneManager->
   createTerrainTriangleSelector(currentTerrain);

 currentTerrain->setTriangleSelector(selector);

 // retrieve the SceneCollisionManager object
 ISceneCollisionManager* collisionManager =

   sceneManager->getSceneCollisionManager();

 // get cursor
 ICursorControl *cursor = device->getCursorControl();

 // simple camera with position and target
 sceneManager->addCameraSceneNode(0,
   core::vector3df(100.f, 60.f, 100.f),
   core::vector3df(900.f, 0.f, 900.f));

 // this sphere will mark our collision point
 ISceneNode* sphere = sceneManager->addSphereSceneNode();

 while (device->run()) {
  core::position2d<s32> pos = cursor->getPosition();

  core::vector3df point;
  core::triangle3df triangle;

  const ISceneNode *node = 0;
  const core::line3d<f32> ray = collisionManager->

    getRayFromScreenCoordinates(pos);
  if (collisionManager->getCollisionPoint
    (ray, selector, point, triangle, node))

  {
   sphere->setPosition(point);
  }

  driver->beginScene(true, true, SColor(255, 50, 130, 50));

  sceneManager->drawAll();
  guiEnvironment->drawAll();
  driver->endScene();

 }

 selector->drop();

 device->drop();

 return 0;
}