git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6000 dfc29bdd-3216-0410-991c-e03cc46cb475
		
			
				
	
	
		
			771 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			771 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /** Example 028 CubeMapping
 | ||
| 
 | ||
| Shows usage of cubemap textures and how to do some simple environment mapping.
 | ||
| Cubemap textures have images for all 6 directions of a cube in a single texture.
 | ||
| Environment is used to reflect the environment around an object onto the object.
 | ||
| Cubemaps only work with shader materials which are written to support cube mapping.
 | ||
| 
 | ||
| @author Michael Zeilfelder, based on EnvCubeMap example from irrSpintz engine.
 | ||
| 
 | ||
| Start with the usual includes.
 | ||
| */
 | ||
| 
 | ||
| #ifdef _MSC_VER
 | ||
| #pragma comment(lib, "Irrlicht.lib")
 | ||
| #endif
 | ||
| 
 | ||
| #include <irrlicht.h>
 | ||
| #include "driverChoice.h"
 | ||
| #include "exampleHelper.h"
 | ||
| 
 | ||
| using namespace irr;
 | ||
| 
 | ||
| 
 | ||
| /*
 | ||
| 	A callback class for our cubemap shader.
 | ||
| 	We need a shader material which maps the cubemap texture to
 | ||
| 	the polygon vertices of objects.
 | ||
| */
 | ||
| class CubeMapReflectionCallback : public video::IShaderConstantSetCallBack
 | ||
| {
 | ||
| public:
 | ||
| 	CubeMapReflectionCallback(scene::ISceneManager* smgr, int styleUVW)
 | ||
| 		: SceneMgr(smgr)
 | ||
| 		, StyleUVW(styleUVW), Roughness(0.f)
 | ||
| 		, styleUvwID(-1) , worldViewProjID(-1), worldID(-1), cameraPosID(-1)
 | ||
| 	{}
 | ||
| 
 | ||
| 	/*
 | ||
| 		Setting the style to map vertex UV-coordinates to the cubemap textures.
 | ||
| 		- Specular style is typically used for mirrors and highlight reflections.
 | ||
| 		- Diffuse style is commonly used in image based lighting calculations and
 | ||
| 		often in combination with a higher roughness. Think of it as the sum of all
 | ||
| 		light which reaches a point on your object.
 | ||
| 		- Using model vertices directly for UV's is just nice for testing sometimes.
 | ||
| 		Maybe has more uses? Experiment around :-)
 | ||
| 	*/
 | ||
| 	void SetStyleUVW(int style)
 | ||
| 	{
 | ||
| 		StyleUVW = style;
 | ||
| 	}
 | ||
| 
 | ||
| 	int GetStyleUVW() const
 | ||
| 	{
 | ||
| 		return StyleUVW;
 | ||
| 	}
 | ||
| 
 | ||
| 	/*
 | ||
|      We could also call this sharpness as the rougher a material the less
 | ||
| 	 sharp the reflections of a cubemap are (light for rough materials
 | ||
| 	 spreads out more while smooth materials reflect it more like a mirror).
 | ||
| 	 Roughness is calculated using the mipmaps of the cubemap texture.
 | ||
| 	 Note that rendertarget cubemap textures won't have mipmaps, so unfortunately
 | ||
| 	 it won't work for those.
 | ||
| 	 Also currently only OpenGL is able to interpolate seamless over cubemap borders.
 | ||
| 	 On Direct3D9 you will only smooth per side, but not over side-borders.
 | ||
| 	*/
 | ||
| 	void SetRoughness(float roughness)
 | ||
| 	{
 | ||
| 		Roughness = roughness;
 | ||
| 	}
 | ||
| 
 | ||
| 	float getRoughness() const
 | ||
| 	{
 | ||
| 		return Roughness;
 | ||
| 	}
 | ||
| 
 | ||
| 	/*
 | ||
| 		Typical code which passes a few values from c++ to shader.
 | ||
| 	*/
 | ||
| 	virtual void OnSetMaterial(const video::SMaterial& material)
 | ||
| 	{}
 | ||
| 
 | ||
| 	virtual void OnSetConstants(video::IMaterialRendererServices* services, s32 userData)
 | ||
| 	{
 | ||
| 		video::IVideoDriver* driver = services->getVideoDriver();
 | ||
| 
 | ||
| 		if ( worldViewProjID < 0 )	// first update
 | ||
| 		{
 | ||
| 			styleUvwID = services->getVertexShaderConstantID("StyleUVW");
 | ||
| 			if( driver->getDriverType() == video::EDT_DIRECT3D9 )
 | ||
| 			{
 | ||
| 				worldViewProjID = services->getVertexShaderConstantID("WorldViewProj");
 | ||
| 			}
 | ||
| 			worldID = services->getVertexShaderConstantID("World");
 | ||
| 			cameraPosID = services->getVertexShaderConstantID("CameraPos");
 | ||
| 			roughnessID = services->getPixelShaderConstantID("Roughness");
 | ||
| 		}
 | ||
| 
 | ||
| 		services->setVertexShaderConstant(styleUvwID, &StyleUVW, 1 );
 | ||
| 
 | ||
| 		irr::core::matrix4 world = driver->getTransform(irr::video::ETS_WORLD);
 | ||
| 		services->setVertexShaderConstant(worldID, world.pointer(), 16);
 | ||
| 
 | ||
| 		if( driver->getDriverType() == video::EDT_DIRECT3D9 )
 | ||
| 		{
 | ||
| 			irr::core::matrix4 worldViewProj;
 | ||
| 			worldViewProj = driver->getTransform(irr::video::ETS_PROJECTION);
 | ||
| 			worldViewProj *= driver->getTransform(irr::video::ETS_VIEW);
 | ||
| 			worldViewProj *= world;
 | ||
| 			services->setVertexShaderConstant(worldViewProjID, worldViewProj.pointer(), 16);
 | ||
| 		}
 | ||
| 
 | ||
| 		core::vector3df cameraPos = SceneMgr->getActiveCamera()->getAbsolutePosition();
 | ||
| 		services->setVertexShaderConstant(cameraPosID, &cameraPos.X, 3 );
 | ||
| 		services->setPixelShaderConstant(roughnessID, &Roughness, 1 );
 | ||
| 	}
 | ||
| 
 | ||
| private:
 | ||
| 	scene::ISceneManager* SceneMgr;
 | ||
| 
 | ||
| 	int StyleUVW;		// 0 = specular, 1=diffuse, 2 = use model vertex coordinates for uvw.
 | ||
| 	float Roughness;	// cubemap 0 = specular ... highest value depends on number of mipmaps in the texture
 | ||
| 
 | ||
| 	irr::s32 styleUvwID;
 | ||
| 	irr::s32 worldViewProjID;
 | ||
| 	irr::s32 worldID;
 | ||
| 	irr::s32 cameraPosID;
 | ||
| 	irr::s32 roughnessID;
 | ||
| };
 | ||
| 
 | ||
| /*
 | ||
| 	To keep the example compact our event-receiver acts also like a main
 | ||
| 	application class. So it handles user input, updates the dynamic parts of
 | ||
| 	the UI and it keeps some 3d nodes around.
 | ||
| */
 | ||
| class MyEventReceiver : public IEventReceiver
 | ||
| {
 | ||
| public:
 | ||
| 	MyEventReceiver() : Driver(0), Shader(0)
 | ||
| 		,BackgroundSkybox(0), BackgroundCube(0)
 | ||
| 		, CubemapUpdates(2)
 | ||
| 		, CurrentStyleUVW(0), CurrentRoughness(0)
 | ||
| 		, NeedCubemapUpdate(true)
 | ||
| 	{
 | ||
| 		StyleNamesUVW.push_back( L"specular" );
 | ||
| 		StyleNamesUVW.push_back( L"diffuse" );
 | ||
| 		StyleNamesUVW.push_back( L"model coordinates" );
 | ||
| 	}
 | ||
| 
 | ||
| 	// Handle the key input
 | ||
| 	virtual bool OnEvent(const SEvent& event)
 | ||
| 	{
 | ||
| 		if (event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown == false)
 | ||
| 		{
 | ||
| 			switch(event.KeyInput.Key )
 | ||
| 			{
 | ||
| 			case KEY_SPACE:
 | ||
| 				// Switch between different texture mapping styles
 | ||
| 				if ( Shader )
 | ||
| 				{
 | ||
| 					Shader->SetStyleUVW((Shader->GetStyleUVW()+1)%StyleNamesUVW.size());
 | ||
| 					updateStyleUVW();
 | ||
| 				}
 | ||
| 				break;
 | ||
| 			case KEY_KEY_B:
 | ||
| 				// Switch between our 2 different backgrounds
 | ||
| 				if ( BackgroundSkybox && BackgroundCube )
 | ||
| 				{
 | ||
| 					if ( BackgroundSkybox->isVisible() )
 | ||
| 					{
 | ||
| 						BackgroundSkybox->setVisible(false);
 | ||
| 						BackgroundCube->setVisible(true);
 | ||
| 					}
 | ||
| 					else
 | ||
| 					{
 | ||
| 						BackgroundSkybox->setVisible(true);
 | ||
| 						BackgroundCube->setVisible(false);
 | ||
| 					}
 | ||
| 					NeedCubemapUpdate = true;
 | ||
| 				}
 | ||
| 				break;
 | ||
| 			case KEY_KEY_I:
 | ||
| 				// Show/hide the info text nodes
 | ||
| 				for (u32 i=0; i<InfoTextNodes.size(); ++i )
 | ||
| 					InfoTextNodes[i]->setVisible(!InfoTextNodes[i]->isVisible());
 | ||
| 				break;
 | ||
| 			case KEY_KEY_S:
 | ||
| 				// Enable/disable seamless smoothing of mipmaps over cube borders
 | ||
| 				if ( Driver )
 | ||
| 				{
 | ||
| 					Driver->disableFeature(video::EVDF_TEXTURE_CUBEMAP_SEAMLESS, Driver->queryFeature(video::EVDF_TEXTURE_CUBEMAP_SEAMLESS) );
 | ||
| 					updateSeamless();
 | ||
| 				}
 | ||
| 				break;
 | ||
| 			case KEY_KEY_U:
 | ||
| 				// Switch dynamic cubemap updates on/off.
 | ||
| 				CubemapUpdates = (CubemapUpdates+1) % 3;
 | ||
| 				updateCubemapUpdates();
 | ||
| 				break;
 | ||
| 			case KEY_PLUS:
 | ||
| 			case KEY_ADD:
 | ||
| 				// Make material rougher
 | ||
| 				if ( Shader )
 | ||
| 				{
 | ||
| 					Shader->SetRoughness( Shader->getRoughness() + 0.5f );
 | ||
| 					updateRoughness();
 | ||
| 				}
 | ||
| 				break;
 | ||
| 			case KEY_MINUS:
 | ||
| 			case KEY_SUBTRACT:
 | ||
| 			{
 | ||
| 				// Make material smoother
 | ||
| 				if ( Shader )
 | ||
| 				{
 | ||
| 					float roughness = Shader->getRoughness() - 0.5f;
 | ||
| 					if ( roughness >= 0.f )
 | ||
| 					{
 | ||
| 						Shader->SetRoughness(roughness);
 | ||
| 						updateRoughness();
 | ||
| 					}
 | ||
| 				}
 | ||
| 				break;
 | ||
| 			}
 | ||
| 			default:
 | ||
| 				break;
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	// Some helper functions to update the UI
 | ||
| 	void updateStyleUVW()
 | ||
| 	{
 | ||
| 		if ( CurrentStyleUVW && Shader)
 | ||
| 			CurrentStyleUVW->setText(StyleNamesUVW[Shader->GetStyleUVW()].c_str());
 | ||
| 	}
 | ||
| 
 | ||
| 	void updateRoughness()
 | ||
| 	{
 | ||
| 		if ( CurrentRoughness && Shader )
 | ||
| 		{
 | ||
| 			CurrentRoughness->setText( irr::core::stringw(Shader->getRoughness()).c_str() );
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	void updateSeamless()
 | ||
| 	{
 | ||
| 		if ( CurrentSeamlessCubemap && Driver )
 | ||
| 		{
 | ||
| 			CurrentSeamlessCubemap->setText( Driver->queryFeature(video::EVDF_TEXTURE_CUBEMAP_SEAMLESS) ? L"ON" : L"OFF" );
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	void updateCubemapUpdates()
 | ||
| 	{
 | ||
| 		if ( CurrentCubemapUpdates )
 | ||
| 		{
 | ||
| 			switch ( CubemapUpdates )
 | ||
| 			{
 | ||
| 				case 0:	CurrentCubemapUpdates->setText( L"static");	break;
 | ||
| 				case 1:	CurrentCubemapUpdates->setText( L"dynamic" );	break;
 | ||
| 				case 2:	CurrentCubemapUpdates->setText( L"dynamic+mips" );	break;
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	// Check if the cubemap textures should be updated with new screenshots
 | ||
| 	// return 0 for no update, 1 for update, 2 for update and fix mip-maps
 | ||
| 	int checkCubemapUpdate()
 | ||
| 	{
 | ||
| 		if ( NeedCubemapUpdate || CubemapUpdates == 2)
 | ||
| 		{
 | ||
| 			NeedCubemapUpdate = false;
 | ||
| 			return 2;
 | ||
| 		}
 | ||
| 
 | ||
| 		return CubemapUpdates;
 | ||
| 	}
 | ||
| 
 | ||
| 	// Add some text-node floating above it's parent node.
 | ||
| 	void addInfoTextNode(irr::gui::IGUIFont* font, const wchar_t* text, irr::scene::ISceneNode* parent)
 | ||
| 	{
 | ||
| 		if ( parent )
 | ||
| 		{
 | ||
| 			const video::SColor infoTextCol(250, 70, 90, 90);
 | ||
| 			core::dimension2du dim(font->getDimension(text));
 | ||
| 			core::dimension2df dimf((f32)dim.Width, (f32)dim.Height);
 | ||
| 			scene::IBillboardTextSceneNode* infoNode = parent->getSceneManager()->addBillboardTextSceneNode( font, text, parent, dimf, core::vector3df(0, 120, 0), -1, infoTextCol, infoTextCol);
 | ||
| 			InfoTextNodes.push_back(infoNode);
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	irr::video::IVideoDriver* Driver;
 | ||
| 	CubeMapReflectionCallback* Shader;
 | ||
| 
 | ||
| 	scene::ISceneNode* BackgroundSkybox;
 | ||
| 	scene::ISceneNode* BackgroundCube;
 | ||
| 	irr::core::array<scene::ISceneNode*> InfoTextNodes;
 | ||
| 
 | ||
| 	int CubemapUpdates;	// 0 = static, 1 = dynamic, 2 = dynamic with rtt
 | ||
| 
 | ||
| 	irr::core::array<irr::core::stringw> StyleNamesUVW;
 | ||
| 
 | ||
| 	irr::gui::IGUIStaticText* CurrentStyleUVW;
 | ||
| 	irr::gui::IGUIStaticText* CurrentRoughness;
 | ||
| 	irr::gui::IGUIStaticText* CurrentSeamlessCubemap;
 | ||
| 	irr::gui::IGUIStaticText* CurrentCubemapUpdates;
 | ||
| 
 | ||
| private:
 | ||
| 	bool NeedCubemapUpdate;
 | ||
| };
 | ||
| 
 | ||
| /* Workaround for OpenGL's upside-down images.
 | ||
|  Texture origins (0,0) in OpenGL are usually at the left-bottom instead of the more common left-top image formats.
 | ||
|  Irrlicht internally uses textures with left-top origin and then corrects the texture-matrices in the fixed-function pipeline.
 | ||
|  For shader materials it's left to the users to handle those UV-flips for the texture-matrix.
 | ||
|  Render target textures (RTT's) in OpenGL are rendered with left-bottom origin and Irrlicht can't change that, so all RTT textures
 | ||
|  in memory are upside-down (unlike all other Irrlicht textures).
 | ||
|  In the fixed function pipeline Irrlicht handles this by flipping the RTT's texture matrix once more and for shaders it's again
 | ||
|  left to the users to handle it.
 | ||
|  Cubemap textures are different from other textures in OpenGL. Each cube side has left-top as the origin. So not flipping Irrlicht textures for those would be fine.
 | ||
|  Except - OpenGL RTT's still render left-bottom - even when the target is a cubemap RTT.
 | ||
|  I found no good way around this so far - it just seems messed up as we get a left-handed/right handed coordinate system change that way.
 | ||
| 
 | ||
|  So... the following 2 defines are two different workarounds I found. Both are ugly, which one is better in reality depends probably on the scene.
 | ||
|  Only use one of those:
 | ||
| 	CUBEMAP_UPSIDE_DOWN_GL_PROJECTION is relatively fast as it just changes the project matrix. The problem is that changing the projection matrix
 | ||
| 	means changing front/backside culling. So every node rendered has to flip the material flags for those.
 | ||
| 
 | ||
| 	CUBEMAP_USPIDE_DOWN_RTT will change the texture memory itself and flip the image upside-down.
 | ||
| 	While easier to do, this involves texture-locking and is very slow.
 | ||
| */
 | ||
| #define CUBEMAP_UPSIDE_DOWN_GL_PROJECTION
 | ||
| //#define CUBEMAP_USPIDE_DOWN_RTT
 | ||
| 
 | ||
| 
 | ||
| // Flip frontface/backface culling for all nodes
 | ||
| #ifdef CUBEMAP_UPSIDE_DOWN_GL_PROJECTION
 | ||
| void flipCullingFlags(const core::array<scene::ISceneNode*>& nodes)
 | ||
| {
 | ||
| 	for ( irr::u32 n=0; n < nodes.size(); ++n )
 | ||
| 	{
 | ||
| 		scene::ISceneNode* node = nodes[n];
 | ||
| 		const irr::u32 matCount = node->getMaterialCount();
 | ||
| 		for ( irr::u32 m=0; m < matCount; ++m)
 | ||
| 		{
 | ||
| 			video::SMaterial& mat = node->getMaterial(m);
 | ||
| 			mat.BackfaceCulling = !mat.BackfaceCulling;
 | ||
| 			mat.FrontfaceCulling = !mat.FrontfaceCulling;
 | ||
| 		}
 | ||
| 	}
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| 
 | ||
| /*
 | ||
| 	Render the environment around a node into a cubemap texture.
 | ||
| */
 | ||
| void renderEnvironmentCubeMap(irr::video::IVideoDriver* driver, irr::scene::ICameraSceneNode* cubeMapCamera, irr::scene::ISceneNode* cubeCenterNode, video::IRenderTarget* cubeMapRT, video::ITexture* dynamicCubeMapRTT, video::ITexture* depthStencilRTT)
 | ||
| {
 | ||
| 	//	Change to the cubemap camera which has a few specific render-settings
 | ||
| 	scene::ISceneManager* smgr = cubeMapCamera->getSceneManager();
 | ||
| 	scene::ICameraSceneNode * oldCam = smgr->getActiveCamera();
 | ||
| 	smgr->setActiveCamera( cubeMapCamera );
 | ||
| 
 | ||
| 	/*
 | ||
| 		We want to see everything around the center node, so hide the node
 | ||
| 		itself, otherwise it would be in the way.
 | ||
| 		Then set the camera to that node's position.
 | ||
| 	*/
 | ||
| 	cubeCenterNode->setVisible( false );
 | ||
| 	const core::vector3df center( cubeCenterNode->getAbsolutePosition() );
 | ||
| 	cubeMapCamera->setPosition( center );
 | ||
| 
 | ||
| 	/*
 | ||
| 		Render all 6 directions. Which means simple setting the camera target/up
 | ||
| 		vector to all 6 directions and then render the full scene each time.
 | ||
| 		So yeah - updating an environment cube-map means 6 full renders for each
 | ||
| 		object which needs an environment map. In other words - you generally only
 | ||
| 		want to do that in pre-processing, not in realtime.
 | ||
| 	*/
 | ||
| 	const core::vector3df targetVecs[6] = {
 | ||
| 		core::vector3df(1.f, 0.f, 0.f),
 | ||
| 		core::vector3df(-1.f, 0.f, 0.f),
 | ||
| 		core::vector3df(0.f, 1.f, 0.f),
 | ||
| 		core::vector3df(0.f, -1.f, 0.f),
 | ||
| 		core::vector3df(0.f, 0.f, 1.f),
 | ||
| 		core::vector3df(0.f, 0.f, -1.f)
 | ||
| 	};
 | ||
| 
 | ||
| 	const core::vector3df upVecs[6] = {
 | ||
| 		core::vector3df( 0,1,0 ),
 | ||
| 		core::vector3df( 0,1,0 ),
 | ||
| 		core::vector3df( 0,0,-1 ),
 | ||
| 		core::vector3df( 0,0,1 ),
 | ||
| 		core::vector3df( 0,1,0 ),
 | ||
| 		core::vector3df( 0,1,0 )
 | ||
| 	};
 | ||
| 	for ( int s=0; s<6; ++s )
 | ||
| 	{
 | ||
| 		cubeMapCamera->setUpVector( upVecs[s] );
 | ||
| 		cubeMapCamera->setTarget( center + targetVecs[s] );
 | ||
| 		// Here we tell into which side of the cubemap texture we want to write
 | ||
| 		cubeMapRT->setTexture(dynamicCubeMapRTT, depthStencilRTT, (video::E_CUBE_SURFACE)(video::ECS_POSX + s));
 | ||
| 		driver->setRenderTargetEx(cubeMapRT, video::ECBF_ALL);
 | ||
| 		smgr->drawAll();
 | ||
| 
 | ||
| #ifdef CUBEMAP_USPIDE_DOWN_RTT
 | ||
| 		// This works because the lock for rtt's always flips in Irrlicht.
 | ||
| 		// So in this case lock() unlock will result in a flipped texture
 | ||
| 		// But be warned - it's very, very slow!
 | ||
| 		driver->setRenderTarget(0);	// to avoid accessing active rt
 | ||
| 		dynamicCubeMapRTT->lock(video::ETLM_READ_WRITE, 0, s, video::ETLF_FLIP_Y_UP_RTT);
 | ||
| 		dynamicCubeMapRTT->unlock();
 | ||
| #endif
 | ||
| 	}
 | ||
| 
 | ||
| 	//dynamicCubeMapRTT->regenerateMipMapLevels();	// Unfortunately we can't seem to have mipmaps for rtt's
 | ||
| 
 | ||
| 	driver->setRenderTarget(0);
 | ||
| 	cubeCenterNode->setVisible( true );
 | ||
| 	smgr->setActiveCamera( oldCam );
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
| 	Typical setup at the main start.
 | ||
| */
 | ||
| int main()
 | ||
| {
 | ||
| 	// Ask user for driver
 | ||
| 	video::E_DRIVER_TYPE driverType = driverChoiceConsole();
 | ||
| 	if (driverType==video::EDT_COUNT)
 | ||
| 		return 1;
 | ||
| 
 | ||
| 	// Create device
 | ||
| 	MyEventReceiver eventReceiver;
 | ||
| 	const core::dimension2d<u32> dimDevice(1024, 768);
 | ||
| 	IrrlichtDevice* device = createDevice( driverType, dimDevice, 32, false, false, false, &eventReceiver );
 | ||
| 	if (!device)
 | ||
| 		return 1;
 | ||
| 
 | ||
| 	const io::path mediaPath = getExampleMediaPath();
 | ||
| 	video::IVideoDriver* driver = device->getVideoDriver();
 | ||
| 	scene::ISceneManager* smgr = device->getSceneManager();
 | ||
| 	gui::IGUIEnvironment* env = device->getGUIEnvironment();
 | ||
| 	eventReceiver.Driver = driver;
 | ||
| 
 | ||
| 	// Set window title
 | ||
| 	core::stringw strCaption(L"Cubemap example - Irrlicht Engine [");
 | ||
| 	strCaption += driver->getName();
 | ||
| 	strCaption += L"]";
 | ||
| 	device->setWindowCaption(strCaption.c_str());
 | ||
| 
 | ||
| 	// set a nicer font
 | ||
| 	gui::IGUISkin* skin = env->getSkin();
 | ||
| 	gui::IGUIFont* font = env->getFont(mediaPath + "fonthaettenschweiler.bmp");
 | ||
| 	if (font)
 | ||
| 		skin->setFont(font);
 | ||
| 
 | ||
| 	/*
 | ||
| 		Create a shader material for cube mapping
 | ||
| 	*/
 | ||
| 	video::IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();
 | ||
| 	s32 cubeMapReflectionMaterial = 0;
 | ||
| 	if( gpu )
 | ||
| 	{
 | ||
| 		// Decide on shader to use based on active driver
 | ||
| 		irr::io::path vsFileName;
 | ||
| 		irr::io::path psFileName;
 | ||
| 		switch( driverType )
 | ||
| 		{
 | ||
| 			case video::EDT_DIRECT3D9:
 | ||
| 				vsFileName = mediaPath + "cubeMapReflectionVS.hlsl";
 | ||
| 				psFileName = mediaPath + "cubeMapReflectionPS.hlsl";
 | ||
| 				break;
 | ||
| 
 | ||
| 			case video::EDT_OPENGL:
 | ||
| 				vsFileName = mediaPath + "cubeMapReflection.vert";
 | ||
| 				psFileName = mediaPath + "cubeMapReflection.frag";
 | ||
| 				break;
 | ||
| 		}
 | ||
| 
 | ||
| 		CubeMapReflectionCallback* cubeMapCB = new CubeMapReflectionCallback(smgr, 2);
 | ||
| 		cubeMapReflectionMaterial = gpu->addHighLevelShaderMaterialFromFiles(
 | ||
| 			vsFileName, "VS", video::EVST_VS_1_1,
 | ||
| 			psFileName, "PS", video::EPST_PS_3_0,
 | ||
| 			cubeMapCB, video::EMT_SOLID );
 | ||
| 		if ( cubeMapReflectionMaterial >= 0 )
 | ||
| 			eventReceiver.Shader = cubeMapCB;
 | ||
| 		cubeMapCB->drop();
 | ||
| 	}
 | ||
| 
 | ||
| 	// add fps camera
 | ||
| 	scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS(0, 100.f, 1.f);
 | ||
| 	camera->setPosition( core::vector3df( 0,10,-200 ) );
 | ||
| 	device->getCursorControl()->setVisible(false);
 | ||
| 
 | ||
| 	/*
 | ||
| 		Get 6 images forming a cubemap. The coordinate system used in those images
 | ||
| 		seemed to be different than the one in Irrlicht. I decided to leave it like
 | ||
| 		that because it's pretty common that way. If you get cubemap textures which
 | ||
| 		seem to have x/y/z axis named different you'll just have to experiment until
 | ||
| 		you figured out the correct order.
 | ||
| 	*/
 | ||
| 	core::array<video::IImage*> cubeMapImages;
 | ||
| 	cubeMapImages.push_back(driver->createImageFromFile( mediaPath + "cubemap_posx.jpg" ));
 | ||
| 	cubeMapImages.push_back(driver->createImageFromFile( mediaPath + "cubemap_negx.jpg" ));
 | ||
| 	cubeMapImages.push_back(driver->createImageFromFile( mediaPath + "cubemap_posy.jpg" ));
 | ||
| 	cubeMapImages.push_back(driver->createImageFromFile( mediaPath + "cubemap_negy.jpg" ));
 | ||
| 	cubeMapImages.push_back(driver->createImageFromFile( mediaPath + "cubemap_posz.jpg" ));
 | ||
| 	cubeMapImages.push_back(driver->createImageFromFile( mediaPath + "cubemap_negz.jpg" ));
 | ||
| 
 | ||
| 	/* Create a cubemap texture from those images. Note that 6 images become a single texture now. */
 | ||
| 	video::ITexture* cubeMapStaticTex = 0;
 | ||
| 	cubeMapStaticTex = driver->addTextureCubemap("cm", cubeMapImages[0], cubeMapImages[1], cubeMapImages[2], cubeMapImages[3], cubeMapImages[4], cubeMapImages[5]);
 | ||
| 	for ( u32 i=0; i<cubeMapImages.size(); ++i )
 | ||
| 		if ( cubeMapImages[i] )
 | ||
| 			cubeMapImages[i]->drop();
 | ||
| 	cubeMapImages.clear();
 | ||
| 
 | ||
| 	/* Create a render target, cubemap render-target-textures and a camera with settings for cube mapping */
 | ||
| 	video::IRenderTarget* cubeMapRT = driver->addRenderTarget();
 | ||
| 	video::ITexture* dynamicCubeMapRTT = 0;
 | ||
| 	video::ITexture* depthStencilRTT = 0;
 | ||
| 	video::ITexture* dynamicCubeMapRTT_intermediate = 0;	// just for rendering, but not used in material
 | ||
| 	video::ITexture* dynamicCubeMapTex = 0;					// dynamic and with mipmaps
 | ||
| 	scene::ICameraSceneNode* cubeMapCamera = 0;
 | ||
| 	if( driver->queryFeature( video::EVDF_RENDER_TO_TARGET ) )
 | ||
| 	{
 | ||
| 		// Create cube map textures and render target cubemap textures.
 | ||
| 		const u32 dynamicCubeMapSize = 512;
 | ||
| 		dynamicCubeMapRTT = driver->addRenderTargetTextureCubemap(dynamicCubeMapSize, "cube_rtr");
 | ||
| 		depthStencilRTT = driver->addRenderTargetTexture(irr::core::dimension2du(dynamicCubeMapSize, dynamicCubeMapSize), "cubemap_ds", irr::video::ECF_D24S8);
 | ||
| 
 | ||
| 		dynamicCubeMapRTT_intermediate = driver->addRenderTargetTextureCubemap(dynamicCubeMapSize, "cube_rtr");
 | ||
| 		dynamicCubeMapTex = driver->addTextureCubemap(dynamicCubeMapSize, "cube_tex");
 | ||
| 
 | ||
| 		// Camera for creating an environment cubemap
 | ||
| 		cubeMapCamera = smgr->addCameraSceneNode();
 | ||
| 		cubeMapCamera->setFOV(core::PI* 0.5f);	// 90<39> view angle
 | ||
| 		cubeMapCamera->setAspectRatio(1.f);	// it's a cube... all sides have the same length
 | ||
| 		smgr->setActiveCamera( camera );
 | ||
| 	}
 | ||
| 
 | ||
| 	/*
 | ||
| 		Add sphere-nodes which will be using the cubemaps as materials.
 | ||
| 		You may also want to experiment with other node-types here!
 | ||
| 	*/
 | ||
| 
 | ||
| 	scene::ISceneNode* sphereNode = 0;
 | ||
| 	scene::ISceneNode* sphereNode2 = 0;
 | ||
| 	scene::ISceneNode* sphereNode3 = 0;
 | ||
| 	scene::IMesh* sphereMesh = smgr->getGeometryCreator()->createSphereMesh(100.f);
 | ||
| 	if( sphereMesh )
 | ||
| 	{
 | ||
| 		// Nothing really special here except they need the shader material to display cubemaps.
 | ||
| 		sphereNode = smgr->addMeshSceneNode( sphereMesh );
 | ||
| 		sphereNode->setPosition( core::vector3df(-250,0,0) );
 | ||
| 		sphereNode->updateAbsolutePosition();
 | ||
| 		sphereNode->setMaterialFlag( video::EMF_LIGHTING, false );
 | ||
| 		sphereNode->setMaterialTexture( 0, dynamicCubeMapRTT );
 | ||
| 		sphereNode->setMaterialType( (video::E_MATERIAL_TYPE)cubeMapReflectionMaterial );
 | ||
| 		eventReceiver.addInfoTextNode(font, L"Cubemap dynamic rtt, no mip-maps", sphereNode);
 | ||
| 
 | ||
| 		if ( dynamicCubeMapTex )
 | ||
| 		{
 | ||
| 			sphereNode3 = smgr->addMeshSceneNode( sphereMesh );
 | ||
| 			sphereNode3->setPosition( core::vector3df(0,0,250) );
 | ||
| 			sphereNode3->updateAbsolutePosition();
 | ||
| 			sphereNode3->setMaterialFlag( video::EMF_LIGHTING, false );
 | ||
| 			sphereNode3->setMaterialTexture( 0, dynamicCubeMapTex );
 | ||
| 			sphereNode3->getMaterial(0).TextureLayer[0].TrilinearFilter = false; // this is default anyway. It would be faster - but you can only access integer mip-levels - no filtering between mip-levels.
 | ||
| 			sphereNode3->setMaterialType( (video::E_MATERIAL_TYPE)cubeMapReflectionMaterial );
 | ||
| 			eventReceiver.addInfoTextNode(font, L"Cubemap dynamic with mip-maps", sphereNode3);
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( cubeMapStaticTex )
 | ||
| 		{
 | ||
| 			sphereNode2 = smgr->addMeshSceneNode( sphereMesh );
 | ||
| 			sphereNode2->setPosition( core::vector3df(250,0,0) );
 | ||
| 			sphereNode2->updateAbsolutePosition();
 | ||
| 			sphereNode2->setMaterialFlag( video::EMF_LIGHTING, false );
 | ||
| 			sphereNode2->setMaterialTexture( 0, cubeMapStaticTex );
 | ||
| 			sphereNode2->getMaterial(0).TextureLayer[0].TrilinearFilter = true;		// this way smoothing happens between different mip-levels.
 | ||
| 			sphereNode2->setMaterialType( (video::E_MATERIAL_TYPE)cubeMapReflectionMaterial );
 | ||
| 			eventReceiver.addInfoTextNode(font, L"Cubemap fixed images", sphereNode2);
 | ||
| 		}
 | ||
| 
 | ||
| 		sphereMesh->drop();
 | ||
| 	}
 | ||
| 
 | ||
| 	/* Add some background which will show up in the environment maps.
 | ||
| 	   For first one we use the same textures as used in the spheres.
 | ||
| 	   Note the difference between a skybox and a cubemap is that the skybox really uses 6 different
 | ||
| 	   textures. While the cubemap uses a single texture created from 6 images. */
 | ||
| 	eventReceiver.BackgroundSkybox = smgr->addSkyBoxSceneNode(
 | ||
| 		driver->getTexture(mediaPath + "cubemap_posy.jpg"), // top
 | ||
| 		driver->getTexture(mediaPath + "cubemap_negy.jpg"),	// bottom
 | ||
| 		driver->getTexture(mediaPath + "cubemap_posz.jpg"),	// left
 | ||
| 		driver->getTexture(mediaPath + "cubemap_negz.jpg"), // right
 | ||
| 		driver->getTexture(mediaPath + "cubemap_posx.jpg"), // front
 | ||
| 		driver->getTexture(mediaPath + "cubemap_negx.jpg")); // back
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 	/* Another background for comparison and to make it more obvious
 | ||
| 	when the spheres reflect the environment and when they use static cubemaps. */
 | ||
| 	scene::IMesh * cubeMesh = smgr->getGeometryCreator()->createCubeMesh( core::vector3df(10.f, 10.f, 10.f), scene::ECMT_6BUF_4VTX_NP);
 | ||
| 	smgr->getMeshManipulator()->scale(cubeMesh, core::vector3df(-1, 1, 1));
 | ||
| 	if( cubeMesh )
 | ||
| 	{
 | ||
| 		smgr->getMeshManipulator()->setVertexColors( cubeMesh->getMeshBuffer(0), video::SColor(255, 240, 10, 10) );
 | ||
| 		smgr->getMeshManipulator()->setVertexColors( cubeMesh->getMeshBuffer(1), video::SColor(255, 240, 130, 10) );
 | ||
| 		smgr->getMeshManipulator()->setVertexColors( cubeMesh->getMeshBuffer(2), video::SColor(255, 50, 250, 10)  );
 | ||
| 		smgr->getMeshManipulator()->setVertexColors( cubeMesh->getMeshBuffer(3), video::SColor(255, 70, 10, 250) );
 | ||
| 		smgr->getMeshManipulator()->setVertexColors( cubeMesh->getMeshBuffer(4), video::SColor(255, 240, 250, 10) );
 | ||
| 		smgr->getMeshManipulator()->setVertexColors( cubeMesh->getMeshBuffer(5), video::SColor(255, 85, 250, 250)  );
 | ||
| 
 | ||
| 		eventReceiver.BackgroundCube = smgr->addMeshSceneNode( cubeMesh );
 | ||
| 		cubeMesh->drop();
 | ||
| 
 | ||
| 		eventReceiver.BackgroundCube->setScale( core::vector3df( 200, 200, 200 ) );
 | ||
| 		eventReceiver.BackgroundCube->setMaterialFlag( video::EMF_LIGHTING, false );
 | ||
| 		eventReceiver.BackgroundCube->setVisible(false);
 | ||
| 	}
 | ||
| 
 | ||
| #ifdef CUBEMAP_UPSIDE_DOWN_GL_PROJECTION
 | ||
| 	if ( driverType == video::EDT_OPENGL )
 | ||
| 	{
 | ||
| 		// Flip projection matrix (note this also flips front/backface culling)
 | ||
| 		core::matrix4 matProj = cubeMapCamera->getProjectionMatrix();
 | ||
| 		matProj[4] = -matProj[4];
 | ||
| 		matProj[5] = -matProj[5];
 | ||
| 		matProj[6] = -matProj[6];
 | ||
| 		matProj[7] = -matProj[7];
 | ||
| 		cubeMapCamera->setProjectionMatrix(matProj);
 | ||
| 	}
 | ||
| #endif
 | ||
| 
 | ||
| 	/*
 | ||
| 		Add some moving node to show the difference between static/dynamic environment maps
 | ||
| 	*/
 | ||
| 	scene::IMeshSceneNode * movingNode = smgr->addCubeSceneNode(30.f);
 | ||
| 	movingNode->getMaterial(0).Lighting = false;
 | ||
| 	smgr->getMeshManipulator()->setVertexColors( movingNode->getMesh()->getMeshBuffer(0), video::SColor(255, 230, 200, 150));
 | ||
| 	scene::ISceneNodeAnimator* circleAnimator = smgr->createFlyCircleAnimator(core::vector3df(-125, -50.f, 125), 300.f, 0.0005f);
 | ||
| 	movingNode->addAnimator(circleAnimator);
 | ||
| 	circleAnimator->drop();
 | ||
| 
 | ||
| 	/* Add some UI */
 | ||
| 	if ( eventReceiver.Shader )
 | ||
| 	{
 | ||
| 		skin->setColor(gui::EGDC_3D_FACE, video::SColor(50, 160, 120, 120));
 | ||
| 
 | ||
| 		u32 top = dimDevice.Height - 200;
 | ||
| 		const u32 left = dimDevice.Width - 350;
 | ||
| 		const u32 right = dimDevice.Width - 10;
 | ||
| 		irr::gui::IGUIStaticText * stextUVW = env->addStaticText(L" Style of generating texture coordinates:\n Change with (space)", core::recti(left, top, right, top+35), false, true, 0, -1, true);
 | ||
| 		top += 40;
 | ||
| 		stextUVW->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT);
 | ||
| 		eventReceiver.CurrentStyleUVW = env->addStaticText(L"", core::recti(240,0, 400, 20), false, false, stextUVW);
 | ||
| 		eventReceiver.updateStyleUVW();
 | ||
| 
 | ||
| 		irr::gui::IGUIStaticText * stextRoughness = env->addStaticText(L" Roughness:\n Change with (+) and (-)", core::recti(left, top, right, top+35), false, true, 0, -1, true);
 | ||
| 		top += 40;
 | ||
| 		eventReceiver.CurrentRoughness = env->addStaticText( L"", core::recti(240,0, 400, 20), false, false, stextRoughness);
 | ||
| 		eventReceiver.updateRoughness();
 | ||
| 
 | ||
| 		irr::gui::IGUIStaticText * stextSeamlessCupemap = env->addStaticText(L" Seamless cubemap (with roughness):\n Change with (s)", core::recti(left, top, right, top+35), false, true, 0, -1, true);
 | ||
| 		top += 40;
 | ||
| 		eventReceiver.CurrentSeamlessCubemap = env->addStaticText( L"", core::recti(240,0, 400, 20), false, false, stextSeamlessCupemap);
 | ||
| 		eventReceiver.updateSeamless();
 | ||
| 
 | ||
| 		irr::gui::IGUIStaticText * stextUpdates = env->addStaticText(L" Cubemap updates:\n Change with (u)", core::recti(left, top, right, top+35), false, true, 0, -1, true);
 | ||
| 		top += 40;
 | ||
| 		eventReceiver.CurrentCubemapUpdates = env->addStaticText( L"", core::recti(240,0, 400, 20), false, false, stextUpdates);
 | ||
| 		eventReceiver.updateCubemapUpdates();
 | ||
| 
 | ||
| 		env->addStaticText(L" Change background with (b)", core::recti(left, top, right, top+15), false, true, 0, -1, true);
 | ||
| 		top += 20;
 | ||
| 
 | ||
| 		env->addStaticText(L" Show/hide info nodes with (i)", core::recti(left, top, right, top+15), false, true, 0, -1, true);
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/* Main loop */
 | ||
| 	while(device->run())
 | ||
| 	{
 | ||
| 		if (device->isWindowActive())
 | ||
| 		{
 | ||
| 			driver->beginScene(true, true, video::SColor(255, 127, 127, 255));
 | ||
| 
 | ||
| 			/* Check if we want to update the environment maps.
 | ||
| 			   Usually not something you'll do every frame, but either once at the star
 | ||
| 			   or maybe updating an environment map once in a while.
 | ||
| 			*/
 | ||
| 			int updateCubemaps = eventReceiver.checkCubemapUpdate();
 | ||
| 			if( dynamicCubeMapRTT && sphereNode && updateCubemaps > 0 )
 | ||
| 			{
 | ||
| #ifdef CUBEMAP_UPSIDE_DOWN_GL_PROJECTION
 | ||
| 				core::array<scene::ISceneNode*> allNodes;
 | ||
| 				if ( driverType == video::EDT_OPENGL )
 | ||
| 				{
 | ||
| 					/*
 | ||
| 						Flipping projection matrix flips front/backface culling.
 | ||
| 						We only have a skybox so in this case this still would be fast, with more objects it's getting more ugly.
 | ||
| 					*/
 | ||
| 					smgr->getSceneNodesFromType(scene::ESNT_ANY, allNodes);
 | ||
| 					flipCullingFlags(allNodes);
 | ||
| 				}
 | ||
| #endif
 | ||
| 				/*
 | ||
| 					If rendered just once then this node has still a white (or even undefined) texture at this point
 | ||
| 					Just hiding it and render the background when rendering the cubemap for the other node is less noticable
 | ||
| 					than having a big white dot in the environment texture.
 | ||
| 					Render order can matter if you want several environment maps in your scene.
 | ||
| 				*/
 | ||
| 				if (sphereNode3)
 | ||
| 					sphereNode3->setVisible(false);
 | ||
| 
 | ||
| 				renderEnvironmentCubeMap(driver, cubeMapCamera, sphereNode, cubeMapRT, dynamicCubeMapRTT, depthStencilRTT);
 | ||
| 
 | ||
| 				if ( sphereNode3)
 | ||
| 				{
 | ||
| 					if ( updateCubemaps == 2 )
 | ||
| 					{
 | ||
| 						/*
 | ||
| 							Our rtt's unfortunately don't have mipmaps (sorry, not sure if we can get that somehow...)
 | ||
| 							So if we want mipmaps in the dynamic cubemap we have to copy it to a non-rtt texture.
 | ||
| 							Warning: Very, very slow. Far slower than just creating an environment map as this
 | ||
| 							will copy the texture from GPU to main memory - copy it to a new texture, create mip-maps and
 | ||
| 							upload the result back to the GPU.
 | ||
| 						 */
 | ||
| 						renderEnvironmentCubeMap(driver, cubeMapCamera, sphereNode3, cubeMapRT, dynamicCubeMapRTT_intermediate, depthStencilRTT);
 | ||
| 						for ( int i=0; i<6; ++i)
 | ||
| 						{
 | ||
| 							void * rtData = dynamicCubeMapRTT_intermediate->lock(video::ETLM_READ_ONLY, 0, i, video::ETLF_NONE);
 | ||
| 							void * tData = dynamicCubeMapTex->lock(video::ETLM_READ_WRITE, 0, i);
 | ||
| 							memcpy(tData, rtData, dynamicCubeMapTex->getPitch()*dynamicCubeMapTex->getSize().Width);
 | ||
| 							dynamicCubeMapRTT_intermediate->unlock();
 | ||
| 							dynamicCubeMapTex->unlock();
 | ||
| 							dynamicCubeMapTex->regenerateMipMapLevels();
 | ||
| 						}
 | ||
| 					}
 | ||
| 					sphereNode3->setVisible(true);
 | ||
| 				}
 | ||
| 
 | ||
| #ifdef CUBEMAP_UPSIDE_DOWN_GL_PROJECTION
 | ||
| 				if ( driverType == video::EDT_OPENGL )
 | ||
| 				{
 | ||
| 					flipCullingFlags(allNodes);
 | ||
| 				}
 | ||
| #endif
 | ||
| 			}
 | ||
| 
 | ||
| 			smgr->drawAll();
 | ||
| 			env->drawAll();
 | ||
| 
 | ||
| 			driver->endScene();
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	device->drop();
 | ||
| 
 | ||
| 	return 0;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
| **/
 |