6440bb04478f8e5b3f3f89df90dad72022834bf1
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / shadow-view / shadow-view-impl.cpp
1 /*
2  * Copyright (c) 2021 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
18 // CLASS HEADER
19 #include "shadow-view-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/common/stage.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/animation/constraint.h>
25 #include <dali/public-api/object/type-registry-helper.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/public-api/render-tasks/render-task-list.h>
28 #include <dali/public-api/rendering/shader.h>
29 #include <iomanip>
30 #include <sstream>
31
32 // INTERNAL INCLUDES
33 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
34 #include <dali-toolkit/internal/controls/control/control-renderers.h>
35 #include <dali-toolkit/internal/controls/shadow-view/shadow-view-impl.h>
36 #include <dali-toolkit/internal/filters/blur-two-pass-filter.h>
37 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
38 #include <dali-toolkit/public-api/visuals/visual-properties.h>
39
40 // TODO:
41 // pixel format / size - set from JSON
42 // aspect ratio property needs to be able to be constrained also for cameras. (now do-able)
43 // default near clip value
44
45 /////////////////////////////////////////////////////////
46 // IMPLEMENTATION NOTES
47
48 // As the ShadowView actor changes size, the amount of pixels we need to blur changes. Therefore we need some way of doing this. However:-
49 // OnSetSize() does not get called when ShadowView object size is modified using a Constraint.
50 // OnSizeAnimation() only gets called once per AnimateTo/By() and if an Animation has N such calls then only the final one will end up being used. Therefore we can't use
51 // OnSizeAnimation() to alter render target sizes.
52 // To get around the above problems, we use fixed sized render targets, from the last SetSize() call (which calls OnSetSize()), then we adjust the internal cameras / actors
53 // to take account of the changed ShadowView object size, projecting to the unchanged render target sizes. This is done relative to the fixed render target / actor sizes
54 // by using constraints relative to the ShadowView actor size.
55
56 namespace Dali
57 {
58 namespace Toolkit
59 {
60 namespace Internal
61 {
62 namespace
63 {
64 using namespace Dali;
65
66 BaseHandle Create()
67 {
68   return Toolkit::ShadowView::New();
69 }
70
71 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::ShadowView, Toolkit::Control, Create)
72 DALI_TYPE_REGISTRATION_END()
73
74 const float BLUR_STRENGTH_DEFAULT = 1.0f;
75
76 const Vector3 DEFAULT_LIGHT_POSITION(300.0f, 250.0f, 600.0f);
77 const float   DEFAULT_FIELD_OF_VIEW_RADIANS = Math::PI / 4.0f; // 45 degrees
78
79 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.8f);
80
81 const char* const SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME = "uLightCameraProjectionMatrix";
82 const char* const SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME       = "uLightCameraViewMatrix";
83 const char* const SHADER_SHADOW_COLOR_PROPERTY_NAME                   = "uShadowColor";
84 const char* const BLUR_STRENGTH_PROPERTY_NAME                         = "BlurStrengthProperty";
85 const char* const SHADOW_COLOR_PROPERTY_NAME                          = "ShadowColorProperty";
86
87 } // namespace
88
89 ShadowView::ShadowView(float downsampleWidthScale, float downsampleHeightScale)
90 : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)),
91   mChildrenRoot(Actor::New()),
92   mCachedShadowColor(DEFAULT_SHADOW_COLOR),
93   mCachedBackgroundColor(DEFAULT_SHADOW_COLOR.r, DEFAULT_SHADOW_COLOR.g, DEFAULT_SHADOW_COLOR.b, 0.0f),
94   mBlurStrengthPropertyIndex(Property::INVALID_INDEX),
95   mShadowColorPropertyIndex(Property::INVALID_INDEX),
96   mDownsampleWidthScale(downsampleWidthScale),
97   mDownsampleHeightScale(downsampleHeightScale)
98 {
99 }
100
101 ShadowView::~ShadowView()
102 {
103 }
104
105 Toolkit::ShadowView ShadowView::New(float downsampleWidthScale, float downsampleHeightScale)
106 {
107   ShadowView* impl = new ShadowView(downsampleWidthScale, downsampleHeightScale);
108
109   Dali::Toolkit::ShadowView handle = Dali::Toolkit::ShadowView(*impl);
110
111   // Second-phase init of the implementation
112   // This can only be done after the CustomActor connection has been made...
113   impl->Initialize();
114
115   return handle;
116 }
117
118 void ShadowView::SetShadowPlaneBackground(Actor shadowPlaneBackground)
119 {
120   mShadowPlaneBg = shadowPlaneBackground;
121
122   mShadowPlane = Actor::New();
123   mShadowPlane.SetProperty(Actor::Property::NAME, "SHADOW_PLANE");
124   mShadowPlane.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
125   mShadowPlane.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
126   Renderer   shadowRenderer = CreateRenderer(SHADER_SHADOW_VIEW_RENDER_SHADER_VERT, SHADER_SHADOW_VIEW_RENDER_SHADER_FRAG, Shader::Hint::OUTPUT_IS_TRANSPARENT, Uint16Pair(20, 20));
127   TextureSet textureSet     = shadowRenderer.GetTextures();
128   textureSet.SetTexture(0u, mOutputFrameBuffer.GetColorTexture());
129   mShadowPlane.AddRenderer(shadowRenderer);
130
131   SetShaderConstants();
132
133   // Rather than parent the shadow plane drawable and have constraints to move it to the same
134   // position, instead parent the shadow plane drawable on the shadow plane passed in.
135   mShadowPlaneBg.Add(mShadowPlane);
136   mShadowPlane.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
137   mShadowPlane.SetProperty(Actor::Property::POSITION_Z, 1.0f);
138
139   ConstrainCamera();
140
141   mShadowPlane.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
142
143   mBlurRootActor.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
144 }
145
146 void ShadowView::SetPointLight(Actor pointLight)
147 {
148   mPointLight = pointLight;
149
150   ConstrainCamera();
151 }
152
153 void ShadowView::SetPointLightFieldOfView(float fieldOfView)
154 {
155   mCameraActor.SetFieldOfView(fieldOfView);
156 }
157
158 void ShadowView::SetShadowColor(Vector4 color)
159 {
160   mCachedShadowColor       = color;
161   mCachedBackgroundColor.r = color.r;
162   mCachedBackgroundColor.g = color.g;
163   mCachedBackgroundColor.b = color.b;
164
165   if(mShadowPlane)
166   {
167     mShadowPlane.SetProperty(mShadowColorPropertyIndex, mCachedShadowColor);
168   }
169   if(mRenderSceneTask)
170   {
171     mRenderSceneTask.SetClearColor(mCachedBackgroundColor);
172   }
173 }
174
175 void ShadowView::Activate()
176 {
177   DALI_ASSERT_ALWAYS(Self().GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE) && "ShadowView should be on stage before calling Activate()\n");
178
179   // make sure resources are allocated and start the render tasks processing
180   CreateRenderTasks();
181 }
182
183 void ShadowView::Deactivate()
184 {
185   DALI_ASSERT_ALWAYS(Self().GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE) && "ShadowView should be on stage before calling Deactivate()\n")
186
187   // stop render tasks processing
188   // Note: render target resources are automatically freed since we set the Image::Unused flag
189   RemoveRenderTasks();
190 }
191
192 ///////////////////////////////////////////////////////////
193 //
194 // Private methods
195 //
196
197 void ShadowView::OnInitialize()
198 {
199   // root actor to parent all user added actors. Used as source actor for shadow render task.
200   mChildrenRoot.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
201   mChildrenRoot.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
202
203   Vector2 stageSize = Stage::GetCurrent().GetSize();
204   mCameraActor      = CameraActor::New(stageSize);
205
206   mCameraActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
207
208   // Target is constrained to point at the shadow plane origin
209   mCameraActor.SetNearClippingPlane(1.0f);
210   mCameraActor.SetType(Dali::Camera::FREE_LOOK); // Camera orientation constrained to point at shadow plane world position
211   mCameraActor.SetProperty(Actor::Property::ORIENTATION, Quaternion(Radian(Degree(180)), Vector3::YAXIS));
212   mCameraActor.SetProperty(Actor::Property::POSITION, DEFAULT_LIGHT_POSITION);
213
214   // Create render targets needed for rendering from light's point of view
215   mSceneFromLightRenderTarget = FrameBuffer::New(stageSize.width, stageSize.height, FrameBuffer::Attachment::NONE);
216   Texture textureFromLight    = Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, unsigned(stageSize.width), unsigned(stageSize.height));
217   mSceneFromLightRenderTarget.AttachColorTexture(textureFromLight);
218
219   mOutputFrameBuffer    = FrameBuffer::New(stageSize.width * 0.5f, stageSize.height * 0.5f, FrameBuffer::Attachment::NONE);
220   Texture outputTexture = Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, unsigned(stageSize.width * 0.5f), unsigned(stageSize.height * 0.5f));
221   mOutputFrameBuffer.AttachColorTexture(outputTexture);
222
223   //////////////////////////////////////////////////////
224   // Connect to actor tree
225
226   Self().Add(mChildrenRoot);
227   Stage::GetCurrent().Add(mCameraActor);
228
229   mBlurFilter.SetRefreshOnDemand(false);
230   mBlurFilter.SetInputTexture(mSceneFromLightRenderTarget.GetColorTexture());
231   mBlurFilter.SetOutputFrameBuffer(mOutputFrameBuffer);
232   mBlurFilter.SetSize(stageSize * 0.5f);
233   mBlurFilter.SetPixelFormat(Pixel::RGBA8888);
234
235   mBlurRootActor = Actor::New();
236   mBlurRootActor.SetProperty(Actor::Property::NAME, "BLUR_ROOT_ACTOR");
237
238   // Turn off inheritance to ensure filter renders properly
239   mBlurRootActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
240   mBlurRootActor.SetProperty(Actor::Property::INHERIT_POSITION, false);
241   mBlurRootActor.SetProperty(Actor::Property::INHERIT_ORIENTATION, false);
242   mBlurRootActor.SetProperty(Actor::Property::INHERIT_SCALE, false);
243   mBlurRootActor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_COLOR);
244
245   Self().Add(mBlurRootActor);
246
247   mBlurFilter.SetRootActor(mBlurRootActor);
248   mBlurFilter.SetBackgroundColor(Vector4::ZERO);
249
250   CustomActor self = Self();
251   // Register a property that the user can use to control the blur in the internal object
252   mBlurStrengthPropertyIndex = self.RegisterProperty(BLUR_STRENGTH_PROPERTY_NAME, BLUR_STRENGTH_DEFAULT);
253
254   Constraint blurStrengthConstraint = Constraint::New<float>(mBlurFilter.GetHandleForAnimateBlurStrength(), mBlurFilter.GetBlurStrengthPropertyIndex(), EqualToConstraint());
255   blurStrengthConstraint.AddSource(Source(self, mBlurStrengthPropertyIndex));
256   blurStrengthConstraint.Apply();
257
258   DevelControl::SetAccessibilityConstructor(Self(), [](Dali::Actor actor) {
259     return std::unique_ptr<Dali::Accessibility::Accessible>(
260       new Control::Impl::AccessibleImpl(actor, Dali::Accessibility::Role::FILLER));
261   });
262 }
263
264 void ShadowView::OnChildAdd(Actor& child)
265 {
266   if(child != mChildrenRoot && child != mBlurRootActor)
267   {
268     mChildrenRoot.Add(child);
269   }
270
271   Control::OnChildAdd(child);
272 }
273
274 void ShadowView::OnChildRemove(Actor& child)
275 {
276   mChildrenRoot.Remove(child);
277
278   Control::OnChildRemove(child);
279 }
280
281 void ShadowView::ConstrainCamera()
282 {
283   if(mPointLight && mShadowPlane)
284   {
285     // Constrain camera to look directly at center of shadow plane. (mPointLight position
286     // is under control of application, can't use transform inheritance)
287
288     Constraint cameraOrientationConstraint = Constraint::New<Quaternion>(mCameraActor, Actor::Property::ORIENTATION, &LookAt);
289     cameraOrientationConstraint.AddSource(Source(mShadowPlane, Actor::Property::WORLD_POSITION));
290     cameraOrientationConstraint.AddSource(Source(mPointLight, Actor::Property::WORLD_POSITION));
291     cameraOrientationConstraint.AddSource(Source(mShadowPlane, Actor::Property::WORLD_ORIENTATION));
292     cameraOrientationConstraint.Apply();
293
294     Constraint pointLightPositionConstraint = Constraint::New<Vector3>(mCameraActor, Actor::Property::POSITION, EqualToConstraint());
295     pointLightPositionConstraint.AddSource(Source(mPointLight, Actor::Property::WORLD_POSITION));
296     pointLightPositionConstraint.Apply();
297   }
298 }
299
300 void ShadowView::CreateRenderTasks()
301 {
302   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
303
304   // We want the first task to render the scene from the light
305   mRenderSceneTask = taskList.CreateTask();
306
307   mRenderSceneTask.SetCameraActor(mCameraActor);
308   mRenderSceneTask.SetSourceActor(mChildrenRoot);
309   mRenderSceneTask.SetFrameBuffer(mSceneFromLightRenderTarget);
310   mRenderSceneTask.SetInputEnabled(false);
311   mRenderSceneTask.SetClearEnabled(true);
312
313   // background color for render task should be the shadow color, but with alpha 0
314   // we don't want to blend the edges of the content with a BLACK at alpha 0, but
315   // the same shadow color at alpha 0.
316   mRenderSceneTask.SetClearColor(mCachedBackgroundColor);
317
318   mBlurFilter.Enable();
319 }
320
321 void ShadowView::RemoveRenderTasks()
322 {
323   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
324
325   taskList.RemoveTask(mRenderSceneTask);
326   mRenderSceneTask.Reset();
327
328   mBlurFilter.Disable();
329 }
330
331 void ShadowView::SetShaderConstants()
332 {
333   Property::Index lightCameraProjectionMatrixPropertyIndex = mShadowPlane.RegisterProperty(SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME, Matrix::IDENTITY);
334   Constraint      projectionMatrixConstraint               = Constraint::New<Dali::Matrix>(mShadowPlane, lightCameraProjectionMatrixPropertyIndex, EqualToConstraint());
335   projectionMatrixConstraint.AddSource(Source(mCameraActor, CameraActor::Property::PROJECTION_MATRIX));
336   projectionMatrixConstraint.Apply();
337
338   Property::Index lightCameraViewMatrixPropertyIndex = mShadowPlane.RegisterProperty(SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME, Matrix::IDENTITY);
339   Constraint      viewMatrixConstraint               = Constraint::New<Dali::Matrix>(mShadowPlane, lightCameraViewMatrixPropertyIndex, EqualToConstraint());
340   viewMatrixConstraint.AddSource(Source(mCameraActor, CameraActor::Property::VIEW_MATRIX));
341   viewMatrixConstraint.Apply();
342
343   mShadowColorPropertyIndex = mShadowPlane.RegisterProperty(SHADER_SHADOW_COLOR_PROPERTY_NAME, mCachedShadowColor);
344 }
345
346 } // namespace Internal
347
348 } // namespace Toolkit
349
350 } // namespace Dali