(C++) Mirror node for use with Irrlicht

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Panther
Posts: 34
Joined: Tue Jun 06, 2006 1:05 am

(C++) Mirror node for use with Irrlicht

Post by Panther »

I wrote a CMirror class that people can use. I looked around the forums, and it didn't seem like anyone posted one yet. Also, I saw lotsa questions about how to flip images, etc. So hopefully this is helpful. I added many comments, so it should perform like a tutorial. I am using Visual C++ 2005.

Code: Select all

/*
Mirror.cpp

This is a simple example that uses the Irrlicht 3D Engine to create a mirror effect
*/

// BAD CODE CHOPPED OUT! - See new version posted below!!

In the above example, you can rotate the mirror in X, Y, or Z (using the keys O,K,P,L, comma, and period). You can spin the mirror around to some pretty wicked angles. I'm sure this isn't the fastest implementation, but it works pretty well. :wink: This is my first C++ program ever, so there may be some bad coding :shock:, or I may have forgotten to use "->drop" when I'm supposed to. I started with the terrain tutorial, the render-to-texture tutorial, mixed in the "custom scene node" tutorial, then added my own code.

Below is a screen shot of the example in action. You can see the dwarf in front of the mirror, and a cube. Both are reflected. Enjoy!

Image
Last edited by Panther on Thu Jul 13, 2006 2:36 am, edited 1 time in total.
andrei25ni
Posts: 326
Joined: Wed Dec 14, 2005 10:08 pm

Post by andrei25ni »

That looks nice. I will try it.
niko
Site Admin
Posts: 1759
Joined: Fri Aug 22, 2003 4:44 am
Location: Vienna, Austria
Contact:

Post by niko »

Hey, that's cool :)
RhavoX
Posts: 33
Joined: Tue Jul 04, 2006 7:27 pm

Post by RhavoX »

Image
This doesn't look quite right, does it?? :P I think that this code still needs some attention :P
Teh Uber-Pwner xD
Panther
Posts: 34
Joined: Tue Jun 06, 2006 1:05 am

Post by Panther »

RhavoX,

What pattern of keys (rotation) did you use to get the mirror like that? I'd like to fix this bug. :oops: (Or if you know about "refletology" - what did I do incorrectly?) :o
RhavoX
Posts: 33
Joined: Tue Jul 04, 2006 7:27 pm

Post by RhavoX »

I don't know nothing about "reflectology" :P so I can't tell what's wrong. All I know is that when I turn this mirror 90 degrees and then look at it from different angles then it happens sometimes...
Here's another example
Image
Well you need to play with it for a minute to get that effect :P
Teh Uber-Pwner xD
xterminhate
Posts: 206
Joined: Thu Sep 01, 2005 9:26 pm
Location: France

Post by xterminhate »

Is that a problem with Euler angles... quaternions may be the solution.
Return to Irrlicht after years... I'm lovin it.
It's hard to be a Man !
Si vis pacem para belum
Panther
Posts: 34
Joined: Tue Jun 06, 2006 1:05 am

Mirror bug fixed!

Post by Panther »

Ok, I fixed the bug that RhavoX found. 8) As it turns out, I wasn't changing the "Up Vector" properly. (To repeat RhavoX's bug in the old code, hit "P" 15 times, then hit "K" 15 times. Then walk around and look at the mirror. The bug is that the reflection was sideways!).

I took out some of the comments to make this post shorter. (I left my comments on the mirror code in). Most of the other code is from the Tutorials, which have all of the comments - so it should be understandable.

Thanks for the idea to use quaternions, xterminhate. I looked into this, but decided to just stick with the usual XYZ Euler angles. The 4 dimensional complex numbers were tying knots in my neurons!

Does anyone know why it says ""DirectX unfreed memory 7250907 bytes"?? I'm assuming I'm not dropping some objects... but what?

Code: Select all

// Mirror.cpp - version 2 - This is a simple example that uses the Irrlicht 3D Engine to create a mirror effect
#define _CRT_SECURE_NO_DEPRECATE 1 //suppress stupid swprintf warning! (for VC++ 2005)
#include <stdio.h>
#include <wchar.h>
#include <irrlicht.h>
#include <windows.h>
#include <stdlib.h>
#include <math.h>
#include "irrMath.h"
#include "irrTypes.h"
using namespace irr;
#pragma comment(lib, "Irrlicht.lib")

scene::ISceneNode* cube = 0;
scene::ISceneNode* box = 0;
scene::IAnimatedMeshSceneNode* dwarf = 0;
scene::ICameraSceneNode* camera = 0;
IrrlichtDevice* device = 0;
bool quit;
core::vector3df myReflectionVector, cameraPosition;
scene::ICameraSceneNode* fixedCam = 0;

class CMirror : public scene::ISceneNode
{
	core::aabbox3d<f32> Box;
	video::SMaterial Material;

public:
	video::S3DVertex Vertices[4];

		CMirror(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id, IrrlichtDevice* myDevice)
		: scene::ISceneNode(parent, mgr, id)
	{
		Material.Wireframe = false;
		Material.Lighting = false;

		if (myDevice->getVideoDriver()->getDriverType() == video::EDT_OPENGL)
		{
			//EDT_OPENGL requires we flip the image vertically
			Vertices[0] = video::S3DVertex(-70,70,0, 1,1,0, video::SColor(255,255,255,255), 0, 1);
			Vertices[1] = video::S3DVertex(-70,-70,0, 1,0,0, video::SColor(255,255,255,255), 0, 0);
			Vertices[2] = video::S3DVertex(70,70,0, 0,1,1, video::SColor(255,255,255,255), 1, 1);
			Vertices[3] = video::S3DVertex(70,-70,0, 0,0,1, video::SColor(255,255,255,255), 1, 0);
		}
		else
		{
			//For other drivers don't need to flip it
			Vertices[0] = video::S3DVertex(-70,-70,0, 1,1,0, video::SColor(255,255,255,255), 1, 1);
			Vertices[1] = video::S3DVertex(-70,70,0, 1,0,0, video::SColor(255,255,255,255), 1, 0);
			Vertices[2] = video::S3DVertex(70,-70,0, 0,1,1, video::SColor(255,255,255,255), 0, 1);
			Vertices[3] = video::S3DVertex(70,70,0, 0,0,1, video::SColor(255,255,255,255), 0, 0);
		}

		Box.reset(Vertices[0].Pos);
		for (s32 i=1; i<4; ++i)
			Box.addInternalPoint(Vertices[i].Pos);
	}

	virtual void OnPreRender()
	{
		if (IsVisible)
			SceneManager->registerNodeForRendering(this);

		ISceneNode::OnPreRender();
	}

	virtual void render()
	{
		u16 indices[] = {	0,2,3, 2,1,3, 1,0,3, 2,0,1	};
		video::IVideoDriver* driver = SceneManager->getVideoDriver();

		driver->setMaterial(Material);
		driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
		driver->drawIndexedTriangleList(&Vertices[0], 4, &indices[0], 4);
	}

	virtual const core::aabbox3d<f32>& getBoundingBox() const
	{
		return Box;
	}

	virtual s32 getMaterialCount()
	{
		return 1;
	}

	virtual video::SMaterial& getMaterial(s32 i)
	{
		return Material;
	}

	virtual irr::f32 acosd(irr::f32 input)  //arc-cosine in degrees, uses f32 type (instead of "double")
	{
	   double value = acos((double)input) * 180.0 / 3.14159265358979;  //Converts from radians back to degrees
	   return (irr::f32)value;
	}

	virtual irr::f32 len(core::vector3df v)  //vector length
	{
	   return (irr::f32)sqrt((v.X * v.X) + (v.Y * v.Y) + (v.Z * v.Z));
	}

	virtual irr::f32 angle(core::vector3df v1, core::vector3df v2)  //angle between two vectors
	{
		if ((len(v1) == 0) || (len(v2) == 0))
		{
			return 0;  //avoid division by zero!
		} else {
			return (irr::f32)acosd(v1.dotProduct(v2) / (len(v1) * len(v2)));
		}
	}

	virtual void setRotationUp(core::vector3df rotationVector, scene::ICameraSceneNode* renderCamera)
	{
		// If you just use setRotation(), then the whole image rotates (real mirrors don't do this)!
		// If you were rotating a television, the whole image would rotate.  But not a mirror.
		// Unfortunately, this does cause some "jitter" in the image (not sure why - looks like it is being drawn twice).
		core::vector3df upV;
		upV = core::vector3df(0, 1, 0); //fixedCam->getUpVector();
		upV.rotateYZBy(rotationVector.X, core::vector3df(0, 0, 0));
		upV.rotateXZBy(-rotationVector.Y, core::vector3df(0, 0, 0));
		upV.rotateXYBy(rotationVector.Z, core::vector3df(0, 0, 0));
		renderCamera->setUpVector(upV);
		setRotation(rotationVector);
	}

	virtual core::vector3df getMirrorReflection(scene::ISceneNode* nodeLookingAtMirror, scene::ICameraSceneNode* renderCamera)
	{
		f32 myAngle, dP, tempX, tempY;
		core::vector3df reflectionVector, mirrorV1, mirrorV2, mirrorNormal, dwarfVector, delVector;
		bool oldFrontSide;
		static bool frontSide = true;

		dwarfVector = nodeLookingAtMirror->getPosition() - getPosition(); // Store the node's vector to the mirror

		mirrorV1 = Vertices[0].Pos - Vertices[3].Pos; // Get a vector on the surface of the mirror
		mirrorV1.rotateYZBy(getRotation().X, core::vector3df(0, 0, 0));
		mirrorV1.rotateXZBy(-getRotation().Y, core::vector3df(0, 0, 0));
		mirrorV1.rotateXYBy(getRotation().Z, core::vector3df(0, 0, 0));

		mirrorV2 = Vertices[2].Pos - Vertices[1].Pos; // Get another vector on the surface of the mirror
		mirrorV2.rotateYZBy(getRotation().X, core::vector3df(0, 0, 0));
		mirrorV2.rotateXZBy(-getRotation().Y, core::vector3df(0, 0, 0));
		mirrorV2.rotateXYBy(getRotation().Z, core::vector3df(0, 0, 0));

		mirrorNormal = mirrorV1.crossProduct(mirrorV2); // Use the above 2 vectors to find the mirror's Normal
		mirrorNormal = mirrorNormal.normalize(); // Normalize the Normal (to a length of 1).
		myAngle = angle(dwarfVector, mirrorNormal);
		dP = 2.0f * mirrorNormal.dotProduct(dwarfVector); // Calculate the "dot product" - used to find the reflection vector below
		reflectionVector = core::vector3df(dP, dP, dP) * mirrorNormal - dwarfVector; // Standard formula for a reflection vector!
		reflectionVector.setLength(100.0f);

		oldFrontSide = frontSide;
		if (myAngle < 90)
			frontSide = false;
		else
			frontSide = true;

		if (frontSide != oldFrontSide) // This flips the image horizontally, so the image is reversed, like a real mirror.
		{
			tempX = Vertices[0].TCoords.X; // Set the texture coordinates so the image is reversed on front side
			tempY = Vertices[0].TCoords.Y;
			Vertices[0].TCoords.X = Vertices[2].TCoords.X;
			Vertices[0].TCoords.Y = Vertices[2].TCoords.Y;
			Vertices[2].TCoords.X = tempX;
			Vertices[2].TCoords.Y = tempY;

			tempX = Vertices[1].TCoords.X;
			tempY = Vertices[1].TCoords.Y;
			Vertices[1].TCoords.X = Vertices[3].TCoords.X;
			Vertices[1].TCoords.Y = Vertices[3].TCoords.Y;
			Vertices[3].TCoords.X = tempX;
			Vertices[3].TCoords.Y = tempY;
		}

		if (getRotation().X > 90.0f) //This flips the image, so if the user flips the mirror vertically, it still shows the ground on the bottom
			setRotation(core::vector3df(getRotation().X - 180.0f, getRotation().Y, getRotation().Z));
		if (getRotation().X < -90.0f) //This flips the image, so if the user flips the mirror vertically, it still shows the ground on the bottom
			setRotation(core::vector3df(getRotation().X + 180.0f, getRotation().Y, getRotation().Z));
		return reflectionVector;
	}
};

CMirror* mirror = 0;

class MyEventReceiver : public IEventReceiver
{
public:
	virtual bool OnEvent(SEvent event)
	{
		core::vector3df mirVector;

		if (cube != 0 && event.EventType == irr::EET_KEY_INPUT_EVENT&&
			!event.KeyInput.PressedDown)
		{
			switch(event.KeyInput.Key)
			{
			case KEY_ESCAPE: //Quit the program
				{
				quit = true;
				return true;
				}
			case KEY_COMMA: // rotate mirror left
				{
					mirVector = mirror->getRotation();
					mirVector.Y += 6.0f; //Rotate a little
					mirror->setRotationUp(mirVector, fixedCam);
					return true;
				}
			case KEY_PERIOD:  // rotate mirror right
				{
					mirVector = mirror->getRotation();
					mirVector.Y -= 6.0f; //Rotate a little
					mirror->setRotationUp(mirVector, fixedCam);
					return true;
				}
			case KEY_KEY_P:  // rotate mirror +Z
				{
					mirVector = mirror->getRotation();
					mirVector.Z += 6.0f; // Rotate a little
					mirror->setRotationUp(mirVector, fixedCam);
					return true;
				}
			case KEY_KEY_L:  // rotate mirror -Z
				{
					mirVector = mirror->getRotation();
					mirVector.Z -= 6.0f; // Rotate a little
					mirror->setRotationUp(mirVector, fixedCam);
					return true;
				}
			case KEY_KEY_O:  // rotate mirror +X
				{
					mirVector = mirror->getRotation();
					mirVector.X += 6.0f; //Rotate a little
					mirror->setRotationUp(mirVector, fixedCam);
					return true;
				}
			case KEY_KEY_K:  // rotate mirror -X
				{
					mirVector = mirror->getRotation();
					mirVector.X -= 6.0f; //Rotate a little
					mirror->setRotationUp(mirVector, fixedCam);
					return true;
				}
			}
		}
		return false;
	}
};


int main()
{
	MyEventReceiver receiver;

	quit = false;

	// If your window is not square, the mirror's reflection will have a slight lensing
	// effect around the sides.  For example, 800x800 will give you a nice reflection.
	// But 1024x768 will make the mirror behave as if it's slightly concave mirror.
	// This effect is not pronounced, and for most applications, is probably acceptable.
	// Note: OPENGL seems to perform slower than DIRECT3D8 with a mirror, for some reason.
	//device = createDevice(video::EDT_OPENGL, core::dimension2d<s32>(1024, 768),
	//	16, false, false, false, &receiver);
	device = createDevice(video::EDT_DIRECT3D8, core::dimension2d<s32>(1024, 768),
		16, false, false, false, &receiver);

	video::IVideoDriver* driver = device->getVideoDriver();
	scene::ISceneManager* smgr = device->getSceneManager();
	gui::IGUIEnvironment* guiEnv = device->getGUIEnvironment();

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

	// Add a Camera
	camera = smgr->addCameraSceneNodeFPS(0,100.0f,750.0f); //Adds a camera
	camera->setPosition(core::vector3df(4100, 100, 7500));
	camera->setFarValue(12000.0f);

	//Add a Dwarf in (to represent the player)
	scene::IAnimatedMesh* meshDwarf = smgr->getMesh("../../media/dwarf.x");
	dwarf = smgr->addAnimatedMeshSceneNode(meshDwarf);
	dwarf->setPosition(core::vector3df(camera->getPosition().X, camera->getPosition().Y - 80.0f, camera->getPosition().Z)); // Make the dwarf stay with the camera
	dwarf->setMaterialFlag(video::EMF_LIGHTING, true);
	dwarf->setAnimationSpeed(10);

	//------ Add a Terrain in --------------------------------------------------------------------------------
	scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode( 
		"../../media/terrain-heightmap.bmp");

	terrain->setScale(core::vector3df(40, 4.4f, 40)); // To make the terrain look bigger, we change the scale factor of it to (40, 4.4, 40).
	terrain->setMaterialFlag(video::EMF_LIGHTING, true);

	// The first texture will be repeated only once over the whole terrain, and the second one (detail map) 20 times.
	terrain->setMaterialTexture(0, driver->getTexture("../../media/terrain-texture.jpg"));
	terrain->setMaterialTexture(1, driver->getTexture("../../media/detailmap3.jpg"));
	terrain->setMaterialType(video::EMT_DETAIL_MAP);
	terrain->scaleTexture(1.0f, 20.0f);
	terrain->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);

	// Triangle selector for terrain (so we can create a collision response animator below
	scene::ITriangleSelector* selector
		= smgr->createTerrainTriangleSelector(terrain, 0);
	terrain->setTriangleSelector(selector);
	selector->drop();

   	// Create a skybox (so we can see clouds, etc)
	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
	smgr->addSkyBoxSceneNode(
		driver->getTexture("../../media/irrlicht2_up.jpg"),
		driver->getTexture("../../media/irrlicht2_dn.jpg"),
		driver->getTexture("../../media/irrlicht2_lf.jpg"),
		driver->getTexture("../../media/irrlicht2_rt.jpg"),
		driver->getTexture("../../media/irrlicht2_ft.jpg"),
		driver->getTexture("../../media/irrlicht2_bk.jpg"));
	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
	//------- End of Terrain --------------------------------------------------------------------------------

	// We create a collision response animator and attach it to the camera, so that the camera will not be
	// able to fly through the terrain.
	scene::ISceneNodeAnimator* anim2 = smgr->createCollisionResponseAnimator(
		selector, camera, core::vector3df(30,60,30),
		core::vector3df(0,0,0), 
		core::vector3df(0,0,0));
	camera->addAnimator(anim2);
	anim2->drop();

	driver->setAmbientLight(video::SColor(0,255,255,255)); // Let there be light!

	// Create a cube to place in front of the mirror (to look at)
	cube = smgr->addTestSceneNode();
	cube->setPosition(core::vector3df(camera->getPosition().X + 150.0f, camera->getPosition().Y, camera->getPosition().Z));
	cube->setScale(core::vector3df(6.0f, 6.0f, 6.0f));
	cube->setMaterialFlag(video::EMF_LIGHTING, true);
	cube->setMaterialTexture(0, driver->getTexture("../../media/wall.jpg"));

	// Create a CMirror to be used as a mirror
	mirror = new CMirror(smgr->getRootSceneNode(), smgr, 999, device);
	mirror->setPosition(core::vector3df(camera->getPosition().X + 200.0f, camera->getPosition().Y, camera->getPosition().Z + 300.0f));
	mirror->setRotation(core::vector3df(0, 0, 0));
	mirror->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true); // Only really necessary if the scale isn't (1,1,1)
	mirror->setMaterialFlag(video::EMF_LIGHTING, false);
	video::ITexture* rt = 0; // create render target (used to "render-to-texture" for the mirror)

	if (driver->queryFeature(video::EVDF_RENDER_TO_TARGET)) //If the driver can handle Render-To-Target, then...
	{
		rt = driver->createRenderTargetTexture(core::dimension2d<s32>(256,256));
		mirror->setMaterialTexture(0, rt); // set material of mirror to render target
		fixedCam = smgr->addCameraSceneNode(0, mirror->getPosition(),  // add fixed camera..
			dwarf->getPosition()); //..looking at anything (doesn't matter)
	}
	else  // create problem text if device doesn't support render-to-texture
	{
		gui::IGUISkin* skin = guiEnv->getSkin();
		gui::IGUIFont* font = guiEnv->getFont("../../media/fonthaettenschweiler.bmp");
		if (font)
			skin->setFont(font);
		gui::IGUIStaticText* text = guiEnv->addStaticText(
			L"Your hardware or this renderer is not able to use the "\
			L"render to texture feature. RTT Disabled.",
			core::rect<s32>(150,20,470,60));
		text->setOverrideColor(video::SColor(100,255,255,255));
	}

	// Set other variables before the drawing loop
	device->getCursorControl()->setVisible(false); // disable mouse cursor
	int lastFPS = -1;

	while(device->run() && !quit)
	if (device->isWindowActive())
	{
		dwarf->setPosition(core::vector3df(camera->getPosition().X, camera->getPosition().Y - 80.0f, camera->getPosition().Z)); // Make the dwarf stay with the camera
		dwarf->setRotation(core::vector3df(0, camera->getRotation().Y + 180.0f, 0)); //Make the dwarf turn with the camera

		myReflectionVector = mirror->getMirrorReflection(dwarf, fixedCam);

		driver->beginScene(false, true, video::SColor(255,113,113,133));

		if (rt) // draw scene into render target
		{
			driver->setRenderTarget(rt, true, true, video::SColor(0,0,0,255)); // set render target texture (the mirror's texture)
			mirror->setVisible(false); // make mirror invisible (so it doesn't get painted over)
			smgr->setActiveCamera(fixedCam); // set fixed camera as active camera
			fixedCam->setTarget(myReflectionVector + mirror->getPosition()); // make the mirror's camera "look" in the direction of the reflection
			smgr->drawAll(); // draw whole scene into render buffer
			driver->setRenderTarget(0); // set back old render target
			mirror->setVisible(true); // make the mirror visible again
			smgr->setActiveCamera(camera); // set the user controlled camera as active one
		}

		smgr->drawAll(); // draw the 3d scene
		guiEnv->drawAll(); // draw the gui (if anything to draw - could contain the RTT warning above)

		driver->endScene();

		int fps = driver->getFPS(); // This section displays the frames-per-second on the window
		if (lastFPS != fps)
		{
			wchar_t tmp[1024];
			swprintf(tmp, 1024, L"Mirror Example - Irrlicht Engine [%s] fps:%d", 
				driver->getName(), fps);
			device->setWindowCaption(tmp);
			lastFPS = fps;
		}
	}

	device->drop(); // Delete Irrlicht Device
	return 0;
}
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

You're leaking a D3D resource... Probably this one...

Code: Select all

rt = driver->createRenderTargetTexture(core::dimension2d<s32>(256,256)); 
Panther
Posts: 34
Joined: Tue Jun 06, 2006 1:05 am

Memory leak fixed!

Post by Panther »

Yup, that fixes it! This code needs to be added right before the device->drop(); (near the end):

Code: Select all


	rt->drop();

No more "unfreed memory"!

Thanks Vitek! :)
game
Posts: 13
Joined: Sun Jul 09, 2006 2:47 pm

Post by game »

thanks for this code i needed one like that
vesa
Posts: 3
Joined: Sat Jul 15, 2006 11:26 am

Post by vesa »

Thank's for the code, Panther.

But the mirror doesn't work correctly when objects are close to mirror surface.

Image


Changes to code:
mirror->setRotationUp(core::vector3df(90.0f,0,0), fixedCam);
and
cube->setPosition(core::vector3df(mirror->getPosition().X - 40.0f, mirror->getPosition().Y+32.0f, mirror->getPosition().Z));
Panther
Posts: 34
Joined: Tue Jun 06, 2006 1:05 am

Post by Panther »

Hi vesa,

Yeah, the mirror I made is a "fake mirror". It was made with one camera/vector, that looks out of the center of the object. (Real mirrors have an infinite number of cameras/vectors. In our case, we'd need one per pixel). This is an "fps friendly" way of doing it. I made an example using four mirrors, all laying flat. As you add more mirrors (2x2, 3x3, 140x140) perhaps the reflection would be more accurate.

Image

If I used a more sophisticated method for mirror reflection generation, it would look better. But I'm too lazy for that. :wink: If you want to take the class to the next level, please feel free!

The following code produces the image above; I didn't repaste the CMirror class or the event handler.

Code: Select all

CMirror* mirror1 = 0;
CMirror* mirror2 = 0;
CMirror* mirror3 = 0;
CMirror* mirror4 = 0;

int main()
{
	MyEventReceiver receiver;

	quit = false;

	// If your window is not square, the mirror's reflection will have a slight lensing
	// effect around the sides.  For example, 800x800 will give you a nice reflection.
	// But 1024x768 will make the mirror behave as if it's slightly concave mirror.
	// This effect is not pronounced, and for most applications, is probably acceptable.
	// Note: OPENGL seems to perform slower than DIRECT3D8 with a mirror, for some reason.
	//device = createDevice(video::EDT_OPENGL, core::dimension2d<s32>(1024, 768),
	//	16, false, false, false, &receiver);
	device = createDevice(video::EDT_DIRECT3D8, core::dimension2d<s32>(1024, 768),
		16, false, false, false, &receiver);

	video::IVideoDriver* driver = device->getVideoDriver();
	scene::ISceneManager* smgr = device->getSceneManager();
	gui::IGUIEnvironment* guiEnv = device->getGUIEnvironment();

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

	// Add a Camera
	camera = smgr->addCameraSceneNodeFPS(0,100.0f,750.0f); //Adds a camera
	camera->setPosition(core::vector3df(4100, 100, 7500));
	camera->setFarValue(12000.0f);

	//Add a Dwarf in (to represent the player)
	scene::IAnimatedMesh* meshDwarf = smgr->getMesh("../../media/dwarf.x");
	dwarf = smgr->addAnimatedMeshSceneNode(meshDwarf);
	dwarf->setPosition(core::vector3df(camera->getPosition().X, camera->getPosition().Y - 80.0f, camera->getPosition().Z)); // Make the dwarf stay with the camera
	dwarf->setMaterialFlag(video::EMF_LIGHTING, true);
	dwarf->setAnimationSpeed(10);

	//------ Add a Terrain in --------------------------------------------------------------------------------
	scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode( 
		"../../media/terrain-heightmap.bmp");

	terrain->setScale(core::vector3df(40, 4.4f, 40)); // To make the terrain look bigger, we change the scale factor of it to (40, 4.4, 40).
	terrain->setMaterialFlag(video::EMF_LIGHTING, true);

	// The first texture will be repeated only once over the whole terrain, and the second one (detail map) 20 times.
	terrain->setMaterialTexture(0, driver->getTexture("../../media/terrain-texture.jpg"));
	terrain->setMaterialTexture(1, driver->getTexture("../../media/detailmap3.jpg"));
	terrain->setMaterialType(video::EMT_DETAIL_MAP);
	terrain->scaleTexture(1.0f, 20.0f);
	terrain->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);

	// Triangle selector for terrain (so we can create a collision response animator below
	scene::ITriangleSelector* selector
		= smgr->createTerrainTriangleSelector(terrain, 0);
	terrain->setTriangleSelector(selector);
	selector->drop();

   	// Create a skybox (so we can see clouds, etc)
	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
	smgr->addSkyBoxSceneNode(
		driver->getTexture("../../media/irrlicht2_up.jpg"),
		driver->getTexture("../../media/irrlicht2_dn.jpg"),
		driver->getTexture("../../media/irrlicht2_lf.jpg"),
		driver->getTexture("../../media/irrlicht2_rt.jpg"),
		driver->getTexture("../../media/irrlicht2_ft.jpg"),
		driver->getTexture("../../media/irrlicht2_bk.jpg"));
	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
	//------- End of Terrain --------------------------------------------------------------------------------

	// We create a collision response animator and attach it to the camera, so that the camera will not be
	// able to fly through the terrain.
	scene::ISceneNodeAnimator* anim2 = smgr->createCollisionResponseAnimator(
		selector, camera, core::vector3df(30,60,30),
		core::vector3df(0,0,0), 
		core::vector3df(0,0,0));
	camera->addAnimator(anim2);
	anim2->drop();

	driver->setAmbientLight(video::SColor(0,255,255,255)); // Let there be light!

	// Create a cube to place in front of the mirror (to look at)
	cube = smgr->addTestSceneNode();
	cube->setPosition(core::vector3df(camera->getPosition().X + 150.0f, camera->getPosition().Y, camera->getPosition().Z));
	cube->setScale(core::vector3df(6.0f, 6.0f, 6.0f));
	cube->setMaterialFlag(video::EMF_LIGHTING, true);
	cube->setMaterialTexture(0, driver->getTexture("../../media/wall.jpg"));

	// Create a CMirror1
	mirror1 = new CMirror(smgr->getRootSceneNode(), smgr, 999, device);
	mirror1->setPosition(core::vector3df(camera->getPosition().X + 130.0f, camera->getPosition().Y, camera->getPosition().Z + 230.0f));
	mirror1->setRotation(core::vector3df(0, 0, 0));
	mirror1->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true); // Only really necessary if the scale isn't (1,1,1)
	mirror1->setMaterialFlag(video::EMF_LIGHTING, false);
	video::ITexture* rt1 = 0; // create render target (used to "render-to-texture" for the mirror)

	// Create a CMirror2
	mirror2 = new CMirror(smgr->getRootSceneNode(), smgr, 999, device);
	mirror2->setPosition(core::vector3df(camera->getPosition().X + 130.0f, camera->getPosition().Y, camera->getPosition().Z + 370.0f));
	mirror2->setRotation(core::vector3df(0, 0, 0));
	mirror2->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true); // Only really necessary if the scale isn't (1,1,1)
	mirror2->setMaterialFlag(video::EMF_LIGHTING, false);
	video::ITexture* rt2 = 0; // create render target (used to "render-to-texture" for the mirror)

	// Create a CMirror3
	mirror3 = new CMirror(smgr->getRootSceneNode(), smgr, 999, device);
	mirror3->setPosition(core::vector3df(camera->getPosition().X + 270.0f, camera->getPosition().Y, camera->getPosition().Z + 230.0f));
	mirror3->setRotation(core::vector3df(0, 0, 0));
	mirror3->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true); // Only really necessary if the scale isn't (1,1,1)
	mirror3->setMaterialFlag(video::EMF_LIGHTING, false);
	video::ITexture* rt3 = 0; // create render target (used to "render-to-texture" for the mirror)

	// Create a CMirror4
	mirror4 = new CMirror(smgr->getRootSceneNode(), smgr, 999, device);
	mirror4->setPosition(core::vector3df(camera->getPosition().X + 270.0f, camera->getPosition().Y, camera->getPosition().Z + 370.0f));
	mirror4->setRotation(core::vector3df(0, 0, 0));
	mirror4->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true); // Only really necessary if the scale isn't (1,1,1)
	mirror4->setMaterialFlag(video::EMF_LIGHTING, false);
	video::ITexture* rt4 = 0; // create render target (used to "render-to-texture" for the mirror)

	if (driver->queryFeature(video::EVDF_RENDER_TO_TARGET)) //If the driver can handle Render-To-Target, then...
	{
		rt1 = driver->createRenderTargetTexture(core::dimension2d<s32>(256,256));
		mirror1->setMaterialTexture(0, rt1); // set material of mirror to render target
		fixedCam1 = smgr->addCameraSceneNode(0, mirror1->getPosition(),  // add fixed camera..
			dwarf->getPosition()); //..looking at anything (doesn't matter)

		rt2 = driver->createRenderTargetTexture(core::dimension2d<s32>(256,256));
		mirror2->setMaterialTexture(0, rt2); // set material of mirror to render target
		fixedCam2 = smgr->addCameraSceneNode(0, mirror2->getPosition(),  // add fixed camera..
			dwarf->getPosition()); //..looking at anything (doesn't matter)

		rt3 = driver->createRenderTargetTexture(core::dimension2d<s32>(256,256));
		mirror3->setMaterialTexture(0, rt3); // set material of mirror to render target
		fixedCam3 = smgr->addCameraSceneNode(0, mirror3->getPosition(),  // add fixed camera..
			dwarf->getPosition()); //..looking at anything (doesn't matter)

		rt4 = driver->createRenderTargetTexture(core::dimension2d<s32>(256,256));
		mirror4->setMaterialTexture(0, rt4); // set material of mirror to render target
		fixedCam4 = smgr->addCameraSceneNode(0, mirror4->getPosition(),  // add fixed camera..
			dwarf->getPosition()); //..looking at anything (doesn't matter)
	}
	else  // create problem text if device doesn't support render-to-texture
	{
		gui::IGUISkin* skin = guiEnv->getSkin();
		gui::IGUIFont* font = guiEnv->getFont("../../media/fonthaettenschweiler.bmp");
		if (font)
			skin->setFont(font);
		gui::IGUIStaticText* text = guiEnv->addStaticText(
			L"Your hardware or this renderer is not able to use the "\
			L"render to texture feature. RTT Disabled.",
			core::rect<s32>(150,20,470,60));
		text->setOverrideColor(video::SColor(100,255,255,255));
	}

	// Set other variables before the drawing loop
	device->getCursorControl()->setVisible(false); // disable mouse cursor
	int lastFPS = -1;

	mirror1->setRotationUp(core::vector3df(90.0f,0,0), fixedCam1);
	mirror2->setRotationUp(core::vector3df(90.0f,0,0), fixedCam2);
	mirror3->setRotationUp(core::vector3df(90.0f,0,0), fixedCam3);
	mirror4->setRotationUp(core::vector3df(90.0f,0,0), fixedCam4);

	cube->setPosition(core::vector3df(camera->getPosition().X + 160.0f, camera->getPosition().Y + 25.0f, camera->getPosition().Z + 300.0f));

	while(device->run() && !quit)
	if (device->isWindowActive())
	{
		dwarf->setPosition(core::vector3df(camera->getPosition().X, camera->getPosition().Y - 80.0f, camera->getPosition().Z)); // Make the dwarf stay with the camera
		dwarf->setRotation(core::vector3df(0, camera->getRotation().Y + 180.0f, 0)); //Make the dwarf turn with the camera

		myReflectionVector1 = mirror1->getMirrorReflection(dwarf, fixedCam1);
		myReflectionVector2 = mirror2->getMirrorReflection(dwarf, fixedCam2);
		myReflectionVector3 = mirror3->getMirrorReflection(dwarf, fixedCam3);
		myReflectionVector4 = mirror4->getMirrorReflection(dwarf, fixedCam4);

		driver->beginScene(false, true, video::SColor(255,113,113,133));

		if (rt1) // draw scene into render target
		{
			driver->setRenderTarget(rt1, true, true, video::SColor(0,0,0,255)); // set render target texture (the mirror's texture)
			mirror1->setVisible(false); // make mirror invisible (so it doesn't get painted over)
			smgr->setActiveCamera(fixedCam1); // set fixed camera as active camera
			fixedCam1->setTarget(myReflectionVector1 + mirror1->getPosition()); // make the mirror's camera "look" in the direction of the reflection
			smgr->drawAll(); // draw whole scene into render buffer
			driver->setRenderTarget(0); // set back old render target
			mirror1->setVisible(true); // make the mirror visible again
			smgr->setActiveCamera(camera); // set the user controlled camera as active one
		}

		if (rt2) // draw scene into render target
		{
			driver->setRenderTarget(rt2, true, true, video::SColor(0,0,0,255)); // set render target texture (the mirror's texture)
			mirror2->setVisible(false); // make mirror invisible (so it doesn't get painted over)
			smgr->setActiveCamera(fixedCam2); // set fixed camera as active camera
			fixedCam2->setTarget(myReflectionVector2 + mirror2->getPosition()); // make the mirror's camera "look" in the direction of the reflection
			smgr->drawAll(); // draw whole scene into render buffer
			driver->setRenderTarget(0); // set back old render target
			mirror2->setVisible(true); // make the mirror visible again
			smgr->setActiveCamera(camera); // set the user controlled camera as active one
		}

		if (rt3) // draw scene into render target
		{
			driver->setRenderTarget(rt3, true, true, video::SColor(0,0,0,255)); // set render target texture (the mirror's texture)
			mirror3->setVisible(false); // make mirror invisible (so it doesn't get painted over)
			smgr->setActiveCamera(fixedCam3); // set fixed camera as active camera
			fixedCam3->setTarget(myReflectionVector3 + mirror3->getPosition()); // make the mirror's camera "look" in the direction of the reflection
			smgr->drawAll(); // draw whole scene into render buffer
			driver->setRenderTarget(0); // set back old render target
			mirror3->setVisible(true); // make the mirror visible again
			smgr->setActiveCamera(camera); // set the user controlled camera as active one
		}

		if (rt4) // draw scene into render target
		{
			driver->setRenderTarget(rt4, true, true, video::SColor(0,0,0,255)); // set render target texture (the mirror's texture)
			mirror4->setVisible(false); // make mirror invisible (so it doesn't get painted over)
			smgr->setActiveCamera(fixedCam4); // set fixed camera as active camera
			fixedCam4->setTarget(myReflectionVector4 + mirror4->getPosition()); // make the mirror's camera "look" in the direction of the reflection
			smgr->drawAll(); // draw whole scene into render buffer
			driver->setRenderTarget(0); // set back old render target
			mirror4->setVisible(true); // make the mirror visible again
			smgr->setActiveCamera(camera); // set the user controlled camera as active one
		}

		smgr->drawAll(); // draw the 3d scene
		guiEnv->drawAll(); // draw the gui (if anything to draw - could contain the RTT warning above)

		driver->endScene();

		int fps = driver->getFPS(); // This section displays the frames-per-second on the window
		if (lastFPS != fps)
		{
			wchar_t tmp[1024];
			swprintf(tmp, 1024, L"Mirror Example - Irrlicht Engine [%s] fps:%d", 
				driver->getName(), fps);
			device->setWindowCaption(tmp);
			lastFPS = fps;
		}
	}

	rt1->drop();
	rt2->drop();
	rt3->drop();
	rt4->drop();
	device->drop(); // Delete Irrlicht Device
	return 0;
}
Panther
Posts: 34
Joined: Tue Jun 06, 2006 1:05 am

Close-up reflections not accurate

Post by Panther »

On further examination, vesa is right, the mirror is not accurate at close up. I tried a 4x4 grid, and even an 8x8 grid of 64 mirrors, and the reflection is skewed. Not sure why. I tried playing with the FixedCamera->SetFOV(), and adjusting the farValue. Nothing seems to help.

If anyone has an idea how to make the mirror "perfect" please let me know. Meanwhile, it is still useful for most applications (the reflection is believeable when you don't put objects right beside it).
vesa
Posts: 3
Joined: Sat Jul 15, 2006 11:26 am

Re: Close-up reflections not accurate

Post by vesa »

Panther wrote: If anyone has an idea how to make the mirror "perfect" please let me know. Meanwhile, it is still useful for most applications (the reflection is believeable when you don't put objects right beside it).
OGRE team has made a "perfect" mirror by using render-to-texture and one camera. I haven't studied how they actually did it. Sample code:
http://ogre.cvs.sourceforge.net/ogre/og ... iew=markup
Post Reply