1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
|
/** 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° 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;
}
/*
**/
|