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