a4d2995efb0a516452ff9cf1fceb0e163fbfbd9c
[platform/core/uifw/dali-demo.git] / examples / waves / waves-example.cpp
1 /*
2  * Copyright (c) 2020 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 "utils.h"
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"
31 #include <fstream>
32 #include <iostream>
33 #include <numeric>
34
35 #include "generated/waves-vert.h"
36 #include "generated/waves-frag.h"
37
38 using namespace Dali;
39
40 namespace
41 {
42
43 const float TIME_STEP = 0.0952664626;
44
45 const std::string UNIFORM_LIGHT_COLOR_SQR = "uLightColorSqr";
46 const std::string UNIFORM_AMBIENT_COLOR = "uAmbientColor";
47 const std::string UNIFORM_INV_LIGHT_DIR = "uInvLightDir";
48 const std::string UNIFORM_SCROLL_SCALE = "uScrollScale";
49 const std::string UNIFORM_WAVE_RATE = "uWaveRate";
50 const std::string UNIFORM_WAVE_AMPLITUDE = "uWaveAmplitude";
51 const std::string UNIFORM_NORMAL_MAP_WEIGHT = "uNormalMapWeight";
52 const std::string UNIFORM_SPECULARITY = "uSpecularity";
53 const std::string UNIFORM_PARALLAX_AMOUNT = "uParallaxAmount";
54 const std::string UNIFORM_TIME = "uTime";
55
56 const Vector3 WAVES_COLOR { .78f, .64f, .26f };
57 const Vector3 LIGHT_COLOR { 1.0f, 0.91f, 0.6f };
58 const Vector3 AMBIENT_COLOR { .002f, .001f, .001f };
59
60 const Vector3 INV_LIGHT_DIR = Normalized(Vector3{ .125f, .8f, -.55f });
61
62 const Vector2 SCROLL_SCALE{ 1.f, 3.5f };
63 const float WAVE_RATE = 12.17f;
64 const float WAVE_AMPLITUDE = 1.f;
65 const float NORMAL_MAP_WEIGHT = 0.05f;
66 const float SPECULARITY = 512.f;
67 const float PARALLAX_AMOUNT = .25f;
68
69 const float TILT_RANGE_DEGREES = 30.f;
70
71 const float TRANSITION_DURATION = 1.2f;
72 const float TRANSITION_TIME_SCALE = 6.f;
73
74 const std::string_view NORMAL_MAP_NAME = "noise512.png";
75
76 Vector3 RandomColor()
77 {
78   float r = .5f + (rand() % RAND_MAX) / float(RAND_MAX) * .5f;
79   float g = .5f + (rand() % RAND_MAX) / float(RAND_MAX) * .5f;
80   float b = .5f + (rand() % RAND_MAX) / float(RAND_MAX) * .5f;
81   return Vector3(r, g, b);
82 }
83
84 class TiltFilter
85 {
86 public:
87   void Reset()
88   {
89     std::fill(mTiltSamples, mTiltSamples + FILTER_SIZE, Vector2(.0f, .0f));
90   }
91
92   void Add(Dali::Vector2 tilt)
93   {
94     mTiltSamples[mIdxNextSample] = tilt;
95     mIdxNextSample = (mIdxNextSample + 1) % FILTER_SIZE;
96   }
97
98   Dali::Vector2 Filter() const
99   {
100     return std::accumulate(mTiltSamples, mTiltSamples + FILTER_SIZE, Vector2(.0f, .0f)) / FILTER_SIZE;
101   }
102
103 private:
104   enum { FILTER_SIZE = 8u };
105
106   Dali::Vector2 mTiltSamples[FILTER_SIZE];
107   size_t mIdxNextSample = 0;
108 };
109
110 } // nonamespace
111
112 class WavesExample : public ConnectionTracker
113 {
114 public:
115   WavesExample( Application& app )
116   : mApp( app )
117   {
118     mApp.InitSignal().Connect( this, &WavesExample::Create );
119     mApp.TerminateSignal().Connect( this, &WavesExample::Destroy );
120   }
121
122   ~WavesExample() = default;
123
124 private:
125   Application& mApp;
126
127   CameraActor mCamera;  // no ownership
128
129   Actor mWaves;
130   Shader mWaveShader;
131
132   Property::Index mUInvLightDir{Property::INVALID_INDEX};
133   Property::Index mULightColorSqr{Property::INVALID_INDEX};
134   Property::Index mUAmbientColor{Property::INVALID_INDEX};
135   Property::Index mUWaveRate{Property::INVALID_INDEX};
136   Property::Index mUWaveAmplitude{Property::INVALID_INDEX};
137   Property::Index mUScrollScale{Property::INVALID_INDEX};
138   Property::Index mUNormalMapWeight{Property::INVALID_INDEX};
139   Property::Index mUSpecularity{Property::INVALID_INDEX};
140   Property::Index mUParallaxAmount{Property::INVALID_INDEX};
141   Property::Index mUTime{Property::INVALID_INDEX};
142
143   TapGestureDetector mDoubleTapGesture;
144
145   TiltSensor mTiltSensor;
146   TiltFilter mTiltFilter;
147
148   PanGestureDetector mPanGesture;
149
150   Animation mTimeAnim;
151   Animation mTransitionAnim;
152
153   void Create( Application& application )
154   {
155     Window window = application.GetWindow();
156     auto rootLayer = window.GetRootLayer();
157
158     window.SetBackgroundColor(Vector4(WAVES_COLOR * .5f));
159
160     // Get camera
161     RenderTaskList tasks = window.GetRenderTaskList();
162     RenderTask mainPass = tasks.GetTask(0);
163     CameraActor camera = mainPass.GetCameraActor();
164     mCamera = camera;
165
166     // NOTE: watchface doesn't tolerate modification of the camera well;
167     /// we're better off rotating the world.
168     Quaternion baseOrientation (Radian(Degree(-150.f)), Radian(M_PI), Radian(0.f));
169
170     auto shader = CreateShader();
171
172     // Create geometry
173     Geometry geom = CreateTesselatedQuad(16, 64, Vector2{ .25f, 3.8f }, [](const Vector2& v) {
174       float y = v.y + .5f;  // 0..1
175       y = std::sqrt(y) - .5f; // perspective correction - increase vertex density closer to viewer
176
177       float x = v.x + v.x * (1.f - y) * 5.5f;
178
179       y -= .24f;  // further translation
180       return Vector2{ x, y };
181     }, [](const Vector2& v) {
182       return Vector2{ v.x, std::sqrt(v.y) };
183     });
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 }