I really love all the work done on Irrlicht, and the great community support I have experienced so far. I do, however, have a math problem dealing with portals, as seen in Valve's Portal. I have been thinking about a game with several additional types of portals (Time, Quantum, Scale), and I tried to get some basic portals working with RTT. I have my rendering set up OK, and it would not be too hard to write code to transport objects between portals. My setup involves two flat surfaces on which I draw a texture captured from a camera at the centre of the opposite portal. I simply can't figure out how to set the target of portal2.camera based on the angle between player's camera and portal1.centre (and vice versa of course). I thought of creating a line3df between player's camera and portal1.centre, and then using the angle and length of that line to determine the target of portal2.camera, but I can't figure out how to manipulate the line3df class in this way. Here is my source code (messy), and some images as well. Any help or alternate methods of implementing portals would be greatly appreciated.
Cheers,
Marc
Code: Select all
/* Project: Strange Attraction (portals)
Author: Marc Burns (2008)
Parts taken from Irrlicht examples, and
the Irrlicht forum. Those who wrote said
parts deserve credit for them.
*/
#include <irrlicht.h>
#include <iostream>
#include "CollisionSystem.h"
using namespace irr;
using namespace std;
int main()
{
IrrlichtDevice *device =
createDevice(video::EDT_OPENGL, core::dimension2d<s32>(800, 600), 16, false,true);
if (device == 0)
return 1; // could not create selected driver.
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
device->getFileSystem()->addZipFileArchive("./data/TestMapScene.zip");
device->getCursorControl()->setVisible(false);
/*I'm about to load a simple testing scene from TestMapScene.zip. It contains a lightmapped
room with a few pillars and a pool.*/
smgr->loadScene("sa1.irr");
//This mesh is to be used as the surface of portals
scene::IAnimatedMesh* portalmesh = smgr->addHillPlaneMesh("portal",
core::dimension2d<f32>(30,20),
core::dimension2d<u32>(8,8), 0, 0,
core::dimension2d<f32>(0,0),
core::dimension2d<f32>(1,1));
if (!(driver->queryFeature(video::EVDF_RENDER_TO_TARGET))) {
cout << "FAIL: No RTT for Portals!\n";
device->drop();
return 1;
}
//Create RTT textures for portals
video::ITexture* portalrt1 = driver->createRenderTargetTexture(core::dimension2d<s32>(512,512));
video::ITexture* portalrt2 = driver->createRenderTargetTexture(core::dimension2d<s32>(512,512));
/*Create nodes for portals, where the mesh is a flat plane of 250x160 units.
This mesh is textured with the RTT output from the camera positioned at the
center of the other portal. A transparent texture is drawn on top of the RTT texture
to create portal effects.*/
scene::IAnimatedMeshSceneNode* portalnode1 = smgr->addAnimatedMeshSceneNode(portalmesh);
scene::ICameraSceneNode* portalcamera1 = smgr->addCameraSceneNode();
portalcamera1->setPosition(core::vector3df(80,120,3));
portalcamera1->setTarget(core::vector3df(-903,108,831));
portalnode1->setPosition(core::vector3df(0,120,0));
portalnode1->setRotation(core::vector3df(90,0,90));
//portalnode1->setMaterialType(video::EMT_REFLECTION_2_LAYER);
//portalnode1->setMaterialTexture(0, driver->getTexture("./data/portalstatic.png"));
portalnode1->setMaterialTexture(0, portalrt1);
portalnode1->setMaterialFlag(video::EMF_LIGHTING,false);
scene::IAnimatedMeshSceneNode* portalnode2 = smgr->addAnimatedMeshSceneNode(portalmesh);
scene::ICameraSceneNode* portalcamera2 = smgr->addCameraSceneNode();
portalcamera2->setPosition(core::vector3df(240,120,3));
portalcamera2->setTarget(core::vector3df(-903,108,831));
portalnode2->setPosition(core::vector3df(240,120,0));
portalnode2->setRotation(core::vector3df(90,0,90));
//portalnode2->setMaterialType(video::EMT_REFLECTION_2_LAYER);
//portalnode2->setMaterialTexture(0, driver->getTexture("./data/portalstatic.png"));
portalnode2->setMaterialTexture(0, portalrt2);
portalnode2->setMaterialFlag(video::EMF_LIGHTING,false);
/*CS_ functions are a simple interface to Irrlicht's built-in collision system. Triangle selectors are
assigned to each mesh scene node in the scene. They are then added to a meta selector if within
20 units of the camera.*/
//Build collision system:
CS_assignTriangleSelectors(smgr, device->getFileSystem());
// add a user controlled camera
scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS(0, 100.0f, 300.0f, -1, 0, 0, true);
// Position the Camera
camera->setPosition(core::vector3df(-50,80,0));
// Rotate the Camera
camera->setRotation(core::vector3df(0,90,0));
//Put the Meta Triangle Selector
scene::IMetaTriangleSelector* meta = smgr->createMetaTriangleSelector();
// Node "meta" animation for the Camera
scene::ISceneNodeAnimator* anim =
smgr->createCollisionResponseAnimator(meta, camera, core::vector3df(30,50,30), core::vector3df(0,-1,0), core::vector3df(0,50,0));
// Assign the node anim to the camera
camera->addAnimator(anim);
anim->drop();
meta->drop();
/*For a pool. Taken from Irrlicht's Special Effects example.*/
scene::IAnimatedMesh* mesh = smgr->addHillPlaneMesh("pool",
core::dimension2d<f32>(20,20),
core::dimension2d<u32>(65,65), 0, 0,
core::dimension2d<f32>(0,0),
core::dimension2d<f32>(10,10));
scene::ISceneNode* node = smgr->addWaterSurfaceSceneNode(mesh->getMesh(0), 3.0f, 300.0f, 30.0f);
node->setPosition(core::vector3df(-175,-120,-3117));
node->setMaterialTexture(0, driver->getTexture("./data/submerged.jpg"));
node->setMaterialTexture(1, driver->getTexture("./data/water.jpg"));
node->setMaterialType(video::EMT_REFLECTION_2_LAYER);
node->setMaterialFlag(video::EMF_LIGHTING,false);
int lastFPS = -1;
bool whichPortal = false;
while(device->run())
if (device->isWindowActive())
{
driver->beginScene(true, true, 0);
/*Portal rendering alternates between portals to make gameplay smoother.
I hope to internalize all of this into a class soon.*/
if(whichPortal) {
driver->setRenderTarget(portalrt1, true, true, 0);
//--------> Here is where I would have some code to calculate the target of portalcamera2, which is drawn on portalnode1
smgr->setActiveCamera(portalcamera2);
smgr->drawAll();
} else {
driver->setRenderTarget(portalrt2, true, true, 0);
smgr->setActiveCamera(portalcamera1);
smgr->drawAll();
}
whichPortal = !whichPortal;
driver->setRenderTarget(0);
smgr->setActiveCamera(camera);
smgr->drawAll();
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"RTT Portals example - Irrlicht Engine [";
str += driver->getName();
str += "] FPS:";
str += fps;
str += " Pos: ";
str += camera->getPosition().X;
str += ", ";
str += camera->getPosition().Y;
str += ", ";
str += camera->getPosition().Z;
str += ", ";
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
//Update collision:
meta->removeAllTriangleSelectors();
core::aabbox3df box(-10.f, -10.f, -10.f, 10.f, 10.f, 10.f);
camera->getAbsoluteTransformation().transformBoxEx(box);
// put all selectors into meta selector if they intersect the bounding box
CS_gatherTriangleSelectors(smgr, meta, box);
}
device->drop();
return 0;
}
A little diagram I drew.
Thank you in advance!