Ok, I fixed the bug that RhavoX found.
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;
}