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.
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 "gltf-scene.h"
25 #include "generated/reflection-vert.h"
26 #include "generated/reflection-frag.h"
27 #include "generated/reflection-simple-frag.h"
28 #include "generated/reflection-textured-frag.h"
29 #include "generated/reflection-plasma-frag.h"
30 #include "generated/reflection-tex-frag.h"
42 using ModelPtr = std::unique_ptr<Model>;
44 using ActorContainer = std::vector<Actor>;
45 using CameraContainer = std::vector<CameraActor>;
46 using ModelContainer = std::vector<ModelPtr>;
47 using TextureSetContainer = std::vector<TextureSet>;
49 const Vector3 DEFAULT_LIGHT_DIRECTION(0.5, 0.5, -1);
52 bool LoadFile(const std::string& filename, std::vector<T>& bytes)
54 Dali::FileStream fileStream(filename, Dali::FileStream::READ | Dali::FileStream::BINARY);
55 FILE* fin = fileStream.GetFile();
59 if(fseek(fin, 0, SEEK_END))
63 bytes.resize(uint32_t(ftell(fin)));
64 std::fill(bytes.begin(), bytes.end(), 0);
65 if(fseek(fin, 0, SEEK_SET))
69 size_t result = fread(bytes.data(), 1, bytes.size(), fin);
76 Shader CreateShader(const std::string& vsh, const std::string& fsh)
78 std::vector<char> vshShaderSource;
79 std::vector<char> fshShaderSource;
84 std::string vshPath(DEMO_GAME_DIR);
87 LoadFile(vshPath, vshShaderSource);
91 vshShaderSource.insert(vshShaderSource.end(), vsh.begin(), vsh.end());
97 std::string fshPath(DEMO_GAME_DIR);
100 LoadFile(fshPath, fshShaderSource);
104 fshShaderSource.insert(fshShaderSource.end(), fsh.begin(), fsh.end());
107 vshShaderSource.emplace_back(0);
108 fshShaderSource.emplace_back(0);
109 return Shader::New(std::string(vshShaderSource.data()), std::string(fshShaderSource.data()));
112 ModelPtr CreateModel(
114 const glTF_Mesh* mesh,
115 const std::string& vertexShaderSource,
116 const std::string& fragmentShaderSource)
119 * Obtain interleaved buffer for first mesh with position and normal attributes
121 auto positionBuffer = gltf.GetMeshAttributeBuffer(*mesh,
122 {glTFAttributeType::POSITION,
123 glTFAttributeType::NORMAL,
124 glTFAttributeType::TEXCOORD_0});
126 auto attributeCount = gltf.GetMeshAttributeCount(mesh);
128 * Create matching property buffer
130 auto vertexBuffer = VertexBuffer::New(Property::Map()
131 .Add("aPosition", Property::VECTOR3)
132 .Add("aNormal", Property::VECTOR3)
133 .Add("aTexCoord", Property::VECTOR2));
136 vertexBuffer.SetData(positionBuffer.data(), attributeCount);
138 auto geometry = Geometry::New();
139 geometry.AddVertexBuffer(vertexBuffer);
140 auto indexBuffer = gltf.GetMeshIndexBuffer(mesh);
141 geometry.SetIndexBuffer(indexBuffer.data(), indexBuffer.size());
142 geometry.SetType(Geometry::Type::TRIANGLES);
143 ModelPtr retval(new Model());
144 retval->shader = CreateShader(vertexShaderSource, fragmentShaderSource);
145 retval->geometry = geometry;
149 void ReplaceShader(Actor& actor, const std::string& vsh, const std::string& fsh)
151 auto renderer = actor.GetRendererAt(0);
152 auto shader = CreateShader(vsh, fsh);
153 renderer.SetShader(shader);
156 void CreateTextureSetsFromGLTF(glTF* gltf, const std::string& basePath, TextureSetContainer& textureSets)
158 const auto& materials = gltf->GetMaterials();
159 const auto& textures = gltf->GetTextures();
161 std::map<std::string, Texture> textureCache{};
163 for(const auto& material : materials)
165 TextureSet textureSet;
166 if(material.pbrMetallicRoughness.enabled)
168 textureSet = TextureSet::New();
169 std::string filename(basePath);
171 filename += textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
172 Dali::PixelData pixelData = Dali::Toolkit::SyncImageLoader::Load(filename);
174 auto cacheKey = textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
175 auto iter = textureCache.find(cacheKey);
177 if(iter == textureCache.end())
179 texture = Texture::New(TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight());
180 texture.Upload(pixelData);
181 texture.GenerateMipmaps();
182 textureCache[cacheKey] = texture;
186 texture = iter->second;
188 textureSet.SetTexture(0, texture);
189 Dali::Sampler sampler = Dali::Sampler::New();
190 sampler.SetWrapMode(Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT);
191 sampler.SetFilterMode(Dali::FilterMode::LINEAR_MIPMAP_LINEAR, Dali::FilterMode::LINEAR);
192 textureSet.SetSampler(0, sampler);
194 textureSets.emplace_back(textureSet);
199 * Creates models from glTF
201 void CreateModelsFromGLTF(glTF* gltf, ModelContainer& models)
203 const auto& meshes = gltf->GetMeshes();
204 for(const auto& mesh : meshes)
206 // change shader to use texture if material indicates that
207 if(mesh->material != 0xffffffff && gltf->GetMaterials()[mesh->material].pbrMetallicRoughness.enabled)
209 models.emplace_back(CreateModel(*gltf, mesh, SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_TEXTURED_FRAG.data()));
213 models.emplace_back(CreateModel(*gltf, mesh, SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_FRAG.data()));
218 Actor CreateSceneFromGLTF(
221 ModelContainer& models,
222 ActorContainer& actors,
223 CameraContainer& cameras,
224 TextureSetContainer& textureSets)
226 const auto& nodes = gltf->GetNodes();
228 Vector3 cameraPosition;
230 // for each node create nodes and children
231 // resolve parents later
232 actors.reserve(nodes.size());
233 for(const auto& node : nodes)
235 auto actor = node.cameraId != 0xffffffff ? CameraActor::New(window.GetSize()) : Actor::New();
237 actor.SetProperty(Actor::Property::SIZE, Vector3(1, 1, 1));
238 actor.SetProperty(Dali::Actor::Property::NAME, node.name);
239 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
240 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
241 actor.SetProperty(Actor::Property::POSITION, Vector3(node.translation[0], node.translation[1], node.translation[2]));
242 actor.SetProperty(Actor::Property::SCALE, Vector3(node.scale[0], node.scale[1], node.scale[2]));
243 actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3], node.rotationQuaternion[0], node.rotationQuaternion[1], node.rotationQuaternion[2]));
245 actors.emplace_back(actor);
247 // Initially add each actor to the very first one
248 if(actors.size() > 1)
250 actors[0].Add(actor);
253 // If mesh, create and add renderer
254 if(node.meshId != 0xffffffff)
256 const auto& model = models[node.meshId].get();
257 auto renderer = Renderer::New(model->geometry, model->shader);
259 // if textured, add texture set
260 auto materialId = gltf->GetMeshes()[node.meshId]->material;
261 if(materialId != 0xffffffff)
263 if(gltf->GetMaterials()[materialId].pbrMetallicRoughness.enabled)
265 renderer.SetTextures(textureSets[materialId]);
269 actor.AddRenderer(renderer);
272 // Reset and attach main camera
273 if(node.cameraId != 0xffffffff)
275 cameraPosition = Vector3(node.translation[0], node.translation[1], node.translation[2]);
276 auto quatY = Quaternion(Degree(180.0f), Vector3(0.0, 1.0, 0.0));
277 auto cameraActor = CameraActor::DownCast(actor);
278 cameraActor.SetProperty(Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3], node.rotationQuaternion[0], node.rotationQuaternion[1], node.rotationQuaternion[2]) * quatY);
279 cameraActor.SetProjectionMode(Camera::PERSPECTIVE_PROJECTION);
281 const auto camera = gltf->GetCameras()[node.cameraId];
282 cameraActor.SetNearClippingPlane(camera->znear);
283 cameraActor.SetFarClippingPlane(camera->zfar);
284 cameraActor.SetFieldOfView(camera->yfov);
286 cameraActor.SetProperty(CameraActor::Property::INVERT_Y_AXIS, true);
287 cameraActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
288 cameraActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
290 cameras.emplace_back(cameraActor);
294 // Resolve hierarchy dependencies
296 for(const auto& node : nodes)
298 if(!node.children.empty())
300 for(const auto& childId : node.children)
302 actors[i].Add(actors[childId + 1]);
308 for(auto& actor : actors)
310 actor.RegisterProperty("lightDir", DEFAULT_LIGHT_DIRECTION);
311 actor.RegisterProperty("eyePos", cameraPosition);
317 } // unnamed namespace
319 // This example shows how to create and display mirrored reflection using CameraActor
321 class ReflectionExample : public ConnectionTracker
324 ReflectionExample(Application& application)
325 : mApplication(application)
327 // Connect to the Application's Init signal
328 mApplication.InitSignal().Connect(this, &ReflectionExample::Create);
331 ~ReflectionExample() = default;
334 // The Init signal is received once (only) during the Application lifetime
335 void Create(Application& application)
337 // Get a handle to the window
338 Window window = application.GetWindow();
339 uint32_t windowWidth = uint32_t(window.GetSize().GetWidth());
340 uint32_t windowHeight = uint32_t(window.GetSize().GetHeight());
342 window.GetRenderTaskList().GetTask(0).SetClearEnabled(false);
343 mLayer3D = Layer::New();
344 mLayer3D.SetProperty(Actor::Property::SIZE, Vector2(windowWidth, windowHeight));
345 window.Add(mLayer3D);
347 mLayer3D.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
348 mLayer3D.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
349 mLayer3D.SetProperty(Layer::Property::BEHAVIOR, Layer::LAYER_3D);
350 mLayer3D.SetProperty(Layer::Property::DEPTH_TEST, true);
352 auto gltf = glTF(DEMO_GAME_DIR "/reflection");
354 // Define direction of light
357 * Instantiate texture sets
359 CreateTextureSetsFromGLTF(&gltf, DEMO_GAME_DIR, mTextureSets);
364 CreateModelsFromGLTF(&gltf, mModels);
367 * Create scene nodes & add to 3D Layer
369 mLayer3D.Add(CreateSceneFromGLTF(window, &gltf, mModels, mActors, mCameras, mTextureSets));
371 auto planeActor = mLayer3D.FindChildByName("Plane");
372 auto solarActor = mLayer3D.FindChildByName("solar_root");
373 auto backgroundActor = mLayer3D.FindChildByName("background");
374 ReplaceShader(backgroundActor, SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_SIMPLE_FRAG.data());
375 mCenterActor = mLayer3D.FindChildByName("center");
376 mCenterHorizActor = mLayer3D.FindChildByName("center2");
379 auto sun = mLayer3D.FindChildByName("sun");
380 ReplaceShader(sun, SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_PLASMA_FRAG.data());
381 mSunTimeUniformIndex = sun.RegisterProperty("uTime", 0.0f);
382 mSunKFactorUniformIndex = sun.RegisterProperty("uKFactor", 0.0f);
384 mTickTimer = Timer::New(16);
385 mTickTimer.TickSignal().Connect(this, &ReflectionExample::TickTimerSignal);
388 auto milkyway = mLayer3D.FindChildByName("milkyway");
389 ReplaceShader(milkyway, SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_FRAG.data());
391 auto renderTaskSourceActor = mLayer3D.FindChildByName("RenderTaskSource");
394 * Access camera (it's a child of "Camera" node)
396 auto camera = mLayer3D.FindChildByName("Camera_Orientation");
397 auto cameraRef = mLayer3D.FindChildByName("CameraReflection_Orientation");
399 auto cameraActor = CameraActor::DownCast(camera);
400 mCamera3D = cameraActor;
402 auto cameraRefActor = CameraActor::DownCast(cameraRef);
403 cameraRefActor.SetProperty(DevelCameraActor::Property::REFLECTION_PLANE, Vector4(0.0f, -1.0f, 0.0f, 0.0f));
404 mReflectionCamera3D = cameraRefActor;
406 auto task3D = window.GetRenderTaskList().CreateTask();
407 task3D.SetSourceActor(mLayer3D);
408 task3D.SetViewport(Rect<int>(0, 0, windowWidth, windowHeight));
409 task3D.SetCameraActor(cameraActor);
410 task3D.SetClearColor(Color::BLACK);
411 task3D.SetClearEnabled(true);
412 task3D.SetExclusive(false);
413 task3D.SetCameraActor(cameraActor);
416 * Change shader to textured
418 Shader texShader = CreateShader(SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_TEX_FRAG.data());
419 planeActor.RegisterProperty("uScreenSize", Vector2(windowWidth, windowHeight));
420 auto renderer = planeActor.GetRendererAt(0);
421 auto textureSet = renderer.GetTextures();
422 renderer.SetShader(texShader);
424 Texture fbTexture = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGBA8888, windowWidth, windowHeight);
425 textureSet.SetTexture(1u, fbTexture);
427 auto fb = FrameBuffer::New(windowWidth, windowHeight, FrameBuffer::Attachment::DEPTH);
429 fb.AttachColorTexture(fbTexture);
431 auto renderTask = window.GetRenderTaskList().CreateTask();
432 renderTask.SetFrameBuffer(fb);
433 renderTask.SetSourceActor(renderTaskSourceActor);
434 renderTask.SetViewport(Rect<int>(0, 0, windowWidth, windowHeight));
435 renderTask.SetCameraActor(cameraRefActor);
436 renderTask.SetClearColor(Color::BLACK);
437 renderTask.SetClearEnabled(true);
438 renderTask.SetExclusive(false);
440 mAnimation = Animation::New(30.0f);
441 mAnimation.AnimateBy(Property(solarActor, Actor::Property::ORIENTATION),
442 Quaternion(Degree(359), Vector3(0.0, 1.0, 0.0)));
443 mAnimation.AnimateBy(Property(milkyway, Actor::Property::ORIENTATION),
444 Quaternion(Degree(-359), Vector3(0.0, 1.0, 0.0)));
445 mAnimation.SetLooping(true);
448 Actor panScreen = Actor::New();
449 Vector2 windowSize = window.GetSize();
450 panScreen.SetProperty(Actor::Property::SIZE, windowSize);
451 panScreen.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
452 panScreen.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
453 auto camera2d = window.GetRenderTaskList().GetTask(0).GetCameraActor();
454 panScreen.SetProperty(Actor::Property::POSITION, Vector3(0, 0, camera2d.GetNearClippingPlane()));
455 camera2d.Add(panScreen);
456 camera2d.RotateBy(Degree(180.0f), Vector3(0.0, 1.0, 0.0));
457 mPanGestureDetector = PanGestureDetector::New();
458 mPanGestureDetector.Attach(panScreen);
459 mPanGestureDetector.DetectedSignal().Connect(this, &ReflectionExample::OnPan);
461 // Respond to key events
462 window.KeyEventSignal().Connect(this, &ReflectionExample::OnKeyEvent);
467 void OnPan(Actor actor, const PanGesture& panGesture)
469 Vector2 displacement = panGesture.GetScreenDisplacement();
471 displacement.y * -0.1f,
472 displacement.x * 0.1f
475 Quaternion q(Degree(0.f), Degree(rotation.y), Degree(rotation.x));
476 Quaternion q0 = mCenterActor.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>();
477 mCenterActor.SetProperty(Actor::Property::ORIENTATION, q * q0);
479 Matrix m = Matrix::IDENTITY;
480 m.SetTransformComponents(Vector3::ONE, q0, Vector3::ZERO);
481 auto yAxis = m.GetYAxis() * -1.0f;
484 mReflectionCamera3D.SetProperty(DevelCameraActor::Property::REFLECTION_PLANE, Vector4(yAxis.x, yAxis.y, yAxis.z, 0.0f));
487 void OnKeyEvent(const KeyEvent& event)
489 if(event.GetState() == KeyEvent::DOWN)
491 if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
498 bool TickTimerSignal()
500 auto root = mLayer3D;
501 static float rotationAngle = 0.0f;
503 const auto ROTATION_ANGLE_STEP = 0.05f;
504 const auto FRAME_DELTA_TIME = 0.016f;
505 const auto PLASMA_K_FACTOR = 12.0f; // 'granularity' of plasma effect
507 rotationAngle += ROTATION_ANGLE_STEP;
508 mMockTime += FRAME_DELTA_TIME;
509 mKFactor = PLASMA_K_FACTOR;
511 auto sun = root.FindChildByName("sun");
512 sun.SetProperty(mSunTimeUniformIndex, mMockTime);
513 sun.SetProperty(mSunKFactorUniformIndex, mKFactor);
514 sun.SetProperty(Actor::Property::ORIENTATION, Quaternion(Radian(Degree(rotationAngle)), Vector3(0.0, 1.0, 0.0)));
519 Application& mApplication;
523 ActorContainer mActors{};
524 CameraContainer mCameras{};
525 ModelContainer mModels{};
526 TextureSetContainer mTextureSets{};
528 Animation mAnimation{};
529 float mMockTime{0.0f};
530 float mKFactor{0.0f};
531 Property::Index mSunTimeUniformIndex{};
532 Property::Index mSunKFactorUniformIndex{};
533 PanGestureDetector mPanGestureDetector{};
537 CameraActor mCamera3D{};
538 CameraActor mReflectionCamera3D{};
539 Actor mCenterActor{};
540 Actor mCenterHorizActor{};
543 int DALI_EXPORT_API main(int argc, char** argv)
545 Application application = Application::New(&argc, &argv);
546 ReflectionExample test(application);
547 application.MainLoop();