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.
18 #include <dali-toolkit/dali-toolkit.h>
19 #include <dali/devel-api/actors/camera-actor-devel.h>
20 #include <dali/devel-api/adaptor-framework/file-stream.h>
24 #include "generated/reflection-frag.h"
25 #include "generated/reflection-plasma-frag.h"
26 #include "generated/reflection-simple-frag.h"
27 #include "generated/reflection-tex-frag.h"
28 #include "generated/reflection-textured-frag.h"
29 #include "generated/reflection-vert.h"
30 #include "gltf-scene.h"
41 using ModelPtr = std::unique_ptr<Model>;
43 using ActorContainer = std::vector<Actor>;
44 using CameraContainer = std::vector<CameraActor>;
45 using ModelContainer = std::vector<ModelPtr>;
46 using TextureSetContainer = std::vector<TextureSet>;
48 const Vector3 DEFAULT_LIGHT_DIRECTION(0.5, 0.5, -1);
51 bool LoadFile(const std::string& filename, std::vector<T>& bytes)
53 Dali::FileStream fileStream(filename, Dali::FileStream::READ | Dali::FileStream::BINARY);
54 FILE* fin = fileStream.GetFile();
58 if(fseek(fin, 0, SEEK_END))
62 bytes.resize(uint32_t(ftell(fin)));
63 std::fill(bytes.begin(), bytes.end(), 0);
64 if(fseek(fin, 0, SEEK_SET))
68 size_t result = fread(bytes.data(), 1, bytes.size(), fin);
75 Shader CreateShader(const std::string& vsh, const std::string& fsh)
77 std::vector<char> vshShaderSource;
78 std::vector<char> fshShaderSource;
83 std::string vshPath(DEMO_GAME_DIR);
86 LoadFile(vshPath, vshShaderSource);
90 vshShaderSource.insert(vshShaderSource.end(), vsh.begin(), vsh.end());
96 std::string fshPath(DEMO_GAME_DIR);
99 LoadFile(fshPath, fshShaderSource);
103 fshShaderSource.insert(fshShaderSource.end(), fsh.begin(), fsh.end());
106 vshShaderSource.emplace_back(0);
107 fshShaderSource.emplace_back(0);
108 return Shader::New(std::string(vshShaderSource.data()), std::string(fshShaderSource.data()));
111 ModelPtr CreateModel(
113 const glTF_Mesh* mesh,
114 const std::string& vertexShaderSource,
115 const std::string& fragmentShaderSource)
118 * Obtain interleaved buffer for first mesh with position and normal attributes
120 auto positionBuffer = gltf.GetMeshAttributeBuffer(*mesh,
121 {glTFAttributeType::POSITION,
122 glTFAttributeType::NORMAL,
123 glTFAttributeType::TEXCOORD_0});
125 auto attributeCount = gltf.GetMeshAttributeCount(mesh);
127 * Create matching property buffer
129 auto vertexBuffer = VertexBuffer::New(Property::Map()
130 .Add("aPosition", Property::VECTOR3)
131 .Add("aNormal", Property::VECTOR3)
132 .Add("aTexCoord", Property::VECTOR2));
135 vertexBuffer.SetData(positionBuffer.data(), attributeCount);
137 auto geometry = Geometry::New();
138 geometry.AddVertexBuffer(vertexBuffer);
139 auto indexBuffer = gltf.GetMeshIndexBuffer(mesh);
140 geometry.SetIndexBuffer(indexBuffer.data(), indexBuffer.size());
141 geometry.SetType(Geometry::Type::TRIANGLES);
142 ModelPtr retval(new Model());
143 retval->shader = CreateShader(vertexShaderSource, fragmentShaderSource);
144 retval->geometry = geometry;
148 void ReplaceShader(Actor& actor, const std::string& vsh, const std::string& fsh)
150 auto renderer = actor.GetRendererAt(0);
151 auto shader = CreateShader(vsh, fsh);
152 renderer.SetShader(shader);
155 void CreateTextureSetsFromGLTF(glTF* gltf, const std::string& basePath, TextureSetContainer& textureSets)
157 const auto& materials = gltf->GetMaterials();
158 const auto& textures = gltf->GetTextures();
160 std::map<std::string, Texture> textureCache{};
162 for(const auto& material : materials)
164 TextureSet textureSet;
165 if(material.pbrMetallicRoughness.enabled)
167 textureSet = TextureSet::New();
168 std::string filename(basePath);
170 filename += textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
171 Dali::PixelData pixelData = Dali::Toolkit::SyncImageLoader::Load(filename);
173 auto cacheKey = textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
174 auto iter = textureCache.find(cacheKey);
176 if(iter == textureCache.end())
178 texture = Texture::New(TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight());
179 texture.Upload(pixelData);
180 texture.GenerateMipmaps();
181 textureCache[cacheKey] = texture;
185 texture = iter->second;
187 textureSet.SetTexture(0, texture);
188 Dali::Sampler sampler = Dali::Sampler::New();
189 sampler.SetWrapMode(Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT);
190 sampler.SetFilterMode(Dali::FilterMode::LINEAR_MIPMAP_LINEAR, Dali::FilterMode::LINEAR);
191 textureSet.SetSampler(0, sampler);
193 textureSets.emplace_back(textureSet);
198 * Creates models from glTF
200 void CreateModelsFromGLTF(glTF* gltf, ModelContainer& models)
202 const auto& meshes = gltf->GetMeshes();
203 for(const auto& mesh : meshes)
205 // change shader to use texture if material indicates that
206 if(mesh->material != 0xffffffff && gltf->GetMaterials()[mesh->material].pbrMetallicRoughness.enabled)
208 models.emplace_back(CreateModel(*gltf, mesh, SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_TEXTURED_FRAG.data()));
212 models.emplace_back(CreateModel(*gltf, mesh, SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_FRAG.data()));
217 Actor CreateSceneFromGLTF(
220 ModelContainer& models,
221 ActorContainer& actors,
222 CameraContainer& cameras,
223 TextureSetContainer& textureSets)
225 const auto& nodes = gltf->GetNodes();
227 Vector3 cameraPosition;
229 // for each node create nodes and children
230 // resolve parents later
231 actors.reserve(nodes.size());
232 for(const auto& node : nodes)
234 auto actor = node.cameraId != 0xffffffff ? CameraActor::New(window.GetSize()) : Actor::New();
236 actor.SetProperty(Actor::Property::SIZE, Vector3(1, 1, 1));
237 actor.SetProperty(Dali::Actor::Property::NAME, node.name);
238 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
239 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
240 actor.SetProperty(Actor::Property::POSITION, Vector3(node.translation[0], node.translation[1], node.translation[2]));
241 actor.SetProperty(Actor::Property::SCALE, Vector3(node.scale[0], node.scale[1], node.scale[2]));
242 actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3], node.rotationQuaternion[0], node.rotationQuaternion[1], node.rotationQuaternion[2]));
244 actors.emplace_back(actor);
246 // Initially add each actor to the very first one
247 if(actors.size() > 1)
249 actors[0].Add(actor);
252 // If mesh, create and add renderer
253 if(node.meshId != 0xffffffff)
255 const auto& model = models[node.meshId].get();
256 auto renderer = Renderer::New(model->geometry, model->shader);
258 // if textured, add texture set
259 auto materialId = gltf->GetMeshes()[node.meshId]->material;
260 if(materialId != 0xffffffff)
262 if(gltf->GetMaterials()[materialId].pbrMetallicRoughness.enabled)
264 renderer.SetTextures(textureSets[materialId]);
268 actor.AddRenderer(renderer);
271 // Reset and attach main camera
272 if(node.cameraId != 0xffffffff)
274 cameraPosition = Vector3(node.translation[0], node.translation[1], node.translation[2]);
275 auto quatY = Quaternion(Degree(180.0f), Vector3(0.0, 1.0, 0.0));
276 auto cameraActor = CameraActor::DownCast(actor);
277 cameraActor.SetProperty(Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3], node.rotationQuaternion[0], node.rotationQuaternion[1], node.rotationQuaternion[2]) * quatY);
278 cameraActor.SetProjectionMode(Camera::PERSPECTIVE_PROJECTION);
280 const auto camera = gltf->GetCameras()[node.cameraId];
281 cameraActor.SetNearClippingPlane(camera->znear);
282 cameraActor.SetFarClippingPlane(camera->zfar);
283 cameraActor.SetFieldOfView(camera->yfov);
285 cameraActor.SetProperty(CameraActor::Property::INVERT_Y_AXIS, true);
286 cameraActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
287 cameraActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
289 cameras.emplace_back(cameraActor);
293 // Resolve hierarchy dependencies
295 for(const auto& node : nodes)
297 if(!node.children.empty())
299 for(const auto& childId : node.children)
301 actors[i].Add(actors[childId + 1]);
307 for(auto& actor : actors)
309 actor.RegisterProperty("lightDir", DEFAULT_LIGHT_DIRECTION);
310 actor.RegisterProperty("eyePos", cameraPosition);
316 } // unnamed namespace
318 // This example shows how to create and display mirrored reflection using CameraActor
320 class ReflectionExample : public ConnectionTracker
323 ReflectionExample(Application& application)
324 : mApplication(application)
326 // Connect to the Application's Init signal
327 mApplication.InitSignal().Connect(this, &ReflectionExample::Create);
330 ~ReflectionExample() = default;
333 // The Init signal is received once (only) during the Application lifetime
334 void Create(Application& application)
336 // Get a handle to the window
337 Window window = application.GetWindow();
338 uint32_t windowWidth = uint32_t(window.GetSize().GetWidth());
339 uint32_t windowHeight = uint32_t(window.GetSize().GetHeight());
341 window.GetRenderTaskList().GetTask(0).SetClearEnabled(false);
342 mLayer3D = Layer::New();
343 mLayer3D.SetProperty(Actor::Property::SIZE, Vector2(windowWidth, windowHeight));
344 window.Add(mLayer3D);
346 mLayer3D.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
347 mLayer3D.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
348 mLayer3D.SetProperty(Layer::Property::BEHAVIOR, Layer::LAYER_3D);
349 mLayer3D.SetProperty(Layer::Property::DEPTH_TEST, true);
351 auto gltf = glTF(DEMO_GAME_DIR "/reflection");
353 // Define direction of light
356 * Instantiate texture sets
358 CreateTextureSetsFromGLTF(&gltf, DEMO_GAME_DIR, mTextureSets);
363 CreateModelsFromGLTF(&gltf, mModels);
366 * Create scene nodes & add to 3D Layer
368 mLayer3D.Add(CreateSceneFromGLTF(window, &gltf, mModels, mActors, mCameras, mTextureSets));
370 auto planeActor = mLayer3D.FindChildByName("Plane");
371 auto solarActor = mLayer3D.FindChildByName("solar_root");
372 auto backgroundActor = mLayer3D.FindChildByName("background");
373 ReplaceShader(backgroundActor, SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_SIMPLE_FRAG.data());
374 mCenterActor = mLayer3D.FindChildByName("center");
375 mCenterHorizActor = mLayer3D.FindChildByName("center2");
378 auto sun = mLayer3D.FindChildByName("sun");
379 ReplaceShader(sun, SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_PLASMA_FRAG.data());
380 mSunTimeUniformIndex = sun.RegisterProperty("uTime", 0.0f);
381 mSunKFactorUniformIndex = sun.RegisterProperty("uKFactor", 0.0f);
383 mTickTimer = Timer::New(16);
384 mTickTimer.TickSignal().Connect(this, &ReflectionExample::TickTimerSignal);
387 auto milkyway = mLayer3D.FindChildByName("milkyway");
388 ReplaceShader(milkyway, SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_FRAG.data());
390 auto renderTaskSourceActor = mLayer3D.FindChildByName("RenderTaskSource");
393 * Access camera (it's a child of "Camera" node)
395 auto camera = mLayer3D.FindChildByName("Camera_Orientation");
396 auto cameraRef = mLayer3D.FindChildByName("CameraReflection_Orientation");
398 auto cameraActor = CameraActor::DownCast(camera);
399 mCamera3D = cameraActor;
401 auto cameraRefActor = CameraActor::DownCast(cameraRef);
402 cameraRefActor.SetProperty(DevelCameraActor::Property::REFLECTION_PLANE, Vector4(0.0f, -1.0f, 0.0f, 0.0f));
403 mReflectionCamera3D = cameraRefActor;
405 auto task3D = window.GetRenderTaskList().CreateTask();
406 task3D.SetSourceActor(mLayer3D);
407 task3D.SetViewport(Rect<int>(0, 0, windowWidth, windowHeight));
408 task3D.SetCameraActor(cameraActor);
409 task3D.SetClearColor(Color::BLACK);
410 task3D.SetClearEnabled(true);
411 task3D.SetExclusive(false);
412 task3D.SetCameraActor(cameraActor);
415 * Change shader to textured
417 Shader texShader = CreateShader(SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_TEX_FRAG.data());
418 planeActor.RegisterProperty("uScreenSize", Vector2(windowWidth, windowHeight));
419 auto renderer = planeActor.GetRendererAt(0);
420 auto textureSet = renderer.GetTextures();
421 renderer.SetShader(texShader);
423 Texture fbTexture = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGBA8888, windowWidth, windowHeight);
424 textureSet.SetTexture(1u, fbTexture);
426 auto fb = FrameBuffer::New(windowWidth, windowHeight, FrameBuffer::Attachment::DEPTH);
428 fb.AttachColorTexture(fbTexture);
430 auto renderTask = window.GetRenderTaskList().CreateTask();
431 renderTask.SetFrameBuffer(fb);
432 renderTask.SetSourceActor(renderTaskSourceActor);
433 renderTask.SetViewport(Rect<int>(0, 0, windowWidth, windowHeight));
434 renderTask.SetCameraActor(cameraRefActor);
435 renderTask.SetClearColor(Color::BLACK);
436 renderTask.SetClearEnabled(true);
437 renderTask.SetExclusive(false);
439 mAnimation = Animation::New(30.0f);
440 mAnimation.AnimateBy(Property(solarActor, Actor::Property::ORIENTATION),
441 Quaternion(Degree(359), Vector3(0.0, 1.0, 0.0)));
442 mAnimation.AnimateBy(Property(milkyway, Actor::Property::ORIENTATION),
443 Quaternion(Degree(-359), Vector3(0.0, 1.0, 0.0)));
444 mAnimation.SetLooping(true);
447 Actor panScreen = Actor::New();
448 Vector2 windowSize = window.GetSize();
449 panScreen.SetProperty(Actor::Property::SIZE, windowSize);
450 panScreen.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
451 panScreen.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
452 auto camera2d = window.GetRenderTaskList().GetTask(0).GetCameraActor();
453 panScreen.SetProperty(Actor::Property::POSITION, Vector3(0, 0, camera2d.GetNearClippingPlane()));
454 camera2d.Add(panScreen);
455 camera2d.RotateBy(Degree(180.0f), Vector3(0.0, 1.0, 0.0));
456 mPanGestureDetector = PanGestureDetector::New();
457 mPanGestureDetector.Attach(panScreen);
458 mPanGestureDetector.DetectedSignal().Connect(this, &ReflectionExample::OnPan);
460 // Respond to key events
461 window.KeyEventSignal().Connect(this, &ReflectionExample::OnKeyEvent);
466 void OnPan(Actor actor, const PanGesture& panGesture)
468 Vector2 displacement = panGesture.GetScreenDisplacement();
469 Vector2 rotation{displacement.y * -0.1f, displacement.x * 0.1f};
471 Quaternion q(Degree(0.f), Degree(rotation.y), Degree(rotation.x));
472 Quaternion q0 = mCenterActor.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>();
473 mCenterActor.SetProperty(Actor::Property::ORIENTATION, q * q0);
475 Matrix m = Matrix::IDENTITY;
476 m.SetTransformComponents(Vector3::ONE, q0, Vector3::ZERO);
477 auto yAxis = m.GetYAxis() * -1.0f;
480 mReflectionCamera3D.SetProperty(DevelCameraActor::Property::REFLECTION_PLANE, Vector4(yAxis.x, yAxis.y, yAxis.z, 0.0f));
483 void OnKeyEvent(const KeyEvent& event)
485 if(event.GetState() == KeyEvent::DOWN)
487 if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
494 bool TickTimerSignal()
496 auto root = mLayer3D;
497 static float rotationAngle = 0.0f;
499 const auto ROTATION_ANGLE_STEP = 0.05f;
500 const auto FRAME_DELTA_TIME = 0.016f;
501 const auto PLASMA_K_FACTOR = 12.0f; // 'granularity' of plasma effect
503 rotationAngle += ROTATION_ANGLE_STEP;
504 mMockTime += FRAME_DELTA_TIME;
505 mKFactor = PLASMA_K_FACTOR;
507 auto sun = root.FindChildByName("sun");
508 sun.SetProperty(mSunTimeUniformIndex, mMockTime);
509 sun.SetProperty(mSunKFactorUniformIndex, mKFactor);
510 sun.SetProperty(Actor::Property::ORIENTATION, Quaternion(Radian(Degree(rotationAngle)), Vector3(0.0, 1.0, 0.0)));
515 Application& mApplication;
519 ActorContainer mActors{};
520 CameraContainer mCameras{};
521 ModelContainer mModels{};
522 TextureSetContainer mTextureSets{};
524 Animation mAnimation{};
525 float mMockTime{0.0f};
526 float mKFactor{0.0f};
527 Property::Index mSunTimeUniformIndex{};
528 Property::Index mSunKFactorUniformIndex{};
529 PanGestureDetector mPanGestureDetector{};
533 CameraActor mCamera3D{};
534 CameraActor mReflectionCamera3D{};
535 Actor mCenterActor{};
536 Actor mCenterHorizActor{};
539 int DALI_EXPORT_API main(int argc, char** argv)
541 Application application = Application::New(&argc, &argv);
542 ReflectionExample test(application);
543 application.MainLoop();