2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 ( "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.
20 #include "dali/devel-api/adaptor-framework/tilt-sensor.h"
21 #include "dali/public-api/adaptor-framework/application.h"
22 #include "dali/public-api/adaptor-framework/key.h"
23 #include "dali/public-api/animation/animation.h"
24 #include "dali/public-api/events/pan-gesture-detector.h"
25 #include "dali/public-api/events/tap-gesture-detector.h"
26 #include "dali/public-api/events/key-event.h"
27 #include "dali/public-api/actors/camera-actor.h"
28 #include "dali/public-api/actors/layer.h"
29 #include "dali/public-api/render-tasks/render-task.h"
30 #include "dali/public-api/render-tasks/render-task-list.h"
40 constexpr std::string_view WAVES_VSH =
41 "#define FMA(a, b, c) ((a) * (b) + (c))\n" // fused multiply-add
43 precision highp float;
45 const float kTile = 1.;
47 const float kPi = 3.1415926535;
48 const float kEpsilon = 1. / 32.;
52 uniform mat4 uModelView;
53 uniform mat4 uProjection;
54 uniform mat3 uNormalMatrix;
58 uniform vec2 uScrollScale;
59 uniform float uWaveRate;
60 uniform float uWaveAmplitude;
61 uniform float uParallaxAmount;
63 attribute vec2 aPosition;
64 attribute vec2 aTexCoord;
67 varying vec3 vViewPos;
69 varying float vHeight;
71 float CubicHermite(float B, float C, float t)
73 float dCB = (C - B) * .5;
76 vec3 p = vec3(D + .5 * (((B - C) * 3.) - A), A - 2.5 * B + 2. * C - D,
78 return FMA(FMA(FMA(p.x, t, p.y), t, p.z), t, B);
83 return fract(sin(n) * 43751.5453123);
86 float HeightAtTile(vec2 pos)
88 float rate = Hash(Hash(pos.x) * Hash(pos.y));
90 return (sin(uTime * rate * uWaveRate) * .5 + .5) * uWaveAmplitude;
93 float CalculateHeight(vec2 position)
95 vec2 tile = floor(position);
96 position = fract(position);
100 HeightAtTile(tile + vec2( kTile * -0.5, kTile * -0.5)),
101 HeightAtTile(tile + vec2( kTile * +0.5, kTile * -0.5)),
104 HeightAtTile(tile + vec2( kTile * -0.5, kTile * +0.5)),
105 HeightAtTile(tile + vec2( kTile * +0.5, kTile * +0.5)),
109 return CubicHermite(cp.x, cp.y, position.y);
112 vec3 CalculateNormal(vec2 position)
115 CalculateHeight(vec2(position.x - kEpsilon, position.y)) -
116 CalculateHeight(vec2(position.x + kEpsilon, position.y)),
118 CalculateHeight(vec2(position.x, position.y - kEpsilon)) -
119 CalculateHeight(vec2(position.x, position.y + kEpsilon))
128 vec2 scrollPosition = aPosition * uScrollScale + vec2(0., uTime * -kPi);
129 vNormal = uNormalMatrix * CalculateNormal(scrollPosition);
131 float h = CalculateHeight(scrollPosition);
132 vHeight = h * uParallaxAmount;
133 vec3 position = vec3(aPosition.x, h, aPosition.y);
135 vec4 viewPosition = uModelView * vec4(position * uSize, 1.);
136 vViewPos = -viewPosition.xyz;
138 gl_Position = uProjection * viewPosition;
141 constexpr std::string_view WAVES_FSH = DALI_COMPOSE_SHADER(
142 precision highp float;
144 uniform vec4 uColor; // DALi
145 uniform sampler2D uNormalMap; // DALi
147 uniform vec3 uInvLightDir;
148 uniform vec3 uLightColorSqr;
149 uniform vec3 uAmbientColor;
151 uniform float uNormalMapWeight;
152 uniform float uSpecularity;
155 varying vec3 vNormal;
156 varying vec3 vViewPos;
157 varying float vHeight;
161 return fract(sin(dot(co.xy, vec2(12.98981, 78.2331))) * 43758.5453);
166 return v.x + v.y + v.z;
171 vec3 viewPos = normalize(vViewPos);
172 vec2 uv2 = vUv + vViewPos.xy / vViewPos.z * vHeight + vec2(.5, 0.);
174 vec3 perturbNormal = texture2D(uNormalMap, vUv).rgb * 2. - 1.;
175 vec3 perturbNormal2 = texture2D(uNormalMap, uv2).rgb * 2. - 1.;
176 vec3 normal = normalize(vNormal + perturbNormal * uNormalMapWeight);
177 vec3 normal2 = normalize(vNormal + perturbNormal2 * uNormalMapWeight);
179 vec3 color = uAmbientColor;
180 float d = max(0., dot(normal, -uInvLightDir));
181 color += uColor.rgb * d;
183 vec3 reflected = reflect(uInvLightDir, normal);
184 d = max(0., dot(reflected, viewPos));
185 color += pow(d, uSpecularity) * uLightColorSqr;
187 reflected = reflect(uInvLightDir, normal2);
188 d = max(0., dot(reflected, viewPos));
189 color += pow(d, uSpecularity) * uLightColorSqr;
191 gl_FragColor = vec4(color, 1.);
194 const float TIME_STEP = 0.0952664626;
196 const std::string UNIFORM_LIGHT_COLOR_SQR = "uLightColorSqr";
197 const std::string UNIFORM_AMBIENT_COLOR = "uAmbientColor";
198 const std::string UNIFORM_INV_LIGHT_DIR = "uInvLightDir";
199 const std::string UNIFORM_SCROLL_SCALE = "uScrollScale";
200 const std::string UNIFORM_WAVE_RATE = "uWaveRate";
201 const std::string UNIFORM_WAVE_AMPLITUDE = "uWaveAmplitude";
202 const std::string UNIFORM_NORMAL_MAP_WEIGHT = "uNormalMapWeight";
203 const std::string UNIFORM_SPECULARITY = "uSpecularity";
204 const std::string UNIFORM_PARALLAX_AMOUNT = "uParallaxAmount";
205 const std::string UNIFORM_TIME = "uTime";
207 const Vector3 WAVES_COLOR { .78f, .64f, .26f };
208 const Vector3 LIGHT_COLOR { 1.0f, 0.91f, 0.6f };
209 const Vector3 AMBIENT_COLOR { .002f, .001f, .001f };
211 const Vector3 INV_LIGHT_DIR = Normalized(Vector3{ .125f, .8f, -.55f });
213 const Vector2 SCROLL_SCALE{ 1.f, 3.5f };
214 const float WAVE_RATE = 12.17f;
215 const float WAVE_AMPLITUDE = 1.f;
216 const float NORMAL_MAP_WEIGHT = 0.05f;
217 const float SPECULARITY = 512.f;
218 const float PARALLAX_AMOUNT = .25f;
220 const float TILT_RANGE_DEGREES = 30.f;
222 const float TRANSITION_DURATION = 1.2f;
223 const float TRANSITION_TIME_SCALE = 6.f;
225 const std::string_view NORMAL_MAP_NAME = "noise512.png";
227 Vector3 RandomColor()
229 float r = .5f + (rand() % RAND_MAX) / float(RAND_MAX) * .5f;
230 float g = .5f + (rand() % RAND_MAX) / float(RAND_MAX) * .5f;
231 float b = .5f + (rand() % RAND_MAX) / float(RAND_MAX) * .5f;
232 return Vector3(r, g, b);
240 std::fill(mTiltSamples, mTiltSamples + FILTER_SIZE, Vector2(.0f, .0f));
243 void Add(Dali::Vector2 tilt)
245 mTiltSamples[mIdxNextSample] = tilt;
246 mIdxNextSample = (mIdxNextSample + 1) % FILTER_SIZE;
249 Dali::Vector2 Filter() const
251 return std::accumulate(mTiltSamples, mTiltSamples + FILTER_SIZE, Vector2(.0f, .0f)) / FILTER_SIZE;
255 enum { FILTER_SIZE = 8u };
257 Dali::Vector2 mTiltSamples[FILTER_SIZE];
258 size_t mIdxNextSample = 0;
263 class WavesExample : public ConnectionTracker
266 WavesExample( Application& app )
269 mApp.InitSignal().Connect( this, &WavesExample::Create );
270 mApp.TerminateSignal().Connect( this, &WavesExample::Destroy );
273 ~WavesExample() = default;
278 CameraActor mCamera; // no ownership
283 Property::Index mUInvLightDir;
284 Property::Index mULightColorSqr;
285 Property::Index mUAmbientColor;
286 Property::Index mUWaveRate;
287 Property::Index mUWaveAmplitude;
288 Property::Index mUScrollScale;
289 Property::Index mUNormalMapWeight;
290 Property::Index mUSpecularity;
291 Property::Index mUParallaxAmount;
292 Property::Index mUTime;
294 TapGestureDetector mDoubleTapGesture;
296 TiltSensor mTiltSensor;
297 TiltFilter mTiltFilter;
299 PanGestureDetector mPanGesture;
302 Animation mTransitionAnim;
304 void Create( Application& application )
306 Window window = application.GetWindow();
307 auto rootLayer = window.GetRootLayer();
309 window.SetBackgroundColor(Vector4(WAVES_COLOR * .5f));
312 RenderTaskList tasks = window.GetRenderTaskList();
313 RenderTask mainPass = tasks.GetTask(0);
314 CameraActor camera = mainPass.GetCameraActor();
317 // NOTE: watchface doesn't tolerate modification of the camera well;
318 /// we're better off rotating the world.
319 Quaternion baseOrientation (Radian(Degree(-150.f)), Radian(M_PI), Radian(0.f));
321 auto shader = CreateShader();
324 Geometry geom = CreateTesselatedQuad(16, 64, Vector2{ .25f, 3.8f }, [](const Vector2& v) {
325 float y = v.y + .5f; // 0..1
326 y = std::sqrt(y) - .5f; // perspective correction - increase vertex density closer to viewer
328 float x = v.x + v.x * (1.f - y) * 5.5f;
330 y -= .24f; // further translation
331 return Vector2{ x, y };
332 }, [](const Vector2& v) {
333 return Vector2{ v.x, std::sqrt(v.y) };
337 auto normalMap = LoadTexture(std::string(DEMO_IMAGE_DIR) + NORMAL_MAP_NAME.data());
339 TextureSet textures = TextureSet::New();
340 textures.SetTexture(0, normalMap);
342 Sampler sampler = Sampler::New();
343 sampler.SetFilterMode(FilterMode::NEAREST, FilterMode::NEAREST);
344 sampler.SetWrapMode(WrapMode::REPEAT, WrapMode::REPEAT);
345 textures.SetSampler(0, sampler);
348 Renderer renderer = CreateRenderer(textures, geom, shader, OPTION_DEPTH_TEST | OPTION_DEPTH_WRITE);
350 auto waves = CreateActor();
351 auto size = Vector2(window.GetSize());
352 waves.SetProperty(Actor::Property::SIZE, Vector3(size.x, 100.f, size.y));
353 waves.SetProperty(Actor::Property::ORIENTATION, baseOrientation);
354 waves.SetProperty(Actor::Property::COLOR, WAVES_COLOR);
355 waves.AddRenderer(renderer);
360 window.KeyEventSignal().Connect( this, &WavesExample::OnKeyEvent );
362 // Setup double tap detector for color change
363 mDoubleTapGesture = TapGestureDetector::New(2);
364 mDoubleTapGesture.Attach(rootLayer);
365 mDoubleTapGesture.DetectedSignal().Connect(this, &WavesExample::OnDoubleTap);
368 mTiltSensor = TiltSensor::Get();
369 if ( mTiltSensor.Start() )
371 // Get notifications when the device is tilted
372 mTiltSensor.TiltedSignal().Connect( this, &WavesExample::OnTilted );
376 mPanGesture = PanGestureDetector::New();
377 mPanGesture.Attach(rootLayer);
378 mPanGesture.DetectedSignal().Connect(this, &WavesExample::OnPan);
381 // Register for suspend / resume
382 application.PauseSignal().Connect(this, &WavesExample::OnPause);
383 application.ResumeSignal().Connect(this, &WavesExample::OnResume);
385 // Create animation for the simulation of time
386 Animation animTime = Animation::New(1.f);
387 animTime.AnimateBy(Property(mWaveShader, mUTime), TIME_STEP);
388 animTime.FinishedSignal().Connect(this, &WavesExample::OnTimeAnimFinished);
390 mTimeAnim = animTime;
393 void Destroy( Application& app)
397 mDoubleTapGesture.Reset();
400 UnparentAndReset(mWaves);
403 Shader CreateShader()
405 Vector3 lightColorSqr{ LIGHT_COLOR };
406 Vector3 ambientColor = AMBIENT_COLOR;
407 Vector3 invLightDir = INV_LIGHT_DIR;
408 Vector2 scrollScale = SCROLL_SCALE;
409 float waveRate = WAVE_RATE;
410 float waveAmp = WAVE_AMPLITUDE;
411 float normalMapWeight = NORMAL_MAP_WEIGHT;
412 float specularity = SPECULARITY;
413 float parallaxAmount = PARALLAX_AMOUNT;
416 lightColorSqr = mWaveShader.GetProperty(mULightColorSqr).Get<Vector3>();
417 ambientColor = mWaveShader.GetProperty(mUAmbientColor).Get<Vector3>();
418 invLightDir = mWaveShader.GetProperty(mUInvLightDir).Get<Vector3>();
419 scrollScale = mWaveShader.GetProperty(mUScrollScale).Get<Vector2>();
420 waveRate = mWaveShader.GetProperty(mUWaveRate).Get<float>();
421 waveAmp = mWaveShader.GetProperty(mUWaveAmplitude).Get<float>();
422 normalMapWeight = mWaveShader.GetProperty(mUNormalMapWeight).Get<float>();
423 specularity = mWaveShader.GetProperty(mUSpecularity).Get<float>();
426 Shader shader = Shader::New(WAVES_VSH.data(), WAVES_FSH.data(), Shader::Hint::MODIFIES_GEOMETRY);
427 mULightColorSqr = shader.RegisterProperty(UNIFORM_LIGHT_COLOR_SQR, lightColorSqr);
428 mUAmbientColor = shader.RegisterProperty(UNIFORM_AMBIENT_COLOR, ambientColor);
429 mUInvLightDir = shader.RegisterProperty(UNIFORM_INV_LIGHT_DIR, invLightDir);
430 mUScrollScale = shader.RegisterProperty(UNIFORM_SCROLL_SCALE, scrollScale);
431 mUWaveRate = shader.RegisterProperty(UNIFORM_WAVE_RATE, waveRate);
432 mUWaveAmplitude = shader.RegisterProperty(UNIFORM_WAVE_AMPLITUDE, waveAmp);
433 mUNormalMapWeight = shader.RegisterProperty(UNIFORM_NORMAL_MAP_WEIGHT, normalMapWeight);
434 mUSpecularity = shader.RegisterProperty(UNIFORM_SPECULARITY, specularity);
435 mUParallaxAmount = shader.RegisterProperty(UNIFORM_PARALLAX_AMOUNT, parallaxAmount);
436 mUTime = shader.RegisterProperty(UNIFORM_TIME, 0.f);
438 auto window = mApp.GetWindow();
439 shader.RegisterProperty("uScreenHalfSize", Vector2(window.GetSize()) * .5f);
440 mWaveShader = shader;
445 void TriggerColorTransition(Vector3 wavesColor, Vector3 lightColor)
449 mTransitionAnim.Stop();
452 mTimeAnim.FinishedSignal().Disconnect(this, &WavesExample::OnTimeAnimFinished);
455 Animation anim = Animation::New(TRANSITION_DURATION);
456 anim.AnimateTo(Property(mWaves, Actor::Property::COLOR), Vector4(wavesColor), AlphaFunction::EASE_IN_OUT);
457 anim.AnimateTo(Property(mWaveShader, mULightColorSqr), lightColor * lightColor, AlphaFunction::EASE_IN_OUT);
458 anim.AnimateBy(Property(mWaveShader, mUTime), TRANSITION_DURATION * TIME_STEP * TRANSITION_TIME_SCALE, AlphaFunction::EASE_IN_OUT);
459 anim.FinishedSignal().Connect(this, &WavesExample::OnTransitionFinished);
461 mTransitionAnim = anim;
464 void OnTimeAnimFinished(Animation& anim)
469 void OnTransitionFinished(Animation& anim)
471 mTransitionAnim.Reset();
472 mTimeAnim.FinishedSignal().Connect(this, &WavesExample::OnTimeAnimFinished);
476 void OnPause(Application& app)
482 void OnResume(Application& app)
488 void OnKeyEvent(const KeyEvent& event)
490 if ( event.GetState() == KeyEvent::UP) // single keystrokes
492 if( IsKey( event, DALI_KEY_ESCAPE ) || IsKey( event, DALI_KEY_BACK ) )
499 void OnDoubleTap(Actor /*actor*/, const TapGesture& gesture)
501 Vector3 lightColor = mWaveShader.GetProperty(mULightColorSqr).Get<Vector3>();
502 TriggerColorTransition(lightColor, RandomColor());
505 void OnPan(Actor actor, const PanGesture& gesture)
507 auto tilt = gesture.GetDisplacement() / Vector2(mApp.GetWindow().GetSize());
508 switch (gesture.GetState())
510 case GestureState::STARTED:
511 mTiltFilter.Add(tilt);
514 case GestureState::CONTINUING:
515 mTiltFilter.Add(mTiltFilter.Filter() + tilt);
522 UpdateLightDirection();
525 void OnTilted( const TiltSensor& sensor)
527 mTiltFilter.Add(Vector2(sensor.GetPitch(), sensor.GetRoll()));
529 UpdateLightDirection();
532 void UpdateLightDirection()
534 Vector2 tilt = mTiltFilter.Filter();
535 Quaternion q(Radian(tilt.y), Radian(-tilt.x), Radian(0.f));
536 Vector3 lightDir = q.Rotate(INV_LIGHT_DIR);
537 mWaveShader.SetProperty(mUInvLightDir, lightDir);
541 int DALI_EXPORT_API main( int argc, char **argv )
543 Application application = Application::New( &argc, &argv, DEMO_THEME_PATH );
544 WavesExample example( application);
545 application.MainLoop();