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