2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "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.
19 #include <dali-toolkit/dali-toolkit.h>
20 #include <dali/dali.h>
21 #include <dali/devel-api/adaptor-framework/file-loader.h>
22 #include <dali/integration-api/debug.h>
29 #include "generated/refraction-effect-flat-frag.h"
30 #include "generated/refraction-effect-flat-vert.h"
31 #include "generated/refraction-effect-refraction-frag.h"
32 #include "generated/refraction-effect-refraction-vert.h"
33 #include "shared/utility.h"
34 #include "shared/view.h"
40 const char* const APPLICATION_TITLE("Refraction Effect");
41 const char* const TOOLBAR_IMAGE(DEMO_IMAGE_DIR "top-bar.png");
42 const char* const CHANGE_TEXTURE_ICON(DEMO_IMAGE_DIR "icon-change.png");
43 const char* const CHANGE_TEXTURE_ICON_SELECTED(DEMO_IMAGE_DIR "icon-change-selected.png");
44 const char* const CHANGE_MESH_ICON(DEMO_IMAGE_DIR "icon-replace.png");
45 const char* const CHANGE_MESH_ICON_SELECTED(DEMO_IMAGE_DIR "icon-replace-selected.png");
47 const char* MESH_FILES[] =
49 DEMO_MODEL_DIR "surface_pattern_v01.obj",
50 DEMO_MODEL_DIR "surface_pattern_v02.obj"};
51 const unsigned int NUM_MESH_FILES(sizeof(MESH_FILES) / sizeof(MESH_FILES[0]));
53 const char* TEXTURE_IMAGES[] =
55 DEMO_IMAGE_DIR "background-1.jpg",
56 DEMO_IMAGE_DIR "background-2.jpg",
57 DEMO_IMAGE_DIR "background-3.jpg",
58 DEMO_IMAGE_DIR "background-4.jpg"};
59 const unsigned int NUM_TEXTURE_IMAGES(sizeof(TEXTURE_IMAGES) / sizeof(TEXTURE_IMAGES[0]));
61 struct LightOffsetConstraint
63 LightOffsetConstraint(float radius)
68 void operator()(Vector2& current, const PropertyInputContainer& inputs)
70 float spinAngle = inputs[0]->GetFloat();
71 current.x = cos(spinAngle);
72 current.y = sin(spinAngle);
81 * structure of the vertex in the mesh
92 /*************************************************/
93 /*Demo using RefractionEffect*****************/
94 /*************************************************/
95 class RefractionEffectExample : public ConnectionTracker
98 RefractionEffectExample(Application& application)
99 : mApplication(application),
109 mLightXYOffsetIndex(Property::INVALID_INDEX),
110 mSpinAngleIndex(Property::INVALID_INDEX),
111 mLightIntensityIndex(Property::INVALID_INDEX),
112 mEffectStrengthIndex(Property::INVALID_INDEX),
113 mChangeTextureButton(),
115 mCurrentTextureId(1),
118 // Connect to the Application's Init signal
119 application.InitSignal().Connect(this, &RefractionEffectExample::Create);
122 ~RefractionEffectExample()
127 // The Init signal is received once (only) during the Application lifetime
128 void Create(Application& application)
130 Window window = application.GetWindow();
131 Vector2 windowSize = window.GetSize();
133 window.KeyEventSignal().Connect(this, &RefractionEffectExample::OnKeyEvent);
135 // Creates a default view with a default tool bar.
136 // The view is added to the window.
137 Toolkit::ToolBar toolBar;
138 Toolkit::Control view;
139 mContent = DemoHelper::CreateView(application,
146 // Add a button to change background. (right of toolbar)
147 mChangeTextureButton = Toolkit::PushButton::New();
148 mChangeTextureButton.SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, CHANGE_TEXTURE_ICON);
149 mChangeTextureButton.SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, CHANGE_TEXTURE_ICON_SELECTED);
150 mChangeTextureButton.ClickedSignal().Connect(this, &RefractionEffectExample::OnChangeTexture);
151 toolBar.AddControl(mChangeTextureButton,
152 DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage,
153 Toolkit::Alignment::HORIZONTAL_RIGHT,
154 DemoHelper::DEFAULT_MODE_SWITCH_PADDING);
155 // Add a button to change mesh pattern. ( left of bar )
156 mChangeMeshButton = Toolkit::PushButton::New();
157 mChangeMeshButton.SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, CHANGE_MESH_ICON);
158 mChangeMeshButton.SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, CHANGE_MESH_ICON_SELECTED);
159 mChangeMeshButton.ClickedSignal().Connect(this, &RefractionEffectExample::OnChangeMesh);
160 toolBar.AddControl(mChangeMeshButton,
161 DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage,
162 Toolkit::Alignment::HORIZONTAL_LEFT,
163 DemoHelper::DEFAULT_MODE_SWITCH_PADDING);
165 // shader used when the screen is not touched, render a flat surface
166 mShaderFlat = Shader::New(SHADER_REFRACTION_EFFECT_FLAT_VERT, SHADER_REFRACTION_EFFECT_FLAT_FRAG);
167 mGeometry = CreateGeometry(MESH_FILES[mCurrentMeshId]);
169 Texture texture = DemoHelper::LoadWindowFillingTexture(window.GetSize(), TEXTURE_IMAGES[mCurrentTextureId]);
170 mTextureSet = TextureSet::New();
171 mTextureSet.SetTexture(0u, texture);
173 mRenderer = Renderer::New(mGeometry, mShaderFlat);
174 mRenderer.SetTextures(mTextureSet);
176 mMeshActor = Actor::New();
177 mMeshActor.AddRenderer(mRenderer);
178 mMeshActor.SetProperty(Actor::Property::SIZE, windowSize);
179 mMeshActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
180 mContent.Add(mMeshActor);
182 // Connect the callback to the touch signal on the mesh actor
183 mContent.TouchedSignal().Connect(this, &RefractionEffectExample::OnTouch);
185 // shader used when the finger is touching the screen. render refraction effect
186 mShaderRefraction = Shader::New(SHADER_REFRACTION_EFFECT_REFRACTION_VERT, SHADER_REFRACTION_EFFECT_REFRACTION_FRAG);
189 mLightXYOffsetIndex = mMeshActor.RegisterProperty("uLightXYOffset", Vector2::ZERO);
191 mLightIntensityIndex = mMeshActor.RegisterProperty("uLightIntensity", 2.5f);
193 mEffectStrengthIndex = mMeshActor.RegisterProperty("uEffectStrength", 0.f);
195 Vector3 lightPosition(-windowSize.x * 0.5f, -windowSize.y * 0.5f, windowSize.x * 0.5f); // top_left
196 mMeshActor.RegisterProperty("uLightPosition", lightPosition);
198 Property::Index lightSpinOffsetIndex = mMeshActor.RegisterProperty("uLightSpinOffset", Vector2::ZERO);
200 mSpinAngleIndex = mMeshActor.RegisterProperty("uSpinAngle", 0.f);
201 Constraint constraint = Constraint::New<Vector2>(mMeshActor, lightSpinOffsetIndex, LightOffsetConstraint(windowSize.x * 0.1f));
202 constraint.AddSource(LocalSource(mSpinAngleIndex));
205 // the animation which spin the light around the finger touch position
206 mLightAnimation = Animation::New(2.f);
207 mLightAnimation.AnimateTo(Property(mMeshActor, mSpinAngleIndex), Math::PI * 2.f);
208 mLightAnimation.SetLooping(true);
209 mLightAnimation.Pause();
212 void SetLightXYOffset(const Vector2& offset)
214 mMeshActor.SetProperty(mLightXYOffsetIndex, offset);
218 * Create a mesh actor with different geometry to replace the current one
220 bool OnChangeMesh(Toolkit::Button button)
222 mCurrentMeshId = (mCurrentMeshId + 1) % NUM_MESH_FILES;
223 mGeometry = CreateGeometry(MESH_FILES[mCurrentMeshId]);
224 mRenderer.SetGeometry(mGeometry);
229 bool OnChangeTexture(Toolkit::Button button)
231 mCurrentTextureId = (mCurrentTextureId + 1) % NUM_TEXTURE_IMAGES;
232 Texture texture = DemoHelper::LoadWindowFillingTexture(mApplication.GetWindow().GetSize(), TEXTURE_IMAGES[mCurrentTextureId]);
233 mTextureSet.SetTexture(0u, texture);
237 bool OnTouch(Actor actor, const TouchEvent& event)
239 switch(event.GetState(0))
241 case PointState::DOWN:
243 mRenderer.SetShader(mShaderRefraction);
245 SetLightXYOffset(event.GetScreenPosition(0));
247 mLightAnimation.Play();
249 if(mStrenghAnimation)
251 mStrenghAnimation.Clear();
254 mStrenghAnimation = Animation::New(0.5f);
255 mStrenghAnimation.AnimateTo(Property(mMeshActor, mEffectStrengthIndex), 1.f);
256 mStrenghAnimation.Play();
260 case PointState::MOTION:
262 // make the light position following the finger movement
263 SetLightXYOffset(event.GetScreenPosition(0));
267 case PointState::LEAVE:
268 case PointState::INTERRUPTED:
270 mLightAnimation.Pause();
272 if(mStrenghAnimation)
274 mStrenghAnimation.Clear();
276 mStrenghAnimation = Animation::New(0.5f);
277 mStrenghAnimation.AnimateTo(Property(mMeshActor, mEffectStrengthIndex), 0.f);
278 mStrenghAnimation.FinishedSignal().Connect(this, &RefractionEffectExample::OnTouchFinished);
279 mStrenghAnimation.Play();
282 case PointState::STATIONARY:
291 void OnTouchFinished(Animation& source)
293 mRenderer.SetShader(mShaderFlat);
294 SetLightXYOffset(Vector2::ZERO);
297 Geometry CreateGeometry(const std::string& objFileName)
299 std::vector<Vector3> vertexPositions;
300 Vector<unsigned int> faceIndices;
301 Vector<float> boundingBox;
302 // read the vertice and faces from the .obj file, and record the bounding box
303 ReadObjFile(objFileName, boundingBox, vertexPositions, faceIndices);
305 std::vector<Vector2> textureCoordinates;
306 // align the mesh, scale it to fit the screen size, and calculate the texture coordinate for each vertex
307 ShapeResizeAndTexureCoordinateCalculation(boundingBox, vertexPositions, textureCoordinates);
309 // re-organize the mesh, the vertices are duplicated, each vertex only belongs to one triangle.
310 // Without sharing vertex between triangle, so we can manipulate the texture offset on each triangle conveniently.
311 std::vector<Vertex> vertices;
313 std::size_t size = faceIndices.Size();
314 vertices.reserve(size);
316 for(std::size_t i = 0; i < size; i = i + 3)
318 Vector3 edge1 = vertexPositions[faceIndices[i + 2]] - vertexPositions[faceIndices[i]];
319 Vector3 edge2 = vertexPositions[faceIndices[i + 1]] - vertexPositions[faceIndices[i]];
320 Vector3 normal = edge1.Cross(edge2);
323 // make sure all the faces are front-facing
326 vertices.push_back(Vertex{vertexPositions[faceIndices[i]], normal, textureCoordinates[faceIndices[i]]});
327 vertices.push_back(Vertex{vertexPositions[faceIndices[i + 1]], normal, textureCoordinates[faceIndices[i + 1]]});
328 vertices.push_back(Vertex{vertexPositions[faceIndices[i + 2]], normal, textureCoordinates[faceIndices[i + 2]]});
333 vertices.push_back(Vertex{vertexPositions[faceIndices[i]], normal, textureCoordinates[faceIndices[i]]});
334 vertices.push_back(Vertex{vertexPositions[faceIndices[i + 2]], normal, textureCoordinates[faceIndices[i + 2]]});
335 vertices.push_back(Vertex{vertexPositions[faceIndices[i + 1]], normal, textureCoordinates[faceIndices[i + 1]]});
339 Property::Map vertexFormat;
340 vertexFormat["aPosition"] = Property::VECTOR3;
341 vertexFormat["aNormal"] = Property::VECTOR3;
342 vertexFormat["aTexCoord"] = Property::VECTOR2;
343 VertexBuffer surfaceVertices = VertexBuffer::New(vertexFormat);
344 surfaceVertices.SetData(&vertices[0], vertices.size());
346 Geometry surface = Geometry::New();
347 surface.AddVertexBuffer(surfaceVertices);
352 void ReadObjFile(const std::string& objFileName,
353 Vector<float>& boundingBox,
354 std::vector<Vector3>& vertexPositions,
355 Vector<unsigned int>& faceIndices)
357 std::streampos bufferSize = 0;
358 Dali::Vector<char> fileBuffer;
359 if(!Dali::FileLoader::ReadFile(objFileName, bufferSize, fileBuffer, Dali::FileLoader::FileType::TEXT))
361 DALI_LOG_WARNING("file open failed for: \"%s\"", objFileName.c_str());
365 fileBuffer.PushBack('\0');
367 std::stringstream iss(&fileBuffer[0], std::ios::in);
369 boundingBox.Resize(6);
370 boundingBox[0] = boundingBox[2] = boundingBox[4] = std::numeric_limits<float>::max();
371 boundingBox[1] = boundingBox[3] = boundingBox[5] = -std::numeric_limits<float>::max();
374 while(std::getline(iss, line))
376 if(line[0] == 'v' && std::isspace(line[1])) // vertex
378 std::istringstream vertexIss(line.substr(2), std::istringstream::in);
381 while(vertexIss >> vertex[i++] && i < 3)
383 if(vertex.x < boundingBox[0]) boundingBox[0] = vertex.x;
384 if(vertex.x > boundingBox[1]) boundingBox[1] = vertex.x;
385 if(vertex.y < boundingBox[2]) boundingBox[2] = vertex.y;
386 if(vertex.y > boundingBox[3]) boundingBox[3] = vertex.y;
387 if(vertex.z < boundingBox[4]) boundingBox[4] = vertex.z;
388 if(vertex.z > boundingBox[5]) boundingBox[5] = vertex.z;
389 vertexPositions.push_back(vertex);
391 else if(line[0] == 'f') //face
393 unsigned int numOfInt = 3;
396 std::size_t found = line.find('/');
397 if(found == std::string::npos)
405 std::istringstream faceIss(line.substr(2), std::istringstream::in);
406 Dali::Vector<unsigned int> indices;
407 indices.Resize(numOfInt);
409 while(faceIss >> indices[i++] && i < numOfInt)
411 unsigned int step = (i + 1) / 3;
412 faceIndices.PushBack(indices[0] - 1);
413 faceIndices.PushBack(indices[step] - 1);
414 faceIndices.PushBack(indices[2 * step] - 1);
419 void ShapeResizeAndTexureCoordinateCalculation(const Vector<float>& boundingBox,
420 std::vector<Vector3>& vertexPositions,
421 std::vector<Vector2>& textureCoordinates)
423 Vector3 bBoxSize(boundingBox[1] - boundingBox[0], boundingBox[3] - boundingBox[2], boundingBox[5] - boundingBox[4]);
424 Vector3 bBoxMinCorner(boundingBox[0], boundingBox[2], boundingBox[4]);
426 Vector2 windowSize = mApplication.GetWindow().GetSize();
427 Vector3 scale(windowSize.x / bBoxSize.x, windowSize.y / bBoxSize.y, 1.f);
428 scale.z = (scale.x + scale.y) / 2.f;
430 textureCoordinates.reserve(vertexPositions.size());
432 for(std::vector<Vector3>::iterator iter = vertexPositions.begin(); iter != vertexPositions.end(); iter++)
434 Vector3 newPosition((*iter) - bBoxMinCorner);
436 textureCoordinates.push_back(Vector2(newPosition.x / bBoxSize.x, newPosition.y / bBoxSize.y));
438 newPosition -= bBoxSize * 0.5f;
439 (*iter) = newPosition * scale;
444 * Main key event handler
446 void OnKeyEvent(const KeyEvent& event)
448 if(event.GetState() == KeyEvent::DOWN)
450 if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
458 Application& mApplication;
460 TextureSet mTextureSet;
466 Shader mShaderRefraction;
468 Animation mLightAnimation;
469 Animation mStrenghAnimation;
471 Property::Index mLightXYOffsetIndex;
472 Property::Index mSpinAngleIndex;
473 Property::Index mLightIntensityIndex;
474 Property::Index mEffectStrengthIndex;
476 Toolkit::PushButton mChangeTextureButton;
477 Toolkit::PushButton mChangeMeshButton;
478 unsigned int mCurrentTextureId;
479 unsigned int mCurrentMeshId;
482 /*****************************************************************************/
484 int DALI_EXPORT_API main(int argc, char** argv)
486 Application app = Application::New(&argc, &argv, DEMO_THEME_PATH);
487 RefractionEffectExample theApp(app);