How to fix that rotation bug??

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
wsw1231
Posts: 148
Joined: Fri Oct 01, 2010 7:55 am

How to fix that rotation bug??

Post by wsw1231 »

Code: Select all

#include <irrlicht.h>

using namespace irr;

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


#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif

IrrlichtDevice *Device = 0;
IVideoDriver *driver = 0; 
ISceneManager *smgr  = 0; 
IGUIEnvironment *env = 0;
ICameraSceneNode  *camera = 0;
ISceneCollisionManager  *CollMan = 0; 
IMetaTriangleSelector * meta = 0;

#define ID_TRANSLATE   102 
#define ID_ROTATE      103 
#define ID_SCALE      104 
#define ID_ADDBOX      105 

enum
{
	// I use this ISceneNode ID to indicate a scene node that is
	// not pickable by getSceneNodeAndCollisionPointFromRay()
	ID_IsNotPickable = 0,

	// I use this flag in ISceneNode IDs to indicate that the
	// scene node can be picked by ray selection.
	IDFlag_IsPickable = 1 << 0,

	// I use this flag in ISceneNode IDs to indicate that the
	// scene node can be highlighted.  In this example, the
	// homonids can be highlighted, but the level mesh can't.
	IDFlag_IsHighlightable = 1 << 1
};

enum EditorMode 
{ 
   MODE_NONE, 
   MODE_QUIT, 
   MODE_TRANSLATE, 
   MODE_ROTATE, 
   MODE_SCALE, 
};

position2di        initialCursorPosition;      // where mouse cursor started 
position2di        initialObjectPosition;      // where ray from camera to object intersected screen 
ISceneNode*        SelectedNode         = 0;   // the selected node 
bool               ShiftIsPressed      = 0;   // is the shift key pressed? 
bool               LMouseButtonDown      = 0;   // is the left mouse button down? 
EditorMode         Mode               = MODE_NONE; 
float              RotateSpeed         = 0.01f; 
float              ScaleSpeed         = 0.01f; 

class MyEventReceiver : public IEventReceiver
{
public:
	virtual bool OnEvent(const SEvent& event)
	{
		switch(event.EventType) 
		{
			case EET_GUI_EVENT:
			{
				switch (event.GUIEvent.EventType)
				{
					case EGET_BUTTON_CLICKED: 
                     { 
                        switch (event.GUIEvent.Caller->getID()) 
						{
							case ID_TRANSLATE  : { Mode = MODE_TRANSLATE;  } break; 
							case ID_ROTATE      : { Mode = MODE_ROTATE;      } break; 
							case ID_SCALE      : { Mode = MODE_SCALE;      } break; 
							case ID_ADDBOX      : 
							{
								ISceneNode* box = smgr->addCubeSceneNode(); 
								box->setMaterialTexture(0, driver->getTexture("fire.bmp")); 
								box->setMaterialFlag(video::EMF_LIGHTING, false); 
								box->setMaterialFlag(video::EMF_FOG_ENABLE,false);
								ITriangleSelector * selector = smgr->createTriangleSelectorFromBoundingBox(box);;
								meta->addTriangleSelector(selector);
							} break; 
						}
					 } break;
				}
			} break;
			
			case EET_KEY_INPUT_EVENT :
			{ 
				//check if "Shift" key is pressed
				if (event.KeyInput.Shift)
					ShiftIsPressed = true; 
				else 
					ShiftIsPressed = false; 

				switch (event.KeyInput.Char)
				{ 
					case 'm':
						Mode = MODE_TRANSLATE;
						break;
					case 'r':
						Mode = MODE_ROTATE;
						break;
					case 's':
						Mode = MODE_SCALE;
						break;
				}

			} break; 

			// if it is a mouse event 
			case EET_MOUSE_INPUT_EVENT : 
			 { 
				// based upon the mouse event type 
				switch (event.MouseInput.Event) 
				{ 
				   case EMIE_LMOUSE_PRESSED_DOWN : 
					{
						if (Mode == MODE_NONE) return false; 

						LMouseButtonDown = true; 
						
						
						// check for a node being selected 
						SelectedNode = CollMan->getSceneNodeFromScreenCoordinatesBB(Device->getCursorControl()->getPosition(),0,true); 

						//!need to check the node type

						// If we selected a node 
						if(SelectedNode) 
						{ 
							// Remember where the node and cursor were when it was clicked on 
							initialCursorPosition = Device->getCursorControl()->getPosition(); 
							initialObjectPosition = CollMan->getScreenCoordinatesFrom3DPosition(SelectedNode->getAbsolutePosition(), camera); 
							SelectedNode->setDebugDataVisible(scene::EDS_BBOX); 

							SMaterial m; 
							m.setTexture(0,0);
							m.Lighting=false; 
							driver->setMaterial(m); 
							driver->setTransform(video::ETS_WORLD, core::matrix4()); 
							driver->draw3DLine(SelectedNode->getAbsolutePosition(), SelectedNode->getAbsolutePosition() + vector3df(0,500,0), SColor(255,255,0,0)); 
						}                    
						return false;
					} break; 

					case EMIE_LMOUSE_LEFT_UP : 
					{ 
						LMouseButtonDown = false; 
						if (SelectedNode) SelectedNode->setDebugDataVisible(scene::EDS_OFF); 
						SelectedNode = 0; 
						return false; 
					} break; 
					

					case EMIE_RMOUSE_LEFT_UP : 
					{ 
						if (camera) 
						{ 
							camera->setInputReceiverEnabled(!camera->isInputReceiverEnabled()); 
							Device->getCursorControl()->setVisible(!camera->isInputReceiverEnabled()); 	
							 if (camera->isInputReceiverEnabled()) Mode = MODE_NONE; 
						}                
					} break; 

				   case EMIE_MOUSE_MOVED :
					{ 
						if (!LMouseButtonDown) return false; 

						if (Mode == MODE_TRANSLATE) 
						{ 
							if (SelectedNode) 
							{ 
								vector3df p = SelectedNode->getPosition(); 

								if (ShiftIsPressed) 
								{ 
									plane3df const planeXY(SelectedNode->getAbsolutePosition(), vector3df(0.f, 0.f, 1.f)); 
									position2di currentCursorPosition(Device->getCursorControl()->getPosition()); 
									position2di effectiveObjectPosition = initialObjectPosition + currentCursorPosition - initialCursorPosition; 
									line3df ray(CollMan->getRayFromScreenCoordinates(effectiveObjectPosition, camera)); 
									vector3df intersectWithPlane; 
									vector3df oldPos = SelectedNode->getAbsolutePosition(); 

									if(planeXY.getIntersectionWithLine(ray.start, ray.getVector(), intersectWithPlane)) 
									{ 
										intersectWithPlane.X = oldPos.X; 
										intersectWithPlane.Z = oldPos.Z; 
										SelectedNode->setPosition(intersectWithPlane); 
									} 
								} 
								else 
								{ 
									plane3df const planeXZ(SelectedNode->getAbsolutePosition(), vector3df(0.f, 1.f, 0.f)); 
									position2di currentCursorPosition(Device->getCursorControl()->getPosition()); 
									position2di effectiveObjectPosition = initialObjectPosition + currentCursorPosition - initialCursorPosition; 
									line3df ray(CollMan->getRayFromScreenCoordinates(effectiveObjectPosition, camera)); 
									vector3df intersectWithPlane; 
									if(planeXZ.getIntersectionWithLine(ray.start, ray.getVector(), intersectWithPlane)) 
									{ 
										SelectedNode->setPosition(intersectWithPlane); 
									} 
								} 
							} 
						} // end mode translate 
					  
					  else 
					  if (Mode == MODE_ROTATE) 
					  { 
						 if (SelectedNode) 
						 { 
							vector3df rot = SelectedNode->getRotation(); 
							position2di currentCursorPosition(Device->getCursorControl()->getPosition()); 
							if (ShiftIsPressed) 
							{ 
							   if (initialObjectPosition.X < currentCursorPosition.X) rot.X -= RotateSpeed; 
							   if (initialObjectPosition.X > currentCursorPosition.X) rot.X += RotateSpeed; 
							} 
							else 
							{ 
							  // if (initialObjectPosition.X < currentCursorPosition.X) rot.Y -= RotateSpeed; 
							   //if (initialObjectPosition.X > currentCursorPosition.X) rot.Y += RotateSpeed; 
							   if (initialObjectPosition.X < currentCursorPosition.X) rot.Y -= 1; 
							   if (initialObjectPosition.X > currentCursorPosition.X) rot.Y += 1; 
							   if (initialObjectPosition.Y < currentCursorPosition.Y) rot.X -= 1;
							   if (initialObjectPosition.Y > currentCursorPosition.Y) rot.X += 1;
							} 
							initialObjectPosition = currentCursorPosition; 
							SelectedNode->setRotation(rot); 
						 } 
					  } 
					  else 
					  if (Mode == MODE_SCALE) 
					  { 
						 if (SelectedNode) 
						 { 
							vector3df scale = SelectedNode->getScale(); 
							position2di currentCursorPosition(Device->getCursorControl()->getPosition()); 
							if (initialObjectPosition.X > currentCursorPosition.X) 
							{ 
							   scale.X -= ScaleSpeed; 
							   scale.Y -= ScaleSpeed; 
							   scale.Z -= ScaleSpeed; 
							} 
							if (initialObjectPosition.X < currentCursorPosition.X) 
							{ 
							   scale.X += ScaleSpeed; 
							   scale.Y += ScaleSpeed; 
							   scale.Z += ScaleSpeed; 
							} 
							initialObjectPosition = currentCursorPosition; 
							SelectedNode->setScale(scale); 
						 } 
					  } 

					} break; 
				} 
			}
		 }
		 return false;	
	}
		
};



enum
{
	GUI_ID_DIALOG_ROOT_WINDOW  = 0x10000,

	GUI_ID_X_SCALE,
	GUI_ID_Y_SCALE,
	GUI_ID_Z_SCALE,

	GUI_ID_NEW_MODEL,
	GUI_ID_LOAD_FLOOR_PLAN,
	GUI_ID_SAVE_FLOOR_PLAN,

	GUI_ID_SKY_BOX_VISIBLE,
	GUI_ID_TOGGLE_DEBUG_INFO,

	GUI_ID_DEBUG_OFF,
	GUI_ID_DEBUG_BOUNDING_BOX,
	GUI_ID_DEBUG_NORMALS,
	GUI_ID_DEBUG_SKELETON,
	GUI_ID_DEBUG_WIRE_OVERLAY,
	GUI_ID_DEBUG_HALF_TRANSPARENT,
	GUI_ID_DEBUG_BUFFERS_BOUNDING_BOXES,
	GUI_ID_DEBUG_ALL,

	GUI_ID_MODEL_MATERIAL_SOLID,
	GUI_ID_MODEL_MATERIAL_TRANSPARENT,
	GUI_ID_MODEL_MATERIAL_REFLECTION,

	GUI_ID_CAMERA_MAYA,
	GUI_ID_CAMERA_FIRST_PERSON,

	GUI_ID_POSITION_TEXT,

	GUI_ID_ABOUT,
	GUI_ID_QUIT,

	GUI_ID_TEXTUREFILTER,
	GUI_ID_SKIN_TRANSPARENCY,
	GUI_ID_SKIN_ANIMATION_FPS,

	GUI_ID_BUTTON_SET_SCALE,
	GUI_ID_BUTTON_SCALE_MUL10,
	GUI_ID_BUTTON_SCALE_DIV10,
	GUI_ID_BUTTON_OPEN_MODEL,
	GUI_ID_BUTTON_SHOW_ABOUT,
	GUI_ID_BUTTON_SHOW_TOOLBOX,
	GUI_ID_BUTTON_SELECT_ARCHIVE,

	// And some magic numbers
	MAX_FRAMERATE = 1000,
	DEFAULT_FRAMERATE = 30
};

void createToolBox()
{
	// remove tool box if already there
	IGUIElement* root = env->getRootGUIElement();
	IGUIElement* e = root->getElementFromId(GUI_ID_DIALOG_ROOT_WINDOW, true);
	if (e)
		e->remove();

	// create the toolbox window
	IGUIWindow* wnd = env->addWindow(core::rect<s32>(700,95,900,530),
		false, L"Toolset", 0, GUI_ID_DIALOG_ROOT_WINDOW);

	// create tab control and tabs
	IGUITabControl* tab = env->addTabControl(
		core::rect<s32>(2,20,800-602,480-7), wnd, true, true);

	IGUITab* t1 = tab->addTab(L"Config");

	// add some edit boxes and a button to tab one
	env->addStaticText(L"Scale:",
			core::rect<s32>(10,20,60,45), false, false, t1);
	env->addStaticText(L"X:", core::rect<s32>(22,48,40,66), false, false, t1);
	env->addEditBox(L"1.0", core::rect<s32>(40,46,130,66), true, t1, GUI_ID_X_SCALE);
	env->addStaticText(L"Y:", core::rect<s32>(22,82,40,GUI_ID_NEW_MODEL), false, false, t1);
	env->addEditBox(L"1.0", core::rect<s32>(40,76,130,96), true, t1, GUI_ID_Y_SCALE);
	env->addStaticText(L"Z:", core::rect<s32>(22,108,40,126), false, false, t1);
	env->addEditBox(L"1.0", core::rect<s32>(40,106,130,126), true, t1, GUI_ID_Z_SCALE);

	env->addButton(core::rect<s32>(10,134,85,165), t1, GUI_ID_BUTTON_SET_SCALE, L"Set");

	// quick scale buttons
	env->addButton(core::rect<s32>(65,20,95,40), t1, GUI_ID_BUTTON_SCALE_MUL10, L"* 10");
	env->addButton(core::rect<s32>(100,20,130,40), t1, GUI_ID_BUTTON_SCALE_DIV10, L"* 0.1");

//	UpdateScaleInfo(Model);

	// add transparency control
	env->addStaticText(L"GUI Transparency Control:",
			core::rect<s32>(10,200,150,225), true, false, t1);
	IGUIScrollBar* scrollbar = env->addScrollBar(true,
			core::rect<s32>(10,225,150,240), t1, GUI_ID_SKIN_TRANSPARENCY);
	scrollbar->setMax(255);
	scrollbar->setPos(255);

	// add framerate control
	env->addStaticText(L"Framerate:",
			core::rect<s32>(10,240,150,265), true, false, t1);
	scrollbar = env->addScrollBar(true,
			core::rect<s32>(10,265,150,280), t1, GUI_ID_SKIN_ANIMATION_FPS);
	scrollbar->setMax(MAX_FRAMERATE);
	scrollbar->setMin(-MAX_FRAMERATE);
	scrollbar->setPos(DEFAULT_FRAMERATE);

	// bring irrlicht engine logo to front, because it
	// now may be below the newly created toolbox
	root->bringToFront(root->getElementFromId(666, true));
}

void GUI()
{
	// add my little plug 
	int x      = 10; 
	int y      = 60; 
	int width   = 100; 
	int height   = 20; 
	int space   = 5; 

	env->addButton(core::rect<s32>(x,y,x+width,y+height),0,ID_TRANSLATE,L"Translate"); 
	y += height + space; 
	env->addButton(core::rect<s32>(x,y,x+width,y+height),0,ID_ROTATE,L"Rotate"); 
	y += height + space; 
	env->addButton(core::rect<s32>(x,y,x+width,y+height),0,ID_SCALE,L"Scale"); 
	y += height + space; 
	env->addButton(core::rect<s32>(x,y,x+width,y+height),0,ID_ADDBOX,L"Add A Box"); 

	// set a nicer font

	
	IGUISkin* skin = env->getSkin();
	IGUIFont* font = env->getFont("fonthaettenschweiler.bmp");
	if (font)
		skin->setFont(font);

	// create menu
	gui::IGUIContextMenu* menu = env->addMenu();
	menu->addItem(L"File", -1, true, true);
	menu->addItem(L"Edit", -1, true, true);
	menu->addItem(L"View", -1, true, true);
	menu->addItem(L"Camera", -1, true, true);
	menu->addItem(L"Help", -1, true, true);

	gui::IGUIContextMenu* submenu;
	submenu = menu->getSubMenu(0);
	submenu->addItem(L"New project", GUI_ID_NEW_MODEL);
	submenu->addItem(L"Load 3D floor plan", GUI_ID_LOAD_FLOOR_PLAN);
	submenu->addItem(L"Save 3D floor plan", GUI_ID_SAVE_FLOOR_PLAN);
	submenu->addSeparator();
	submenu->addItem(L"Quit", GUI_ID_QUIT);

	submenu = menu->getSubMenu(2);
	submenu->addItem(L"sky box visible", GUI_ID_SKY_BOX_VISIBLE, true, false, true);
	submenu->addItem(L"toggle model debug information", GUI_ID_TOGGLE_DEBUG_INFO, true, true);
	submenu->addItem(L"model material", -1, true, true );

	submenu = submenu->getSubMenu(1);
	submenu->addItem(L"Off", GUI_ID_DEBUG_OFF);
	submenu->addItem(L"Bounding Box", GUI_ID_DEBUG_BOUNDING_BOX);
	submenu->addItem(L"Normals", GUI_ID_DEBUG_NORMALS);
	submenu->addItem(L"Skeleton", GUI_ID_DEBUG_SKELETON);
	submenu->addItem(L"Wire overlay", GUI_ID_DEBUG_WIRE_OVERLAY);
	submenu->addItem(L"Half-Transparent", GUI_ID_DEBUG_HALF_TRANSPARENT);
	submenu->addItem(L"Buffers bounding boxes", GUI_ID_DEBUG_BUFFERS_BOUNDING_BOXES);
	submenu->addItem(L"All", GUI_ID_DEBUG_ALL);

	submenu = menu->getSubMenu(2)->getSubMenu(2);
	submenu->addItem(L"Solid", GUI_ID_MODEL_MATERIAL_SOLID);
	submenu->addItem(L"Transparent", GUI_ID_MODEL_MATERIAL_TRANSPARENT);
	submenu->addItem(L"Reflection", GUI_ID_MODEL_MATERIAL_REFLECTION);

	submenu = menu->getSubMenu(3);
	submenu->addItem(L"Maya Style", GUI_ID_CAMERA_MAYA);
	submenu->addItem(L"First Person", GUI_ID_CAMERA_FIRST_PERSON);

	submenu = menu->getSubMenu(4);
	submenu->addItem(L"About", GUI_ID_ABOUT);

	/*
	Below the menu we want a toolbar, onto which we can place colored
	buttons and important looking stuff like a senseless combobox.
	*/

	// create toolbar

	gui::IGUIToolBar* bar = env->addToolBar();

	IVideoDriver* driver = Device->getVideoDriver();
	video::ITexture* image = driver->getTexture("open.png");
	bar->addButton(GUI_ID_BUTTON_OPEN_MODEL, 0, L"Open a model",image, 0, true, true);

	image = driver->getTexture("tools.png");
	bar->addButton(GUI_ID_BUTTON_SHOW_TOOLBOX, 0, L"Open Toolset",image, 0, false, true);

	image = driver->getTexture("zip.png");
	bar->addButton(GUI_ID_BUTTON_SELECT_ARCHIVE, 0, L"Set Model Archive",image, 0, false, true);

	image = driver->getTexture("help.png");
	bar->addButton(GUI_ID_BUTTON_SHOW_ABOUT, 0, L"Open Help", image, 0, false, true);

	// create a combobox with some senseless text

	gui::IGUIComboBox* box = env->addComboBox(core::rect<s32>(250,4,350,23), bar, GUI_ID_TEXTUREFILTER);
	box->addItem(L"No filtering");
	box->addItem(L"Bilinear");
	box->addItem(L"Trilinear");
	box->addItem(L"Anisotropic");
	box->addItem(L"Isotropic");

	/*
	To make the editor look a little bit better, we disable transparent gui
	elements, and add an Irrlicht Engine logo. In addition, a text showing
	the current frames per second value is created and the window caption is
	changed.
	*/

	// disable alpha

	for (s32 i=0; i<gui::EGDC_COUNT ; ++i)
	{
		video::SColor col = env->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i);
		col.setAlpha(255);
		env->getSkin()->setColor((gui::EGUI_DEFAULT_COLOR)i, col);
	}

	// add a tabcontrol
	createToolBox();
}

int main(int argc, char* argv[])
{
	// create device and exit if creation failed
	MyEventReceiver receiver;
	Device = createDevice(video::EDT_OPENGL, core::dimension2d<u32>(1024, 768),
		16, false, false, false, &receiver);

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

	Device->setResizable(true);

	Device->setWindowCaption(L"3D Floor Plan Creator");

	driver = Device->getVideoDriver();
	env = Device->getGUIEnvironment();
	smgr = Device->getSceneManager();
	smgr->getParameters()->setAttribute(scene::COLLADA_CREATE_SCENE_INSTANCES, true);
	CollMan = smgr->getSceneCollisionManager(); 

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

	smgr->addLightSceneNode(0, core::vector3df(200,200,200),
		video::SColorf(1.0f,1.0f,1.0f),2000);
	smgr->setAmbientLight(video::SColorf(0.3f,0.3f,0.3f));
	// add media directory as "search path"
	Device->getFileSystem()->addFolderFileArchive("../../media/");
	GUI();

	 // add a camera 
	camera = smgr->addCameraSceneNodeFPS(0, 100.0f, .3f, ID_IsNotPickable, 0, 0, false, 3.f); 
	camera->setPosition(vector3df(0,40,-40)); 
	camera->setTarget(vector3df(0,0,0)); 
	camera->setIsDebugObject(true); 
	camera->setInputReceiverEnabled(false); 
	
	//smgr->loadScene("zzz.irr");

		// Create a meta triangle selector to hold several triangle selectors.
	meta = smgr->createMetaTriangleSelector();

	/*
	Now we will find all the nodes in the scene and create triangle
	selectors for all suitable nodes.  Typically, you would want to make a
	more informed decision about which nodes to performs collision checks
	on; you could capture that information in the node name or Id.
	*/
	core::array<scene::ISceneNode *> nodes;
	smgr->getSceneNodesFromType(scene::ESNT_ANY, nodes); // Find all nodes

	for (u32 i=0; i < nodes.size(); ++i)
	{
		ISceneNode * node = nodes[i];
		ITriangleSelector * selector = 0;

		switch(node->getType())
		{
		case ESNT_CUBE:
		case ESNT_ANIMATED_MESH:
			// Because the selector won't animate with the mesh,
			// and is only being used for camera collision, we'll just use an approximate
			// bounding box instead of ((scene::IAnimatedMeshSceneNode*)node)->getMesh(0)
			selector = smgr->createTriangleSelectorFromBoundingBox(node);
		break;

		case ESNT_MESH:
		case ESNT_SPHERE: // Derived from IMeshSceneNode
			selector = smgr->createTriangleSelector(((scene::IMeshSceneNode*)node)->getMesh(), node);
			break;

		case ESNT_TERRAIN:
			selector = smgr->createTerrainTriangleSelector((scene::ITerrainSceneNode*)node);
			break;

		case ESNT_OCTREE:
			selector = smgr->createOctreeTriangleSelector(((scene::IMeshSceneNode*)node)->getMesh(), node);
			break;

		default:
			// Don't create a selector for this node type
			break;
		}

		if(selector)
		{
			// Add it to the meta selector, which will take a reference to it
			meta->addTriangleSelector(selector);
			// And drop my reference to it, so that the meta selector owns it.
			selector->drop();
		}
	}

	/*
	Now that the mesh scene nodes have had triangle selectors created and added
	to the meta selector, create a collision response animator from that meta selector.
	*/
	ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
		meta, camera, core::vector3df(5,5,5),
		core::vector3df(0,0,0));
	meta->drop();

	camera->addAnimator(anim);
	anim->drop(); 

	while(Device->run())
	{
		driver->beginScene(true, true, SColor(255,100,101,140));

		smgr->drawAll();
		env->drawAll();

		driver->endScene();
	}


	Device->drop();


	return 0;
}
After running the program, click "Add a box" then "Rotate" the box, you will see the rotation is not perfect.

For example, if I move my mouse to right, the box is not rotating around the y axis entirely.
Also, if you right click the mouse and move the camera to another position, then rotate the box again, you will notice that the rotation is not correct.

My question is:

Is there any way to rotate a scene node to a direction according to the camera?
Post Reply