2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 // http://floralicense.org/license/
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.
18 #include "shadow-view-impl.h"
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>
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
36 /////////////////////////////////////////////////////////
37 // IMPLEMENTATION NOTES
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.
63 return Toolkit::ShadowView::New();
66 TypeRegistration mType( typeid(Toolkit::ShadowView), typeid(Toolkit::Control), Create );
69 const float BLUR_STRENGTH_DEFAULT = 1.0f;
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
74 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.8f);
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" );
80 const std::string BLUR_STRENGTH_PROPERTY_NAME( "BlurStrengthProperty" );
81 const std::string SHADOW_COLOR_PROPERTY_NAME( "ShadowColorProperty" );
83 const char* const RENDER_SHADOW_VERTEX_SOURCE =
84 " uniform mediump mat4 uLightCameraProjectionMatrix;\n"
85 " uniform mediump mat4 uLightCameraViewMatrix;\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"
94 const char* const RENDER_SHADOW_FRAGMENT_SOURCE =
95 "uniform lowp vec4 uShadowColor;\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"
103 // TODO: Add this to dali-core constraints.h
105 * EqualToConstraintMatrix
107 * f(current, property) = property
109 struct EqualToConstraintMatrix
111 EqualToConstraintMatrix(){}
113 Dali::Matrix operator()(const Dali::Matrix& current, const PropertyInput& property) {return property.GetMatrix();}
118 ShadowView::ShadowView( float downsampleWidthScale, float downsampleHeightScale )
119 : Control( CONTROL_BEHAVIOUR_NONE ),
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)
130 ShadowView::~ShadowView()
134 Toolkit::ShadowView ShadowView::New(float downsampleWidthScale, float downsampleHeightScale)
136 ShadowView* impl = new ShadowView(downsampleWidthScale, downsampleHeightScale);
138 Dali::Toolkit::ShadowView handle = Dali::Toolkit::ShadowView( *impl );
140 // Second-phase init of the implementation
141 // This can only be done after the CustomActor connection has been made...
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)
152 mChildrenRoot.Add(child);
155 void ShadowView::Remove(Actor child)
157 mChildrenRoot.Remove(child);
160 void ShadowView::SetShadowPlane(Actor shadowPlane)
162 mShadowPlaneBg = shadowPlane;
164 mShadowPlane = ImageActor::New();
165 mShadowPlane.SetParentOrigin(ParentOrigin::CENTER);
166 mShadowPlane.SetAnchorPoint(AnchorPoint::CENTER);
168 mShadowPlane.SetImage(mOutputImage);
169 mShadowPlane.SetShaderEffect(mShadowRenderShader);
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);
179 mShadowPlane.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, Source( mShadowPlaneBg, Actor::SIZE ), EqualToConstraint() ) );
181 mBlurRootActor.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, Source( mShadowPlane, Actor::SIZE ), EqualToConstraint() ) );
184 void ShadowView::SetPointLight(Actor pointLight)
186 mPointLight = pointLight;
191 void ShadowView::SetPointLightFieldOfView(float fieldOfView)
193 mCameraActor.SetFieldOfView(fieldOfView);
196 void ShadowView::SetShadowColor(Vector4 color)
198 mCachedShadowColor = color;
199 mCachedBackgroundColor.r = color.r;
200 mCachedBackgroundColor.g = color.g;
201 mCachedBackgroundColor.b = color.b;
203 Self().SetProperty( mShadowColorPropertyIndex, mCachedShadowColor );
206 mRenderSceneTask.SetClearColor( mCachedBackgroundColor );
210 void ShadowView::Activate()
212 DALI_ASSERT_ALWAYS( Self().OnStage() && "ShadowView should be on stage before calling Activate()\n" );
214 // make sure resources are allocated and start the render tasks processing
218 void ShadowView::Deactivate()
220 DALI_ASSERT_ALWAYS( Self().OnStage() && "ShadowView should be on stage before calling Deactivate()\n" )
222 // stop render tasks processing
223 // Note: render target resources are automatically freed since we set the Image::Unused flag
227 ///////////////////////////////////////////////////////////
232 void ShadowView::OnInitialize()
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() ));
238 Vector2 stageSize = Stage::GetCurrent().GetSize();
239 mCameraActor = CameraActor::New(stageSize);
241 mCameraActor.SetParentOrigin( ParentOrigin::CENTER );
243 // Target is constrained to point at the shadow plane origin
244 mCameraActor.SetNearClippingPlane( 1.0f );
245 mCameraActor.SetType( Dali::Camera::FREE_LOOK ); // Camera orientation constrained to point at shadow plane world position
246 mCameraActor.SetRotation(Radian(Degree(180)), Vector3::YAXIS);
247 mCameraActor.SetPosition(DEFAULT_LIGHT_POSITION);
249 mShadowRenderShader = ShaderEffect::New( RENDER_SHADOW_VERTEX_SOURCE, RENDER_SHADOW_FRAGMENT_SOURCE,
250 Dali::GeometryType( GEOMETRY_TYPE_IMAGE ),
251 ShaderEffect::GeometryHints( ShaderEffect::HINT_GRID | ShaderEffect::HINT_BLENDING ));
253 // Create render targets needed for rendering from light's point of view
254 mSceneFromLightRenderTarget = FrameBufferImage::New( stageSize.width, stageSize.height, Pixel::RGBA8888 );
256 mOutputImage = FrameBufferImage::New( stageSize.width * 0.5f, stageSize.height * 0.5f, Pixel::RGBA8888 );
258 //////////////////////////////////////////////////////
259 // Connect to actor tree
261 Self().Add( mChildrenRoot );
262 Stage::GetCurrent().Add( mCameraActor );
264 mBlurFilter.SetRefreshOnDemand(false);
265 mBlurFilter.SetInputImage(mSceneFromLightRenderTarget);
266 mBlurFilter.SetOutputImage(mOutputImage);
267 mBlurFilter.SetSize(stageSize * 0.5f);
268 mBlurFilter.SetPixelFormat(Pixel::RGBA8888);
270 mBlurRootActor = Actor::New();
272 // Turn off inheritance to ensure filter renders properly
273 mBlurRootActor.SetPositionInheritanceMode(USE_PARENT_POSITION);
274 mBlurRootActor.SetInheritRotation(false);
275 mBlurRootActor.SetInheritScale(false);
276 mBlurRootActor.SetColorMode(USE_OWN_COLOR);
278 Self().Add(mBlurRootActor);
280 mBlurFilter.SetRootActor(mBlurRootActor);
281 mBlurFilter.SetBackgroundColor(Vector4::ZERO);
283 SetShaderConstants();
286 void ShadowView::OnSizeSet(const Vector3& targetSize)
290 void ShadowView::OnStageConnection()
292 // TODO: can't call this here, since SetImage() calls fail to connect images to stage, since parent chain not fully on stage yet
293 // 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()
297 void ShadowView::OnStageDisconnection()
299 // TODO: can't call this here, since SetImage() calls fails similarly to above
300 // 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()
304 void ShadowView::ConstrainCamera()
306 if( mPointLight && mShadowPlane )
308 // Constrain camera to look directly at center of shadow plane. (mPointLight position
309 // is under control of application, can't use transform inheritance)
311 Constraint cameraOrientationConstraint =
312 Constraint::New<Quaternion> ( Actor::ROTATION,
313 Source( mShadowPlane, Actor::WORLD_POSITION ),
314 Source( mPointLight, Actor::WORLD_POSITION ),
315 Source( mShadowPlane, Actor::WORLD_ROTATION ),
318 mCameraActor.ApplyConstraint( cameraOrientationConstraint );
320 Constraint pointLightPositionConstraint = Constraint::New<Vector3>( Actor::POSITION, Source( mPointLight, Actor::WORLD_POSITION ), EqualToConstraint() );
322 mCameraActor.ApplyConstraint( pointLightPositionConstraint );
326 void ShadowView::CreateRenderTasks()
328 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
330 // We want the first task to render the scene from the light
331 mRenderSceneTask = taskList.CreateTask();
333 mRenderSceneTask.SetCameraActor( mCameraActor );
334 mRenderSceneTask.SetSourceActor( mChildrenRoot );
335 mRenderSceneTask.SetTargetFrameBuffer( mSceneFromLightRenderTarget );
336 mRenderSceneTask.SetInputEnabled( false );
337 mRenderSceneTask.SetClearEnabled( true );
339 // background color for render task should be the shadow color, but with alpha 0
340 // we don't want to blend the edges of the content with a BLACK at alpha 0, but
341 // the same shadow color at alpha 0.
342 mRenderSceneTask.SetClearColor( mCachedBackgroundColor );
344 mBlurFilter.Enable();
347 void ShadowView::RemoveRenderTasks()
349 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
351 taskList.RemoveTask(mRenderSceneTask);
352 mRenderSceneTask.Reset();
354 mBlurFilter.Disable();
357 void ShadowView::SetShaderConstants()
359 CustomActor self = Self();
361 mShadowRenderShader.SetUniform( SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME, Matrix::IDENTITY );
362 mShadowRenderShader.SetUniform( SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME, Matrix::IDENTITY );
363 mShadowRenderShader.SetUniform( SHADER_SHADOW_COLOR_PROPERTY_NAME, mCachedShadowColor );
365 Property::Index lightCameraProjectionMatrixPropertyIndex = mShadowRenderShader.GetPropertyIndex(SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME);
366 Property::Index lightCameraViewMatrixPropertyIndex = mShadowRenderShader.GetPropertyIndex(SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME);
368 Constraint projectionMatrixConstraint = Constraint::New<Dali::Matrix>( lightCameraProjectionMatrixPropertyIndex, Source( mCameraActor, CameraActor::PROJECTION_MATRIX ), EqualToConstraintMatrix());
369 Constraint viewMatrixConstraint = Constraint::New<Dali::Matrix>( lightCameraViewMatrixPropertyIndex, Source( mCameraActor, CameraActor::VIEW_MATRIX ), EqualToConstraintMatrix());
371 mShadowRenderShader.ApplyConstraint(projectionMatrixConstraint);
372 mShadowRenderShader.ApplyConstraint(viewMatrixConstraint);
374 // Register a property that the user can use to control the blur in the internal object
375 mBlurStrengthPropertyIndex = self.RegisterProperty(BLUR_STRENGTH_PROPERTY_NAME, BLUR_STRENGTH_DEFAULT);
376 mBlurFilter.GetHandleForAnimateBlurStrength().ApplyConstraint( Constraint::New<float>( mBlurFilter.GetBlurStrengthPropertyIndex() ,
377 Source( self, mBlurStrengthPropertyIndex),
378 EqualToConstraint()) );
380 // Register a property that the user can use to control the color of the shadow.
381 Property::Index index = mShadowRenderShader.GetPropertyIndex(SHADER_SHADOW_COLOR_PROPERTY_NAME);
382 mShadowColorPropertyIndex = self.RegisterProperty(SHADOW_COLOR_PROPERTY_NAME, mCachedShadowColor);
384 mShadowRenderShader.ApplyConstraint(Constraint::New<Dali::Vector4>( index, Source( self, mShadowColorPropertyIndex ), EqualToConstraint()) );
387 } // namespace Internal
389 } // namespace Toolkit