fbc2e1c7b5e0cf91edc4efe5c83b9f6de3fc228e
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / shadow-view / shadow-view-impl.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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 // CLASS HEADER
18 #include "shadow-view-impl.h"
19
20 // EXTERNAL INCLUDES
21 #include <sstream>
22 #include <iomanip>
23
24 // INTERNAL INCLUDES
25 #include <dali-toolkit/internal/controls/shadow-view/shadow-view-impl.h>
26 #include <dali-toolkit/internal/filters/blur-two-pass-filter.h>
27 #include <dali/integration-api/debug.h>
28
29 // TODO:
30 // pixel format / size - set from JSON
31 // aspect ratio property needs to be able to be constrained also for cameras. (now do-able)
32 // default near clip value
33 // mChildrenRoot Add()/Remove() overloads - better solution
34
35
36 /////////////////////////////////////////////////////////
37 // IMPLEMENTATION NOTES
38
39 // As the ShadowView actor changes size, the amount of pixels we need to blur changes. Therefore we need some way of doing this. However:-
40 // OnSetSize() does not get called when ShadowView object size is modified using a Constraint.
41 // 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
42 // OnSizeAnimation() to alter render target sizes.
43 // 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
44 // 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
45 // by using constraints relative to the ShadowView actor size.
46
47 namespace Dali
48 {
49
50 namespace Toolkit
51 {
52
53 namespace Internal
54 {
55
56 namespace
57 {
58
59 using namespace Dali;
60
61 BaseHandle Create()
62 {
63   return Toolkit::ShadowView::New();
64 }
65
66 TypeRegistration mType( typeid(Toolkit::ShadowView), typeid(Toolkit::Control), Create );
67
68
69 const float BLUR_STRENGTH_DEFAULT = 1.0f;
70
71 const Vector3 DEFAULT_LIGHT_POSITION(300.0f, 250.0f, 600.0f);
72 const float DEFAULT_FIELD_OF_VIEW_RADIANS = Math::PI / 4.0f; // 45 degrees
73
74 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.8f);
75
76 const std::string SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME( "uLightCameraProjectionMatrix" );
77 const std::string SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME( "uLightCameraViewMatrix" );
78 const std::string SHADER_SHADOW_COLOR_PROPERTY_NAME( "uShadowColor" );
79
80 const std::string BLUR_STRENGTH_PROPERTY_NAME( "BlurStrengthProperty" );
81 const std::string SHADOW_COLOR_PROPERTY_NAME( "ShadowColorProperty" );
82
83 const char* const RENDER_SHADOW_VERTEX_SOURCE =
84   " uniform mediump mat4 uLightCameraProjectionMatrix;\n"
85   " uniform mediump mat4 uLightCameraViewMatrix;\n"
86   "\n"
87   "void main()\n"
88   "{\n"
89     "  gl_Position = uProjection * uModelView * vec4(aPosition,1.0);\n"
90     "  vec4 textureCoords = uLightCameraProjectionMatrix * uLightCameraViewMatrix * uModelMatrix  * vec4(aPosition,1.0);\n"
91     "  vTexCoord = 0.5 + 0.5 * (textureCoords.xy/textureCoords.w);\n"
92   "}\n";
93
94 const char* const RENDER_SHADOW_FRAGMENT_SOURCE =
95   "uniform lowp vec4 uShadowColor;\n"
96   "void main()\n"
97   "{\n"
98   "  lowp float alpha;\n"
99   "  alpha = texture2D(sTexture, vec2(vTexCoord.x, vTexCoord.y)).a;\n"
100   "  gl_FragColor = vec4(uShadowColor.rgb, uShadowColor.a * alpha);\n"
101   "}\n";
102
103 // TODO: Add this to dali-core constraints.h
104 /**
105  * EqualToConstraintMatrix
106  *
107  * f(current, property) = property
108  */
109 struct EqualToConstraintMatrix
110 {
111   EqualToConstraintMatrix(){}
112
113   Dali::Matrix operator()(const Dali::Matrix& current, const PropertyInput& property) {return property.GetMatrix();}
114 };
115
116 } // namespace
117
118 ShadowView::ShadowView( float downsampleWidthScale, float downsampleHeightScale )
119 : ControlImpl( false ),  // doesn't require touch events
120   mChildrenRoot(Actor::New()),
121   mCachedShadowColor(DEFAULT_SHADOW_COLOR),
122   mCachedBackgroundColor(DEFAULT_SHADOW_COLOR.r, DEFAULT_SHADOW_COLOR.g, DEFAULT_SHADOW_COLOR.b, 0.0f),
123   mBlurStrengthPropertyIndex(Property::INVALID_INDEX),
124   mShadowColorPropertyIndex(Property::INVALID_INDEX),
125   mDownsampleWidthScale(downsampleWidthScale),
126   mDownsampleHeightScale(downsampleHeightScale)
127 {
128 }
129
130 ShadowView::~ShadowView()
131 {
132 }
133
134 Toolkit::ShadowView ShadowView::New(float downsampleWidthScale, float downsampleHeightScale)
135 {
136   ShadowView* impl = new ShadowView(downsampleWidthScale, downsampleHeightScale);
137
138   Dali::Toolkit::ShadowView handle = Dali::Toolkit::ShadowView( *impl );
139
140   // Second-phase init of the implementation
141   // This can only be done after the CustomActor connection has been made...
142   impl->Initialize();
143
144   return handle;
145 }
146
147 /////////////////////////////////////////////////////////////
148 // for creating a subtree for all user added child actors.
149 // TODO: overloading Actor::Add()/Remove() not nice since breaks polymorphism. Need another method to pass ownership of added child actors to our internal actor root.
150 void ShadowView::Add(Actor child)
151 {
152   mChildrenRoot.Add(child);
153 }
154
155 void ShadowView::Remove(Actor child)
156 {
157   mChildrenRoot.Remove(child);
158 }
159
160 void ShadowView::SetShadowPlane(Actor shadowPlane)
161 {
162   mShadowPlaneBg = shadowPlane;
163
164   mShadowPlane = ImageActor::New();
165   mShadowPlane.SetParentOrigin(ParentOrigin::CENTER);
166   mShadowPlane.SetAnchorPoint(AnchorPoint::CENTER);
167
168   mShadowPlane.SetImage(mOutputImage);
169   mShadowPlane.SetShaderEffect(mShadowRenderShader);
170
171   // Rather than parent the shadow plane drawable and have constraints to move it to the same
172   // position, instead parent the shadow plane drawable on the shadow plane passed in.
173   mShadowPlaneBg.Add(mShadowPlane);
174   mShadowPlane.SetParentOrigin(ParentOrigin::CENTER);
175   mShadowPlane.SetZ(1.0f);
176
177   ConstrainCamera();
178
179   mShadowPlane.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, Source( mShadowPlaneBg, Actor::SIZE ), EqualToConstraint() ) );
180
181   mBlurRootActor.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, Source( mShadowPlane, Actor::SIZE ), EqualToConstraint() ) );
182 }
183
184 void ShadowView::SetPointLight(Actor pointLight)
185 {
186   mPointLight = pointLight;
187
188   ConstrainCamera();
189 }
190
191 void ShadowView::SetPointLightFieldOfView(float fieldOfView)
192 {
193   mCameraActor.SetFieldOfView(fieldOfView);
194 }
195
196 void ShadowView::SetShadowColor(Vector4 color)
197 {
198   mCachedShadowColor = color;
199   mCachedBackgroundColor.r = color.r;
200   mCachedBackgroundColor.g = color.g;
201   mCachedBackgroundColor.b = color.b;
202
203   Self().SetProperty( mShadowColorPropertyIndex, mCachedShadowColor );
204   if(mRenderSceneTask)
205   {
206     mRenderSceneTask.SetClearColor( mCachedBackgroundColor );
207   }
208 }
209
210 void ShadowView::Activate()
211 {
212   DALI_ASSERT_ALWAYS( Self().OnStage() && "ShadowView should be on stage before calling Activate()\n" );
213
214   // make sure resources are allocated and start the render tasks processing
215   CreateRenderTasks();
216 }
217
218 void ShadowView::Deactivate()
219 {
220   DALI_ASSERT_ALWAYS( Self().OnStage() && "ShadowView should be on stage before calling Deactivate()\n" )
221
222   // stop render tasks processing
223   // Note: render target resources are automatically freed since we set the Image::Unused flag
224   RemoveRenderTasks();
225 }
226
227 ///////////////////////////////////////////////////////////
228 //
229 // Private methods
230 //
231
232 void ShadowView::OnInitialize()
233 {
234   // root actor to parent all user added actors. Used as source actor for shadow render task.
235   mChildrenRoot.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
236   mChildrenRoot.ApplyConstraint(Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ));
237
238   Vector2 stageSize = Stage::GetCurrent().GetSize();
239   mCameraActor = CameraActor::New(stageSize);
240   mCameraActor.SetParentOrigin( ParentOrigin::CENTER );
241
242   // Target is constrained to point at the shadow plane origin
243   mCameraActor.SetNearClippingPlane( 1.0f );
244   mCameraActor.SetType( Dali::Camera::FREE_LOOK ); // Camera orientation constrained to point at shadow plane world position
245   mCameraActor.SetInvertYAxis(false);
246   mCameraActor.SetPosition(DEFAULT_LIGHT_POSITION);
247
248   mShadowRenderShader = ShaderEffect::New( RENDER_SHADOW_VERTEX_SOURCE, RENDER_SHADOW_FRAGMENT_SOURCE,
249                                            Dali::GeometryType( GEOMETRY_TYPE_IMAGE ),
250                                            ShaderEffect::GeometryHints( ShaderEffect::HINT_GRID | ShaderEffect::HINT_BLENDING ));
251
252   // Create render targets needed for rendering from light's point of view
253   mSceneFromLightRenderTarget = FrameBufferImage::New( stageSize.width, stageSize.height, Pixel::RGBA8888 );
254
255   mOutputImage = FrameBufferImage::New( stageSize.width * 0.5f, stageSize.height * 0.5f, Pixel::RGBA8888 );
256
257   //////////////////////////////////////////////////////
258   // Connect to actor tree
259
260   Self().Add( mChildrenRoot );
261   Stage::GetCurrent().Add( mCameraActor );
262
263   mBlurFilter.SetRefreshOnDemand(false);
264   mBlurFilter.SetInputImage(mSceneFromLightRenderTarget);
265   mBlurFilter.SetOutputImage(mOutputImage);
266   mBlurFilter.SetSize(stageSize * 0.5f);
267   mBlurFilter.SetPixelFormat(Pixel::RGBA8888);
268
269   mBlurRootActor = Actor::New();
270
271   // Turn off inheritance to ensure filter renders properly
272   mBlurRootActor.SetPositionInheritanceMode(USE_PARENT_POSITION);
273   mBlurRootActor.SetInheritRotation(false);
274   mBlurRootActor.SetInheritScale(false);
275   mBlurRootActor.SetColorMode(USE_OWN_COLOR);
276
277   Self().Add(mBlurRootActor);
278
279   mBlurFilter.SetRootActor(mBlurRootActor);
280   mBlurFilter.SetBackgroundColor(Vector4::ZERO);
281
282   SetShaderConstants();
283 }
284
285 void ShadowView::OnSizeSet(const Vector3& targetSize)
286 {
287 }
288
289 void ShadowView::OnStageConnection()
290 {
291   // TODO: can't call this here, since SetImage() calls fail to connect images to stage, since parent chain not fully on stage yet
292   // Need to fix the stage connection so this callback can be used arbitrarily. At that point we  can simplify the API by removing the need for Activate() / Deactivate()
293   //Activate();
294 }
295
296 void ShadowView::OnStageDisconnection()
297 {
298   // TODO: can't call this here, since SetImage() calls fails similarly to above
299   // Need to fix the stage connection so this callback can be used arbitrarily. At that point we  can simplify the API by removing the need for Activate() / Deactivate()
300   //Deactivate();
301 }
302
303 void ShadowView::ConstrainCamera()
304 {
305   if( mPointLight && mShadowPlane )
306   {
307     // Constrain camera to look directly at center of shadow plane. (mPointLight position
308     // is under control of application, can't use transform inheritance)
309
310     Constraint cameraOrientationConstraint =
311       Constraint::New<Quaternion> ( Actor::ROTATION,
312                                     Source( mShadowPlane, Actor::WORLD_POSITION ),
313                                     Source( mPointLight,  Actor::WORLD_POSITION ),
314                                     Source( mShadowPlane, Actor::WORLD_ROTATION ),
315                                     &LookAt );
316
317     mCameraActor.ApplyConstraint( cameraOrientationConstraint );
318
319     Constraint pointLightPositionConstraint = Constraint::New<Vector3>( Actor::POSITION, Source( mPointLight, Actor::WORLD_POSITION ), EqualToConstraint() );
320
321     mCameraActor.ApplyConstraint( pointLightPositionConstraint );
322   }
323 }
324
325 void ShadowView::CreateRenderTasks()
326 {
327   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
328
329   // We want the first task to render the scene from the light
330   mRenderSceneTask = taskList.CreateTask();
331
332   mRenderSceneTask.SetCameraActor( mCameraActor );
333   mRenderSceneTask.SetSourceActor( mChildrenRoot );
334   mRenderSceneTask.SetTargetFrameBuffer( mSceneFromLightRenderTarget );
335   mRenderSceneTask.SetInputEnabled( false );
336   mRenderSceneTask.SetClearEnabled( true );
337
338   // background color for render task should be the shadow color, but with alpha 0
339   // we don't want to blend the edges of the content with a BLACK at alpha 0, but
340   // the same shadow color at alpha 0.
341   mRenderSceneTask.SetClearColor( mCachedBackgroundColor );
342
343   mBlurFilter.Enable();
344 }
345
346 void ShadowView::RemoveRenderTasks()
347 {
348   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
349
350   taskList.RemoveTask(mRenderSceneTask);
351   mRenderSceneTask.Reset();
352
353   mBlurFilter.Disable();
354 }
355
356 void ShadowView::SetShaderConstants()
357 {
358   CustomActor self = Self();
359
360   mShadowRenderShader.SetUniform( SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME, Matrix::IDENTITY );
361   mShadowRenderShader.SetUniform( SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME, Matrix::IDENTITY );
362   mShadowRenderShader.SetUniform( SHADER_SHADOW_COLOR_PROPERTY_NAME, mCachedShadowColor );
363
364   Property::Index lightCameraProjectionMatrixPropertyIndex = mShadowRenderShader.GetPropertyIndex(SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME);
365   Property::Index lightCameraViewMatrixPropertyIndex = mShadowRenderShader.GetPropertyIndex(SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME);
366
367   Constraint projectionMatrixConstraint = Constraint::New<Dali::Matrix>( lightCameraProjectionMatrixPropertyIndex, Source( mCameraActor, CameraActor::PROJECTION_MATRIX ), EqualToConstraintMatrix());
368   Constraint viewMatrixConstraint = Constraint::New<Dali::Matrix>( lightCameraViewMatrixPropertyIndex, Source( mCameraActor, CameraActor::VIEW_MATRIX ), EqualToConstraintMatrix());
369
370   mShadowRenderShader.ApplyConstraint(projectionMatrixConstraint);
371   mShadowRenderShader.ApplyConstraint(viewMatrixConstraint);
372
373   // Register a property that the user can use to control the blur in the internal object
374   mBlurStrengthPropertyIndex = self.RegisterProperty(BLUR_STRENGTH_PROPERTY_NAME, BLUR_STRENGTH_DEFAULT);
375   mBlurFilter.GetHandleForAnimateBlurStrength().ApplyConstraint( Constraint::New<float>( mBlurFilter.GetBlurStrengthPropertyIndex() ,
376                                                                            Source( self, mBlurStrengthPropertyIndex),
377                                                                            EqualToConstraint()) );
378
379   //  Register a property that the user can use to control the color of the shadow.
380   Property::Index index = mShadowRenderShader.GetPropertyIndex(SHADER_SHADOW_COLOR_PROPERTY_NAME);
381   mShadowColorPropertyIndex = self.RegisterProperty(SHADOW_COLOR_PROPERTY_NAME, mCachedShadowColor);
382
383   mShadowRenderShader.ApplyConstraint(Constraint::New<Dali::Vector4>( index, Source( self, mShadowColorPropertyIndex ), EqualToConstraint()) );
384 }
385
386 } // namespace Internal
387
388 } // namespace Toolkit
389
390 } // namespace Dali