2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "shadow-view-impl.h"
24 #include <dali/public-api/animation/constraint.h>
25 #include <dali/public-api/common/stage.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/devel-api/object/type-registry-helper.h>
28 #include <dali/public-api/render-tasks/render-task-list.h>
29 #include <dali/integration-api/debug.h>
32 #include <dali-toolkit/internal/controls/shadow-view/shadow-view-impl.h>
33 #include <dali-toolkit/internal/filters/blur-two-pass-filter.h>
36 // pixel format / size - set from JSON
37 // aspect ratio property needs to be able to be constrained also for cameras. (now do-able)
38 // default near clip value
39 // mChildrenRoot Add()/Remove() overloads - better solution
42 /////////////////////////////////////////////////////////
43 // IMPLEMENTATION NOTES
45 // As the ShadowView actor changes size, the amount of pixels we need to blur changes. Therefore we need some way of doing this. However:-
46 // OnSetSize() does not get called when ShadowView object size is modified using a Constraint.
47 // 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
48 // OnSizeAnimation() to alter render target sizes.
49 // 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
50 // 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
51 // by using constraints relative to the ShadowView actor size.
69 return Toolkit::ShadowView::New();
72 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ShadowView, Toolkit::Control, Create )
73 DALI_TYPE_REGISTRATION_END()
75 const float BLUR_STRENGTH_DEFAULT = 1.0f;
77 const Vector3 DEFAULT_LIGHT_POSITION(300.0f, 250.0f, 600.0f);
78 const float DEFAULT_FIELD_OF_VIEW_RADIANS = Math::PI / 4.0f; // 45 degrees
80 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.8f);
82 const char* const SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME = "uLightCameraProjectionMatrix";
83 const char* const SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME = "uLightCameraViewMatrix";
84 const char* const SHADER_SHADOW_COLOR_PROPERTY_NAME = "uShadowColor";
85 const char* const BLUR_STRENGTH_PROPERTY_NAME = "BlurStrengthProperty";
86 const char* const SHADOW_COLOR_PROPERTY_NAME = "ShadowColorProperty";
88 const char* const RENDER_SHADOW_VERTEX_SOURCE =
89 " uniform mediump mat4 uLightCameraProjectionMatrix;\n"
90 " uniform mediump mat4 uLightCameraViewMatrix;\n"
94 " gl_Position = uProjection * uModelView * vec4(aPosition,1.0);\n"
95 " vec4 textureCoords = uLightCameraProjectionMatrix * uLightCameraViewMatrix * uModelMatrix * vec4(aPosition,1.0);\n"
96 " vTexCoord = 0.5 + 0.5 * (textureCoords.xy/textureCoords.w);\n"
99 const char* const RENDER_SHADOW_FRAGMENT_SOURCE =
100 "uniform lowp vec4 uShadowColor;\n"
103 " lowp float alpha;\n"
104 " alpha = texture2D(sTexture, vec2(vTexCoord.x, vTexCoord.y)).a;\n"
105 " gl_FragColor = vec4(uShadowColor.rgb, uShadowColor.a * alpha);\n"
110 ShadowView::ShadowView( float downsampleWidthScale, float downsampleHeightScale )
111 : Control( ControlBehaviour( ACTOR_BEHAVIOUR_NONE ) ),
112 mChildrenRoot(Actor::New()),
113 mCachedShadowColor(DEFAULT_SHADOW_COLOR),
114 mCachedBackgroundColor(DEFAULT_SHADOW_COLOR.r, DEFAULT_SHADOW_COLOR.g, DEFAULT_SHADOW_COLOR.b, 0.0f),
115 mBlurStrengthPropertyIndex(Property::INVALID_INDEX),
116 mShadowColorPropertyIndex(Property::INVALID_INDEX),
117 mDownsampleWidthScale(downsampleWidthScale),
118 mDownsampleHeightScale(downsampleHeightScale)
122 ShadowView::~ShadowView()
126 Toolkit::ShadowView ShadowView::New(float downsampleWidthScale, float downsampleHeightScale)
128 ShadowView* impl = new ShadowView(downsampleWidthScale, downsampleHeightScale);
130 Dali::Toolkit::ShadowView handle = Dali::Toolkit::ShadowView( *impl );
132 // Second-phase init of the implementation
133 // This can only be done after the CustomActor connection has been made...
139 /////////////////////////////////////////////////////////////
140 // for creating a subtree for all user added child actors.
141 // 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.
142 void ShadowView::Add(Actor child)
144 mChildrenRoot.Add(child);
147 void ShadowView::Remove(Actor child)
149 mChildrenRoot.Remove(child);
152 void ShadowView::SetShadowPlane(Actor shadowPlane)
154 mShadowPlaneBg = shadowPlane;
156 mShadowPlane = ImageActor::New();
157 mShadowPlane.SetName( "SHADOW_PLANE" );
158 mShadowPlane.SetParentOrigin(ParentOrigin::CENTER);
159 mShadowPlane.SetAnchorPoint(AnchorPoint::CENTER);
161 mShadowPlane.SetImage(mOutputImage);
162 mShadowPlane.SetShaderEffect(mShadowRenderShader);
164 // Rather than parent the shadow plane drawable and have constraints to move it to the same
165 // position, instead parent the shadow plane drawable on the shadow plane passed in.
166 mShadowPlaneBg.Add(mShadowPlane);
167 mShadowPlane.SetParentOrigin(ParentOrigin::CENTER);
168 mShadowPlane.SetZ(1.0f);
172 mShadowPlane.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
174 mBlurRootActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
177 void ShadowView::SetPointLight(Actor pointLight)
179 mPointLight = pointLight;
184 void ShadowView::SetPointLightFieldOfView(float fieldOfView)
186 mCameraActor.SetFieldOfView(fieldOfView);
189 void ShadowView::SetShadowColor(Vector4 color)
191 mCachedShadowColor = color;
192 mCachedBackgroundColor.r = color.r;
193 mCachedBackgroundColor.g = color.g;
194 mCachedBackgroundColor.b = color.b;
196 Self().SetProperty( mShadowColorPropertyIndex, mCachedShadowColor );
199 mRenderSceneTask.SetClearColor( mCachedBackgroundColor );
203 void ShadowView::Activate()
205 DALI_ASSERT_ALWAYS( Self().OnStage() && "ShadowView should be on stage before calling Activate()\n" );
207 // make sure resources are allocated and start the render tasks processing
211 void ShadowView::Deactivate()
213 DALI_ASSERT_ALWAYS( Self().OnStage() && "ShadowView should be on stage before calling Deactivate()\n" )
215 // stop render tasks processing
216 // Note: render target resources are automatically freed since we set the Image::Unused flag
220 ///////////////////////////////////////////////////////////
225 void ShadowView::OnInitialize()
227 // root actor to parent all user added actors. Used as source actor for shadow render task.
228 mChildrenRoot.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
229 mChildrenRoot.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
231 Vector2 stageSize = Stage::GetCurrent().GetSize();
232 mCameraActor = CameraActor::New(stageSize);
234 mCameraActor.SetParentOrigin( ParentOrigin::CENTER );
236 // Target is constrained to point at the shadow plane origin
237 mCameraActor.SetNearClippingPlane( 1.0f );
238 mCameraActor.SetType( Dali::Camera::FREE_LOOK ); // Camera orientation constrained to point at shadow plane world position
239 mCameraActor.SetOrientation(Radian(Degree(180)), Vector3::YAXIS);
240 mCameraActor.SetPosition(DEFAULT_LIGHT_POSITION);
242 mShadowRenderShader = ShaderEffect::New( RENDER_SHADOW_VERTEX_SOURCE, RENDER_SHADOW_FRAGMENT_SOURCE,
243 ShaderEffect::GeometryHints( ShaderEffect::HINT_GRID | ShaderEffect::HINT_BLENDING ));
245 // Create render targets needed for rendering from light's point of view
246 mSceneFromLightRenderTarget = FrameBufferImage::New( stageSize.width, stageSize.height, Pixel::RGBA8888 );
248 mOutputImage = FrameBufferImage::New( stageSize.width * 0.5f, stageSize.height * 0.5f, Pixel::RGBA8888 );
250 //////////////////////////////////////////////////////
251 // Connect to actor tree
253 Self().Add( mChildrenRoot );
254 Stage::GetCurrent().Add( mCameraActor );
256 mBlurFilter.SetRefreshOnDemand(false);
257 mBlurFilter.SetInputImage(mSceneFromLightRenderTarget);
258 mBlurFilter.SetOutputImage(mOutputImage);
259 mBlurFilter.SetSize(stageSize * 0.5f);
260 mBlurFilter.SetPixelFormat(Pixel::RGBA8888);
262 mBlurRootActor = Actor::New();
263 mBlurRootActor.SetName( "BLUR_ROOT_ACTOR" );
265 // Turn off inheritance to ensure filter renders properly
266 mBlurRootActor.SetPositionInheritanceMode(USE_PARENT_POSITION);
267 mBlurRootActor.SetInheritOrientation(false);
268 mBlurRootActor.SetInheritScale(false);
269 mBlurRootActor.SetColorMode(USE_OWN_COLOR);
271 Self().Add(mBlurRootActor);
273 mBlurFilter.SetRootActor(mBlurRootActor);
274 mBlurFilter.SetBackgroundColor(Vector4::ZERO);
276 SetShaderConstants();
279 void ShadowView::OnSizeSet(const Vector3& targetSize)
283 void ShadowView::ConstrainCamera()
285 if( mPointLight && mShadowPlane )
287 // Constrain camera to look directly at center of shadow plane. (mPointLight position
288 // is under control of application, can't use transform inheritance)
290 Constraint cameraOrientationConstraint = Constraint::New<Quaternion> ( mCameraActor, Actor::Property::ORIENTATION, &LookAt );
291 cameraOrientationConstraint.AddSource( Source( mShadowPlane, Actor::Property::WORLD_POSITION ) );
292 cameraOrientationConstraint.AddSource( Source( mPointLight, Actor::Property::WORLD_POSITION ) );
293 cameraOrientationConstraint.AddSource( Source( mShadowPlane, Actor::Property::WORLD_ORIENTATION ) );
294 cameraOrientationConstraint.Apply();
296 Constraint pointLightPositionConstraint = Constraint::New<Vector3>( mCameraActor, Actor::Property::POSITION, EqualToConstraint() );
297 pointLightPositionConstraint.AddSource( Source( mPointLight, Actor::Property::WORLD_POSITION ) );
298 pointLightPositionConstraint.Apply();
302 void ShadowView::CreateRenderTasks()
304 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
306 // We want the first task to render the scene from the light
307 mRenderSceneTask = taskList.CreateTask();
309 mRenderSceneTask.SetCameraActor( mCameraActor );
310 mRenderSceneTask.SetSourceActor( mChildrenRoot );
311 mRenderSceneTask.SetTargetFrameBuffer( mSceneFromLightRenderTarget );
312 mRenderSceneTask.SetInputEnabled( false );
313 mRenderSceneTask.SetClearEnabled( true );
315 // background color for render task should be the shadow color, but with alpha 0
316 // we don't want to blend the edges of the content with a BLACK at alpha 0, but
317 // the same shadow color at alpha 0.
318 mRenderSceneTask.SetClearColor( mCachedBackgroundColor );
320 mBlurFilter.Enable();
323 void ShadowView::RemoveRenderTasks()
325 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
327 taskList.RemoveTask(mRenderSceneTask);
328 mRenderSceneTask.Reset();
330 mBlurFilter.Disable();
333 void ShadowView::SetShaderConstants()
335 CustomActor self = Self();
337 mShadowRenderShader.SetUniform( SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME, Matrix::IDENTITY );
338 mShadowRenderShader.SetUniform( SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME, Matrix::IDENTITY );
339 mShadowRenderShader.SetUniform( SHADER_SHADOW_COLOR_PROPERTY_NAME, mCachedShadowColor );
341 Property::Index lightCameraProjectionMatrixPropertyIndex = mShadowRenderShader.GetPropertyIndex(SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME);
342 Property::Index lightCameraViewMatrixPropertyIndex = mShadowRenderShader.GetPropertyIndex(SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME);
344 Constraint projectionMatrixConstraint = Constraint::New<Dali::Matrix>( mShadowRenderShader, lightCameraProjectionMatrixPropertyIndex, EqualToConstraint() );
345 projectionMatrixConstraint.AddSource( Source( mCameraActor, CameraActor::Property::PROJECTION_MATRIX ) );
347 Constraint viewMatrixConstraint = Constraint::New<Dali::Matrix>( mShadowRenderShader, lightCameraViewMatrixPropertyIndex, EqualToConstraint() );
348 viewMatrixConstraint.AddSource( Source( mCameraActor, CameraActor::Property::VIEW_MATRIX ) );
350 projectionMatrixConstraint.Apply();
351 viewMatrixConstraint.Apply();
353 // Register a property that the user can use to control the blur in the internal object
354 mBlurStrengthPropertyIndex = self.RegisterProperty(BLUR_STRENGTH_PROPERTY_NAME, BLUR_STRENGTH_DEFAULT);
356 Constraint blurStrengthConstraint = Constraint::New<float>( mBlurFilter.GetHandleForAnimateBlurStrength(), mBlurFilter.GetBlurStrengthPropertyIndex(), EqualToConstraint() );
357 blurStrengthConstraint.AddSource( Source( self, mBlurStrengthPropertyIndex) );
358 blurStrengthConstraint.Apply();
360 // Register a property that the user can use to control the color of the shadow.
361 Property::Index index = mShadowRenderShader.GetPropertyIndex(SHADER_SHADOW_COLOR_PROPERTY_NAME);
362 mShadowColorPropertyIndex = self.RegisterProperty(SHADOW_COLOR_PROPERTY_NAME, mCachedShadowColor);
364 Constraint shadowRenderShaderConstraint = Constraint::New<Dali::Vector4>( mShadowRenderShader, index, EqualToConstraint() );
365 shadowRenderShaderConstraint.AddSource( Source( self, mShadowColorPropertyIndex ) );
366 shadowRenderShaderConstraint.Apply();
369 } // namespace Internal
371 } // namespace Toolkit