2 * Copyright (c) 2020 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 "shared/utility.h"
30 #include "shared/view.h"
31 #include "generated/refraction-effect-flat-vert.h"
32 #include "generated/refraction-effect-flat-frag.h"
33 #include "generated/refraction-effect-refraction-vert.h"
34 #include "generated/refraction-effect-refraction-frag.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
93 Vertex(const Vector3& position, const Vector3& normal, const Vector2& textureCoord)
96 textureCoord(textureCoord)
103 /*************************************************/
104 /*Demo using RefractionEffect*****************/
105 /*************************************************/
106 class RefractionEffectExample : public ConnectionTracker
109 RefractionEffectExample(Application& application)
110 : mApplication(application),
120 mLightXYOffsetIndex(Property::INVALID_INDEX),
121 mSpinAngleIndex(Property::INVALID_INDEX),
122 mLightIntensityIndex(Property::INVALID_INDEX),
123 mEffectStrengthIndex(Property::INVALID_INDEX),
124 mChangeTextureButton(),
126 mCurrentTextureId(1),
129 // Connect to the Application's Init signal
130 application.InitSignal().Connect(this, &RefractionEffectExample::Create);
133 ~RefractionEffectExample()
138 // The Init signal is received once (only) during the Application lifetime
139 void Create(Application& application)
141 Window window = application.GetWindow();
142 Vector2 windowSize = window.GetSize();
144 window.KeyEventSignal().Connect(this, &RefractionEffectExample::OnKeyEvent);
146 // Creates a default view with a default tool bar.
147 // The view is added to the window.
148 Toolkit::ToolBar toolBar;
149 Toolkit::Control view;
150 mContent = DemoHelper::CreateView(application,
157 // Add a button to change background. (right of toolbar)
158 mChangeTextureButton = Toolkit::PushButton::New();
159 mChangeTextureButton.SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, CHANGE_TEXTURE_ICON);
160 mChangeTextureButton.SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, CHANGE_TEXTURE_ICON_SELECTED);
161 mChangeTextureButton.ClickedSignal().Connect(this, &RefractionEffectExample::OnChangeTexture);
162 toolBar.AddControl(mChangeTextureButton,
163 DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage,
164 Toolkit::Alignment::HORIZONTAL_RIGHT,
165 DemoHelper::DEFAULT_MODE_SWITCH_PADDING);
166 // Add a button to change mesh pattern. ( left of bar )
167 mChangeMeshButton = Toolkit::PushButton::New();
168 mChangeMeshButton.SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, CHANGE_MESH_ICON);
169 mChangeMeshButton.SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, CHANGE_MESH_ICON_SELECTED);
170 mChangeMeshButton.ClickedSignal().Connect(this, &RefractionEffectExample::OnChangeMesh);
171 toolBar.AddControl(mChangeMeshButton,
172 DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage,
173 Toolkit::Alignment::HORIZONTAL_LEFT,
174 DemoHelper::DEFAULT_MODE_SWITCH_PADDING);
176 // shader used when the screen is not touched, render a flat surface
177 mShaderFlat = Shader::New(SHADER_REFRACTION_EFFECT_FLAT_VERT, SHADER_REFRACTION_EFFECT_FLAT_FRAG);
178 mGeometry = CreateGeometry(MESH_FILES[mCurrentMeshId]);
180 Texture texture = DemoHelper::LoadWindowFillingTexture(window.GetSize(), TEXTURE_IMAGES[mCurrentTextureId]);
181 mTextureSet = TextureSet::New();
182 mTextureSet.SetTexture(0u, texture);
184 mRenderer = Renderer::New(mGeometry, mShaderFlat);
185 mRenderer.SetTextures(mTextureSet);
187 mMeshActor = Actor::New();
188 mMeshActor.AddRenderer(mRenderer);
189 mMeshActor.SetProperty(Actor::Property::SIZE, windowSize);
190 mMeshActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
191 mContent.Add(mMeshActor);
193 // Connect the callback to the touch signal on the mesh actor
194 mContent.TouchedSignal().Connect(this, &RefractionEffectExample::OnTouch);
196 // shader used when the finger is touching the screen. render refraction effect
197 mShaderRefraction = Shader::New(SHADER_REFRACTION_EFFECT_REFRACTION_VERT, SHADER_REFRACTION_EFFECT_REFRACTION_FRAG);
200 mLightXYOffsetIndex = mMeshActor.RegisterProperty("uLightXYOffset", Vector2::ZERO);
202 mLightIntensityIndex = mMeshActor.RegisterProperty("uLightIntensity", 2.5f);
204 mEffectStrengthIndex = mMeshActor.RegisterProperty("uEffectStrength", 0.f);
206 Vector3 lightPosition(-windowSize.x * 0.5f, -windowSize.y * 0.5f, windowSize.x * 0.5f); // top_left
207 mMeshActor.RegisterProperty("uLightPosition", lightPosition);
209 Property::Index lightSpinOffsetIndex = mMeshActor.RegisterProperty("uLightSpinOffset", Vector2::ZERO);
211 mSpinAngleIndex = mMeshActor.RegisterProperty("uSpinAngle", 0.f);
212 Constraint constraint = Constraint::New<Vector2>(mMeshActor, lightSpinOffsetIndex, LightOffsetConstraint(windowSize.x * 0.1f));
213 constraint.AddSource(LocalSource(mSpinAngleIndex));
216 // the animation which spin the light around the finger touch position
217 mLightAnimation = Animation::New(2.f);
218 mLightAnimation.AnimateTo(Property(mMeshActor, mSpinAngleIndex), Math::PI * 2.f);
219 mLightAnimation.SetLooping(true);
220 mLightAnimation.Pause();
223 void SetLightXYOffset(const Vector2& offset)
225 mMeshActor.SetProperty(mLightXYOffsetIndex, offset);
229 * Create a mesh actor with different geometry to replace the current one
231 bool OnChangeMesh(Toolkit::Button button)
233 mCurrentMeshId = (mCurrentMeshId + 1) % NUM_MESH_FILES;
234 mGeometry = CreateGeometry(MESH_FILES[mCurrentMeshId]);
235 mRenderer.SetGeometry(mGeometry);
240 bool OnChangeTexture(Toolkit::Button button)
242 mCurrentTextureId = (mCurrentTextureId + 1) % NUM_TEXTURE_IMAGES;
243 Texture texture = DemoHelper::LoadWindowFillingTexture(mApplication.GetWindow().GetSize(), TEXTURE_IMAGES[mCurrentTextureId]);
244 mTextureSet.SetTexture(0u, texture);
248 bool OnTouch(Actor actor, const TouchEvent& event)
250 switch(event.GetState(0))
252 case PointState::DOWN:
254 mRenderer.SetShader(mShaderRefraction);
256 SetLightXYOffset(event.GetScreenPosition(0));
258 mLightAnimation.Play();
260 if(mStrenghAnimation)
262 mStrenghAnimation.Clear();
265 mStrenghAnimation = Animation::New(0.5f);
266 mStrenghAnimation.AnimateTo(Property(mMeshActor, mEffectStrengthIndex), 1.f);
267 mStrenghAnimation.Play();
271 case PointState::MOTION:
273 // make the light position following the finger movement
274 SetLightXYOffset(event.GetScreenPosition(0));
278 case PointState::LEAVE:
279 case PointState::INTERRUPTED:
281 mLightAnimation.Pause();
283 if(mStrenghAnimation)
285 mStrenghAnimation.Clear();
287 mStrenghAnimation = Animation::New(0.5f);
288 mStrenghAnimation.AnimateTo(Property(mMeshActor, mEffectStrengthIndex), 0.f);
289 mStrenghAnimation.FinishedSignal().Connect(this, &RefractionEffectExample::OnTouchFinished);
290 mStrenghAnimation.Play();
293 case PointState::STATIONARY:
302 void OnTouchFinished(Animation& source)
304 mRenderer.SetShader(mShaderFlat);
305 SetLightXYOffset(Vector2::ZERO);
308 Geometry CreateGeometry(const std::string& objFileName)
310 std::vector<Vector3> vertexPositions;
311 Vector<unsigned int> faceIndices;
312 Vector<float> boundingBox;
313 // read the vertice and faces from the .obj file, and record the bounding box
314 ReadObjFile(objFileName, boundingBox, vertexPositions, faceIndices);
316 std::vector<Vector2> textureCoordinates;
317 // align the mesh, scale it to fit the screen size, and calculate the texture coordinate for each vertex
318 ShapeResizeAndTexureCoordinateCalculation(boundingBox, vertexPositions, textureCoordinates);
320 // re-organize the mesh, the vertices are duplicated, each vertex only belongs to one triangle.
321 // Without sharing vertex between triangle, so we can manipulate the texture offset on each triangle conveniently.
322 std::vector<Vertex> vertices;
324 std::size_t size = faceIndices.Size();
325 vertices.reserve(size);
327 for(std::size_t i = 0; i < size; i = i + 3)
329 Vector3 edge1 = vertexPositions[faceIndices[i + 2]] - vertexPositions[faceIndices[i]];
330 Vector3 edge2 = vertexPositions[faceIndices[i + 1]] - vertexPositions[faceIndices[i]];
331 Vector3 normal = edge1.Cross(edge2);
334 // make sure all the faces are front-facing
337 vertices.push_back(Vertex(vertexPositions[faceIndices[i]], normal, textureCoordinates[faceIndices[i]]));
338 vertices.push_back(Vertex(vertexPositions[faceIndices[i + 1]], normal, textureCoordinates[faceIndices[i + 1]]));
339 vertices.push_back(Vertex(vertexPositions[faceIndices[i + 2]], normal, textureCoordinates[faceIndices[i + 2]]));
344 vertices.push_back(Vertex(vertexPositions[faceIndices[i]], normal, textureCoordinates[faceIndices[i]]));
345 vertices.push_back(Vertex(vertexPositions[faceIndices[i + 2]], normal, textureCoordinates[faceIndices[i + 2]]));
346 vertices.push_back(Vertex(vertexPositions[faceIndices[i + 1]], normal, textureCoordinates[faceIndices[i + 1]]));
350 Property::Map vertexFormat;
351 vertexFormat["aPosition"] = Property::VECTOR3;
352 vertexFormat["aNormal"] = Property::VECTOR3;
353 vertexFormat["aTexCoord"] = Property::VECTOR2;
354 VertexBuffer surfaceVertices = VertexBuffer::New(vertexFormat);
355 surfaceVertices.SetData(&vertices[0], vertices.size());
357 Geometry surface = Geometry::New();
358 surface.AddVertexBuffer(surfaceVertices);
363 void ReadObjFile(const std::string& objFileName,
364 Vector<float>& boundingBox,
365 std::vector<Vector3>& vertexPositions,
366 Vector<unsigned int>& faceIndices)
368 std::streampos bufferSize = 0;
369 Dali::Vector<char> fileBuffer;
370 if(!Dali::FileLoader::ReadFile(objFileName, bufferSize, fileBuffer, Dali::FileLoader::FileType::TEXT))
372 DALI_LOG_WARNING("file open failed for: \"%s\"", objFileName.c_str());
376 fileBuffer.PushBack('\0');
378 std::stringstream iss(&fileBuffer[0], std::ios::in);
380 boundingBox.Resize(6);
381 boundingBox[0] = boundingBox[2] = boundingBox[4] = std::numeric_limits<float>::max();
382 boundingBox[1] = boundingBox[3] = boundingBox[5] = -std::numeric_limits<float>::max();
385 while(std::getline(iss, line))
387 if(line[0] == 'v' && std::isspace(line[1])) // vertex
389 std::istringstream iss(line.substr(2), std::istringstream::in);
392 while(iss >> vertex[i++] && i < 3)
394 if(vertex.x < boundingBox[0]) boundingBox[0] = vertex.x;
395 if(vertex.x > boundingBox[1]) boundingBox[1] = vertex.x;
396 if(vertex.y < boundingBox[2]) boundingBox[2] = vertex.y;
397 if(vertex.y > boundingBox[3]) boundingBox[3] = vertex.y;
398 if(vertex.z < boundingBox[4]) boundingBox[4] = vertex.z;
399 if(vertex.z > boundingBox[5]) boundingBox[5] = vertex.z;
400 vertexPositions.push_back(vertex);
402 else if(line[0] == 'f') //face
404 unsigned int numOfInt = 3;
407 std::size_t found = line.find('/');
408 if(found == std::string::npos)
416 std::istringstream iss(line.substr(2), std::istringstream::in);
417 Dali::Vector<unsigned int> indices;
418 indices.Resize(numOfInt);
420 while(iss >> indices[i++] && i < numOfInt)
422 unsigned int step = (i + 1) / 3;
423 faceIndices.PushBack(indices[0] - 1);
424 faceIndices.PushBack(indices[step] - 1);
425 faceIndices.PushBack(indices[2 * step] - 1);
430 void ShapeResizeAndTexureCoordinateCalculation(const Vector<float>& boundingBox,
431 std::vector<Vector3>& vertexPositions,
432 std::vector<Vector2>& textureCoordinates)
434 Vector3 bBoxSize(boundingBox[1] - boundingBox[0], boundingBox[3] - boundingBox[2], boundingBox[5] - boundingBox[4]);
435 Vector3 bBoxMinCorner(boundingBox[0], boundingBox[2], boundingBox[4]);
437 Vector2 windowSize = mApplication.GetWindow().GetSize();
438 Vector3 scale(windowSize.x / bBoxSize.x, windowSize.y / bBoxSize.y, 1.f);
439 scale.z = (scale.x + scale.y) / 2.f;
441 textureCoordinates.reserve(vertexPositions.size());
443 for(std::vector<Vector3>::iterator iter = vertexPositions.begin(); iter != vertexPositions.end(); iter++)
445 Vector3 newPosition((*iter) - bBoxMinCorner);
447 textureCoordinates.push_back(Vector2(newPosition.x / bBoxSize.x, newPosition.y / bBoxSize.y));
449 newPosition -= bBoxSize * 0.5f;
450 (*iter) = newPosition * scale;
455 * Main key event handler
457 void OnKeyEvent(const KeyEvent& event)
459 if(event.GetState() == KeyEvent::DOWN)
461 if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
469 Application& mApplication;
471 TextureSet mTextureSet;
477 Shader mShaderRefraction;
479 Animation mLightAnimation;
480 Animation mStrenghAnimation;
482 Property::Index mLightXYOffsetIndex;
483 Property::Index mSpinAngleIndex;
484 Property::Index mLightIntensityIndex;
485 Property::Index mEffectStrengthIndex;
487 Toolkit::PushButton mChangeTextureButton;
488 Toolkit::PushButton mChangeMeshButton;
489 unsigned int mCurrentTextureId;
490 unsigned int mCurrentMeshId;
493 /*****************************************************************************/
495 int DALI_EXPORT_API main(int argc, char** argv)
497 Application app = Application::New(&argc, &argv, DEMO_THEME_PATH);
498 RefractionEffectExample theApp(app);