Lineage 2 style cam + mouse control

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
vectorcorpse
Posts: 86
Joined: Thu Feb 14, 2008 7:30 pm
Location: Portugal

Lineage 2 style cam + mouse control

Post by vectorcorpse »

hi
1st of all i have to say this is extremely dirty and unoptimized code has it's still a work in progress and has 1 bug to be fixed.
i made this with pieces of code from several examples and questions of the forum, and mixed it all in one, also i have stopped programing in 1999 because of 3 mental breakdowns and just recently restarted so give me a break has my c++ coding skills are really rusty.
i am posting this for 2 reasons:
1st for those who can't wait
2nd maybe someone can help me fixing the bugs and optimizing the code (i think there's redundant code in it besides the unwanted auto orbit rotation induced by the player rotation).
so here it is :?

Code: Select all

#include <irrlicht.h>

#include <iostream>



using namespace irr;

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


#pragma comment(lib, "Irrlicht.lib")

ICursorControl* CursorControl;
bool keys[irr::KEY_KEY_CODES_COUNT];
bool mouseDownL;
bool mouseDownM;
bool mouseDownR;
f32 lastWheelMovement;
position2d<f32> cursor;
position2d<f32> cursorOld;
position2d<f32> cursorDelta;

f32 cameraOrbit = 45;
f32 cameraAngle = 0;
f32 cameraDistance = 50;
f32 cameraOrbitOld = 0;
f32 cameraAngleOld = 0;

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 > 1000) cameraDistance = 1000;
         }

         // 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;
   }
};

//spherical rotation
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;
}


int main()

{


	video::E_DRIVER_TYPE driverType;



	printf("Please select the driver you want for this example:\n"\

		" (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\

		" (d) Software Renderer\n (e) Burning's Software Renderer\n"\

		" (f) NullDevice\n (otherKey) exit\n\n");



	char i;

	std::cin >> i;



	switch(i)

	{

		case 'a': driverType = video::EDT_DIRECT3D9;break;

		case 'b': driverType = video::EDT_DIRECT3D8;break;

		case 'c': driverType = video::EDT_OPENGL;   break;

		case 'd': driverType = video::EDT_SOFTWARE; break;

		case 'e': driverType = video::EDT_BURNINGSVIDEO;break;

		case 'f': driverType = video::EDT_NULL;     break;

		default: return 1;

	}	





	// create device and exit if creation failed



	IrrlichtDevice *device =

		createDevice(driverType, core::dimension2d<s32>(640, 480));



	if (device == 0)

		return 1; // could not create selected driver.



	/*

	Get a pointer to the video driver and the SceneManager so that

	we do not always have to write device->getVideoDriver() and

	device->getSceneManager().

	*/

	video::IVideoDriver* driver = device->getVideoDriver();

	scene::ISceneManager* smgr = device->getSceneManager();

	//start event receiver
	MyEventReceiver rv;
	device->setEventReceiver(&rv); 
	for(int x=0; x<irr::KEY_KEY_CODES_COUNT; x++) keys[x] = false;


	/*

	To display the Quake 3 map, we first need to load it. Quake 3 maps

	are packed into .pk3 files wich are nothing other than .zip files.

	So we add the .pk3 file to our FileSystem. After it was added,

	we are able to read from the files in that archive as they would

	directly be stored on disk.

	*/

	device->getFileSystem()->addZipFileArchive("../../media/map-20kdm2.pk3");



	/* 

	Now we can load the mesh by calling getMesh(). We get a pointer returned

	to a IAnimatedMesh. As you know, Quake 3 maps are not really animated,

	they are only a huge chunk of static geometry with some materials

	attached. Hence the IAnimated mesh consists of only one frame,

	so we get the "first frame" of the "animation", which is our quake level

	and create an OctTree scene node with it, using addOctTreeSceneNode().

	The OctTree optimizes the scene a little bit, trying to draw only geometry

	which is currently visible. An alternative to the OctTree would be a 

	AnimatedMeshSceneNode, which would draw always the complete geometry of 

	the mesh, without optimization. Try it out: Write addAnimatedMeshSceneNode

	instead of addOctTreeSceneNode and compare the primitives drawed by the

	video driver. (There is a getPrimitiveCountDrawed() method in the 

	IVideoDriver class). Note that this optimization with the Octree is only

	useful when drawing huge meshes consiting of lots of geometry.

	*/

	

	scene::IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");

	scene::ISceneNode* q3node = 0;

	

	if (q3levelmesh)

		q3node = smgr->addOctTreeSceneNode(q3levelmesh->getMesh(0));



		// load a skydome.

		driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);

		smgr->addSkyDomeSceneNode(driver->getTexture("../../media/SkyDome.jpg"),16,16,1.0,1.5);

		driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);


	/*

	So far so good, we've loaded the quake 3 level like in tutorial 2. Now, here

	comes something different: We create a triangle selector. A triangle selector

	is a class which can fetch the triangles from scene nodes for doing different

	things with them, for example collision detection. There are different triangle

	selectors, and all can be created with the ISceneManager. In this example,

	we create an OctTreeTriangleSelector, which optimizes the triangle output a

	little bit by reducing it like an octree. This is very useful for huge meshes

	like quake 3 levels.

	After we created the triangle selector, we attach it to the q3node. This is not

	necessary, but in this way, we do not need to care for the selector, for example

	dropping it after we do not need it anymore.

	*/



	scene::ITriangleSelector* selector = 0;

	

	if (q3node)

	{		

		q3node->setPosition(core::vector3df(-1350,-130,-1400));



		selector = smgr->createOctTreeTriangleSelector(q3levelmesh->getMesh(0), q3node, 128);

		q3node->setTriangleSelector(selector);

	}



	// add a billboard to show the target movement position
	scene::IBillboardSceneNode * bill = smgr->addBillboardSceneNode();
	bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
	bill->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
	bill->setMaterialFlag(video::EMF_LIGHTING, false);
	bill->setMaterialFlag(video::EMF_ZBUFFER, false); // Always appears on top
	bill->setSize(core::dimension2d<f32>(20.0f, 20.0f));


	device->getCursorControl()->setVisible(true);


    // Create a 3rd person player node
    scene::ISceneNode * player = smgr->addEmptySceneNode();
    player->setPosition(core::vector3df(-70,0,-90));
    scene::ISceneNode * soul = smgr->addEmptySceneNode();
    soul->setPosition(core::vector3df(-70,0,-90));

    // The faerie model is oriented towards +X, but I do calculations on +Z
    // so I have to use a child node to rotate it 90 degrees anticlockwise
    scene::IAnimatedMesh* faerieMesh = smgr->getMesh("../../media/faerie.md2");
    scene::IAnimatedMeshSceneNode * visualNode = smgr->addAnimatedMeshSceneNode(faerieMesh, player);
    video::SMaterial material;
    material.setTexture(0, driver->getTexture("../../media/faerie2.bmp"));
    material.Lighting = false;
    visualNode->getMaterial(0) = material;

    visualNode->setRotation(core::vector3df(0.f, -90.f, 0.f)); // rotates +X to +Z
   
    // Work out how big the faerie mesh is
    const core::aabbox3df & box = visualNode->getBoundingBox();
    const f32 height = (box.MaxEdge.Y - box.MinEdge.Y) / 2.f;
    const f32 verticalOffset = -box.MinEdge.Y - box.MaxEdge.Y;
    const f32 waist = core::max_(box.MaxEdge.X - box.MinEdge.X, box.MaxEdge.Z - box.MinEdge.Z) / 2.f;

    // And give the avatar node an appropriate physical presence
    scene::ISceneNodeAnimator * anim = smgr->createCollisionResponseAnimator(
        selector, player, core::vector3df(waist, height, waist),
        core::vector3df(0, -3, 0),
        core::vector3df(0, verticalOffset, 0));
    player->addAnimator(anim);
    anim->drop(); anim = 0;


//	scene::ICameraSceneNode* camera = smgr->addCameraSceneNode();
	//camera->setParent(player);

//	camera->setPosition(core::vector3df(-10,50,-150));


	// setup the camera
	ICameraSceneNode* myCamera = smgr->addCameraSceneNode(soul, sphericalXYZ(cameraOrbit,cameraAngle,cameraDistance), player->getPosition());

	/*

	We have done everything, so lets draw it. We also write the current

	frames per second and the drawn primitives to the caption of the

	window. The 'if (device->isWindowActive())' line is optional, but 

	prevents the engine render to set the position of the mouse cursor 

	after task switching when other program are active.

	*/

	int lastFPS = -1;

	bool wasRunning = false; // Used to control animatins
	u32 lastFrameTime = device->getTimer()->getTime();

	// These values control how the avatar moves
	const f32 TURN_SPEED = 200.f;
	const f32 MOVE_SPEED = 100.f;
	const f32 STOP_AT_DISTANCE_FROM_TARGET = waist * 1.1f;
	const f32 MAXIMUM_YAW_DELTA_BEFORE_MOVING = 60.f; // degrees
	const f32 MAX_TARGET_POSITION_IN_FRONT_OF_CAMERA = waist * 40.f;
	core::vector3df targetP;

	while(device->run())

	if (device->isWindowActive())

	{
	      if(keys[KEY_ESCAPE]){
	         device->closeDevice();
	      }
	myCamera->setTarget(player->getPosition());
        // orient camera
        myCamera->setPosition(sphericalXYZ(cameraOrbit,cameraAngle,cameraDistance));

	 u32 thisFrameDuration = device->getTimer()->getTime() - lastFrameTime;
	 f32 frameTimeMultiplier = (f32)thisFrameDuration / 1000.f;
	 lastFrameTime += thisFrameDuration;
 
		core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(device->getCursorControl()->getPosition());

		core::vector3df collisionPoint;
		core::triangle3df collisionTriangle;
		if(smgr->getSceneCollisionManager()->getCollisionPoint(ray, selector, collisionPoint, collisionTriangle))
		//{
		    // Do something with the collisionPoint
			
		//}

//	if(keys[KEY_KEY_W])
//	{
//	 targetP=collisionPoint;
//	}
	if(mouseDownL==true)
	{
	 targetP=collisionPoint;
	}
	mouseDownL=false;
	bill->setPosition(targetP);
        core::vector3df toTarget(targetP - player->getAbsolutePosition());
        toTarget.Y = 0; // Ignore any vertical difference

        if(toTarget.getLength() <= STOP_AT_DISTANCE_FROM_TARGET)
        {
            // The avatar is close to the target position, so don't move.
            if(wasRunning)
            {
                visualNode->setMD2Animation(scene::EMAT_STAND);
                wasRunning = false;
            }
        }
        else
        {
            // Turn, and if facing near enough to the right direction, move.

            // Work out the required rotation, in degrees
            f32 requiredYaw = atan2(toTarget.Z, toTarget.X) * core::RADTODEG;
            // atan2 calculates anticlockwise from the X axis.  We want clockwise from the Z axis.
            requiredYaw *= -1.f; // Make the result clockwise...
            requiredYaw += 90.f; // ...and rebase from X axis to Z axis.

            f32 actualYaw = player->getRotation().Y;

            // Work out how much the avatar would have to turn
            f32 deltaYaw = requiredYaw - actualYaw;
            if(deltaYaw > 180.f)
                deltaYaw = deltaYaw - 360.f;
            else if(deltaYaw < -180.f)
                deltaYaw = deltaYaw + 360.f;

            // Is it s facing close enough to the right direction in order to move?
            bool move = fabs(deltaYaw) < MAXIMUM_YAW_DELTA_BEFORE_MOVING;

            // Now limit the rate of turn.
            if(deltaYaw > TURN_SPEED * frameTimeMultiplier)
                deltaYaw = TURN_SPEED * frameTimeMultiplier;
            else if(deltaYaw < -TURN_SPEED * frameTimeMultiplier)
                deltaYaw = -TURN_SPEED * frameTimeMultiplier;

            // Do the actual rotation.
            actualYaw += deltaYaw;
            player->setRotation(core::vector3df(0.f, actualYaw, 0.f));
           
            if(move)
            {
                if(!wasRunning)
                {
                    visualNode->setMD2Animation(scene::EMAT_RUN);
                    wasRunning = true;
                }

                actualYaw *= core::DEGTORAD; // Back to radians for sin/cos
                core::vector3df deltaMove(sin(actualYaw), 0.f, cos(actualYaw));
                deltaMove *= MOVE_SPEED * frameTimeMultiplier;
                player->setPosition(player->getAbsolutePosition() + deltaMove);
		soul->setPosition(player->getAbsolutePosition());
            }
        }


		driver->beginScene(true, true, video::SColor(0,200,200,200));

		smgr->drawAll();

		driver->endScene();



		int fps = driver->getFPS();



		if (lastFPS != fps)

		{

			core::stringw str = L"Irrlicht Engine - Quake 3 Map example [";

			str += driver->getName();

			str += "] FPS:";

			str += fps;



			device->setWindowCaption(str.c_str());

			lastFPS = fps;

		}

	}



	/*

	In the end, delete the Irrlicht device.

	*/

	device->drop();

	return 0;

}




revision 1
- duplicated map load removed
- left click changed to avoid drag and get better control
- added a sky dome (just drop u'r favorite sky dome on media dir and uncomment the 3 sky dome lines)

revision 2
- i managed to force the camera to behave correctly but there must be a better way
Last edited by vectorcorpse on Thu May 22, 2008 12:44 am, edited 4 times in total.
frostysnowman
Posts: 83
Joined: Fri Apr 11, 2008 3:06 am
Location: Beaverton, Oregon, US

Post by frostysnowman »

I noticed that you modified the loading a quake map example, so I pasted all of your code over it and compiled. The camera/movement system works really well!
hking2
Posts: 2
Joined: Thu May 15, 2008 6:30 pm

some question....

Post by hking2 »

sorry......i have some question want to ask.....
i am the beginner of irr.

const aabbox3df & box = char1Node->getBoundingBox();

i don't under stand how the pointer work?
vectorcorpse
Posts: 86
Joined: Thu Feb 14, 2008 7:30 pm
Location: Portugal

Post by vectorcorpse »

the getBoundingBox() "draws" a invisible box around the model (char1node) and then stores the box dimensions on a variable in this case a constant then u use those dimensions for collision with the map mesh
it is better explained on the example 07.collision of the tutorials and u can also take a look at the example 09.meshviewer to see the actual bounding boxes representation and visualize how they work. the box variable stores a 3d box and it is composed by box.MaxEdge.Y, box.MinEdge.Y, box.MaxEdge.X, etc... check the api documentation and the examples for more detailed info and if in doubt ask again and i'll try to explain he best i can. :D
noals
Posts: 70
Joined: Sun May 18, 2008 2:59 pm

Post by noals »

thx a lot.
it work pretty fine and will be a great help for me to figure out some stuff about .md2 animation as well.
thx again !
hking2
Posts: 2
Joined: Thu May 15, 2008 6:30 pm

Post by hking2 »

Thx A lot.
I am now understanding.
I just try to change something to make it move on much easy.....
same as many rpg game
vectorcorpse
Posts: 86
Joined: Thu Feb 14, 2008 7:30 pm
Location: Portugal

Post by vectorcorpse »

has i thought i did had redundant code, just updated the code to remove the duplicated map load changed a bit the left mouse control and added a skydome :D
noals
Posts: 70
Joined: Sun May 18, 2008 2:59 pm

Post by noals »

2nd maybe someone can help me fixing the bugs and optimizing the code
sorry, i noticed the 2 same line up and down one of your big comment while deleting all space but didnt take the time to say it here.

im not good in programming but if the previous code without space interested you or anybody, here it is.

the only thing i changed a little for now is the line about choosing your driver so well....

Code: Select all

#include <irrlicht.h>

#include <iostream>



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


#pragma comment(lib, "Irrlicht.lib")

ICursorControl* CursorControl;
bool keys[KEY_KEY_CODES_COUNT];
bool mouseDownL;
bool mouseDownM;
bool mouseDownR;
f32 lastWheelMovement;
position2d<f32> cursor;
position2d<f32> cursorOld;
position2d<f32> cursorDelta;

f32 cameraOrbit = 45;
f32 cameraAngle = 0;
f32 cameraDistance = 50;
f32 cameraOrbitOld = 0;
f32 cameraAngleOld = 0;


    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;

            // mouse wheel
                if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
                {
                    cameraDistance -= -event.MouseInput.Wheel * (cameraDistance / 20) * 2;
                    if(cameraDistance < 10) cameraDistance = 10;
                    if(cameraDistance > 300) cameraDistance = 300;
                }

            // 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;
                }
            //endroit ou mettre la remise a zéro de la position
                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;
        }
    };



//spherical rotation
    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;
    }





int main()
{
    video::E_DRIVER_TYPE driverType;
    printf("Please select the driver you want for this example:\n"\
            "\n\
            (a) Direct3D 9.0c\n\
            (b) Direct3D 8.1\n\
            (c) OpenGL 1.5\n\
            (d) Software Renderer\n\
            (e) Burning's Software Renderer\n\
            (f) NullDevice\n\
            (otherKey) exit\n\n");


    char i;
    std::cin >> i;
    switch(i)
    {
        case 'a': driverType = video::EDT_DIRECT3D9;break;
        case 'b': driverType = video::EDT_DIRECT3D8;break;
        case 'c': driverType = video::EDT_OPENGL;   break;
        case 'd': driverType = video::EDT_SOFTWARE; break;
        case 'e': driverType = video::EDT_BURNINGSVIDEO;break;
        case 'f': driverType = video::EDT_NULL;     break;
        default: return 1;
    }





   // create device and exit if creation failed
    IrrlichtDevice *device =
    createDevice(driverType, core::dimension2d<s32>(640, 480));

    if (device == 0)
    return 1; // could not create selected driver.



   /*
   Get a pointer to the video driver and the SceneManager so that
   we do not always have to write device->getVideoDriver() and
   device->getSceneManager().
   */
   video::IVideoDriver* driver = device->getVideoDriver();
   scene::ISceneManager* smgr = device->getSceneManager();



   //start event receiver
   MyEventReceiver rv;
   device->setEventReceiver(&rv);
   for(int x=0; x<irr::KEY_KEY_CODES_COUNT; x++) keys[x] = false;


   /*
   To display the Quake 3 map, we first need to load it. Quake 3 maps
   are packed into .pk3 files wich are nothing other than .zip files.
   So we add the .pk3 file to our FileSystem. After it was added,
   we are able to read from the files in that archive as they would
   directly be stored on disk.
   */
   
   
   // device->getFileSystem()->addZipFileArchive("media/map-20kdm2.pk3");     SAME LINE HERE



   /*
   Now we can load the mesh by calling getMesh(). We get a pointer returned
   to a IAnimatedMesh. As you know, Quake 3 maps are not really animated,
   they are only a huge chunk of static geometry with some materials
   attached. Hence the IAnimated mesh consists of only one frame,
   so we get the "first frame" of the "animation", which is our quake level
   and create an OctTree scene node with it, using addOctTreeSceneNode().
   The OctTree optimizes the scene a little bit, trying to draw only geometry
   which is currently visible. An alternative to the OctTree would be a
   AnimatedMeshSceneNode, which would draw always the complete geometry of
   the mesh, without optimization. Try it out: Write addAnimatedMeshSceneNode
   instead of addOctTreeSceneNode and compare the primitives drawed by the
   video driver. (There is a getPrimitiveCountDrawed() method in the
   IVideoDriver class). Note that this optimization with the Octree is only
   useful when drawing huge meshes consiting of lots of geometry.
   */
   device->getFileSystem()->addZipFileArchive("media/map-20kdm2.pk3");



   scene::IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");
   scene::ISceneNode* q3node = 0;


   if (q3levelmesh)
   q3node = smgr->addOctTreeSceneNode(q3levelmesh->getMesh(0));





   /*
   So far so good, we've loaded the quake 3 level like in tutorial 2. Now, here
   comes something different: We create a triangle selector. A triangle selector
   is a class which can fetch the triangles from scene nodes for doing different
   things with them, for example collision detection. There are different triangle
   selectors, and all can be created with the ISceneManager. In this example,
   we create an OctTreeTriangleSelector, which optimizes the triangle output a
   little bit by reducing it like an octree. This is very useful for huge meshes
   like quake 3 levels.
   After we created the triangle selector, we attach it to the q3node. This is not
   necessary, but in this way, we do not need to care for the selector, for example
   dropping it after we do not need it anymore.
   */
   scene::ITriangleSelector* selector = 0;
   if (q3node)
   {
        q3node->setPosition(core::vector3df(-1350,-130,-1400));
        selector = smgr->createOctTreeTriangleSelector(q3levelmesh->getMesh(0), q3node, 128);
        q3node->setTriangleSelector(selector);
   }



   // add a billboard to show the target movement position
   scene::IBillboardSceneNode * bill = smgr->addBillboardSceneNode();
   bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
   bill->setMaterialTexture(0, driver->getTexture("media/particle.bmp"));
   bill->setMaterialFlag(video::EMF_LIGHTING, false);
   bill->setMaterialFlag(video::EMF_ZBUFFER, false); // Always appears on top
   bill->setSize(core::dimension2d<f32>(20.0f, 20.0f));


   device->getCursorControl()->setVisible(true);


    // Create a 3rd person player node
    scene::ISceneNode * player = smgr->addEmptySceneNode();
    player->setPosition(core::vector3df(-70,0,-90));

    // The faerie model is oriented towards +X, but I do calculations on +Z
    // so I have to use a child node to rotate it 90 degrees anticlockwise
    scene::IAnimatedMesh* faerieMesh = smgr->getMesh("media/faerie.md2");
    scene::IAnimatedMeshSceneNode * visualNode = smgr->addAnimatedMeshSceneNode(faerieMesh, player);
    video::SMaterial material;
    material.setTexture(0, driver->getTexture("media/faerie2.bmp"));
    material.Lighting = false;
    visualNode->getMaterial(0) = material;

    visualNode->setRotation(core::vector3df(0.f, -90.f, 0.f)); // rotates +X to +Z

    // Work out how big the faerie mesh is
    const core::aabbox3df & box = visualNode->getBoundingBox();
    const f32 height = (box.MaxEdge.Y - box.MinEdge.Y) / 2.f;
    const f32 verticalOffset = -box.MinEdge.Y - box.MaxEdge.Y;
    const f32 waist = core::max_(box.MaxEdge.X - box.MinEdge.X, box.MaxEdge.Z - box.MinEdge.Z) / 2.f;

    // And give the avatar node an appropriate physical presence
    scene::ISceneNodeAnimator * anim = smgr->createCollisionResponseAnimator(
        selector, player, core::vector3df(waist, height, waist),
        core::vector3df(0, -3, 0),
        core::vector3df(0, verticalOffset, 0));
    player->addAnimator(anim);
    anim->drop(); anim = 0;


//   scene::ICameraSceneNode* camera = smgr->addCameraSceneNode();
   //camera->setParent(player);

//   camera->setPosition(core::vector3df(-10,50,-150));


   // setup the camera
   ICameraSceneNode* myCamera =
   smgr->addCameraSceneNode(player, sphericalXYZ(cameraOrbit,cameraAngle,cameraDistance), player->getPosition());







   /*
   We have done everything, so lets draw it. We also write the current
   frames per second and the drawn primitives to the caption of the
   window. The 'if (device->isWindowActive())' line is optional, but
   prevents the engine render to set the position of the mouse cursor
   after task switching when other program are active.
   */

   int lastFPS = -1;

   bool wasRunning = false; // Used to control animatins
   u32 lastFrameTime = device->getTimer()->getTime();

   // These values control how the avatar moves
   const f32 TURN_SPEED = 200.f;
   const f32 MOVE_SPEED = 100.f;
   const f32 STOP_AT_DISTANCE_FROM_TARGET = waist * 1.1f;
   const f32 MAXIMUM_YAW_DELTA_BEFORE_MOVING = 60.f; // degrees
   const f32 MAX_TARGET_POSITION_IN_FRONT_OF_CAMERA = waist * 40.f;
   core::vector3df targetP;

   while(device->run())

   if (device->isWindowActive())
   {
         if(keys[KEY_ESCAPE]){
            device->closeDevice();
    }

   myCamera->setTarget(player->getPosition());
        // orient camera
        myCamera->setPosition(sphericalXYZ(cameraOrbit,cameraAngle,cameraDistance));

    u32 thisFrameDuration = device->getTimer()->getTime() - lastFrameTime;
    f32 frameTimeMultiplier = (f32)thisFrameDuration / 1000.f;
    lastFrameTime += thisFrameDuration;

      core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(device->getCursorControl()->getPosition());

      core::vector3df collisionPoint;
      core::triangle3df collisionTriangle;
      if(smgr->getSceneCollisionManager()->getCollisionPoint(ray, selector, collisionPoint, collisionTriangle))
      //{
          // Do something with the collisionPoint

      //}

   if(mouseDownL==true)
   {
    targetP=collisionPoint;
   }
   bill->setPosition(targetP);
        core::vector3df toTarget(targetP - player->getAbsolutePosition());
        toTarget.Y = 0; // Ignore any vertical difference

        if(toTarget.getLength() <= STOP_AT_DISTANCE_FROM_TARGET)
        {
            // The avatar is close to the target position, so don't move.
            if(wasRunning)
            {
                visualNode->setMD2Animation(scene::EMAT_STAND);
                wasRunning = false;
            }
        }
        else
        {
            // Turn, and if facing near enough to the right direction, move.

            // Work out the required rotation, in degrees
            f32 requiredYaw = atan2(toTarget.Z, toTarget.X) * core::RADTODEG;
            // atan2 calculates anticlockwise from the X axis.  We want clockwise from the Z axis.
            requiredYaw *= -1.f; // Make the result clockwise...
            requiredYaw += 90.f; // ...and rebase from X axis to Z axis.

            f32 actualYaw = player->getRotation().Y;

            // Work out how much the avatar would have to turn
            f32 deltaYaw = requiredYaw - actualYaw;
            if(deltaYaw > 180.f)
                deltaYaw = deltaYaw - 360.f;
            else if(deltaYaw < -180.f)
                deltaYaw = deltaYaw + 360.f;

            // Is it s facing close enough to the right direction in order to move?
            bool move = fabs(deltaYaw) < MAXIMUM_YAW_DELTA_BEFORE_MOVING;

            // Now limit the rate of turn.
            if(deltaYaw > TURN_SPEED * frameTimeMultiplier)
                deltaYaw = TURN_SPEED * frameTimeMultiplier;
            else if(deltaYaw < -TURN_SPEED * frameTimeMultiplier)
                deltaYaw = -TURN_SPEED * frameTimeMultiplier;

            // Do the actual rotation.
            actualYaw += deltaYaw;
            player->setRotation(core::vector3df(0.f, actualYaw, 0.f));

            if(move)
            {
                if(!wasRunning)
                {
                    visualNode->setMD2Animation(scene::EMAT_RUN);
                    wasRunning = true;
                }

                actualYaw *= core::DEGTORAD; // Back to radians for sin/cos
                core::vector3df deltaMove(sin(actualYaw), 0.f, cos(actualYaw));
                deltaMove *= MOVE_SPEED * frameTimeMultiplier;
                player->setPosition(player->getAbsolutePosition() + deltaMove);
            }
        }



      driver->beginScene(true, true, video::SColor(0,200,200,200));
      smgr->drawAll();
      driver->endScene();


      int fps = driver->getFPS();

      if (lastFPS != fps)
      {
         core::stringw str = L"Irrlicht Engine - Quake 3 Map example [";
         str += driver->getName();
         str += "] FPS:";
         str += fps;
         device->setWindowCaption(str.c_str());
         lastFPS = fps;
      }
   }



   /*
   In the end, delete the Irrlicht device.
   */

   device->drop();
   return 0;

}
vectorcorpse
Posts: 86
Joined: Thu Feb 14, 2008 7:30 pm
Location: Portugal

Post by vectorcorpse »

finally managed to stop the auto rotation, but there must be a better way
basically what i did was create a empty node and sync his position with the char position but not the char rotation vector so the node moves with the chat but doesn't rotate and then apply the camera to that node the ideal solution would be getting the chat position for the camera but without the rotation vector. maybe someone more experienced with irrlicht can guide me to on vector and pos system there must be a way to separate the position from the orientation and apply only one using addCameraSceneNode.
anyway for now it is working fine despite of this dirty hack.
noals
Posts: 70
Joined: Sun May 18, 2008 2:59 pm

Post by noals »

dunno if you will like but i changed some stuff at my liking.
(its just the class)

-the camera is behind the elf. (angle 30)
-i removed the camera distance limitation through it still go to 10 if camera distance is less than that
-if you release the right mouse button, the camera go back behind the elf through the angle and distance stay at your liking.
-the wheel go the way i like ^^

Code: Select all

class MyEventReceiver : public IEventReceiver
{
    public:virtual bool OnEvent(const SEvent& event)
    {
        if (event.EventType == EET_MOUSE_INPUT_EVENT)
        {
///////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////  LEFT MOUSE BUTTON /////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////

            // 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;

///////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////  RIGHT MOUSE BUTTON ////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////

            if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)      mouseDownR = false;

        // 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;
            }

            // 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;
                }
                if(mouseDownR == false)
                {
                    cameraOrbit = 90;
                    //cameraAngle = 30;
                }
            }


///////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////  MOUSE WHEEL ///////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////

            // mouse wheel
            if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
            {
                cameraDistance += -event.MouseInput.Wheel * (cameraDistance / 20) * 3;
                if(cameraDistance < 10) cameraDistance = 10;
            }
        }
        return false;
    }
};
vectorcorpse
Posts: 86
Joined: Thu Feb 14, 2008 7:30 pm
Location: Portugal

Post by vectorcorpse »

nice :D but ya it's a bit off for 2 reasons. 1st is because it is supposed to act the same way has on lineage 2, and the 2nd is because when the char's back is facing a wall the camera gets on the other side of the all, since it uses colision to set the target movement position, it will hit the wall and and set that position and the char will slam against the wall insted of going to the desired position :P thats why i wanted the char to rotate independently of the camera orbit.
anyway, a friend of mine said that lineage also allows to set a moving or a fixed camera, so on my next revision i'll try go add that feature and find a way to properly rig the camera to avoid auto rotation without adding a extra node.
noals
Posts: 70
Joined: Sun May 18, 2008 2:59 pm

Post by noals »

yep, i didnt tested it a lot but i encounter the wall problem.
anyway, yes, im a bit off topic, i was happy to actually have done something ^^ through if it can help, why not.
you just need to do :

Code: Select all

                if(mouseDownR == false) 
                { 
                    //this part is what happen when you release the R button
                    //cameraOrbit = 90; 
                    //cameraAngle = 30; 
                }
and its as you like.

personnally i will try to make the camera moving the player like in jedy academy but my final goal is to have 3 camera that i can change with some key (jedy academy style, free camera and doom-like) so i will keep an eyes on your.

cya
xsocom
Posts: 80
Joined: Thu Sep 13, 2007 8:34 am

Post by xsocom »

Nice, The if you get the camera not rotate by the player position it will be 100% :)
shadowslair
Posts: 758
Joined: Mon Mar 31, 2008 3:32 pm
Location: Bulgaria

Post by shadowslair »

I would be happy if there`s an executable linked, so to take a look without creating a new project...

Damn, how lazy I am sometimes... :D
"Although we walk on the ground and step in the mud... our dreams and endeavors reach the immense skies..."
vectorcorpse
Posts: 86
Joined: Thu Feb 14, 2008 7:30 pm
Location: Portugal

Post by vectorcorpse »

sorry for the long wait, but i'v been busy lately, alot of costumers crashed here with extremely damaged computers to be fixed (i even had a laptop that decided to dring some ice tea :P ouch) and i am also studying enet and mysql++ to see if i can get it working with irrlicht. The moment i get either the bugs on this cam/mouse control fixed or a at least partialy working enet/mysql++ with irrlich i'll get back here with the changes :D has for the binary demo since i work with linux only it will not have directx support and i'll have to upload it to my company server so i can't tell when it will be available (maybe tonight or tomorrow).
Post Reply