a06f3f1ac3ac0a9592171a186729c38fabf41f51
[platform/core/uifw/dali-demo.git] / examples / deferred-shading / deferred-shading.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 #include "dali/dali.h"
18 #include "dali/public-api/actors/actor.h"
19 #include "dali/public-api/rendering/renderer.h"
20 #include <random>
21 #include <iostream>
22 #include <cstring>
23
24 using namespace Dali;
25
26 namespace
27 {
28 //=============================================================================
29 // Demonstrates deferred shading with multiple render targets (for color,
30 // position, and normal), a Phong lighting model and 32 point lights.
31 //
32 // Invoked with the --show-lights it will render a mesh at each light position.
33 //=============================================================================
34
35 #define QUOTE(x) DALI_COMPOSE_SHADER(x)
36
37 #define MAX_LIGHTS 32
38
39 #define DEFINE_MAX_LIGHTS "const int kMaxLights = " QUOTE(MAX_LIGHTS) ";"
40
41 #define DEFINE(x) "#define " DALI_COMPOSE_SHADER(x) DALI_COMPOSE_SHADER(\n)
42
43 //=============================================================================
44 // PRE-PASS
45 //=============================================================================
46 const char* const PREPASS_VSH = DALI_COMPOSE_SHADER(#version 300 es\n
47 precision mediump float;)
48   DALI_COMPOSE_SHADER(
49
50 // DALI uniforms
51 uniform mat4 uMvpMatrix;
52 uniform mat3 uNormalMatrix;
53 uniform vec3 uSize;
54
55 uniform vec3 uDepth_InvDepth_Near;\n)
56   DEFINE(DEPTH uDepth_InvDepth_Near.x)
57   DEFINE(INV_DEPTH uDepth_InvDepth_Near.y)
58   DEFINE(NEAR uDepth_InvDepth_Near.z)
59   DALI_COMPOSE_SHADER(
60
61 in vec3 aPosition;
62 in vec3 aNormal;
63
64 out vec4 vPosition;
65 out vec3 vNormal;
66
67 vec4 Map(vec4 v)    // projection space -> texture
68 {
69   return vec4(v.xyz / (2.f * v.w) + vec3(.5f), (v.w - NEAR) * INV_DEPTH);
70 }
71
72 void main()
73 {
74   vec4 position = uMvpMatrix * vec4(aPosition * uSize, 1.f);
75   vPosition = Map(position);
76   gl_Position = position;
77
78   vNormal = normalize(uNormalMatrix * aNormal);
79 });
80
81 //=============================================================================
82 const char* const PREPASS_FSH = DALI_COMPOSE_SHADER(#version 300 es\n
83 precision mediump float;
84
85 // DALI uniform
86 uniform vec4 uColor;
87
88 in vec4 vPosition;
89 in vec3 vNormal;
90
91 // These are our outputs.
92 layout(location = 0) out vec3 oNormal;
93 layout(location = 1) out vec4 oPosition;
94 layout(location = 2) out vec3 oColor;
95
96 void main()
97 {
98   oColor = uColor.rgb;
99   oPosition = vPosition;
100   oNormal = normalize(vNormal) * .5f + .5f;
101 });
102
103 //=============================================================================
104 // MAIN (LIGHTING) PASS
105 //=============================================================================
106 const char* const MAINPASS_VSH = DALI_COMPOSE_SHADER(#version 300 es\n
107 precision mediump float;
108
109 // DALI uniforms
110 uniform mat4 uMvpMatrix;
111 uniform vec3 uSize;
112
113 in vec3 aPosition;
114 in vec2 aTexCoord;
115
116 out vec2 vUv;
117
118 void main()
119 {
120   vec4 position = uMvpMatrix * vec4(aPosition * uSize, 1.f);
121   vUv = aTexCoord;
122
123   gl_Position = position;
124 });
125
126 //=============================================================================
127 const char* const MAINPASS_FSH = DALI_COMPOSE_SHADER(#version 300 es\n
128 precision mediump float;\n)
129   DEFINE_MAX_LIGHTS
130   DALI_COMPOSE_SHADER(
131
132 const float kAttenuationConst = .05f;
133 const float kAttenuationLinear = .1f;
134 const float kAttenuationQuadratic = .15f;
135
136 // G-buffer
137 uniform sampler2D uTextureNormal;
138 uniform sampler2D uTexturePosition;
139 uniform sampler2D uTextureColor;
140
141 uniform mat4 uInvProjection;
142
143 uniform vec3 uDepth_InvDepth_Near;\n)
144   DEFINE(DEPTH uDepth_InvDepth_Near.x)
145   DEFINE(INV_DEPTH uDepth_InvDepth_Near.y)
146   DEFINE(NEAR uDepth_InvDepth_Near.z)
147   DALI_COMPOSE_SHADER(
148
149 // Light source uniforms
150 struct Light
151 {
152   vec3 position;    // view space
153   float radius;
154   vec3 color;
155 };
156
157 uniform Light uLights[kMaxLights];
158
159 in vec2 vUv;
160
161 out vec4 oColor;
162
163 vec4 Unmap(vec4 m)  // texture -> projection
164 {
165   m.w = m.w * DEPTH + NEAR;
166   m.xyz = (m.xyz - vec3(.5)) * (2.f * m.w);
167   return m;
168 }
169
170 vec3 CalculateLighting(vec3 pos, vec3 normal)
171 {
172   vec3 viewDir = normalize(pos);
173   vec3 viewDirRefl = -reflect(viewDir, normal);
174
175   vec3 light = vec3(0.04f); // fake ambient term
176   for (int i = 0; i < kMaxLights; ++i)
177   {
178     vec3 rel = pos - uLights[i].position;
179     float distance = length(rel);
180     rel /= distance;
181
182     float a = uLights[i].radius / (kAttenuationConst + kAttenuationLinear * distance +
183       kAttenuationQuadratic * distance * distance);     // attenuation
184
185     float l = max(0.f, dot(normal, rel));   // lambertian
186     float s = pow(max(0.f, dot(viewDirRefl, rel)), 256.f);  // specular
187
188     light += (uLights[i].color * (l + s)) * a;
189   }
190
191   return light;
192 }
193
194 void main()
195 {
196   vec3 normSample = texture(uTextureNormal, vUv).xyz;
197   if (dot(normSample, normSample) == 0.f)
198   {
199     discard;  // if we didn't write this texel, don't bother lighting it.
200   }
201
202   vec3 normal = normalize(normSample - .5f);
203
204   vec4 posSample = texture(uTexturePosition, vUv);
205   vec3 pos = (uInvProjection * Unmap(posSample)).xyz;
206
207   vec3 color = texture(uTextureColor, vUv).rgb;
208   vec3 finalColor = color * CalculateLighting(pos, normal);
209
210   oColor = vec4(finalColor, 1.f);
211 });
212
213 //=============================================================================
214 // PRNG for floats.
215 struct FloatRand
216 {
217   std::random_device mDevice;
218   std::mt19937 mMersenneTwister;
219   std::uniform_real_distribution<float> mDistribution;
220
221   FloatRand()
222   : mMersenneTwister(mDevice()),
223     mDistribution(0., 1.)
224   {}
225
226   float operator()()
227   {
228     return mDistribution(mMersenneTwister);
229   }
230 };
231
232 //=============================================================================
233 float FastFloor(float x)
234 {
235   return static_cast<int>(x) - static_cast<int>(x < 0);
236 }
237
238 //=============================================================================
239 Vector3 FromHueSaturationLightness(Vector3 hsl)
240 {
241   Vector3 rgb;
242   if (hsl.y * hsl.y > 0.f)
243   {
244     if(hsl.x >= 360.f)
245     {
246       hsl.x -= 360.f;
247     }
248     hsl.x /= 60.f;
249
250     int i = FastFloor(hsl.x);
251     float ff = hsl.x - i;
252     float p = hsl.z * (1.0 - hsl.y);
253     float q = hsl.z * (1.0 - (hsl.y * ff));
254     float t = hsl.z * (1.0 - (hsl.y * (1.f - ff)));
255
256     switch (i)
257     {
258     case 0:
259       rgb.r = hsl.z;
260       rgb.g = t;
261       rgb.b = p;
262       break;
263
264     case 1:
265       rgb.r = q;
266       rgb.g = hsl.z;
267       rgb.b = p;
268       break;
269
270     case 2:
271       rgb.r = p;
272       rgb.g = hsl.z;
273       rgb.b = t;
274       break;
275
276     case 3:
277       rgb.r = p;
278       rgb.g = q;
279       rgb.b = hsl.z;
280       break;
281
282     case 4:
283       rgb.r = t;
284       rgb.g = p;
285       rgb.b = hsl.z;
286       break;
287
288     case 5:
289     default:
290       rgb.r = hsl.z;
291       rgb.g = p;
292       rgb.b = q;
293       break;
294     }
295   }
296   else
297   {
298     rgb = Vector3::ONE * hsl.z;
299   }
300
301   return rgb;
302 }
303
304 //=============================================================================
305 Geometry CreateTexturedQuadGeometry(bool flipV)
306 {
307   // Create geometry -- unit square with whole of the texture mapped to it.
308   struct Vertex
309   {
310     Vector3 aPosition;
311     Vector2 aTexCoord;
312   };
313
314   Vertex vertexData[] = {
315   { Vector3(-.5f, .5f, .0f), Vector2(.0f, 1.0f) },
316   { Vector3(.5f, .5f, .0f), Vector2(1.0f, 1.0f) },
317   { Vector3(-.5f, -.5f, .0f), Vector2(.0f, .0f) },
318   { Vector3(.5f, -.5f, .0f), Vector2(1.0f, .0f) },
319   };
320
321   if (flipV)
322   {
323     std::swap(vertexData[0].aTexCoord, vertexData[2].aTexCoord);
324     std::swap(vertexData[1].aTexCoord, vertexData[3].aTexCoord);
325   }
326
327   VertexBuffer vertexBuffer = VertexBuffer::New( Property::Map()
328     .Add( "aPosition", Property::VECTOR3 )
329     .Add( "aTexCoord", Property::VECTOR2 ) );
330   vertexBuffer.SetData( vertexData, std::extent<decltype(vertexData)>::value );
331
332   Geometry geometry = Geometry::New();
333   geometry.AddVertexBuffer( vertexBuffer );
334   geometry.SetType( Geometry::TRIANGLE_STRIP );
335   return geometry;
336 }
337
338 //=============================================================================
339 Geometry CreateOctahedron(bool invertNormals)
340 {
341   Vector3 positions[] = {
342     Vector3{ -1.f, 0.f, 0.f },
343     Vector3{ 1.f, 0.f, 0.f },
344     Vector3{ 0.f, -1.f, 0.f },
345     Vector3{ 0.f, 1.f, 0.f },
346     Vector3{ 0.f, 0.f, -1.f },
347     Vector3{ 0.f, 0.f, 1.f },
348   };
349
350   struct Vertex
351   {
352     Vector3 position;
353     Vector3 normal;
354   };
355   Vertex vertexData[] = {
356     { positions[0] },
357     { positions[3] },
358     { positions[5] },
359
360     { positions[5] },
361     { positions[3] },
362     { positions[1] },
363
364     { positions[1] },
365     { positions[3] },
366     { positions[4] },
367
368     { positions[4] },
369     { positions[3] },
370     { positions[0] },
371
372     { positions[0] },
373     { positions[5] },
374     { positions[2] },
375
376     { positions[5] },
377     { positions[1] },
378     { positions[2] },
379
380     { positions[1] },
381     { positions[4] },
382     { positions[2] },
383
384     { positions[4] },
385     { positions[0] },
386     { positions[2] },
387   };
388
389   // Calculate normals
390   for (uint32_t i = 0; i < std::extent<decltype(vertexData)>::value / 3; ++i)
391   {
392     uint32_t idx = i * 3;
393
394     Vector3 normal = (vertexData[idx + 2].position - vertexData[idx].position).
395         Cross(vertexData[idx + 1].position - vertexData[idx].position);
396     normal.Normalize();
397     normal *= invertNormals * 2.f - 1.f;
398
399     vertexData[idx++].normal = normal;
400     vertexData[idx++].normal = normal;
401     vertexData[idx].normal = normal;
402   }
403
404   // Configure property buffers and create geometry.
405   VertexBuffer vertexBuffer = VertexBuffer::New(Property::Map()
406     .Add("aPosition", Property::VECTOR3)
407     .Add("aNormal", Property::VECTOR3));
408   vertexBuffer.SetData(vertexData, std::extent<decltype(vertexData)>::value);
409
410   Geometry geometry = Geometry::New();
411   geometry.AddVertexBuffer( vertexBuffer );
412   geometry.SetType( Geometry::TRIANGLES );
413   return geometry;
414 }
415
416 //=============================================================================
417 enum RendererOptions
418 {
419   OPTION_NONE = 0x0,
420   OPTION_BLEND = 0x01,
421   OPTION_DEPTH_TEST = 0x02,
422   OPTION_DEPTH_WRITE = 0x04
423 };
424
425 Renderer CreateRenderer(TextureSet textures, Geometry geometry, Shader shader, uint32_t options = OPTION_NONE)
426 {
427   Renderer renderer = Renderer::New(geometry, shader);
428   renderer.SetProperty(Renderer::Property::BLEND_MODE,
429       (options & OPTION_BLEND) ? BlendMode::ON : BlendMode::OFF);
430   renderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE,
431       (options & OPTION_DEPTH_TEST) ? DepthTestMode::ON : DepthTestMode::OFF);
432   renderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE,
433       (options & OPTION_DEPTH_WRITE) ? DepthWriteMode::ON : DepthWriteMode::OFF);
434   renderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
435
436   if (!textures)
437   {
438     textures = TextureSet::New();
439   }
440
441   renderer.SetTextures(textures);
442   return renderer;
443 }
444
445 //=============================================================================
446 void CenterActor(Actor actor)
447 {
448   actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
449   actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
450 }
451
452 //=============================================================================
453 void RegisterDepthProperties(float depth, float near, Handle& h)
454 {
455   h.RegisterProperty("uDepth_InvDepth_Near", Vector3(depth, 1.f / depth, near));
456 }
457
458 }
459
460 //=============================================================================
461 /// Create a String whose size can be evaluated at compile time
462 struct ConstantString
463 {
464   const char * const string;
465   const uint16_t size;
466
467   template<uint16_t inputSize>
468   constexpr ConstantString(const char (&input)[inputSize])
469   : string(input),
470     size(inputSize)
471   {
472   }
473 };
474
475 constexpr ConstantString POSITION_STRING("position");
476 constexpr ConstantString RADIUS_STRING("radius");
477 constexpr ConstantString COLOR_STRING("color");
478 constexpr uint16_t LIGHT_SOURCE_BUFFER_SIZE(128u);
479
480 //=============================================================================
481 class DeferredShadingExample : public ConnectionTracker
482 {
483 public:
484   struct Options
485   {
486     enum
487     {
488       NONE = 0x0,
489       SHOW_LIGHTS = 0x1,
490     };
491   };
492
493   DeferredShadingExample(Application& app, uint32_t options = Options::NONE)
494   : mApp(app),
495     mOptions(options)
496   {
497     app.InitSignal().Connect( this, &DeferredShadingExample::Create );
498     app.TerminateSignal().Connect( this, &DeferredShadingExample::Destroy );
499   }
500
501 private:
502   void Create(Application& app)
503   {
504     // Grab window, configure layer
505     Window window = app.GetWindow();
506     auto rootLayer = window.GetRootLayer();
507     rootLayer.SetProperty( Layer::Property::BEHAVIOR, Layer::LAYER_3D );
508
509     Vector2 windowSize = window.GetSize();
510
511     float unit = windowSize.y / 24.f;
512
513     // Get camera - we'll be re-using the same old camera in the two passes.
514     RenderTaskList tasks = window.GetRenderTaskList();
515     CameraActor camera = tasks.GetTask(0).GetCameraActor();
516
517     auto zCameraPos = camera.GetProperty(Actor::Property::POSITION_Z).Get<float>();
518     camera.SetFarClippingPlane(zCameraPos + windowSize.y * .5f);
519     camera.SetNearClippingPlane(zCameraPos - windowSize.y * .5f);
520
521     const float zNear = camera.GetNearClippingPlane();
522     const float zFar = camera.GetFarClippingPlane();
523     const float depth = zFar - zNear;
524
525     // Create root of scene that shall be rendered off-screen.
526     auto sceneRoot = Actor::New();
527     CenterActor(sceneRoot);
528
529     mSceneRoot = sceneRoot;
530     window.Add(sceneRoot);
531
532     // Create an axis to spin our actors around.
533     auto axis = Actor::New();
534     CenterActor(axis);
535     sceneRoot.Add(axis);
536     mAxis = axis;
537
538     // Create an octahedral mesh for our main actors and to visualise the light sources.
539     Geometry mesh = CreateOctahedron(false);
540
541     // Create main actors
542     Shader preShader = Shader::New(PREPASS_VSH, PREPASS_FSH);
543     TextureSet noTexturesThanks = TextureSet::New();
544     Renderer meshRenderer = CreateRenderer(noTexturesThanks, mesh, preShader,
545         OPTION_DEPTH_TEST | OPTION_DEPTH_WRITE);
546     meshRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
547     RegisterDepthProperties(depth, zNear, meshRenderer);
548     float c = 1.f;
549     for (auto v: {
550       Vector3{ -c, -c, -c },
551       Vector3{ c, -c, -c },
552       Vector3{ -c, c, -c },
553       Vector3{ c, c, -c },
554       Vector3{ -c, -c, c },
555       Vector3{ c, -c, c },
556       Vector3{ -c, c, c },
557       Vector3{ c, c, c },
558
559       Vector3{ 0.f, -c, -c },
560       Vector3{ 0.f, c, -c },
561       Vector3{ 0.f, -c, c },
562       Vector3{ 0.f, c, c },
563
564       Vector3{ -c, 0.f, -c },
565       Vector3{ c, 0.f, -c },
566       Vector3{ -c, 0.f, c },
567       Vector3{ c, 0.f, c },
568
569       Vector3{ -c, -c, 0.f },
570       Vector3{ c, -c, 0.f },
571       Vector3{ -c, c, 0.f },
572       Vector3{ c, c, 0.f },
573     })
574     {
575       Actor a = Actor::New();
576       CenterActor(a);
577
578       Vector3 position{ v * unit * 5.f };
579       a.SetProperty( Actor::Property::POSITION, position );
580
581       float scale = (c + ((v.x + v.y + v.z) + c * 3.f) * .5f) / (c * 4.f);
582       Vector3 size{ Vector3::ONE * scale * unit * 2.f };
583       a.SetProperty( Actor::Property::SIZE, size);
584
585       a.SetProperty( Actor::Property::COLOR,Color::WHITE * .25f +
586           (Color::RED * (v.x + c) / (c * 2.f) +
587           Color::GREEN * (v.y + c) / (c * 2.f) +
588           Color::BLUE * (v.z + c) / (c * 2.f)) * .015625f);
589       a.AddRenderer(meshRenderer);
590
591       axis.Add(a);
592     }
593
594     // Create off-screen textures, fbo and render task.
595     uint32_t width = static_cast<uint32_t>(windowSize.x);
596     uint32_t height = static_cast<uint32_t>(windowSize.y);
597
598     Texture rttNormal = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGB888,
599         width, height);
600     Texture rttPosition = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGBA8888,
601         width, height);
602     Texture rttColor = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGB888,
603         width, height);
604     FrameBuffer fbo = FrameBuffer::New(width, height, FrameBuffer::Attachment::DEPTH);
605     fbo.AttachColorTexture(rttNormal);
606     fbo.AttachColorTexture(rttPosition);
607     fbo.AttachColorTexture(rttColor);
608
609     RenderTask sceneRender = tasks.CreateTask();
610     sceneRender.SetViewportSize(windowSize);
611     sceneRender.SetFrameBuffer(fbo);
612     sceneRender.SetCameraActor(camera);
613     sceneRender.SetSourceActor(sceneRoot);
614     sceneRender.SetInputEnabled(false);
615     sceneRender.SetCullMode(false);
616     sceneRender.SetClearEnabled(true);
617     sceneRender.SetClearColor(Color::BLACK);
618     sceneRender.SetExclusive(true);
619
620     mSceneRender = sceneRender;
621
622     // Create final image for deferred shading
623     auto finalImage = Actor::New();
624     CenterActor(finalImage);
625     finalImage.SetProperty( Actor::Property::SIZE, windowSize);
626
627     TextureSet finalImageTextures = TextureSet::New();
628     finalImageTextures.SetTexture(0, rttNormal);
629     finalImageTextures.SetTexture(1, rttPosition);
630     finalImageTextures.SetTexture(2, rttColor);
631
632     Sampler sampler = Sampler::New();
633     sampler.SetFilterMode(FilterMode::NEAREST, FilterMode::NEAREST);
634     finalImageTextures.SetSampler(0, sampler);
635     finalImageTextures.SetSampler(1, sampler);
636     finalImageTextures.SetSampler(2, sampler);
637
638     Shader shdMain = Shader::New(MAINPASS_VSH, MAINPASS_FSH);
639     Geometry finalImageGeom = CreateTexturedQuadGeometry(true);
640     Renderer finalImageRenderer = CreateRenderer(finalImageTextures, finalImageGeom, shdMain);
641     RegisterDepthProperties(depth, zNear, finalImageRenderer);
642
643     auto propInvProjection = finalImageRenderer.RegisterProperty("uInvProjection", Matrix::IDENTITY);
644     Constraint cnstrInvProjection = Constraint::New<Matrix>(finalImageRenderer, propInvProjection,
645       [zCameraPos, zNear, depth](Matrix& output, const PropertyInputContainer& input) {
646         output = input[0]->GetMatrix();
647         DALI_ASSERT_ALWAYS(output.Invert() && "Failed to invert projection matrix.");
648       });
649     cnstrInvProjection.AddSource(Source(camera, CameraActor::Property::PROJECTION_MATRIX));
650     cnstrInvProjection.AddSource(Source(camera, CameraActor::Property::VIEW_MATRIX));
651     cnstrInvProjection.Apply();
652
653     finalImage.AddRenderer(finalImageRenderer);
654
655     mFinalImage = finalImage;
656     window.Add(finalImage);
657
658     // Create a node for our lights
659     auto lights = Actor::New();
660     CenterActor(lights);
661     sceneRoot.Add(lights);
662
663     // Create Lights
664     const bool showLights = mOptions & Options::SHOW_LIGHTS;
665     Renderer lightRenderer;
666     if (showLights)
667     {
668       Geometry lightMesh = CreateOctahedron(true);
669       lightRenderer = CreateRenderer(noTexturesThanks, lightMesh, preShader,
670           OPTION_DEPTH_TEST | OPTION_DEPTH_WRITE);
671       lightRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::FRONT);
672     }
673
674     Vector3 lightPos{ unit * 12.f, 0.f, 0.f };
675     float theta = M_PI * 2.f / MAX_LIGHTS;
676     float cosTheta = std::cos(theta);
677     float sinTheta = std::sin(theta);
678     for (int i = 0; i < MAX_LIGHTS; ++i)
679     {
680       Vector3 color = FromHueSaturationLightness(Vector3((360.f * i) / MAX_LIGHTS, .5f, 1.f));
681
682       Actor light = CreateLight(lightPos * (1 + (i % 8)) / 8.f, unit * 16.f, color, camera, finalImageRenderer);
683
684       float z = (((i & 1) << 1) - 1) * unit * 8.f;
685       lightPos = Vector3(cosTheta * lightPos.x - sinTheta * lightPos.y, sinTheta * lightPos.x + cosTheta * lightPos.y, z);
686
687       if (showLights)
688       {
689         light.SetProperty(Actor::Property::SIZE, Vector3::ONE * unit / 8.f);
690         light.AddRenderer(lightRenderer);
691       }
692
693       lights.Add(light);
694     }
695
696     // Take them for a spin.
697     Animation animLights = Animation::New(40.f);
698     animLights.SetLooping(true);
699     animLights.AnimateBy(Property(lights, Actor::Property::ORIENTATION), Quaternion(Radian(M_PI * 2.f), Vector3::YAXIS));
700     animLights.Play();
701
702     // Event handling
703     window.KeyEventSignal().Connect(this, &DeferredShadingExample::OnKeyEvent);
704
705     mPanDetector = PanGestureDetector::New();
706     mPanDetector.DetectedSignal().Connect(this, &DeferredShadingExample::OnPan);
707     mPanDetector.Attach(window.GetRootLayer());
708   }
709
710   void Destroy(Application& app)
711   {
712     app.GetWindow().GetRenderTaskList().RemoveTask(mSceneRender);
713     mSceneRender.Reset();
714
715     UnparentAndReset(mSceneRoot);
716     UnparentAndReset(mFinalImage);
717   }
718
719   Actor CreateLight(Vector3 position, float radius, Vector3 color, CameraActor camera, Renderer renderer)
720   {
721     Actor light = Actor::New();
722     CenterActor(light);
723     light.SetProperty(Actor::Property::COLOR, Color::WHITE);
724     light.SetProperty(Actor::Property::POSITION, position);
725
726     auto iPropRadius = light.RegisterProperty("radius", radius);
727     auto iPropLightColor = light.RegisterProperty("lightcolor", color);
728
729     // Create light source uniforms on lighting shader.
730     char buffer[LIGHT_SOURCE_BUFFER_SIZE];
731     char* writep = buffer + snprintf(buffer, LIGHT_SOURCE_BUFFER_SIZE, "uLights[%d].", mNumLights);
732     ++mNumLights;
733
734     strncpy(writep, POSITION_STRING.string, POSITION_STRING.size);
735     auto oPropLightPos = renderer.RegisterProperty(buffer, position);
736
737     strncpy(writep, RADIUS_STRING.string, RADIUS_STRING.size);
738     auto oPropLightRadius = renderer.RegisterProperty(buffer, radius);
739
740     strncpy(writep, COLOR_STRING.string, COLOR_STRING.size);
741     auto oPropLightColor = renderer.RegisterProperty(buffer, color);
742
743     // Constrain the light position, radius and color to lighting shader uniforms.
744     // Convert light position to view space;
745     Constraint cLightPos = Constraint::New<Vector3>(renderer, oPropLightPos, [](Vector3& output, const PropertyInputContainer& input)
746     {
747       Vector4 worldPos(input[0]->GetVector3());
748       worldPos.w = 1.f;
749
750       worldPos = input[1]->GetMatrix() * worldPos;
751       output = Vector3(worldPos);
752     });
753     cLightPos.AddSource(Source(light, Actor::Property::WORLD_POSITION));
754     cLightPos.AddSource(Source(camera, CameraActor::Property::VIEW_MATRIX));
755     cLightPos.Apply();
756
757     Constraint cLightRadius = Constraint::New<float>(renderer, oPropLightRadius,
758         EqualToConstraint());
759     cLightRadius.AddSource(Source(light, iPropRadius));
760     cLightRadius.Apply();
761
762     Constraint cLightColor = Constraint::New<Vector3>(renderer, oPropLightColor,
763         EqualToConstraint());
764     cLightColor.AddSource(Source(light, iPropLightColor));
765     cLightColor.Apply();
766
767     return light;
768   }
769
770   void OnPan(Actor, PanGesture const& gesture)
771   {
772     Quaternion q = mAxis.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>();
773     Quaternion qx(Radian(Degree(gesture.screenDisplacement.y) * -.5f), Vector3::XAXIS);
774     Quaternion qy(Radian(Degree(gesture.screenDisplacement.x) * .5f), Vector3::YAXIS);
775     mAxis.SetProperty(Actor::Property::ORIENTATION, qy * qx * q);
776   }
777
778   void OnKeyEvent(const KeyEvent& event)
779   {
780     if(event.state == KeyEvent::Down)
781     {
782       if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
783       {
784         mApp.Quit();
785       }
786     }
787   }
788
789   Application& mApp;
790   uint32_t mOptions;
791
792   Actor mSceneRoot;
793   Actor mAxis;
794
795   RenderTask mSceneRender;
796   Actor mFinalImage;
797
798   int mNumLights = 0;
799
800   PanGestureDetector mPanDetector;
801 };
802
803
804 int main(int argc, char** argv)
805 {
806   const bool showLights = [](int argc, char** argv)
807   {
808     auto endArgs = argv + argc;
809     return std::find_if(argv, endArgs, [](const char* arg)
810     {
811       return strcmp(arg, "--show-lights") == 0;
812     }) != endArgs;
813   }(argc, argv);
814
815   Application app = Application::New(&argc, &argv);
816   DeferredShadingExample example(app, (showLights ? DeferredShadingExample::Options::SHOW_LIGHTS : 0));
817   app.MainLoop();
818   return 0;
819 }