2 * Copyright (c) 2021 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.
22 #include "dali/devel-api/adaptor-framework/tilt-sensor.h"
23 #include "dali/public-api/actors/camera-actor.h"
24 #include "dali/public-api/actors/layer.h"
25 #include "dali/public-api/adaptor-framework/application.h"
26 #include "dali/public-api/adaptor-framework/key.h"
27 #include "dali/public-api/animation/animation.h"
28 #include "dali/public-api/events/key-event.h"
29 #include "dali/public-api/events/pan-gesture-detector.h"
30 #include "dali/public-api/events/tap-gesture-detector.h"
31 #include "dali/public-api/render-tasks/render-task-list.h"
32 #include "dali/public-api/render-tasks/render-task.h"
35 #include "generated/waves-frag.h"
36 #include "generated/waves-vert.h"
42 const float TIME_STEP = 0.0952664626;
44 const std::string UNIFORM_LIGHT_COLOR_SQR = "uLightColorSqr";
45 const std::string UNIFORM_AMBIENT_COLOR = "uAmbientColor";
46 const std::string UNIFORM_INV_LIGHT_DIR = "uInvLightDir";
47 const std::string UNIFORM_SCROLL_SCALE = "uScrollScale";
48 const std::string UNIFORM_WAVE_RATE = "uWaveRate";
49 const std::string UNIFORM_WAVE_AMPLITUDE = "uWaveAmplitude";
50 const std::string UNIFORM_NORMAL_MAP_WEIGHT = "uNormalMapWeight";
51 const std::string UNIFORM_SPECULARITY = "uSpecularity";
52 const std::string UNIFORM_PARALLAX_AMOUNT = "uParallaxAmount";
53 const std::string UNIFORM_TIME = "uTime";
55 const Vector3 WAVES_COLOR{.78f, .64f, .26f};
56 const Vector3 LIGHT_COLOR{1.0f, 0.91f, 0.6f};
57 const Vector3 AMBIENT_COLOR{.002f, .001f, .001f};
59 const Vector3 INV_LIGHT_DIR = Normalized(Vector3{.125f, .8f, -.55f});
61 const Vector2 SCROLL_SCALE{1.f, 3.5f};
62 const float WAVE_RATE = 12.17f;
63 const float WAVE_AMPLITUDE = 1.f;
64 const float NORMAL_MAP_WEIGHT = 0.05f;
65 const float SPECULARITY = 512.f;
66 const float PARALLAX_AMOUNT = .25f;
68 const float TILT_RANGE_DEGREES = 30.f;
70 const float TRANSITION_DURATION = 1.2f;
71 const float TRANSITION_TIME_SCALE = 6.f;
73 const std::string_view NORMAL_MAP_NAME = "noise512.png";
77 float r = .5f + (rand() % RAND_MAX) / float(RAND_MAX) * .5f;
78 float g = .5f + (rand() % RAND_MAX) / float(RAND_MAX) * .5f;
79 float b = .5f + (rand() % RAND_MAX) / float(RAND_MAX) * .5f;
80 return Vector3(r, g, b);
88 std::fill(mTiltSamples, mTiltSamples + FILTER_SIZE, Vector2(.0f, .0f));
91 void Add(Dali::Vector2 tilt)
93 mTiltSamples[mIdxNextSample] = tilt;
94 mIdxNextSample = (mIdxNextSample + 1) % FILTER_SIZE;
97 Dali::Vector2 Filter() const
99 return std::accumulate(mTiltSamples, mTiltSamples + FILTER_SIZE, Vector2(.0f, .0f)) / FILTER_SIZE;
108 Dali::Vector2 mTiltSamples[FILTER_SIZE];
109 size_t mIdxNextSample = 0;
114 class WavesExample : public ConnectionTracker
117 WavesExample(Application& app)
120 mApp.InitSignal().Connect(this, &WavesExample::Create);
121 mApp.TerminateSignal().Connect(this, &WavesExample::Destroy);
124 ~WavesExample() = default;
129 CameraActor mCamera; // no ownership
134 Property::Index mUInvLightDir{Property::INVALID_INDEX};
135 Property::Index mULightColorSqr{Property::INVALID_INDEX};
136 Property::Index mUAmbientColor{Property::INVALID_INDEX};
137 Property::Index mUWaveRate{Property::INVALID_INDEX};
138 Property::Index mUWaveAmplitude{Property::INVALID_INDEX};
139 Property::Index mUScrollScale{Property::INVALID_INDEX};
140 Property::Index mUNormalMapWeight{Property::INVALID_INDEX};
141 Property::Index mUSpecularity{Property::INVALID_INDEX};
142 Property::Index mUParallaxAmount{Property::INVALID_INDEX};
143 Property::Index mUTime{Property::INVALID_INDEX};
145 TapGestureDetector mDoubleTapGesture;
147 TiltSensor mTiltSensor;
148 TiltFilter mTiltFilter;
150 PanGestureDetector mPanGesture;
153 Animation mTransitionAnim;
155 void Create(Application& application)
157 Window window = application.GetWindow();
158 auto rootLayer = window.GetRootLayer();
160 window.SetBackgroundColor(Vector4(WAVES_COLOR * .5f));
163 RenderTaskList tasks = window.GetRenderTaskList();
164 RenderTask mainPass = tasks.GetTask(0);
165 CameraActor camera = mainPass.GetCameraActor();
168 // NOTE: watchface doesn't tolerate modification of the camera well;
169 /// we're better off rotating the world.
170 Quaternion baseOrientation(Radian(Degree(-150.f)), Radian(M_PI), Radian(0.f));
172 auto shader = CreateShader();
175 Geometry geom = CreateTesselatedQuad(
176 16, 64, Vector2{.25f, 3.8f}, [](const Vector2& v) {
177 float y = v.y + .5f; // 0..1
178 y = std::sqrt(y) - .5f; // perspective correction - increase vertex density closer to viewer
180 float x = v.x + v.x * (1.f - y) * 5.5f;
182 y -= .24f; // further translation
183 return Vector2{ x, y }; }, [](const Vector2& v) { return Vector2{v.x, std::sqrt(v.y)}; });
186 auto normalMap = LoadTexture(std::string(DEMO_IMAGE_DIR) + NORMAL_MAP_NAME.data());
188 TextureSet textures = TextureSet::New();
189 textures.SetTexture(0, normalMap);
191 Sampler sampler = Sampler::New();
192 sampler.SetFilterMode(FilterMode::NEAREST, FilterMode::NEAREST);
193 sampler.SetWrapMode(WrapMode::REPEAT, WrapMode::REPEAT);
194 textures.SetSampler(0, sampler);
197 Renderer renderer = CreateRenderer(textures, geom, shader, OPTION_DEPTH_TEST | OPTION_DEPTH_WRITE);
199 auto waves = CreateActor();
200 auto size = Vector2(window.GetSize());
201 waves.SetProperty(Actor::Property::SIZE, Vector3(size.x, 100.f, size.y));
202 waves.SetProperty(Actor::Property::ORIENTATION, baseOrientation);
203 waves.SetProperty(Actor::Property::COLOR, WAVES_COLOR);
204 waves.AddRenderer(renderer);
209 window.KeyEventSignal().Connect(this, &WavesExample::OnKeyEvent);
211 // Setup double tap detector for color change
212 mDoubleTapGesture = TapGestureDetector::New(2);
213 mDoubleTapGesture.Attach(rootLayer);
214 mDoubleTapGesture.DetectedSignal().Connect(this, &WavesExample::OnDoubleTap);
217 mTiltSensor = TiltSensor::Get();
218 if(mTiltSensor.Start())
220 // Get notifications when the device is tilted
221 mTiltSensor.TiltedSignal().Connect(this, &WavesExample::OnTilted);
225 mPanGesture = PanGestureDetector::New();
226 mPanGesture.Attach(rootLayer);
227 mPanGesture.DetectedSignal().Connect(this, &WavesExample::OnPan);
230 // Register for suspend / resume
231 application.PauseSignal().Connect(this, &WavesExample::OnPause);
232 application.ResumeSignal().Connect(this, &WavesExample::OnResume);
234 // Create animation for the simulation of time
235 Animation animTime = Animation::New(1.f);
236 animTime.AnimateBy(Property(mWaveShader, mUTime), TIME_STEP);
237 animTime.FinishedSignal().Connect(this, &WavesExample::OnTimeAnimFinished);
239 mTimeAnim = animTime;
242 void Destroy(Application& app)
246 mDoubleTapGesture.Reset();
249 UnparentAndReset(mWaves);
252 Shader CreateShader()
254 Vector3 lightColorSqr{LIGHT_COLOR};
255 Vector3 ambientColor = AMBIENT_COLOR;
256 Vector3 invLightDir = INV_LIGHT_DIR;
257 Vector2 scrollScale = SCROLL_SCALE;
258 float waveRate = WAVE_RATE;
259 float waveAmp = WAVE_AMPLITUDE;
260 float normalMapWeight = NORMAL_MAP_WEIGHT;
261 float specularity = SPECULARITY;
262 float parallaxAmount = PARALLAX_AMOUNT;
265 lightColorSqr = mWaveShader.GetProperty(mULightColorSqr).Get<Vector3>();
266 ambientColor = mWaveShader.GetProperty(mUAmbientColor).Get<Vector3>();
267 invLightDir = mWaveShader.GetProperty(mUInvLightDir).Get<Vector3>();
268 scrollScale = mWaveShader.GetProperty(mUScrollScale).Get<Vector2>();
269 waveRate = mWaveShader.GetProperty(mUWaveRate).Get<float>();
270 waveAmp = mWaveShader.GetProperty(mUWaveAmplitude).Get<float>();
271 normalMapWeight = mWaveShader.GetProperty(mUNormalMapWeight).Get<float>();
272 specularity = mWaveShader.GetProperty(mUSpecularity).Get<float>();
275 Shader shader = Shader::New(SHADER_WAVES_VERT, SHADER_WAVES_FRAG, Shader::Hint::MODIFIES_GEOMETRY);
276 mULightColorSqr = shader.RegisterProperty(UNIFORM_LIGHT_COLOR_SQR, lightColorSqr);
277 mUAmbientColor = shader.RegisterProperty(UNIFORM_AMBIENT_COLOR, ambientColor);
278 mUInvLightDir = shader.RegisterProperty(UNIFORM_INV_LIGHT_DIR, invLightDir);
279 mUScrollScale = shader.RegisterProperty(UNIFORM_SCROLL_SCALE, scrollScale);
280 mUWaveRate = shader.RegisterProperty(UNIFORM_WAVE_RATE, waveRate);
281 mUWaveAmplitude = shader.RegisterProperty(UNIFORM_WAVE_AMPLITUDE, waveAmp);
282 mUNormalMapWeight = shader.RegisterProperty(UNIFORM_NORMAL_MAP_WEIGHT, normalMapWeight);
283 mUSpecularity = shader.RegisterProperty(UNIFORM_SPECULARITY, specularity);
284 mUParallaxAmount = shader.RegisterProperty(UNIFORM_PARALLAX_AMOUNT, parallaxAmount);
285 mUTime = shader.RegisterProperty(UNIFORM_TIME, 0.f);
287 auto window = mApp.GetWindow();
288 shader.RegisterProperty("uScreenHalfSize", Vector2(window.GetSize()) * .5f);
289 mWaveShader = shader;
294 void TriggerColorTransition(Vector3 wavesColor, Vector3 lightColor)
298 mTransitionAnim.Stop();
301 mTimeAnim.FinishedSignal().Disconnect(this, &WavesExample::OnTimeAnimFinished);
304 Animation anim = Animation::New(TRANSITION_DURATION);
305 anim.AnimateTo(Property(mWaves, Actor::Property::COLOR), Vector4(wavesColor), AlphaFunction::EASE_IN_OUT);
306 anim.AnimateTo(Property(mWaveShader, mULightColorSqr), lightColor * lightColor, AlphaFunction::EASE_IN_OUT);
307 anim.AnimateBy(Property(mWaveShader, mUTime), TRANSITION_DURATION * TIME_STEP * TRANSITION_TIME_SCALE, AlphaFunction::EASE_IN_OUT);
308 anim.FinishedSignal().Connect(this, &WavesExample::OnTransitionFinished);
310 mTransitionAnim = anim;
313 void OnTimeAnimFinished(Animation& anim)
318 void OnTransitionFinished(Animation& anim)
320 mTransitionAnim.Reset();
321 mTimeAnim.FinishedSignal().Connect(this, &WavesExample::OnTimeAnimFinished);
325 void OnPause(Application& app)
331 void OnResume(Application& app)
337 void OnKeyEvent(const KeyEvent& event)
339 if(event.GetState() == KeyEvent::UP) // single keystrokes
341 if(IsKey(event, DALI_KEY_ESCAPE) || IsKey(event, DALI_KEY_BACK))
348 void OnDoubleTap(Actor /*actor*/, const TapGesture& gesture)
350 Vector3 lightColor = mWaveShader.GetProperty(mULightColorSqr).Get<Vector3>();
351 TriggerColorTransition(lightColor, RandomColor());
354 void OnPan(Actor actor, const PanGesture& gesture)
356 auto tilt = gesture.GetDisplacement() / Vector2(mApp.GetWindow().GetSize());
357 switch(gesture.GetState())
359 case GestureState::STARTED:
360 mTiltFilter.Add(tilt);
363 case GestureState::CONTINUING:
364 mTiltFilter.Add(mTiltFilter.Filter() + tilt);
371 UpdateLightDirection();
374 void OnTilted(const TiltSensor& sensor)
376 mTiltFilter.Add(Vector2(sensor.GetPitch(), sensor.GetRoll()));
378 UpdateLightDirection();
381 void UpdateLightDirection()
383 Vector2 tilt = mTiltFilter.Filter();
384 Quaternion q(Radian(tilt.y), Radian(-tilt.x), Radian(0.f));
385 Vector3 lightDir = q.Rotate(INV_LIGHT_DIR);
386 mWaveShader.SetProperty(mUInvLightDir, lightDir);
390 int DALI_EXPORT_API main(int argc, char** argv)
392 Application application = Application::New(&argc, &argv, DEMO_THEME_PATH);
393 WavesExample example(application);
394 application.MainLoop();