/*
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "shadow-view-impl.h"
// EXTERNAL INCLUDES
-#include <sstream>
-#include <iomanip>
-#include <dali/public-api/animation/active-constraint.h>
+#include <dali/devel-api/common/stage.h>
+#include <dali/integration-api/debug.h>
#include <dali/public-api/animation/constraint.h>
-#include <dali/public-api/common/stage.h>
+#include <dali/public-api/object/type-registry-helper.h>
#include <dali/public-api/object/type-registry.h>
#include <dali/public-api/render-tasks/render-task-list.h>
-#include <dali/integration-api/debug.h>
+#include <dali/public-api/rendering/shader.h>
+#include <iomanip>
+#include <sstream>
// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/controls/control-devel.h>
+#include <dali-toolkit/internal/controls/control/control-data-impl.h>
+#include <dali-toolkit/internal/controls/control/control-renderers.h>
#include <dali-toolkit/internal/controls/shadow-view/shadow-view-impl.h>
#include <dali-toolkit/internal/filters/blur-two-pass-filter.h>
+#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
+#include <dali-toolkit/public-api/visuals/visual-properties.h>
// TODO:
// pixel format / size - set from JSON
// aspect ratio property needs to be able to be constrained also for cameras. (now do-able)
// default near clip value
-// mChildrenRoot Add()/Remove() overloads - better solution
-
/////////////////////////////////////////////////////////
// IMPLEMENTATION NOTES
namespace Dali
{
-
namespace Toolkit
{
-
namespace Internal
{
-
namespace
{
-
using namespace Dali;
BaseHandle Create()
return Toolkit::ShadowView::New();
}
-TypeRegistration mType( typeid(Toolkit::ShadowView), typeid(Toolkit::Control), Create );
-
+DALI_TYPE_REGISTRATION_BEGIN(Toolkit::ShadowView, Toolkit::Control, Create)
+DALI_TYPE_REGISTRATION_END()
const float BLUR_STRENGTH_DEFAULT = 1.0f;
const Vector3 DEFAULT_LIGHT_POSITION(300.0f, 250.0f, 600.0f);
-const float DEFAULT_FIELD_OF_VIEW_RADIANS = Math::PI / 4.0f; // 45 degrees
+const float DEFAULT_FIELD_OF_VIEW_RADIANS = Math::PI / 4.0f; // 45 degrees
const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.8f);
-const std::string SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME( "uLightCameraProjectionMatrix" );
-const std::string SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME( "uLightCameraViewMatrix" );
-const std::string SHADER_SHADOW_COLOR_PROPERTY_NAME( "uShadowColor" );
-
-const std::string BLUR_STRENGTH_PROPERTY_NAME( "BlurStrengthProperty" );
-const std::string SHADOW_COLOR_PROPERTY_NAME( "ShadowColorProperty" );
-
-const char* const RENDER_SHADOW_VERTEX_SOURCE =
- " uniform mediump mat4 uLightCameraProjectionMatrix;\n"
- " uniform mediump mat4 uLightCameraViewMatrix;\n"
- "\n"
- "void main()\n"
- "{\n"
- " gl_Position = uProjection * uModelView * vec4(aPosition,1.0);\n"
- " vec4 textureCoords = uLightCameraProjectionMatrix * uLightCameraViewMatrix * uModelMatrix * vec4(aPosition,1.0);\n"
- " vTexCoord = 0.5 + 0.5 * (textureCoords.xy/textureCoords.w);\n"
- "}\n";
-
-const char* const RENDER_SHADOW_FRAGMENT_SOURCE =
- "uniform lowp vec4 uShadowColor;\n"
- "void main()\n"
- "{\n"
- " lowp float alpha;\n"
- " alpha = texture2D(sTexture, vec2(vTexCoord.x, vTexCoord.y)).a;\n"
- " gl_FragColor = vec4(uShadowColor.rgb, uShadowColor.a * alpha);\n"
- "}\n";
-
-// TODO: Add this to dali-core constraints.h
-/**
- * EqualToConstraintMatrix
- *
- * f(current, property) = property
- */
-struct EqualToConstraintMatrix
-{
- EqualToConstraintMatrix(){}
-
- Dali::Matrix operator()(const Dali::Matrix& current, const PropertyInput& property) {return property.GetMatrix();}
-};
+const char* const SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME = "uLightCameraProjectionMatrix";
+const char* const SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME = "uLightCameraViewMatrix";
+const char* const SHADER_SHADOW_COLOR_PROPERTY_NAME = "uShadowColor";
+const char* const BLUR_STRENGTH_PROPERTY_NAME = "BlurStrengthProperty";
+const char* const SHADOW_COLOR_PROPERTY_NAME = "ShadowColorProperty";
} // namespace
-ShadowView::ShadowView( float downsampleWidthScale, float downsampleHeightScale )
-: Control( CONTROL_BEHAVIOUR_NONE ),
+ShadowView::ShadowView(float downsampleWidthScale, float downsampleHeightScale)
+: Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)),
mChildrenRoot(Actor::New()),
mCachedShadowColor(DEFAULT_SHADOW_COLOR),
mCachedBackgroundColor(DEFAULT_SHADOW_COLOR.r, DEFAULT_SHADOW_COLOR.g, DEFAULT_SHADOW_COLOR.b, 0.0f),
{
ShadowView* impl = new ShadowView(downsampleWidthScale, downsampleHeightScale);
- Dali::Toolkit::ShadowView handle = Dali::Toolkit::ShadowView( *impl );
+ Dali::Toolkit::ShadowView handle = Dali::Toolkit::ShadowView(*impl);
// Second-phase init of the implementation
// This can only be done after the CustomActor connection has been made...
return handle;
}
-/////////////////////////////////////////////////////////////
-// for creating a subtree for all user added child actors.
-// 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.
-void ShadowView::Add(Actor child)
-{
- mChildrenRoot.Add(child);
-}
-
-void ShadowView::Remove(Actor child)
+void ShadowView::SetShadowPlaneBackground(Actor shadowPlaneBackground)
{
- mChildrenRoot.Remove(child);
-}
-
-void ShadowView::SetShadowPlane(Actor shadowPlane)
-{
- mShadowPlaneBg = shadowPlane;
+ mShadowPlaneBg = shadowPlaneBackground;
- mShadowPlane = ImageActor::New();
- mShadowPlane.SetParentOrigin(ParentOrigin::CENTER);
- mShadowPlane.SetAnchorPoint(AnchorPoint::CENTER);
+ mShadowPlane = Actor::New();
+ mShadowPlane.SetProperty(Actor::Property::NAME, "SHADOW_PLANE");
+ mShadowPlane.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+ mShadowPlane.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+ Renderer shadowRenderer = CreateRenderer(SHADER_SHADOW_VIEW_RENDER_SHADER_VERT, SHADER_SHADOW_VIEW_RENDER_SHADER_FRAG, Shader::Hint::OUTPUT_IS_TRANSPARENT, Uint16Pair(20, 20));
+ TextureSet textureSet = shadowRenderer.GetTextures();
+ textureSet.SetTexture(0u, mOutputFrameBuffer.GetColorTexture());
+ mShadowPlane.AddRenderer(shadowRenderer);
- mShadowPlane.SetImage(mOutputImage);
- mShadowPlane.SetShaderEffect(mShadowRenderShader);
+ SetShaderConstants();
// Rather than parent the shadow plane drawable and have constraints to move it to the same
// position, instead parent the shadow plane drawable on the shadow plane passed in.
mShadowPlaneBg.Add(mShadowPlane);
- mShadowPlane.SetParentOrigin(ParentOrigin::CENTER);
- mShadowPlane.SetZ(1.0f);
+ mShadowPlane.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+ mShadowPlane.SetProperty(Actor::Property::POSITION_Z, 1.0f);
ConstrainCamera();
- mShadowPlane.SetSizeMode( SIZE_EQUAL_TO_PARENT );
+ mShadowPlane.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
- mBlurRootActor.SetSizeMode( SIZE_EQUAL_TO_PARENT );
+ mBlurRootActor.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
}
void ShadowView::SetPointLight(Actor pointLight)
void ShadowView::SetShadowColor(Vector4 color)
{
- mCachedShadowColor = color;
+ mCachedShadowColor = color;
mCachedBackgroundColor.r = color.r;
mCachedBackgroundColor.g = color.g;
mCachedBackgroundColor.b = color.b;
- Self().SetProperty( mShadowColorPropertyIndex, mCachedShadowColor );
+ if(mShadowPlane)
+ {
+ mShadowPlane.SetProperty(mShadowColorPropertyIndex, mCachedShadowColor);
+ }
if(mRenderSceneTask)
{
- mRenderSceneTask.SetClearColor( mCachedBackgroundColor );
+ mRenderSceneTask.SetClearColor(mCachedBackgroundColor);
}
}
void ShadowView::Activate()
{
- DALI_ASSERT_ALWAYS( Self().OnStage() && "ShadowView should be on stage before calling Activate()\n" );
+ DALI_ASSERT_ALWAYS(Self().GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE) && "ShadowView should be on stage before calling Activate()\n");
// make sure resources are allocated and start the render tasks processing
CreateRenderTasks();
void ShadowView::Deactivate()
{
- DALI_ASSERT_ALWAYS( Self().OnStage() && "ShadowView should be on stage before calling Deactivate()\n" )
+ DALI_ASSERT_ALWAYS(Self().GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE) && "ShadowView should be on stage before calling Deactivate()\n")
// stop render tasks processing
// Note: render target resources are automatically freed since we set the Image::Unused flag
void ShadowView::OnInitialize()
{
// root actor to parent all user added actors. Used as source actor for shadow render task.
- mChildrenRoot.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
- mChildrenRoot.SetSizeMode( SIZE_EQUAL_TO_PARENT );
+ mChildrenRoot.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+ mChildrenRoot.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
Vector2 stageSize = Stage::GetCurrent().GetSize();
- mCameraActor = CameraActor::New(stageSize);
+ mCameraActor = CameraActor::New(stageSize);
- mCameraActor.SetParentOrigin( ParentOrigin::CENTER );
+ mCameraActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
// Target is constrained to point at the shadow plane origin
- mCameraActor.SetNearClippingPlane( 1.0f );
- mCameraActor.SetType( Dali::Camera::FREE_LOOK ); // Camera orientation constrained to point at shadow plane world position
- mCameraActor.SetRotation(Radian(Degree(180)), Vector3::YAXIS);
- mCameraActor.SetPosition(DEFAULT_LIGHT_POSITION);
-
- mShadowRenderShader = ShaderEffect::New( RENDER_SHADOW_VERTEX_SOURCE, RENDER_SHADOW_FRAGMENT_SOURCE,
- Dali::GeometryType( GEOMETRY_TYPE_IMAGE ),
- ShaderEffect::GeometryHints( ShaderEffect::HINT_GRID | ShaderEffect::HINT_BLENDING ));
+ mCameraActor.SetNearClippingPlane(1.0f);
+ mCameraActor.SetType(Dali::Camera::FREE_LOOK); // Camera orientation constrained to point at shadow plane world position
+ mCameraActor.SetProperty(Actor::Property::ORIENTATION, Quaternion(Radian(Degree(180)), Vector3::YAXIS));
+ mCameraActor.SetProperty(Actor::Property::POSITION, DEFAULT_LIGHT_POSITION);
// Create render targets needed for rendering from light's point of view
- mSceneFromLightRenderTarget = FrameBufferImage::New( stageSize.width, stageSize.height, Pixel::RGBA8888 );
+ mSceneFromLightRenderTarget = FrameBuffer::New(stageSize.width, stageSize.height, FrameBuffer::Attachment::NONE);
+ Texture textureFromLight = Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, unsigned(stageSize.width), unsigned(stageSize.height));
+ mSceneFromLightRenderTarget.AttachColorTexture(textureFromLight);
- mOutputImage = FrameBufferImage::New( stageSize.width * 0.5f, stageSize.height * 0.5f, Pixel::RGBA8888 );
+ mOutputFrameBuffer = FrameBuffer::New(stageSize.width * 0.5f, stageSize.height * 0.5f, FrameBuffer::Attachment::NONE);
+ Texture outputTexture = Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, unsigned(stageSize.width * 0.5f), unsigned(stageSize.height * 0.5f));
+ mOutputFrameBuffer.AttachColorTexture(outputTexture);
//////////////////////////////////////////////////////
// Connect to actor tree
- Self().Add( mChildrenRoot );
- Stage::GetCurrent().Add( mCameraActor );
+ Self().Add(mChildrenRoot);
+ Stage::GetCurrent().Add(mCameraActor);
mBlurFilter.SetRefreshOnDemand(false);
- mBlurFilter.SetInputImage(mSceneFromLightRenderTarget);
- mBlurFilter.SetOutputImage(mOutputImage);
+ mBlurFilter.SetInputTexture(mSceneFromLightRenderTarget.GetColorTexture());
+ mBlurFilter.SetOutputFrameBuffer(mOutputFrameBuffer);
mBlurFilter.SetSize(stageSize * 0.5f);
mBlurFilter.SetPixelFormat(Pixel::RGBA8888);
mBlurRootActor = Actor::New();
+ mBlurRootActor.SetProperty(Actor::Property::NAME, "BLUR_ROOT_ACTOR");
// Turn off inheritance to ensure filter renders properly
- mBlurRootActor.SetPositionInheritanceMode(USE_PARENT_POSITION);
- mBlurRootActor.SetInheritRotation(false);
- mBlurRootActor.SetInheritScale(false);
- mBlurRootActor.SetColorMode(USE_OWN_COLOR);
+ mBlurRootActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+ mBlurRootActor.SetProperty(Actor::Property::INHERIT_POSITION, false);
+ mBlurRootActor.SetProperty(Actor::Property::INHERIT_ORIENTATION, false);
+ mBlurRootActor.SetProperty(Actor::Property::INHERIT_SCALE, false);
+ mBlurRootActor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_COLOR);
Self().Add(mBlurRootActor);
mBlurFilter.SetRootActor(mBlurRootActor);
mBlurFilter.SetBackgroundColor(Vector4::ZERO);
- SetShaderConstants();
-}
+ CustomActor self = Self();
+ // Register a property that the user can use to control the blur in the internal object
+ mBlurStrengthPropertyIndex = self.RegisterProperty(BLUR_STRENGTH_PROPERTY_NAME, BLUR_STRENGTH_DEFAULT);
-void ShadowView::OnSizeSet(const Vector3& targetSize)
-{
+ Constraint blurStrengthConstraint = Constraint::New<float>(mBlurFilter.GetHandleForAnimateBlurStrength(), mBlurFilter.GetBlurStrengthPropertyIndex(), EqualToConstraint());
+ blurStrengthConstraint.AddSource(Source(self, mBlurStrengthPropertyIndex));
+ blurStrengthConstraint.Apply();
+
+ DevelControl::SetAccessibilityConstructor(Self(), [](Dali::Actor actor) {
+ return std::unique_ptr<Dali::Accessibility::Accessible>(
+ new DevelControl::AccessibleImpl(actor, Dali::Accessibility::Role::FILLER));
+ });
}
-void ShadowView::OnStageConnection()
+void ShadowView::OnChildAdd(Actor& child)
{
- // TODO: can't call this here, since SetImage() calls fail to connect images to stage, since parent chain not fully on stage yet
- // 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()
- //Activate();
+ if(child != mChildrenRoot && child != mBlurRootActor)
+ {
+ mChildrenRoot.Add(child);
+ }
+
+ Control::OnChildAdd(child);
}
-void ShadowView::OnStageDisconnection()
+void ShadowView::OnChildRemove(Actor& child)
{
- // TODO: can't call this here, since SetImage() calls fails similarly to above
- // 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()
- //Deactivate();
+ mChildrenRoot.Remove(child);
+
+ Control::OnChildRemove(child);
}
void ShadowView::ConstrainCamera()
{
- if( mPointLight && mShadowPlane )
+ if(mPointLight && mShadowPlane)
{
// Constrain camera to look directly at center of shadow plane. (mPointLight position
// is under control of application, can't use transform inheritance)
- Constraint cameraOrientationConstraint =
- Constraint::New<Quaternion> ( Actor::ROTATION,
- Source( mShadowPlane, Actor::WORLD_POSITION ),
- Source( mPointLight, Actor::WORLD_POSITION ),
- Source( mShadowPlane, Actor::WORLD_ROTATION ),
- &LookAt );
-
- mCameraActor.ApplyConstraint( cameraOrientationConstraint );
+ Constraint cameraOrientationConstraint = Constraint::New<Quaternion>(mCameraActor, Actor::Property::ORIENTATION, &LookAt);
+ cameraOrientationConstraint.AddSource(Source(mShadowPlane, Actor::Property::WORLD_POSITION));
+ cameraOrientationConstraint.AddSource(Source(mPointLight, Actor::Property::WORLD_POSITION));
+ cameraOrientationConstraint.AddSource(Source(mShadowPlane, Actor::Property::WORLD_ORIENTATION));
+ cameraOrientationConstraint.Apply();
- Constraint pointLightPositionConstraint = Constraint::New<Vector3>( Actor::POSITION, Source( mPointLight, Actor::WORLD_POSITION ), EqualToConstraint() );
-
- mCameraActor.ApplyConstraint( pointLightPositionConstraint );
+ Constraint pointLightPositionConstraint = Constraint::New<Vector3>(mCameraActor, Actor::Property::POSITION, EqualToConstraint());
+ pointLightPositionConstraint.AddSource(Source(mPointLight, Actor::Property::WORLD_POSITION));
+ pointLightPositionConstraint.Apply();
}
}
// We want the first task to render the scene from the light
mRenderSceneTask = taskList.CreateTask();
- mRenderSceneTask.SetCameraActor( mCameraActor );
- mRenderSceneTask.SetSourceActor( mChildrenRoot );
- mRenderSceneTask.SetTargetFrameBuffer( mSceneFromLightRenderTarget );
- mRenderSceneTask.SetInputEnabled( false );
- mRenderSceneTask.SetClearEnabled( true );
+ mRenderSceneTask.SetCameraActor(mCameraActor);
+ mRenderSceneTask.SetSourceActor(mChildrenRoot);
+ mRenderSceneTask.SetFrameBuffer(mSceneFromLightRenderTarget);
+ mRenderSceneTask.SetInputEnabled(false);
+ mRenderSceneTask.SetClearEnabled(true);
// background color for render task should be the shadow color, but with alpha 0
// we don't want to blend the edges of the content with a BLACK at alpha 0, but
// the same shadow color at alpha 0.
- mRenderSceneTask.SetClearColor( mCachedBackgroundColor );
+ mRenderSceneTask.SetClearColor(mCachedBackgroundColor);
mBlurFilter.Enable();
}
void ShadowView::SetShaderConstants()
{
- CustomActor self = Self();
-
- mShadowRenderShader.SetUniform( SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME, Matrix::IDENTITY );
- mShadowRenderShader.SetUniform( SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME, Matrix::IDENTITY );
- mShadowRenderShader.SetUniform( SHADER_SHADOW_COLOR_PROPERTY_NAME, mCachedShadowColor );
-
- Property::Index lightCameraProjectionMatrixPropertyIndex = mShadowRenderShader.GetPropertyIndex(SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME);
- Property::Index lightCameraViewMatrixPropertyIndex = mShadowRenderShader.GetPropertyIndex(SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME);
-
- Constraint projectionMatrixConstraint = Constraint::New<Dali::Matrix>( lightCameraProjectionMatrixPropertyIndex, Source( mCameraActor, CameraActor::PROJECTION_MATRIX ), EqualToConstraintMatrix());
- Constraint viewMatrixConstraint = Constraint::New<Dali::Matrix>( lightCameraViewMatrixPropertyIndex, Source( mCameraActor, CameraActor::VIEW_MATRIX ), EqualToConstraintMatrix());
-
- mShadowRenderShader.ApplyConstraint(projectionMatrixConstraint);
- mShadowRenderShader.ApplyConstraint(viewMatrixConstraint);
-
- // Register a property that the user can use to control the blur in the internal object
- mBlurStrengthPropertyIndex = self.RegisterProperty(BLUR_STRENGTH_PROPERTY_NAME, BLUR_STRENGTH_DEFAULT);
- mBlurFilter.GetHandleForAnimateBlurStrength().ApplyConstraint( Constraint::New<float>( mBlurFilter.GetBlurStrengthPropertyIndex() ,
- Source( self, mBlurStrengthPropertyIndex),
- EqualToConstraint()) );
+ Property::Index lightCameraProjectionMatrixPropertyIndex = mShadowPlane.RegisterProperty(SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME, Matrix::IDENTITY);
+ Constraint projectionMatrixConstraint = Constraint::New<Dali::Matrix>(mShadowPlane, lightCameraProjectionMatrixPropertyIndex, EqualToConstraint());
+ projectionMatrixConstraint.AddSource(Source(mCameraActor, CameraActor::Property::PROJECTION_MATRIX));
+ projectionMatrixConstraint.Apply();
- // Register a property that the user can use to control the color of the shadow.
- Property::Index index = mShadowRenderShader.GetPropertyIndex(SHADER_SHADOW_COLOR_PROPERTY_NAME);
- mShadowColorPropertyIndex = self.RegisterProperty(SHADOW_COLOR_PROPERTY_NAME, mCachedShadowColor);
+ Property::Index lightCameraViewMatrixPropertyIndex = mShadowPlane.RegisterProperty(SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME, Matrix::IDENTITY);
+ Constraint viewMatrixConstraint = Constraint::New<Dali::Matrix>(mShadowPlane, lightCameraViewMatrixPropertyIndex, EqualToConstraint());
+ viewMatrixConstraint.AddSource(Source(mCameraActor, CameraActor::Property::VIEW_MATRIX));
+ viewMatrixConstraint.Apply();
- mShadowRenderShader.ApplyConstraint(Constraint::New<Dali::Vector4>( index, Source( self, mShadowColorPropertyIndex ), EqualToConstraint()) );
+ mShadowColorPropertyIndex = mShadowPlane.RegisterProperty(SHADER_SHADOW_COLOR_PROPERTY_NAME, mCachedShadowColor);
}
} // namespace Internal