Updated all files to new format
[platform/core/uifw/dali-demo.git] / examples / waves / waves-example.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // INTERNAL INCLUDES
19 #include <fstream>
20 #include <iostream>
21 #include <numeric>
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"
33 #include "utils.h"
34
35 #include "generated/waves-frag.h"
36 #include "generated/waves-vert.h"
37
38 using namespace Dali;
39
40 namespace
41 {
42 const float TIME_STEP = 0.0952664626;
43
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";
54
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};
58
59 const Vector3 INV_LIGHT_DIR = Normalized(Vector3{.125f, .8f, -.55f});
60
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;
67
68 const float TILT_RANGE_DEGREES = 30.f;
69
70 const float TRANSITION_DURATION   = 1.2f;
71 const float TRANSITION_TIME_SCALE = 6.f;
72
73 const std::string_view NORMAL_MAP_NAME = "noise512.png";
74
75 Vector3 RandomColor()
76 {
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);
81 }
82
83 class TiltFilter
84 {
85 public:
86   void Reset()
87   {
88     std::fill(mTiltSamples, mTiltSamples + FILTER_SIZE, Vector2(.0f, .0f));
89   }
90
91   void Add(Dali::Vector2 tilt)
92   {
93     mTiltSamples[mIdxNextSample] = tilt;
94     mIdxNextSample               = (mIdxNextSample + 1) % FILTER_SIZE;
95   }
96
97   Dali::Vector2 Filter() const
98   {
99     return std::accumulate(mTiltSamples, mTiltSamples + FILTER_SIZE, Vector2(.0f, .0f)) / FILTER_SIZE;
100   }
101
102 private:
103   enum
104   {
105     FILTER_SIZE = 8u
106   };
107
108   Dali::Vector2 mTiltSamples[FILTER_SIZE];
109   size_t        mIdxNextSample = 0;
110 };
111
112 } // namespace
113
114 class WavesExample : public ConnectionTracker
115 {
116 public:
117   WavesExample(Application& app)
118   : mApp(app)
119   {
120     mApp.InitSignal().Connect(this, &WavesExample::Create);
121     mApp.TerminateSignal().Connect(this, &WavesExample::Destroy);
122   }
123
124   ~WavesExample() = default;
125
126 private:
127   Application& mApp;
128
129   CameraActor mCamera; // no ownership
130
131   Actor  mWaves;
132   Shader mWaveShader;
133
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};
144
145   TapGestureDetector mDoubleTapGesture;
146
147   TiltSensor mTiltSensor;
148   TiltFilter mTiltFilter;
149
150   PanGestureDetector mPanGesture;
151
152   Animation mTimeAnim;
153   Animation mTransitionAnim;
154
155   void Create(Application& application)
156   {
157     Window window    = application.GetWindow();
158     auto   rootLayer = window.GetRootLayer();
159
160     window.SetBackgroundColor(Vector4(WAVES_COLOR * .5f));
161
162     // Get camera
163     RenderTaskList tasks    = window.GetRenderTaskList();
164     RenderTask     mainPass = tasks.GetTask(0);
165     CameraActor    camera   = mainPass.GetCameraActor();
166     mCamera                 = camera;
167
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));
171
172     auto shader = CreateShader();
173
174     // Create geometry
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
179
180       float x = v.x + v.x * (1.f - y) * 5.5f;
181
182       y -= .24f;  // further translation
183       return Vector2{ x, y }; }, [](const Vector2& v) { return Vector2{v.x, std::sqrt(v.y)}; });
184
185     // Create texture
186     auto normalMap = LoadTexture(std::string(DEMO_IMAGE_DIR) + NORMAL_MAP_NAME.data());
187
188     TextureSet textures = TextureSet::New();
189     textures.SetTexture(0, normalMap);
190
191     Sampler sampler = Sampler::New();
192     sampler.SetFilterMode(FilterMode::NEAREST, FilterMode::NEAREST);
193     sampler.SetWrapMode(WrapMode::REPEAT, WrapMode::REPEAT);
194     textures.SetSampler(0, sampler);
195
196     // Create renderer
197     Renderer renderer = CreateRenderer(textures, geom, shader, OPTION_DEPTH_TEST | OPTION_DEPTH_WRITE);
198
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);
205
206     window.Add(waves);
207     mWaves = waves;
208
209     window.KeyEventSignal().Connect(this, &WavesExample::OnKeyEvent);
210
211     // Setup double tap detector for color change
212     mDoubleTapGesture = TapGestureDetector::New(2);
213     mDoubleTapGesture.Attach(rootLayer);
214     mDoubleTapGesture.DetectedSignal().Connect(this, &WavesExample::OnDoubleTap);
215
216     // Touch controls
217     mTiltSensor = TiltSensor::Get();
218     if(mTiltSensor.Start())
219     {
220       // Get notifications when the device is tilted
221       mTiltSensor.TiltedSignal().Connect(this, &WavesExample::OnTilted);
222     }
223     else
224     {
225       mPanGesture = PanGestureDetector::New();
226       mPanGesture.Attach(rootLayer);
227       mPanGesture.DetectedSignal().Connect(this, &WavesExample::OnPan);
228     }
229
230     // Register for suspend / resume
231     application.PauseSignal().Connect(this, &WavesExample::OnPause);
232     application.ResumeSignal().Connect(this, &WavesExample::OnResume);
233
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);
238     animTime.Play();
239     mTimeAnim = animTime;
240   }
241
242   void Destroy(Application& app)
243   {
244     mCamera.Reset();
245
246     mDoubleTapGesture.Reset();
247     mPanGesture.Reset();
248
249     UnparentAndReset(mWaves);
250   }
251
252   Shader CreateShader()
253   {
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;
263     if(mWaveShader)
264     {
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>();
273     }
274
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);
286
287     auto window = mApp.GetWindow();
288     shader.RegisterProperty("uScreenHalfSize", Vector2(window.GetSize()) * .5f);
289     mWaveShader = shader;
290
291     return shader;
292   }
293
294   void TriggerColorTransition(Vector3 wavesColor, Vector3 lightColor)
295   {
296     if(mTransitionAnim)
297     {
298       mTransitionAnim.Stop();
299     }
300
301     mTimeAnim.FinishedSignal().Disconnect(this, &WavesExample::OnTimeAnimFinished);
302     mTimeAnim.Stop();
303
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);
309     anim.Play();
310     mTransitionAnim = anim;
311   }
312
313   void OnTimeAnimFinished(Animation& anim)
314   {
315     anim.Play();
316   }
317
318   void OnTransitionFinished(Animation& anim)
319   {
320     mTransitionAnim.Reset();
321     mTimeAnim.FinishedSignal().Connect(this, &WavesExample::OnTimeAnimFinished);
322     mTimeAnim.Play();
323   }
324
325   void OnPause(Application& app)
326   {
327     mTimeAnim.Pause();
328     mTiltSensor.Stop();
329   }
330
331   void OnResume(Application& app)
332   {
333     mTiltSensor.Start();
334     mTimeAnim.Play();
335   }
336
337   void OnKeyEvent(const KeyEvent& event)
338   {
339     if(event.GetState() == KeyEvent::UP) // single keystrokes
340     {
341       if(IsKey(event, DALI_KEY_ESCAPE) || IsKey(event, DALI_KEY_BACK))
342       {
343         mApp.Quit();
344       }
345     }
346   }
347
348   void OnDoubleTap(Actor /*actor*/, const TapGesture& gesture)
349   {
350     Vector3 lightColor = mWaveShader.GetProperty(mULightColorSqr).Get<Vector3>();
351     TriggerColorTransition(lightColor, RandomColor());
352   }
353
354   void OnPan(Actor actor, const PanGesture& gesture)
355   {
356     auto tilt = gesture.GetDisplacement() / Vector2(mApp.GetWindow().GetSize());
357     switch(gesture.GetState())
358     {
359       case GestureState::STARTED:
360         mTiltFilter.Add(tilt);
361         break;
362
363       case GestureState::CONTINUING:
364         mTiltFilter.Add(mTiltFilter.Filter() + tilt);
365         break;
366
367       default:
368         break;
369     }
370
371     UpdateLightDirection();
372   }
373
374   void OnTilted(const TiltSensor& sensor)
375   {
376     mTiltFilter.Add(Vector2(sensor.GetPitch(), sensor.GetRoll()));
377
378     UpdateLightDirection();
379   }
380
381   void UpdateLightDirection()
382   {
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);
387   }
388 };
389
390 int DALI_EXPORT_API main(int argc, char** argv)
391 {
392   Application  application = Application::New(&argc, &argv, DEMO_THEME_PATH);
393   WavesExample example(application);
394   application.MainLoop();
395   return 0;
396 }