Reflection demo
authoradam.b <adam.b@samsung.com>
Wed, 19 Feb 2020 16:28:27 +0000 (16:28 +0000)
committerneostom432 <minho.sun@samsung.com>
Mon, 23 Mar 2020 06:35:06 +0000 (15:35 +0900)
Packed resources are under Attribution 4.0 International license. The user
may use, adapt and share the textures for any purpose, even commercially.

Change-Id: If9092a8fde225c39daa599367d13e9d0924bd657

13 files changed:
com.samsung.dali-demo.xml
demo/dali-demo.cpp
examples/reflection-demo/gltf-scene.cpp [new file with mode: 0644]
examples/reflection-demo/gltf-scene.h [new file with mode: 0644]
examples/reflection-demo/pico-json.h [new file with mode: 0644]
examples/reflection-demo/reflection-example.cpp [new file with mode: 0644]
resources/game/reflection.bin [new file with mode: 0644]
resources/game/reflection.gltf [new file with mode: 0644]
resources/game/texture-atlas-license.txt [new file with mode: 0644]
resources/game/texture-atlas.jpg [new file with mode: 0644]
resources/po/en_GB.po
resources/po/en_US.po
shared/dali-demo-strings.h

index 4bdee68..55c7471 100644 (file)
        <ui-application appid="ray-marching.example" exec="/usr/apps/com.samsung.dali-demo/bin/ray-marching.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true">
                <label>Ray Marching example</label>
        </ui-application>
+       <ui-application appid="reflection-demo.example" exec="/usr/apps/com.samsung.dali-demo/bin/reflection-demo.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true">
+               <label>Reflection effect</label>
+       </ui-application>
        <ui-application appid="refraction-effect.example" exec="/usr/apps/com.samsung.dali-demo/bin/refraction-effect.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true">
                <label>Refraction effect</label>
        </ui-application>
index 2884a32..a6bee7a 100644 (file)
@@ -50,6 +50,7 @@ int DALI_EXPORT_API main(int argc, char **argv)
   demo.AddExample(Example("metaball-refrac.example", DALI_DEMO_STR_TITLE_METABALL_REFRAC));
   demo.AddExample(Example("motion-blur.example", DALI_DEMO_STR_TITLE_MOTION_BLUR));
   demo.AddExample(Example("page-turn-view.example", DALI_DEMO_STR_TITLE_PAGE_TURN));
+  demo.AddExample(Example("reflection-demo.example", DALI_DEMO_STR_TITLE_REFLECTION));
   demo.AddExample(Example("refraction-effect.example", DALI_DEMO_STR_TITLE_REFRACTION));
   demo.AddExample(Example("renderer-stencil.example", DALI_DEMO_STR_TITLE_RENDERER_STENCIL));
   demo.AddExample(Example("shadows-and-lights.example", DALI_DEMO_STR_TITLE_LIGHTS_AND_SHADOWS));
diff --git a/examples/reflection-demo/gltf-scene.cpp b/examples/reflection-demo/gltf-scene.cpp
new file mode 100644 (file)
index 0000000..424ffa2
--- /dev/null
@@ -0,0 +1,588 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include <dali/devel-api/adaptor-framework/file-stream.h>
+
+#include "gltf-scene.h"
+
+#include "pico-json.h"
+
+namespace
+{
+// string contains enum type index encoded matching glTFAttributeType
+const std::vector<std::string> GLTF_STR_ATTRIBUTE_TYPE = {
+  "POSITION",
+  "NORMAL",
+  "TEXCOORD_0"
+};
+
+const std::vector<std::pair<std::string, uint32_t>> GLTF_STR_COMPONENT_TYPE = {
+  std::make_pair( "VEC2", 2 ),
+  std::make_pair( "VEC3", 3 ),
+  std::make_pair( "VEC4", 4 ),
+  std::make_pair( "SCALAR", 1 )
+};
+
+glTFAttributeType glTFAttributeTypeStrToEnum( const std::string& name )
+{
+  int index = -1;
+  auto iter = std::find_if( GLTF_STR_ATTRIBUTE_TYPE.begin(), GLTF_STR_ATTRIBUTE_TYPE.end(),
+  [name, &index]( const std::string& val )
+  {
+    index++;
+    return val == name;
+  });
+  if( iter == GLTF_STR_ATTRIBUTE_TYPE.end() )
+  {
+    return glTFAttributeType::UNDEFINED;
+  }
+
+  return static_cast<glTFAttributeType>(index);
+}
+
+uint32_t glTFComponentTypeStrToNum( const std::string& name )
+{
+  auto iter = std::find_if( GLTF_STR_COMPONENT_TYPE.begin(), GLTF_STR_COMPONENT_TYPE.end(),
+                            [name]( const std::pair<std::string, uint32_t>& val )
+                            {
+                              return val.first == name;
+                            });
+  if( iter == GLTF_STR_COMPONENT_TYPE.end() )
+  {
+    return 0;
+  }
+
+  return iter->second;
+}
+
+template<class T>
+struct JsonResult
+{
+  bool success;
+  T    result;
+  operator T() const
+  {
+    return static_cast<T>(result);
+  }
+};
+
+template<>
+struct JsonResult<bool>
+{
+  bool success;
+  bool result;
+
+  operator bool() const
+  {
+    return result;
+  }
+};
+
+template<class Expected, class Converted = Expected>
+JsonResult<picojson::value> JsonFindValue( const picojson::object& object, const std::string& name )
+{
+  auto iter = find_if(object.begin(), object.end(), [name](decltype(*object.begin())& item)
+  {
+    return item.first == name;
+  });
+
+  if(iter == object.end())
+  {
+    return {false};
+  }
+
+  return {true, iter->second};
+};
+
+
+template<class Expected, class Converted = Expected>
+JsonResult<Converted> JsonGetValue( const picojson::value& val, const std::string& name, const Converted& onFail )
+{
+  if( val.contains(name) )
+  {
+    return {true, static_cast<Converted>(val.get(name).get<Expected>())};
+  }
+  return {false, onFail};
+}
+
+template<class Expected, class Converted = Expected>
+bool JsonGetValueOut( const picojson::value& val, const std::string& name, Converted& writeOut )
+{
+  if( val.contains(name) )
+  {
+    writeOut = static_cast<Converted>(val.get(name).get<Expected>());
+    return true;
+  }
+  return false;
+}
+
+
+/**
+ * Safe json value accessor
+ */
+template<class Expected, class Converted = Expected>
+JsonResult<std::vector<Converted>> JsonGetArray( const picojson::value& val, const std::string& name, std::vector<Converted> valueOnFail = {} )
+{
+  if(val.contains(name) )
+  {
+    const auto& array = val.get(name).get<picojson::array>();
+    std::vector<Converted> result{};
+    for( const auto& item : array )
+    {
+      result.emplace_back( static_cast<Converted>(item.get<Expected>()) );
+    }
+    return {true, result};
+  }
+  return {false, valueOnFail};
+}
+
+}
+
+glTF::glTF(const std::string& filename)
+{
+  LoadFromFile(filename);
+  ParseJSON();
+}
+
+void glTF::LoadFromFile( const std::string& filename )
+{
+  std::string jsonFile( filename );
+  jsonFile += ".gltf";
+  std::string binFile( filename );
+  binFile += ".bin";
+
+  // load binary
+
+  GLTF_LOG("LoadFromFile: %s", binFile.c_str());
+  mBuffer = LoadFile(binFile);
+  jsonBuffer = LoadFile(jsonFile);
+
+  // Log errors
+  if(mBuffer.empty())
+  {
+    GLTF_LOG("Error, buffer empty!");
+  }
+  else
+  {
+    GLTF_LOG( "GLTF[BIN]: %s loaded, size = %d", binFile.c_str(), int(mBuffer.size()));
+  }
+  if(jsonBuffer.empty())
+  {
+    GLTF_LOG("Error, buffer GLTF empty!");
+  }
+  else
+  {
+    GLTF_LOG( "GLTF: %s loaded, size = %d", binFile.c_str(), int(mBuffer.size()));
+  }
+
+  // Abort if errors
+  if(jsonBuffer.empty() || mBuffer.empty())
+  {
+    return;
+  }
+
+  // parse json
+  auto err = picojson::parse(jsonNode, std::string(reinterpret_cast<char*>(jsonBuffer.data())));
+  if(!err.empty())
+  {
+    GLTF_LOG( "GLTF: Error parsing %s, error: %s", jsonFile.c_str(), err.c_str());
+    return;
+  }
+  else
+  {
+    GLTF_LOG( "GLTF: %s loaded, size = %d", jsonFile.c_str(), int(jsonBuffer.size()));
+  }
+}
+
+bool glTF::ParseJSON()
+{
+  if (!jsonNode.is<picojson::object>())
+  {
+    return false;
+  }
+
+  // Add dummy first node to nodes (scene node)
+  mNodes.emplace_back();
+
+  // Sources for textures to be resolved later
+  std::vector<uint32_t> textureSources{};
+  std::vector<glTF_Texture> images{};
+
+  for( auto& val : jsonNode.get<picojson::object>() )
+  {
+    GLTF_LOG( "node: %s", val.first.c_str());
+
+    // Parse bufferviews
+    if( val.first == "bufferViews" )
+    {
+      auto bufferViews = val.second;
+      for( auto& view : bufferViews.get<picojson::array>() )
+      {
+        auto bufferIndex = uint32_t(view.get("buffer").get<double>());
+        auto byteLength = uint32_t(view.get("byteLength").get<double>());
+        auto byteOffset = uint32_t(view.get("byteOffset").get<double>());
+
+        glTF_BufferView bufferView{};
+        bufferView.bufferIndex = bufferIndex;
+        bufferView.byteLength = byteLength;
+        bufferView.byteOffset = byteOffset;
+
+        mBufferViews.emplace_back( bufferView );
+      }
+    }
+
+    // parse accessors
+    else if( val.first == "accessors" )
+    {
+      for( const auto& accessor : val.second.get<picojson::array>() )
+      {
+        auto gltfAccessor = glTF_Accessor{};
+        gltfAccessor.bufferView = uint32_t(accessor.get( "bufferView" ).get<double>());
+        gltfAccessor.componentType = uint32_t(accessor.get( "componentType" ).get<double>());
+        gltfAccessor.count = uint32_t(accessor.get( "count" ).get<double>());
+        gltfAccessor.type = accessor.get( "type" ).get<std::string>();
+        gltfAccessor.componentSize = glTFComponentTypeStrToNum( gltfAccessor.type );
+        mAccessors.emplace_back( gltfAccessor );
+      }
+    }
+
+    // parse meshes
+    else if( val.first == "meshes" )
+    {
+      for( const auto& mesh : val.second.get<picojson::array>() )
+      {
+        glTF_Mesh gltfMesh{};
+        gltfMesh.name = mesh.get( "name" ).get<std::string>();
+
+        // get primitives (in this implementation assuming single mesh consists of
+        // one and only one primitive)
+        const auto& primitive = mesh.get("primitives").get<picojson::array>()[0];
+        const auto& attrs = primitive.get("attributes").get<picojson::object>();
+        for( const auto& attr : attrs )
+        {
+          auto type = glTFAttributeTypeStrToEnum( attr.first );
+          auto bvIndex = uint32_t(attr.second.get<double>());
+          gltfMesh.attributes.emplace_back( std::make_pair( type, bvIndex ) );
+          GLTF_LOG("GLTF: ATTR: type: %d, index: %d", int(type), int(bvIndex));
+        }
+
+        gltfMesh.indices = uint32_t(primitive.get("indices").get<double>());
+        gltfMesh.material = uint32_t(primitive.get("material").get<double>());
+        mMeshes.emplace_back( gltfMesh );
+      }
+    }
+    // parse cameras
+    else if( val.first == "cameras" )
+    {
+      glTF_Camera tgifCamera{};
+      for( const auto& camera : val.second.get<picojson::array>() )
+      {
+        tgifCamera.name = camera.get( "name" ).to_str();
+        tgifCamera.isPerspective = (camera.get( "type" ).to_str() == "perspective" );
+        if(tgifCamera.isPerspective)
+        {
+          const auto& perspective = camera.get( "perspective" );
+          tgifCamera.yfov = static_cast<float>(perspective.get( "yfov" ).get<double>());
+          tgifCamera.zfar = static_cast<float>(perspective.get( "zfar" ).get<double>());
+          tgifCamera.znear = static_cast<float>(perspective.get( "znear" ).get<double>());
+        }
+
+        mCameras.emplace_back( tgifCamera );
+      }
+    }
+    // parse nodes
+    else if( val.first == "nodes" )
+    {
+      auto nodeIndex = 1u;
+      for( const auto& node : val.second.get<picojson::array>() )
+      {
+        glTF_Node gltfNode{};
+        gltfNode.name = node.get( "name" ).to_str();
+        auto rotation = JsonGetArray<double, float>( node, "rotation", {0.0f, 0.0f, 0.0f, 1.0f} );
+        auto translation = JsonGetArray<double, float>( node, "translation", {0.0f, 0.0f, 0.0f} );
+        auto scale = JsonGetArray<double, float>( node, "scale", {1.0f, 1.0f, 1.0f} );
+        std::copy( rotation.result.begin(), rotation.result.end(), gltfNode.rotationQuaternion );
+        std::copy( translation.result.begin(), translation.result.end(), gltfNode.translation );
+        std::copy( scale.result.begin(), scale.result.end(), gltfNode.scale );
+
+        auto children = JsonGetArray<double, uint32_t>( node, "children" );
+        if(children.success)
+        {
+          gltfNode.children = std::move(children.result);
+        }
+        gltfNode.index = nodeIndex;
+        gltfNode.cameraId = 0xffffffff;
+        gltfNode.meshId = 0xffffffff;
+
+        auto cameraId = JsonGetValue<double, uint32_t>( node, "camera", 0xffffffff );
+        if( cameraId.success )
+        {
+          gltfNode.cameraId = cameraId.result;
+        }
+        auto meshId = JsonGetValue<double, uint32_t>( node, "mesh",  0xffffffff );
+        if( meshId.success )
+        {
+          gltfNode.meshId = meshId.result;
+        }
+        mNodes.emplace_back( gltfNode );
+        ++nodeIndex;
+      }
+
+    }
+    // parse scenes, note: only first scene is being parsed
+    else if( val.first == "scenes" )
+    {
+      auto& sceneNode = mNodes[0];
+      const auto& scene = val.second.get<picojson::array>()[0];
+      sceneNode.name = JsonGetValue<std::string>( scene, "name", std::string() );
+      auto result = JsonGetArray<double, uint32_t>( scene, "nodes" );
+      sceneNode.children = result.result;
+      sceneNode.index = 0;
+    }
+    else if( val.first == "materials" )
+    {
+      for( const auto& node : val.second.get<picojson::array>() )
+      {
+        // Get pbr material, base color texture
+        glTF_Material material{};
+        material.doubleSided = JsonGetValue<bool>( node, "doubleSided", false ).result;
+        material.name = JsonGetValue<std::string>( node, "name", std::string() ).result;
+        if( node.contains("pbrMetallicRoughness") )
+        {
+          const auto& node0 = node.get("pbrMetallicRoughness");
+          if(node0.contains("baseColorTexture"))
+          {
+            const auto& node1 = node0.get("baseColorTexture");
+            auto index = uint32_t(node1.get("index").get<double>());
+            auto texCoord = uint32_t(node1.get("texCoord").get<double>());
+            material.pbrMetallicRoughness.enabled = true;
+            material.pbrMetallicRoughness.baseTextureColor.index = index;
+            material.pbrMetallicRoughness.baseTextureColor.texCoord = texCoord;
+          }
+        }
+        mMaterials.emplace_back( material );
+      }
+    }
+    else if( val.first == "textures" )
+    {
+      for(const auto& item : val.second.get<picojson::array>() )
+      {
+        auto source = JsonGetValue<double, uint32_t>( item, "source", 0xffffffff );
+        textureSources.emplace_back( source.result );
+      }
+    }
+    else if( val.first == "images" )
+    {
+      for(const auto& item : val.second.get<picojson::array>() )
+      {
+        glTF_Texture tex{};
+        JsonGetValueOut<std::string>( item, "name", tex.name );
+        JsonGetValueOut<std::string>( item, "uri", tex.uri );
+        images.emplace_back( tex );
+      }
+    }
+  }
+
+  // Resolve cross-referencing
+  for( const auto& source : textureSources )
+  {
+    mTextures.emplace_back( images[source] );
+  }
+
+  return true;
+}
+
+glTF_Buffer glTF::LoadFile( const std::string& filename )
+{
+  Dali::FileStream fileStream( filename.c_str(), Dali::FileStream::READ | Dali::FileStream::BINARY );
+  FILE* fin = fileStream.GetFile();
+  std::vector<unsigned char> buffer;
+  if( fin )
+  {
+    fseek( fin, 0, SEEK_END );
+    auto size = ftell(fin);
+    fseek( fin, 0, SEEK_SET );
+    buffer.resize(unsigned(size));
+    auto result = fread( buffer.data(), 1, size_t(size), fin );
+    if( result < 0 )
+    {
+      GLTF_LOG("LoadFile: Result: %d", int(result));
+      // return empty buffer
+      return {};
+    }
+  }
+  else
+  {
+    GLTF_LOG("LoadFile: Can't open file: errno = %d", errno);
+  }
+
+  return buffer;
+}
+
+std::vector<const glTF_Mesh*> glTF::GetMeshes() const
+{
+  std::vector<const glTF_Mesh*> retval;
+  for( auto& mesh : mMeshes )
+  {
+    retval.emplace_back( &mesh );
+  }
+  return retval;
+}
+
+std::vector<const glTF_Camera*> glTF::GetCameras()
+{
+  std::vector<const glTF_Camera*> cameras;
+  for( const auto& cam : mCameras )
+  {
+    cameras.emplace_back( &cam );
+  }
+  return cameras;
+}
+
+std::vector<unsigned char> glTF::GetMeshAttributeBuffer( const glTF_Mesh& mesh,  const std::vector<glTFAttributeType>& attrTypes )
+{
+  // find buffer views
+  struct Data
+  {
+    uint32_t accessorIndex{0u};
+    uint32_t byteStride{0u};
+    char*    srcPtr{ nullptr };
+  };
+  std::vector<Data> data{};
+  for( const auto& attrType : attrTypes )
+  {
+    std::find_if( mesh.attributes.begin(), mesh.attributes.end(), [&data, &attrType]( const std::pair<glTFAttributeType, uint32_t>& item ){
+      if( item.first == attrType )
+      {
+        data.emplace_back();
+        data.back().accessorIndex = item.second;
+      }
+      return false;
+    });
+  }
+
+  if(data.empty())
+  {
+    return {};
+  }
+
+  // number of attributes is same for the whole mesh so using very first
+  // accessor
+
+  glTF_Buffer retval{};
+
+  // data is interleaved
+  if( data.size() > 1 )
+  {
+    auto attributeCount = mAccessors[data[0].accessorIndex].count;// / mAccessors[data[0].accessorIndex].componentSize;
+    uint32_t attributeStride = 0;
+    // now find buffer view stride for particular accessor
+    for( auto& item : data )
+    {
+      auto& accessor = mAccessors[item.accessorIndex];
+
+      // Update byte stride for this buffer view
+      auto& bufferView = mBufferViews[accessor.bufferView];
+      item.byteStride = bufferView.byteLength / attributeCount;
+      attributeStride += item.byteStride;
+      item.srcPtr = reinterpret_cast<char*>(mBuffer.data()) + bufferView.byteOffset;
+    }
+
+    // now allocate final buffer and interleave data
+    retval.resize( attributeStride * attributeCount );
+    auto* dstPtr = retval.data();
+    for( auto i = 0u; i < attributeCount; ++i )
+    {
+      for(auto& item : data )
+      {
+        std::copy( item.srcPtr,
+                   item.srcPtr + item.byteStride,
+                   reinterpret_cast<char*>(dstPtr) );
+        dstPtr += item.byteStride;
+        item.srcPtr += item.byteStride;
+      }
+    }
+  }
+  else // copy data directly as single buffer
+  {
+    auto& bufferView = mBufferViews[mAccessors[data[0].accessorIndex].bufferView];
+    retval.resize( bufferView.byteLength );
+    std::copy( mBuffer.begin() + bufferView.byteOffset,
+               mBuffer.begin() + bufferView.byteOffset + bufferView.byteLength,
+               retval.begin());
+
+  }
+  return retval;
+}
+
+
+const glTF_Mesh* glTF::FindMeshByName( const std::string& name ) const
+{
+  for( const auto& mesh : mMeshes )
+  {
+    if(mesh.name == name)
+      return &mesh;
+  }
+  return nullptr;
+}
+
+uint32_t glTF::GetMeshAttributeCount( const glTF_Mesh* mesh ) const
+{
+  const auto& accessor = mAccessors[mesh->attributes[0].second];
+  return accessor.count;// / accessor.componentSize;
+}
+
+std::vector<uint16_t> glTF::GetMeshIndexBuffer( const glTF_Mesh* mesh ) const
+{
+  // check GL component type
+  const auto& accessor = mAccessors[mesh->indices];
+  if( accessor.componentType == 0x1403 ) // GL_UNSIGNED_SHORT
+  {
+    std::vector<uint16_t> retval{};
+    retval.resize( accessor.count );
+    const auto& bufferView = mBufferViews[accessor.bufferView];
+    const auto* srcPtr = reinterpret_cast<const uint16_t*>
+                (reinterpret_cast<const char*>(mBuffer.data()) + bufferView.byteOffset);
+    std::copy( srcPtr, srcPtr + accessor.count, retval.data() );
+    return retval;
+  }
+  return {};
+}
+
+const glTF_Node* glTF::FindNodeByName( const std::string& name ) const
+{
+  auto iter = std::find_if( mNodes.begin(), mNodes.end(), [name]( const glTF_Node& node )
+  {
+    return !name.compare( node.name );
+  });
+
+  if(iter == mNodes.end())
+  {
+    return nullptr;
+  }
+
+  return &*iter;
+}
+
+const std::vector<glTF_Material>& glTF::GetMaterials() const
+{
+  return mMaterials;
+}
+
+const std::vector<glTF_Texture>& glTF::GetTextures() const
+{
+  return mTextures;
+}
diff --git a/examples/reflection-demo/gltf-scene.h b/examples/reflection-demo/gltf-scene.h
new file mode 100644 (file)
index 0000000..faecea8
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GLTF_SCENE_H
+#define GLTF_SCENE_H
+
+#include <dali/integration-api/debug.h>
+
+#include <string>
+
+#include "pico-json.h"
+
+#define GLTF_LOG(...) {DALI_LOG_ERROR( __VA_ARGS__ );}
+
+enum class glTFAttributeType
+{
+  POSITION = 0,
+  NORMAL = 1,
+  TEXCOORD_0 = 2,
+  UNDEFINED,
+};
+
+struct glTF_Camera
+{
+  std::string name;
+
+  // perspective setup
+  float yfov;
+  float zfar;
+  float znear;
+
+  bool isPerspective;
+};
+
+struct glTF_BufferView
+{
+  uint32_t bufferIndex;
+  uint32_t byteLength;
+  uint32_t byteOffset;
+  void*    data;
+};
+
+struct glTF_Accessor
+{
+  uint32_t bufferView;
+  uint32_t componentType;
+  uint32_t count;
+  uint32_t componentSize;
+  std::string type;
+};
+
+struct glTF_Mesh
+{
+  std::string name;
+  std::vector<std::pair<glTFAttributeType, uint32_t>> attributes;
+  uint32_t indices;
+  uint32_t material;
+};
+
+struct glTF_Texture
+{
+  std::string uri;
+  std::string name;
+};
+
+struct glTF_Material
+{
+  bool doubleSided;
+  std::string name;
+  struct pbrMetallicRoughness
+  {
+    bool enabled {false};
+    struct baseTextureColor
+    {
+      uint32_t index;
+      uint32_t texCoord;
+    } baseTextureColor;
+  } pbrMetallicRoughness;
+};
+
+struct glTF_Node
+{
+  uint32_t index{0u};
+  std::string  name{};
+  uint32_t     meshId { 0xffffffff };
+  uint32_t     cameraId{ 0xffffffff };
+  glTF_Node*   parent { nullptr };
+  std::vector<uint32_t> children {};
+
+  // Transform
+  float        rotationQuaternion[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
+  float        translation[3] = { 0.0f, 0.0f, 0.0f };
+  float        scale[3] = { 1.0f, 1.0f, 1.0f };
+};
+
+using glTF_Buffer = std::vector<unsigned char>;
+
+/**
+ * Simple glTF parser
+ *
+ * This implementation requires 2 files (it doesn't decode Base64 embedded in json)
+ */
+struct glTF
+{
+
+  glTF( const std::string& filename );
+  ~glTF() = default;
+
+  std::vector<const glTF_Mesh*> GetMeshes() const;
+
+  std::vector<const glTF_Camera*> GetCameras();
+
+  const std::vector<glTF_Material>& GetMaterials() const;
+
+  const std::vector<glTF_Texture>& GetTextures() const;
+
+  const std::vector<glTF_Node>& GetNodes() const
+  {
+    return mNodes;
+  }
+
+  /**
+   * MESH interface
+   */
+  /**
+   * Returns a copy of attribute buffer
+   * @return
+   */
+  std::vector<unsigned char> GetMeshAttributeBuffer( const glTF_Mesh& mesh, const std::vector<glTFAttributeType>& attrTypes );
+  uint32_t GetMeshAttributeCount( const glTF_Mesh* mesh ) const;
+  const glTF_Mesh* FindMeshByName( const std::string& name ) const;
+
+  /**
+   * Returns a copy of index buffer
+   * @return
+   */
+  std::vector<uint16_t> GetMeshIndexBuffer( const glTF_Mesh* mesh ) const;
+
+  const glTF_Node* FindNodeByName( const std::string& name ) const;
+
+private:
+
+  void LoadFromFile( const std::string& filename );
+
+  glTF_Buffer LoadFile( const std::string& filename );
+
+  bool ParseJSON();
+
+  std::vector<glTF_Mesh>        mMeshes;
+  std::vector<glTF_Camera>      mCameras;
+  std::vector<glTF_BufferView>  mBufferViews;
+  std::vector<glTF_Accessor>    mAccessors;
+  std::vector<glTF_Node>        mNodes;
+  std::vector<glTF_Material>    mMaterials;
+  std::vector<glTF_Texture>     mTextures;
+  glTF_Buffer mBuffer;
+  glTF_Buffer jsonBuffer;
+
+  // json nodes
+  picojson::value jsonNode;
+};
+
+
+#endif //DALI_CMAKE_GLTF_SCENE_H
diff --git a/examples/reflection-demo/pico-json.h b/examples/reflection-demo/pico-json.h
new file mode 100644 (file)
index 0000000..c1cde44
--- /dev/null
@@ -0,0 +1,1086 @@
+/*
+ * Copyright 2009-2010 Cybozu Labs, Inc.
+ * Copyright 2011-2014 Kazuho Oku
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef picojson_h
+#define picojson_h
+
+#include <algorithm>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cstddef>
+#include <iostream>
+#include <iterator>
+#include <limits>
+#include <map>
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <utility>
+
+// for isnan/isinf
+#if __cplusplus>=201103L
+# include <cmath>
+#else
+extern "C" {
+# ifdef _MSC_VER
+#  include <float.h>
+# elif defined(__INTEL_COMPILER)
+#  include <mathimf.h>
+# else
+#  include <math.h>
+# endif
+}
+#endif
+
+#ifndef PICOJSON_USE_RVALUE_REFERENCE
+# if (defined(__cpp_rvalue_references) && __cpp_rvalue_references >= 200610) || (defined(_MSC_VER) && _MSC_VER >= 1600)
+#  define PICOJSON_USE_RVALUE_REFERENCE 1
+# else
+#  define PICOJSON_USE_RVALUE_REFERENCE 0
+# endif
+#endif//PICOJSON_USE_RVALUE_REFERENCE
+
+
+// experimental support for int64_t (see README.mkdn for detail)
+#ifdef PICOJSON_USE_INT64
+# define __STDC_FORMAT_MACROS
+# include <errno.h>
+# include <inttypes.h>
+#endif
+
+// to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0
+#ifndef PICOJSON_USE_LOCALE
+# define PICOJSON_USE_LOCALE 1
+#endif
+#if PICOJSON_USE_LOCALE
+extern "C" {
+# include <locale.h>
+}
+#endif
+
+#ifndef PICOJSON_ASSERT
+# define PICOJSON_ASSERT(e) do { if (! (e)) throw std::runtime_error(#e); } while (0)
+#endif
+
+#ifdef _MSC_VER
+#define SNPRINTF _snprintf_s
+    #pragma warning(push)
+    #pragma warning(disable : 4244) // conversion from int to char
+    #pragma warning(disable : 4127) // conditional expression is constant
+    #pragma warning(disable : 4702) // unreachable code
+#else
+#define SNPRINTF snprintf
+#endif
+
+namespace picojson {
+
+enum {
+  null_type,
+  boolean_type,
+  number_type,
+  string_type,
+  array_type,
+  object_type
+#ifdef PICOJSON_USE_INT64
+  , int64_type
+#endif
+};
+
+enum {
+  INDENT_WIDTH = 2
+};
+
+struct null {};
+
+class value {
+public:
+  typedef std::vector<value> array;
+  typedef std::map<std::string, value> object;
+  union _storage {
+    bool boolean_;
+    double number_;
+#ifdef PICOJSON_USE_INT64
+    int64_t int64_;
+#endif
+    std::string* string_;
+    array* array_;
+    object* object_;
+  };
+protected:
+  int type_;
+  _storage u_;
+public:
+  value();
+  value(int type, bool);
+  explicit value(bool b);
+#ifdef PICOJSON_USE_INT64
+  explicit value(int64_t i);
+#endif
+  explicit value(double n);
+  explicit value(const std::string& s);
+  explicit value(const array& a);
+  explicit value(const object& o);
+  explicit value(const char* s);
+  value(const char* s, size_t len);
+  ~value();
+  value(const value& x);
+  value& operator=(const value& x);
+#if PICOJSON_USE_RVALUE_REFERENCE
+  value(value&& x)throw();
+  value& operator=(value&& x)throw();
+#endif
+  void swap(value& x)throw();
+  template <typename T> bool is() const;
+  template <typename T> const T& get() const;
+  template <typename T> T& get();
+  template <typename T> void set(const T &);
+#if PICOJSON_USE_RVALUE_REFERENCE
+  template <typename T> void set(T &&);
+#endif
+  bool evaluate_as_boolean() const;
+  const value& get(size_t idx) const;
+  const value& get(const std::string& key) const;
+  value& get(size_t idx);
+  value& get(const std::string& key);
+
+  bool contains(size_t idx) const;
+  bool contains(const std::string& key) const;
+  std::string to_str() const;
+  template <typename Iter> void serialize(Iter os, bool prettify = false) const;
+  std::string serialize(bool prettify = false) const;
+private:
+  template <typename T> value(const T*); // intentionally defined to block implicit conversion of pointer to bool
+  template <typename Iter> static void _indent(Iter os, int indent);
+  template <typename Iter> void _serialize(Iter os, int indent) const;
+  std::string _serialize(int indent) const;
+  void clear();
+};
+
+typedef value::array array;
+typedef value::object object;
+
+inline value::value() : type_(null_type) {}
+
+inline value::value(int type, bool) : type_(type) {
+  switch (type) {
+#define INIT(p, v) case p##type: u_.p = v; break
+    INIT(boolean_, false);
+    INIT(number_, 0.0);
+#ifdef PICOJSON_USE_INT64
+    INIT(int64_, 0);
+#endif
+    INIT(string_, new std::string());
+    INIT(array_, new array());
+    INIT(object_, new object());
+#undef INIT
+    default: break;
+  }
+}
+
+inline value::value(bool b) : type_(boolean_type) {
+  u_.boolean_ = b;
+}
+
+#ifdef PICOJSON_USE_INT64
+inline value::value(int64_t i) : type_(int64_type) {
+    u_.int64_ = i;
+  }
+#endif
+
+inline value::value(double n) : type_(number_type) {
+  if (
+#ifdef _MSC_VER
+! _finite(n)
+#elif __cplusplus>=201103L || !(defined(isnan) && defined(isinf))
+std::isnan(n) || std::isinf(n)
+#else
+    isnan(n) || isinf(n)
+#endif
+    ) {
+    throw std::overflow_error("");
+  }
+  u_.number_ = n;
+}
+
+inline value::value(const std::string& s) : type_(string_type) {
+  u_.string_ = new std::string(s);
+}
+
+inline value::value(const array& a) : type_(array_type) {
+  u_.array_ = new array(a);
+}
+
+inline value::value(const object& o) : type_(object_type) {
+  u_.object_ = new object(o);
+}
+
+inline value::value(const char* s) : type_(string_type) {
+  u_.string_ = new std::string(s);
+}
+
+inline value::value(const char* s, size_t len) : type_(string_type) {
+  u_.string_ = new std::string(s, len);
+}
+
+inline void value::clear() {
+  switch (type_) {
+#define DEINIT(p) case p##type: delete u_.p; break
+    DEINIT(string_);
+    DEINIT(array_);
+    DEINIT(object_);
+#undef DEINIT
+    default: break;
+  }
+}
+
+inline value::~value() {
+  clear();
+}
+
+inline value::value(const value& x) : type_(x.type_) {
+  switch (type_) {
+#define INIT(p, v) case p##type: u_.p = v; break
+    INIT(string_, new std::string(*x.u_.string_));
+    INIT(array_, new array(*x.u_.array_));
+    INIT(object_, new object(*x.u_.object_));
+#undef INIT
+    default:
+      u_ = x.u_;
+      break;
+  }
+}
+
+inline value& value::operator=(const value& x) {
+  if (this != &x) {
+    value t(x);
+    swap(t);
+  }
+  return *this;
+}
+
+#if PICOJSON_USE_RVALUE_REFERENCE
+inline value::value(value&& x)throw() : type_(null_type) {
+  swap(x);
+}
+inline value& value::operator=(value&& x)throw() {
+  swap(x);
+  return *this;
+}
+#endif
+inline void value::swap(value& x)throw() {
+  std::swap(type_, x.type_);
+  std::swap(u_, x.u_);
+}
+
+#define IS(ctype, jtype)                            \
+  template <> inline bool value::is<ctype>() const { \
+    return type_ == jtype##_type;                   \
+  }
+IS(null, null)
+IS(bool, boolean)
+#ifdef PICOJSON_USE_INT64
+IS(int64_t, int64)
+#endif
+IS(std::string, string)
+IS(array, array)
+IS(object, object)
+#undef IS
+template <> inline bool value::is<double>() const {
+  return type_ == number_type
+#ifdef PICOJSON_USE_INT64
+    || type_ == int64_type
+#endif
+    ;
+}
+
+#define GET(ctype, var)                                                \
+  template <> inline const ctype& value::get<ctype>() const {  \
+    PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()" \
+          && is<ctype>());                                     \
+    return var;                                                        \
+  }                                                            \
+  template <> inline ctype& value::get<ctype>() {              \
+    PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()"        \
+          && is<ctype>());                                     \
+    return var;                                                        \
+  }
+GET(bool, u_.boolean_)
+GET(std::string, *u_.string_)
+GET(array, *u_.array_)
+GET(object, *u_.object_)
+#ifdef PICOJSON_USE_INT64
+GET(double, (type_ == int64_type && (const_cast<value*>(this)->type_ = number_type, const_cast<value*>(this)->u_.number_ = u_.int64_), u_.number_))
+  GET(int64_t, u_.int64_)
+#else
+GET(double, u_.number_)
+#endif
+#undef GET
+
+#define SET(ctype, jtype, setter)           \
+  template <> inline void value::set<ctype>(const ctype &_val) {  \
+    clear();                      \
+    type_ = jtype##_type;         \
+    setter                        \
+  }
+SET(bool, boolean, u_.boolean_ = _val;)
+SET(std::string, string, u_.string_ = new std::string(_val);)
+SET(array, array, u_.array_ = new array(_val);)
+SET(object, object, u_.object_ = new object(_val);)
+SET(double, number, u_.number_ = _val;)
+#ifdef PICOJSON_USE_INT64
+SET(int64_t, int64, u_.int64_ = _val;)
+#endif
+#undef SET
+
+#if PICOJSON_USE_RVALUE_REFERENCE
+#define MOVESET(ctype, jtype, setter)       \
+  template <> inline void value::set<ctype>(ctype &&_val) {     \
+    clear();                      \
+    type_ = jtype##_type;         \
+    setter                        \
+  }
+MOVESET(std::string, string, u_.string_ = new std::string(std::move(_val));)
+MOVESET(array, array, u_.array_ = new array(std::move(_val));)
+MOVESET(object, object, u_.object_ = new object(std::move(_val));)
+#undef MOVESET
+#endif
+
+inline bool value::evaluate_as_boolean() const {
+  switch (type_) {
+    case null_type:
+      return false;
+    case boolean_type:
+      return u_.boolean_;
+    case number_type:
+      return u_.number_ != 0;
+#ifdef PICOJSON_USE_INT64
+    case int64_type:
+      return u_.int64_ != 0;
+#endif
+    case string_type:
+      return ! u_.string_->empty();
+    default:
+      return true;
+  }
+}
+
+inline const value& value::get(size_t idx) const {
+  static value s_null;
+  PICOJSON_ASSERT(is<array>());
+  return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
+}
+
+inline value& value::get(size_t idx) {
+  static value s_null;
+  PICOJSON_ASSERT(is<array>());
+  return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
+}
+
+inline const value& value::get(const std::string& key) const {
+  static value s_null;
+  PICOJSON_ASSERT(is<object>());
+  object::const_iterator i = u_.object_->find(key);
+  return i != u_.object_->end() ? i->second : s_null;
+}
+
+inline value& value::get(const std::string& key) {
+  static value s_null;
+  PICOJSON_ASSERT(is<object>());
+  object::iterator i = u_.object_->find(key);
+  return i != u_.object_->end() ? i->second : s_null;
+}
+
+inline bool value::contains(size_t idx) const {
+  PICOJSON_ASSERT(is<array>());
+  return idx < u_.array_->size();
+}
+
+inline bool value::contains(const std::string& key) const {
+  PICOJSON_ASSERT(is<object>());
+  object::const_iterator i = u_.object_->find(key);
+  return i != u_.object_->end();
+}
+
+inline std::string value::to_str() const {
+  switch (type_) {
+    case null_type:      return "null";
+    case boolean_type:   return u_.boolean_ ? "true" : "false";
+#ifdef PICOJSON_USE_INT64
+    case int64_type: {
+      char buf[sizeof("-9223372036854775808")];
+      SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_);
+      return buf;
+    }
+#endif
+    case number_type:    {
+      char buf[256];
+      double tmp;
+      SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_);
+#if PICOJSON_USE_LOCALE
+      char *decimal_point = localeconv()->decimal_point;
+      if (strcmp(decimal_point, ".") != 0) {
+        size_t decimal_point_len = strlen(decimal_point);
+        for (char *p = buf; *p != '\0'; ++p) {
+          if (strncmp(p, decimal_point, decimal_point_len) == 0) {
+            return std::string(buf, p) + "." + (p + decimal_point_len);
+          }
+        }
+      }
+#endif
+      return buf;
+    }
+    case string_type:    return *u_.string_;
+    case array_type:     return "array";
+    case object_type:    return "object";
+    default:             PICOJSON_ASSERT(0);
+#ifdef _MSC_VER
+      __assume(0);
+#endif
+  }
+  return std::string();
+}
+
+template <typename Iter> void copy(const std::string& s, Iter oi) {
+  std::copy(s.begin(), s.end(), oi);
+}
+
+template <typename Iter>
+struct serialize_str_char {
+  Iter oi;
+  void operator()(char c) {
+    switch (c) {
+#define MAP(val, sym) case val: copy(sym, oi); break
+      MAP('"', "\\\"");
+      MAP('\\', "\\\\");
+      MAP('/', "\\/");
+      MAP('\b', "\\b");
+      MAP('\f', "\\f");
+      MAP('\n', "\\n");
+      MAP('\r', "\\r");
+      MAP('\t', "\\t");
+#undef MAP
+      default:
+        if (static_cast<unsigned char>(c) < 0x20 || c == 0x7f) {
+          char buf[7];
+          SNPRINTF(buf, sizeof(buf), "\\u%04x", c & 0xff);
+          copy(buf, buf + 6, oi);
+        } else {
+          *oi++ = c;
+        }
+        break;
+    }
+  }
+};
+
+template <typename Iter> void serialize_str(const std::string& s, Iter oi) {
+  *oi++ = '"';
+  serialize_str_char<Iter> process_char = { oi };
+  std::for_each(s.begin(), s.end(), process_char);
+  *oi++ = '"';
+}
+
+template <typename Iter> void value::serialize(Iter oi, bool prettify) const {
+  return _serialize(oi, prettify ? 0 : -1);
+}
+
+inline std::string value::serialize(bool prettify) const {
+  return _serialize(prettify ? 0 : -1);
+}
+
+template <typename Iter> void value::_indent(Iter oi, int indent) {
+  *oi++ = '\n';
+  for (int i = 0; i < indent * INDENT_WIDTH; ++i) {
+    *oi++ = ' ';
+  }
+}
+
+template <typename Iter> void value::_serialize(Iter oi, int indent) const {
+  switch (type_) {
+    case string_type:
+      serialize_str(*u_.string_, oi);
+      break;
+    case array_type: {
+      *oi++ = '[';
+      if (indent != -1) {
+        ++indent;
+      }
+      for (array::const_iterator i = u_.array_->begin();
+           i != u_.array_->end();
+           ++i) {
+        if (i != u_.array_->begin()) {
+          *oi++ = ',';
+        }
+        if (indent != -1) {
+          _indent(oi, indent);
+        }
+        i->_serialize(oi, indent);
+      }
+      if (indent != -1) {
+        --indent;
+        if (! u_.array_->empty()) {
+          _indent(oi, indent);
+        }
+      }
+      *oi++ = ']';
+      break;
+    }
+    case object_type: {
+      *oi++ = '{';
+      if (indent != -1) {
+        ++indent;
+      }
+      for (object::const_iterator i = u_.object_->begin();
+           i != u_.object_->end();
+           ++i) {
+        if (i != u_.object_->begin()) {
+          *oi++ = ',';
+        }
+        if (indent != -1) {
+          _indent(oi, indent);
+        }
+        serialize_str(i->first, oi);
+        *oi++ = ':';
+        if (indent != -1) {
+          *oi++ = ' ';
+        }
+        i->second._serialize(oi, indent);
+      }
+      if (indent != -1) {
+        --indent;
+        if (! u_.object_->empty()) {
+          _indent(oi, indent);
+        }
+      }
+      *oi++ = '}';
+      break;
+    }
+    default:
+      copy(to_str(), oi);
+      break;
+  }
+  if (indent == 0) {
+    *oi++ = '\n';
+  }
+}
+
+inline std::string value::_serialize(int indent) const {
+  std::string s;
+  _serialize(std::back_inserter(s), indent);
+  return s;
+}
+
+template <typename Iter> class input {
+protected:
+  Iter cur_, end_;
+  bool consumed_;
+  int line_;
+public:
+  input(const Iter& first, const Iter& last) : cur_(first), end_(last), consumed_(false), line_(1) {}
+  int getc() {
+    if (consumed_) {
+      if (*cur_ == '\n') {
+        ++line_;
+      }
+      ++cur_;
+    }
+    if (cur_ == end_) {
+      consumed_ = false;
+      return -1;
+    }
+    consumed_ = true;
+    return *cur_ & 0xff;
+  }
+  void ungetc() {
+    consumed_ = false;
+  }
+  Iter cur() const {
+    if (consumed_) {
+      input<Iter> *self = const_cast<input<Iter>*>(this);
+      self->consumed_ = false;
+      ++self->cur_;
+    }
+    return cur_;
+  }
+  int line() const { return line_; }
+  void skip_ws() {
+    while (1) {
+      int ch = getc();
+      if (! (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) {
+        ungetc();
+        break;
+      }
+    }
+  }
+  bool expect(int expect) {
+    skip_ws();
+    if (getc() != expect) {
+      ungetc();
+      return false;
+    }
+    return true;
+  }
+  bool match(const std::string& pattern) {
+    for (std::string::const_iterator pi(pattern.begin());
+         pi != pattern.end();
+         ++pi) {
+      if (getc() != *pi) {
+        ungetc();
+        return false;
+      }
+    }
+    return true;
+  }
+};
+
+template<typename Iter> inline int _parse_quadhex(input<Iter> &in) {
+  int uni_ch = 0, hex;
+  for (int i = 0; i < 4; i++) {
+    if ((hex = in.getc()) == -1) {
+      return -1;
+    }
+    if ('0' <= hex && hex <= '9') {
+      hex -= '0';
+    } else if ('A' <= hex && hex <= 'F') {
+      hex -= 'A' - 0xa;
+    } else if ('a' <= hex && hex <= 'f') {
+      hex -= 'a' - 0xa;
+    } else {
+      in.ungetc();
+      return -1;
+    }
+    uni_ch = uni_ch * 16 + hex;
+  }
+  return uni_ch;
+}
+
+template<typename String, typename Iter> inline bool _parse_codepoint(String& out, input<Iter>& in) {
+  int uni_ch;
+  if ((uni_ch = _parse_quadhex(in)) == -1) {
+    return false;
+  }
+  if (0xd800 <= uni_ch && uni_ch <= 0xdfff) {
+    if (0xdc00 <= uni_ch) {
+      // a second 16-bit of a surrogate pair appeared
+      return false;
+    }
+    // first 16-bit of surrogate pair, get the next one
+    if (in.getc() != '\\' || in.getc() != 'u') {
+      in.ungetc();
+      return false;
+    }
+    int second = _parse_quadhex(in);
+    if (! (0xdc00 <= second && second <= 0xdfff)) {
+      return false;
+    }
+    uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff);
+    uni_ch += 0x10000;
+  }
+  if (uni_ch < 0x80) {
+    out.push_back(uni_ch);
+  } else {
+    if (uni_ch < 0x800) {
+      out.push_back(0xc0 | (uni_ch >> 6));
+    } else {
+      if (uni_ch < 0x10000) {
+        out.push_back(0xe0 | (uni_ch >> 12));
+      } else {
+        out.push_back(0xf0 | (uni_ch >> 18));
+        out.push_back(0x80 | ((uni_ch >> 12) & 0x3f));
+      }
+      out.push_back(0x80 | ((uni_ch >> 6) & 0x3f));
+    }
+    out.push_back(0x80 | (uni_ch & 0x3f));
+  }
+  return true;
+}
+
+template<typename String, typename Iter> inline bool _parse_string(String& out, input<Iter>& in) {
+  while (1) {
+    int ch = in.getc();
+    if (ch < ' ') {
+      in.ungetc();
+      return false;
+    } else if (ch == '"') {
+      return true;
+    } else if (ch == '\\') {
+      if ((ch = in.getc()) == -1) {
+        return false;
+      }
+      switch (ch) {
+#define MAP(sym, val) case sym: out.push_back(val); break
+        MAP('"', '\"');
+        MAP('\\', '\\');
+        MAP('/', '/');
+        MAP('b', '\b');
+        MAP('f', '\f');
+        MAP('n', '\n');
+        MAP('r', '\r');
+        MAP('t', '\t');
+#undef MAP
+        case 'u':
+          if (! _parse_codepoint(out, in)) {
+            return false;
+          }
+          break;
+        default:
+          return false;
+      }
+    } else {
+      out.push_back(ch);
+    }
+  }
+  return false;
+}
+
+template <typename Context, typename Iter> inline bool _parse_array(Context& ctx, input<Iter>& in) {
+  if (! ctx.parse_array_start()) {
+    return false;
+  }
+  size_t idx = 0;
+  if (in.expect(']')) {
+    return ctx.parse_array_stop(idx);
+  }
+  do {
+    if (! ctx.parse_array_item(in, idx)) {
+      return false;
+    }
+    idx++;
+  } while (in.expect(','));
+  return in.expect(']') && ctx.parse_array_stop(idx);
+}
+
+template <typename Context, typename Iter> inline bool _parse_object(Context& ctx, input<Iter>& in) {
+  if (! ctx.parse_object_start()) {
+    return false;
+  }
+  if (in.expect('}')) {
+    return true;
+  }
+  do {
+    std::string key;
+    if (! in.expect('"')
+        || ! _parse_string(key, in)
+        || ! in.expect(':')) {
+      return false;
+    }
+    if (! ctx.parse_object_item(in, key)) {
+      return false;
+    }
+  } while (in.expect(','));
+  return in.expect('}');
+}
+
+template <typename Iter> inline std::string _parse_number(input<Iter>& in) {
+  std::string num_str;
+  while (1) {
+    int ch = in.getc();
+    if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-'
+        || ch == 'e' || ch == 'E') {
+      num_str.push_back(ch);
+    } else if (ch == '.') {
+#if PICOJSON_USE_LOCALE
+      num_str += localeconv()->decimal_point;
+#else
+      num_str.push_back('.');
+#endif
+    } else {
+      in.ungetc();
+      break;
+    }
+  }
+  return num_str;
+}
+
+template <typename Context, typename Iter> inline bool _parse(Context& ctx, input<Iter>& in) {
+  in.skip_ws();
+  int ch = in.getc();
+  switch (ch) {
+#define IS(ch, text, op) case ch: \
+      if (in.match(text) && op) { \
+       return true; \
+      } else { \
+       return false; \
+      }
+    IS('n', "ull", ctx.set_null());
+    IS('f', "alse", ctx.set_bool(false));
+    IS('t', "rue", ctx.set_bool(true));
+#undef IS
+    case '"':
+      return ctx.parse_string(in);
+    case '[':
+      return _parse_array(ctx, in);
+    case '{':
+      return _parse_object(ctx, in);
+    default:
+      if (('0' <= ch && ch <= '9') || ch == '-') {
+        double f;
+        char *endp;
+        in.ungetc();
+        std::string num_str = _parse_number(in);
+        if (num_str.empty()) {
+          return false;
+        }
+#ifdef PICOJSON_USE_INT64
+        {
+          errno = 0;
+          intmax_t ival = strtoimax(num_str.c_str(), &endp, 10);
+          if (errno == 0
+              && std::numeric_limits<int64_t>::min() <= ival
+              && ival <= std::numeric_limits<int64_t>::max()
+              && endp == num_str.c_str() + num_str.size()) {
+            ctx.set_int64(ival);
+            return true;
+          }
+        }
+#endif
+        f = strtod(num_str.c_str(), &endp);
+        if (endp == num_str.c_str() + num_str.size()) {
+          ctx.set_number(f);
+          return true;
+        }
+        return false;
+      }
+      break;
+  }
+  in.ungetc();
+  return false;
+}
+
+class deny_parse_context {
+public:
+  bool set_null() { return false; }
+  bool set_bool(bool) { return false; }
+#ifdef PICOJSON_USE_INT64
+  bool set_int64(int64_t) { return false; }
+#endif
+  bool set_number(double) { return false; }
+  template <typename Iter> bool parse_string(input<Iter>&) { return false; }
+  bool parse_array_start() { return false; }
+  template <typename Iter> bool parse_array_item(input<Iter>&, size_t) {
+    return false;
+  }
+  bool parse_array_stop(size_t) { return false; }
+  bool parse_object_start() { return false; }
+  template <typename Iter> bool parse_object_item(input<Iter>&, const std::string&) {
+    return false;
+  }
+};
+
+class default_parse_context {
+protected:
+  value* out_;
+public:
+  default_parse_context(value* out) : out_(out) {}
+  bool set_null() {
+    *out_ = value();
+    return true;
+  }
+  bool set_bool(bool b) {
+    *out_ = value(b);
+    return true;
+  }
+#ifdef PICOJSON_USE_INT64
+  bool set_int64(int64_t i) {
+      *out_ = value(i);
+      return true;
+    }
+#endif
+  bool set_number(double f) {
+    *out_ = value(f);
+    return true;
+  }
+  template<typename Iter> bool parse_string(input<Iter>& in) {
+    *out_ = value(string_type, false);
+    return _parse_string(out_->get<std::string>(), in);
+  }
+  bool parse_array_start() {
+    *out_ = value(array_type, false);
+    return true;
+  }
+  template <typename Iter> bool parse_array_item(input<Iter>& in, size_t) {
+    array& a = out_->get<array>();
+    a.push_back(value());
+    default_parse_context ctx(&a.back());
+    return _parse(ctx, in);
+  }
+  bool parse_array_stop(size_t) { return true; }
+  bool parse_object_start() {
+    *out_ = value(object_type, false);
+    return true;
+  }
+  template <typename Iter> bool parse_object_item(input<Iter>& in, const std::string& key) {
+    object& o = out_->get<object>();
+    default_parse_context ctx(&o[key]);
+    return _parse(ctx, in);
+  }
+private:
+  default_parse_context(const default_parse_context&);
+  default_parse_context& operator=(const default_parse_context&);
+};
+
+class null_parse_context {
+public:
+  struct dummy_str {
+    void push_back(int) {}
+  };
+public:
+  null_parse_context() {}
+  bool set_null() { return true; }
+  bool set_bool(bool) { return true; }
+#ifdef PICOJSON_USE_INT64
+  bool set_int64(int64_t) { return true; }
+#endif
+  bool set_number(double) { return true; }
+  template <typename Iter> bool parse_string(input<Iter>& in) {
+    dummy_str s;
+    return _parse_string(s, in);
+  }
+  bool parse_array_start() { return true; }
+  template <typename Iter> bool parse_array_item(input<Iter>& in, size_t) {
+    return _parse(*this, in);
+  }
+  bool parse_array_stop(size_t) { return true; }
+  bool parse_object_start() { return true; }
+  template <typename Iter> bool parse_object_item(input<Iter>& in, const std::string&) {
+    return _parse(*this, in);
+  }
+private:
+  null_parse_context(const null_parse_context&);
+  null_parse_context& operator=(const null_parse_context&);
+};
+
+// obsolete, use the version below
+template <typename Iter> inline std::string parse(value& out, Iter& pos, const Iter& last) {
+  std::string err;
+  pos = parse(out, pos, last, &err);
+  return err;
+}
+
+template <typename Context, typename Iter> inline Iter _parse(Context& ctx, const Iter& first, const Iter& last, std::string* err) {
+  input<Iter> in(first, last);
+  if (! _parse(ctx, in) && err != NULL) {
+    char buf[64];
+    SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line());
+    *err = buf;
+    while (1) {
+      int ch = in.getc();
+      if (ch == -1 || ch == '\n') {
+        break;
+      } else if (ch >= ' ') {
+        err->push_back(ch);
+      }
+    }
+  }
+  return in.cur();
+}
+
+template <typename Iter> inline Iter parse(value& out, const Iter& first, const Iter& last, std::string* err) {
+  default_parse_context ctx(&out);
+  return _parse(ctx, first, last, err);
+}
+
+inline std::string parse(value& out, const std::string& s) {
+  std::string err;
+  parse(out, s.begin(), s.end(), &err);
+  return err;
+}
+
+inline std::string parse(value& out, std::istream& is) {
+  std::string err;
+  parse(out, std::istreambuf_iterator<char>(is.rdbuf()),
+        std::istreambuf_iterator<char>(), &err);
+  return err;
+}
+
+template <typename T> struct last_error_t {
+  static std::string s;
+};
+template <typename T> std::string last_error_t<T>::s;
+
+inline void set_last_error(const std::string& s) {
+  last_error_t<bool>::s = s;
+}
+
+inline const std::string& get_last_error() {
+  return last_error_t<bool>::s;
+}
+
+inline bool operator==(const value& x, const value& y) {
+  if (x.is<null>())
+    return y.is<null>();
+#define PICOJSON_CMP(type)                                     \
+    if (x.is<type>())                                          \
+      return y.is<type>() && x.get<type>() == y.get<type>()
+  PICOJSON_CMP(bool);
+  PICOJSON_CMP(double);
+  PICOJSON_CMP(std::string);
+  PICOJSON_CMP(array);
+  PICOJSON_CMP(object);
+#undef PICOJSON_CMP
+  PICOJSON_ASSERT(0);
+#ifdef _MSC_VER
+  __assume(0);
+#endif
+  return false;
+}
+
+inline bool operator!=(const value& x, const value& y) {
+  return ! (x == y);
+}
+}
+
+#if !PICOJSON_USE_RVALUE_REFERENCE
+namespace std {
+  template<> inline void swap(picojson::value& x, picojson::value& y)
+    {
+      x.swap(y);
+    }
+}
+#endif
+
+inline std::istream& operator>>(std::istream& is, picojson::value& x)
+{
+  picojson::set_last_error(std::string());
+  std::string err = picojson::parse(x, is);
+  if (! err.empty()) {
+    picojson::set_last_error(err);
+    is.setstate(std::ios::failbit);
+  }
+  return is;
+}
+
+inline std::ostream& operator<<(std::ostream& os, const picojson::value& x)
+{
+  x.serialize(std::ostream_iterator<char>(os));
+  return os;
+}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#endif
diff --git a/examples/reflection-demo/reflection-example.cpp b/examples/reflection-demo/reflection-example.cpp
new file mode 100644 (file)
index 0000000..c0eb880
--- /dev/null
@@ -0,0 +1,674 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali-toolkit/dali-toolkit.h>
+#include <dali/devel-api/actors/camera-actor-devel.h>
+#include <dali/devel-api/adaptor-framework/file-stream.h>
+
+#include <map>
+
+#include "gltf-scene.h"
+
+using namespace Dali;
+using Dali::Toolkit::TextLabel;
+
+const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
+  attribute mediump vec3 aPosition;\n
+  attribute mediump vec3 aNormal;\n
+  attribute mediump vec2 aTexCoord;\n
+  uniform mediump mat4 uMvpMatrix;\n
+  uniform mediump mat3 uNormalMatrix;\n
+  uniform mediump vec3 uSize;\n
+  \n
+  varying mediump vec2 vTexCoord; \n
+  varying mediump vec3 vNormal; \n
+  varying mediump vec3 vPosition; \n
+  void main()\n
+{\n
+  mediump vec4 vertexPosition = vec4(aPosition, 1.0);\n
+  vertexPosition.xyz *= uSize;\n
+  vTexCoord = aTexCoord;\n
+  vNormal = normalize(uNormalMatrix * aNormal);\n
+  vPosition = aPosition; \n
+  gl_Position = uMvpMatrix * vertexPosition;\n
+}\n
+);
+
+const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
+  uniform lowp vec4 uColor;\n
+  uniform sampler2D sTexture; \n
+  varying mediump vec3 vNormal;\n
+  varying mediump vec3 vPosition; \n
+  varying mediump vec2 vTexCoord; \n
+  \n
+  void main()\n
+{\n
+  gl_FragColor = texture2D(sTexture, vTexCoord) * 50.0;\n
+}\n
+);
+
+const char* FRAGMENT_SIMPLE_SHADER = DALI_COMPOSE_SHADER(
+        uniform lowp vec4 uColor;\n
+        uniform sampler2D sTexture; \n
+        varying mediump vec3 vNormal;\n
+        varying mediump vec3 vPosition; \n
+        varying mediump vec2 vTexCoord; \n
+        \n
+        void main()\n
+{\n
+        gl_FragColor = texture2D(sTexture, vTexCoord) * 2.0;\n
+}\n
+);
+
+const char* TEXTURED_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
+  uniform lowp vec4 uColor;\n
+  uniform sampler2D sTexture; \n
+  uniform mediump vec2 uScreenSize;\n
+
+  uniform mediump vec3 eyePos;\n
+  uniform mediump vec3 lightDir;\n
+
+  varying mediump vec3 vNormal;\n
+  varying mediump vec3 vPosition; \n
+  varying mediump vec2 vTexCoord; \n
+  \n
+  void main()\n
+{\n
+  mediump vec3 n = normalize( vNormal );\n
+  mediump vec3 l = normalize( lightDir );\n
+  mediump vec3 e = normalize( eyePos );\n
+  mediump float intensity = max(dot(n,l), 0.0);\n
+  gl_FragColor = texture2D(sTexture, vTexCoord) * intensity;\n
+}\n
+);
+
+const char* PLASMA_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
+  precision mediump float;\n
+  uniform sampler2D sTexture; \n
+
+  uniform float uTime;
+  uniform float uKFactor;
+  uniform mediump vec3 eyePos;\n
+  uniform mediump vec3 lightDir;\n
+  varying mediump vec3 vNormal;\n
+  varying mediump vec3 vPosition; \n
+  varying mediump vec2 vTexCoord; \n
+  \n
+  void main()\n
+{\n
+  mediump vec3 n = normalize( vNormal );\n
+  mediump vec3 l = normalize( lightDir );\n
+  mediump vec3 e = normalize( eyePos );\n
+  mediump float intensity = max(dot(n,l), 0.0);\n
+\n
+  const mediump float PI = 3.1415926535897932384626433832795;\n
+  mediump float v = 0.0;\n
+  mediump vec2 c = vTexCoord * uKFactor - uKFactor/2.0;\n
+  v += sin((c.x+uTime));\n
+  v += sin((c.y+uTime)/2.0);\n
+  v += sin((c.x+c.y+uTime)/2.0);\n
+  c += uKFactor/2.0 * vec2(sin(uTime/3.0), cos(uTime/2.0));\n
+  v += sin(sqrt(c.x*c.x+c.y*c.y+1.0)+uTime);\n
+  v = v/2.0;\n
+  mediump vec3 col = vec3(1, sin(PI*v), cos(PI*v));\n
+  gl_FragColor = (texture2D(sTexture, vTexCoord)) * (((col.r+col.g+col.b)/3.0)+1.0+intensity);\n
+}\n
+);
+
+const char* TEX_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
+  uniform lowp vec4 uColor;\n
+  uniform sampler2D sTexture0; \n
+  uniform sampler2D sTexture1; \n
+  uniform mediump vec3 eyePos;\n
+  uniform mediump vec3 lightDir;\n
+  uniform mediump vec2 uScreenSize;\n
+  varying mediump vec3 vNormal;\n
+  varying mediump vec3 vPosition; \n
+  varying mediump vec2 vTexCoord; \n
+  \n
+
+  mediump float rand(mediump vec2 co){\n
+    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);\n
+  }\n
+  \n
+  void main()\n
+{\n
+  mediump vec2 tx = (gl_FragCoord.xy / uScreenSize.xy);\n
+  mediump vec3 n = normalize( vNormal );\n
+  mediump vec3 l = normalize( lightDir );\n
+  mediump vec3 e = normalize( eyePos );\n
+  mediump float factor = gl_FragCoord.y / uScreenSize.y;\n
+  mediump float intensity = max(dot(n,l), 0.0);\n
+  mediump vec2 uv = tx;\n
+  gl_FragColor = ((texture2D(sTexture0, vTexCoord) * factor ) + \n
+                 (texture2D(sTexture1, uv))) * intensity;\n
+}\n
+);
+
+struct Model
+{
+  Shader    shader;
+  Geometry  geometry;
+};
+
+// This example shows how to create and display mirrored reflection using CameraActor
+//
+class ReflectionExample : public ConnectionTracker
+{
+public:
+
+  ReflectionExample( Application& application )
+  : mApplication( application )
+  {
+    // Connect to the Application's Init signal
+    mApplication.InitSignal().Connect( this, &ReflectionExample::Create );
+  }
+
+  ~ReflectionExample() = default;
+
+private:
+
+  // The Init signal is received once (only) during the Application lifetime
+  void Create( Application& application )
+  {
+    // Get a handle to the stage
+    Stage stage = Stage::GetCurrent();
+    uint32_t stageWidth  = uint32_t(stage.GetSize().x);
+    uint32_t stageHeight = uint32_t(stage.GetSize().y);
+
+    stage.GetRenderTaskList().GetTask(0).SetClearEnabled(false);
+    mLayer3D = Layer::New();
+    mLayer3D.SetSize( stageWidth, stageHeight );
+    stage.Add(mLayer3D);
+
+    mLayer3D.SetAnchorPoint( AnchorPoint::CENTER );
+    mLayer3D.SetParentOrigin( ParentOrigin::CENTER );
+    mLayer3D.SetBehavior( Layer::LAYER_3D );
+    mLayer3D.SetDepthTestDisabled( false );
+
+
+    auto gltf = glTF(DEMO_GAME_DIR "/reflection");
+
+    // Define direction of light
+    mLightDir = Vector3( 0.5, 0.5, -1 );
+
+    /**
+     * Instantiate texture sets
+     */
+    CreateTextureSetsFromGLTF( &gltf, DEMO_GAME_DIR );
+
+    /**
+     * Create models
+     */
+    CreateModelsFromGLTF( &gltf );
+
+    /**
+     * Create scene nodes
+     */
+    CreateSceneFromGLTF( stage, &gltf );
+
+    auto planeActor = mLayer3D.FindChildByName( "Plane" );
+    auto solarActor = mLayer3D.FindChildByName( "solar_root" );
+    auto backgroundActor = mLayer3D.FindChildByName( "background" );
+    ReplaceShader( backgroundActor, VERTEX_SHADER, FRAGMENT_SIMPLE_SHADER );
+    mCenterActor = mLayer3D.FindChildByName( "center" );
+    mCenterHorizActor = mLayer3D.FindChildByName( "center2" );
+
+    // Prepare Sun
+    auto sun = mLayer3D.FindChildByName( "sun" );
+    ReplaceShader( sun, VERTEX_SHADER, PLASMA_FRAGMENT_SHADER );
+    mSunTimeUniformIndex = sun.RegisterProperty( "uTime", 0.0f );
+    mSunKFactorUniformIndex = sun.RegisterProperty( "uKFactor", 0.0f );
+
+    mTickTimer = Timer::New( 16 );
+    mTickTimer.TickSignal().Connect( this, &ReflectionExample::TickTimerSignal);
+
+    // Milkyway
+    auto milkyway = mLayer3D.FindChildByName( "milkyway" );
+    ReplaceShader( milkyway, VERTEX_SHADER, FRAGMENT_SHADER );
+
+    auto renderTaskSourceActor = mLayer3D.FindChildByName( "RenderTaskSource" );
+
+    /**
+     * Access camera (it's a child of "Camera" node)
+     */
+    auto camera = mLayer3D.FindChildByName( "Camera_Orientation" );
+    auto cameraRef = mLayer3D.FindChildByName( "CameraReflection_Orientation" );
+
+    auto cameraActor = CameraActor::DownCast( camera );
+    mCamera3D = cameraActor;
+
+    auto cameraRefActor = CameraActor::DownCast( cameraRef );
+    cameraRefActor.SetProperty( DevelCameraActor::Property::REFLECTION_PLANE, Vector4(0.0f, -1.0f, 0.0f, 0.0f));
+    mReflectionCamera3D = cameraRefActor;
+
+    auto task3D = stage.GetRenderTaskList().CreateTask();
+    task3D.SetSourceActor( mLayer3D );
+    task3D.SetViewport( Rect<int>(0, 0, stageWidth, stageHeight ) );
+    task3D.SetCameraActor( cameraActor );
+    task3D.SetClearColor( Color::BLACK );
+    task3D.SetClearEnabled( true );
+    task3D.SetExclusive( false );
+    task3D.SetCameraActor( cameraActor );
+
+    /**
+     * Change shader to textured
+     */
+    Shader texShader = CreateShader( VERTEX_SHADER, TEX_FRAGMENT_SHADER );
+    planeActor.RegisterProperty( "uScreenSize", Vector2(stageWidth, stageHeight) );
+    auto renderer = planeActor.GetRendererAt(0);
+    auto textureSet = renderer.GetTextures();
+    renderer.SetShader( texShader );
+
+    Texture fbTexture = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGBA8888, stageWidth, stageHeight );
+    textureSet.SetTexture( 1u, fbTexture );
+
+    auto fb = FrameBuffer::New(stageWidth, stageHeight,
+                               FrameBuffer::Attachment::DEPTH );
+
+    fb.AttachColorTexture( fbTexture );
+
+    auto renderTask = stage.GetRenderTaskList().CreateTask();
+    renderTask.SetFrameBuffer( fb );
+    renderTask.SetSourceActor( renderTaskSourceActor );
+    renderTask.SetViewport( Rect<int>(0, 0, stageWidth, stageHeight ) );
+    renderTask.SetCameraActor( cameraRefActor );
+    renderTask.SetClearColor( Color::BLACK );
+    renderTask.SetClearEnabled( true );
+    renderTask.SetExclusive( false );
+
+    mAnimation = Animation::New(30.0f );
+    mAnimation.AnimateBy(Property(solarActor, Actor::Property::ORIENTATION ),
+                         Quaternion( Degree(359), Vector3(0.0, 1.0, 0.0)));
+    mAnimation.AnimateBy(Property(milkyway, Actor::Property::ORIENTATION ),
+                         Quaternion( Degree(-359), Vector3(0.0, 1.0, 0.0)));
+    mAnimation.SetLooping(true );
+    mAnimation.Play();
+
+    Actor panScreen = Actor::New();
+    auto stageSize = stage.GetSize();
+    panScreen.SetSize( stageSize.width, stageSize.height );
+    panScreen.SetAnchorPoint( AnchorPoint::CENTER );
+    panScreen.SetParentOrigin( ParentOrigin::CENTER );
+    auto camera2d = stage.GetRenderTaskList().GetTask(0).GetCameraActor();
+    panScreen.SetPosition( 0, 0, camera2d.GetNearClippingPlane() );
+    camera2d.Add(panScreen);
+    camera2d.RotateBy( Degree(180.0f), Vector3( 0.0, 1.0, 0.0 ) );
+    mPanGestureDetector = PanGestureDetector::New();
+    mPanGestureDetector.Attach( panScreen );
+    mPanGestureDetector.DetectedSignal().Connect( this, &ReflectionExample::OnPan );
+
+    // Respond to key events
+    stage.KeyEventSignal().Connect( this, &ReflectionExample::OnKeyEvent );
+
+    mTickTimer.Start();
+  }
+
+  void CreateSceneFromGLTF( Stage stage, glTF* gltf )
+  {
+    const auto& nodes = gltf->GetNodes();
+
+    // for each node create nodes and children
+    // resolve parents later
+    std::vector<Actor> actors;
+    actors.reserve( nodes.size() );
+    for( const auto& node : nodes )
+    {
+      auto actor = node.cameraId != 0xffffffff ? CameraActor::New( stage.GetSize() ) : Actor::New();
+
+      actor.SetSize( 1, 1, 1 );
+      actor.SetName( node.name );
+      actor.SetAnchorPoint( AnchorPoint::CENTER );
+      actor.SetParentOrigin( ParentOrigin::CENTER );
+      actor.SetPosition( node.translation[0], node.translation[1], node.translation[2] );
+      actor.SetScale( node.scale[0], node.scale[1], node.scale[2] );
+      actor.SetOrientation( Quaternion(node.rotationQuaternion[3],
+                                       node.rotationQuaternion[0],
+                                       node.rotationQuaternion[1],
+                                       node.rotationQuaternion[2]));
+
+      actors.emplace_back( actor );
+
+      // Initially add each actor to the very first one
+      if(actors.size() > 1)
+      {
+        actors[0].Add(actor);
+      }
+
+      // If mesh, create and add renderer
+      if(node.meshId != 0xffffffff)
+      {
+        const auto& model = mModels[node.meshId].get();
+        auto renderer = Renderer::New( model->geometry, model->shader );
+
+        // if textured, add texture set
+        auto materialId = gltf->GetMeshes()[node.meshId]->material;
+        if( materialId != 0xffffffff )
+        {
+          if( gltf->GetMaterials()[materialId].pbrMetallicRoughness.enabled )
+          {
+            renderer.SetTextures( mTextureSets[materialId] );
+          }
+        }
+
+        actor.AddRenderer( renderer );
+      }
+
+      // Reset and attach main camera
+      if( node.cameraId != 0xffffffff )
+      {
+        mCameraPos = Vector3(node.translation[0], node.translation[1], node.translation[2]);
+        auto quatY = Quaternion( Degree(180.0f), Vector3( 0.0, 1.0, 0.0) );
+        auto cameraActor = CameraActor::DownCast( actor );
+        cameraActor.SetOrientation( Quaternion(node.rotationQuaternion[3],
+                                               node.rotationQuaternion[0],
+                                               node.rotationQuaternion[1],
+                                               node.rotationQuaternion[2] )
+                                    * quatY
+                                      );
+        cameraActor.SetProjectionMode( Camera::PERSPECTIVE_PROJECTION );
+
+        const auto camera = gltf->GetCameras()[node.cameraId];
+        cameraActor.SetNearClippingPlane( camera->znear );
+        cameraActor.SetFarClippingPlane( camera->zfar );
+        cameraActor.SetFieldOfView( camera->yfov );
+
+        cameraActor.SetProperty( CameraActor::Property::INVERT_Y_AXIS, true);
+        cameraActor.SetAnchorPoint( AnchorPoint::CENTER );
+        cameraActor.SetParentOrigin( ParentOrigin::CENTER );
+
+        mCameras.emplace_back( cameraActor );
+      }
+    }
+
+    // Resolve hierarchy dependencies
+    auto i = 0u;
+    for( const auto& node : nodes )
+    {
+      if(!node.children.empty())
+      {
+        for(const auto& childId : node.children)
+        {
+          actors[i].Add( actors[childId+1] );
+        }
+      }
+      ++i;
+    }
+
+    mActors = std::move(actors);
+
+    // Add root actor to the stage
+    mLayer3D.Add( mActors[0] );
+
+    for( auto& actor : mActors )
+    {
+      actor.RegisterProperty( "lightDir", mLightDir );
+      actor.RegisterProperty( "eyePos", mCameraPos );
+    }
+
+  }
+
+  /**
+   * Creates models from glTF
+   */
+  void CreateModelsFromGLTF( glTF* gltf )
+  {
+    const auto& meshes = gltf->GetMeshes();
+    for( const auto& mesh : meshes )
+    {
+      // change shader to use texture if material indicates that
+      if(mesh->material != 0xffffffff && gltf->GetMaterials()[mesh->material].pbrMetallicRoughness.enabled)
+      {
+        mModels.emplace_back( CreateModel( *gltf, mesh, VERTEX_SHADER, TEXTURED_FRAGMENT_SHADER ) );
+      }
+      else
+      {
+        mModels.emplace_back( CreateModel( *gltf, mesh, VERTEX_SHADER, FRAGMENT_SHADER ) );
+      }
+    }
+  }
+
+  void CreateTextureSetsFromGLTF( glTF* gltf, const std::string& basePath )
+  {
+    const auto& materials = gltf->GetMaterials();
+    const auto& textures = gltf->GetTextures();
+
+    std::map<std::string, Texture> textureCache{};
+
+    for(const auto& material : materials )
+    {
+      TextureSet textureSet;
+      if(material.pbrMetallicRoughness.enabled)
+      {
+        textureSet = TextureSet::New();
+        std::string filename( basePath );
+        filename += '/';
+        filename += textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
+        Dali::PixelData pixelData = Dali::Toolkit::SyncImageLoader::Load( filename );
+
+        auto cacheKey = textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
+        auto iter = textureCache.find(cacheKey);
+        Texture texture;
+        if(iter == textureCache.end())
+        {
+          texture = Texture::New(TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(),
+                                         pixelData.GetHeight());
+          texture.Upload(pixelData);
+          texture.GenerateMipmaps();
+          textureCache[cacheKey] = texture;
+        }
+        else
+        {
+          texture = iter->second;
+        }
+        textureSet.SetTexture( 0, texture );
+        Dali::Sampler sampler = Dali::Sampler::New();
+        sampler.SetWrapMode( Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT );
+        sampler.SetFilterMode( Dali::FilterMode::LINEAR_MIPMAP_LINEAR, Dali::FilterMode::LINEAR );
+        textureSet.SetSampler( 0, sampler );
+      }
+      mTextureSets.emplace_back( textureSet );
+    }
+  }
+
+  template<class T>
+  bool LoadFile( const std::string& filename, std::vector<T>& bytes )
+  {
+    Dali::FileStream fileStream( filename, Dali::FileStream::READ | Dali::FileStream::BINARY );
+    FILE* fin = fileStream.GetFile();
+
+    if( fin )
+    {
+      if( fseek( fin, 0, SEEK_END ) )
+      {
+        return false;
+      }
+      bytes.resize( uint32_t(ftell( fin )) );
+      std::fill( bytes.begin(), bytes.end(), 0 );
+      if( fseek( fin, 0, SEEK_SET ) )
+      {
+        return false;
+      }
+      size_t result = fread( bytes.data(), 1, bytes.size(), fin );
+      return ( result != 0 );
+    }
+
+    return false;
+  }
+
+  Shader CreateShader( const std::string& vsh, const std::string& fsh )
+  {
+    std::vector<char> vshShaderSource;
+    std::vector<char> fshShaderSource;
+
+    // VSH
+    if(vsh[0] == '/')
+    {
+      std::string vshPath( DEMO_GAME_DIR );
+      vshPath += '/';
+      vshPath += vsh;
+      LoadFile( vshPath, vshShaderSource );
+    }
+    else
+    {
+      vshShaderSource.insert(vshShaderSource.end(), vsh.begin(), vsh.end());
+    }
+
+    // FSH
+    if(fsh[0] == '/')
+    {
+      std::string fshPath( DEMO_GAME_DIR );
+      fshPath += '/';
+      fshPath += fsh;
+      LoadFile( fshPath, fshShaderSource );
+    }
+    else
+    {
+      fshShaderSource.insert(fshShaderSource.end(), fsh.begin(), fsh.end());
+    }
+
+    vshShaderSource.emplace_back(0);
+    fshShaderSource.emplace_back(0);
+    return Shader::New( std::string(vshShaderSource.data()), std::string(fshShaderSource.data()) );
+  }
+
+  std::unique_ptr<Model> CreateModel( glTF& gltf,
+                                      const glTF_Mesh* mesh,
+                                      const std::string& vertexShaderSource,
+                                      const std::string& fragmentShaderSource )
+  {
+    /*
+     * Obtain interleaved buffer for first mesh with position and normal attributes
+     */
+    auto positionBuffer = gltf.GetMeshAttributeBuffer( *mesh,
+                                                       {
+                                                         glTFAttributeType::POSITION,
+                                                         glTFAttributeType::NORMAL,
+                                                         glTFAttributeType::TEXCOORD_0
+                                                       } );
+
+    auto attributeCount = gltf.GetMeshAttributeCount( mesh );
+    /**
+     * Create matching property buffer
+     */
+    auto vertexBuffer = PropertyBuffer::New( Property::Map()
+                                               .Add("aPosition", Property::VECTOR3 )
+                                               .Add("aNormal", Property::VECTOR3)
+                                               .Add("aTexCoord", Property::VECTOR2)
+    );
+
+    // set vertex data
+    vertexBuffer.SetData( positionBuffer.data(), attributeCount );
+
+    auto geometry = Geometry::New();
+    geometry.AddVertexBuffer( vertexBuffer );
+    auto indexBuffer = gltf.GetMeshIndexBuffer( mesh );
+    geometry.SetIndexBuffer( indexBuffer.data(), indexBuffer.size() );
+    geometry.SetType( Geometry::Type::TRIANGLES );
+    std::unique_ptr<Model> retval( new Model() );
+    retval->shader = CreateShader( vertexShaderSource, fragmentShaderSource );
+    retval->geometry = geometry;
+    return retval;
+  }
+
+  void ReplaceShader( Actor& actor, const std::string& vsh, const std::string& fsh )
+  {
+    auto renderer = actor.GetRendererAt(0);
+    auto shader = CreateShader(vsh, fsh);
+    renderer.SetShader( shader );
+  }
+
+  void OnPan( Actor actor, const PanGesture& panGesture )
+  {
+    auto displacement = panGesture.screenDisplacement;
+    mCenterActor.RotateBy( Degree( displacement.y *0.1f ), Vector3( 0.0, 0.0, 1.0) );
+    mCenterActor.RotateBy( Degree( displacement.x *0.1f ), Vector3( 0.0, 1.0, 0.0) );
+    Quaternion q;
+    mCenterActor.GetProperty( Actor::Property::ORIENTATION ).Get(q);
+    Matrix m = Matrix::IDENTITY;
+    m.SetTransformComponents( Vector3::ONE, q, Vector3::ZERO );
+    auto yAxis = m.GetYAxis() * -1.0f;
+
+    yAxis.Normalize();
+    mReflectionCamera3D.SetProperty( DevelCameraActor::Property::REFLECTION_PLANE, Vector4(yAxis.x, yAxis.y, yAxis.z, 0.0f));
+  }
+
+  void OnKeyEvent( const KeyEvent& event )
+  {
+    if( event.state == KeyEvent::Down )
+    {
+      if ( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
+      {
+        mApplication.Quit();
+      }
+    }
+  }
+
+  bool TickTimerSignal()
+  {
+    auto root = mLayer3D;
+    static float rotationAngle = 0.0f;
+
+    const auto ROTATION_ANGLE_STEP = 0.05f;
+    const auto FRAME_DELTA_TIME = 0.016f;
+    const auto PLASMA_K_FACTOR = 12.0f; // 'granularity' of plasma effect
+
+    rotationAngle += ROTATION_ANGLE_STEP;
+    mMockTime += FRAME_DELTA_TIME;
+    mKFactor = PLASMA_K_FACTOR;
+
+    auto sun = root.FindChildByName( "sun" );
+    sun.SetProperty( mSunTimeUniformIndex, mMockTime );
+    sun.SetProperty( mSunKFactorUniformIndex, mKFactor );
+    sun.SetOrientation( Quaternion( Radian(Degree(rotationAngle)), Vector3(0.0, 1.0, 0.0)));
+    return true;
+  }
+
+private:
+  Application&  mApplication;
+
+  Layer mLayer3D;
+
+  std::vector<Actor>                      mActors;
+  std::vector<CameraActor>                mCameras;
+  std::vector<std::unique_ptr<Model>>     mModels;
+  std::vector<TextureSet>                 mTextureSets;
+
+  Animation mAnimation;
+  float mMockTime = 0.0f;
+  float mKFactor = 0.0f;
+  Property::Index mSunTimeUniformIndex;
+  Property::Index mSunKFactorUniformIndex;
+  PanGestureDetector mPanGestureDetector;
+
+  Vector3 mCameraPos;
+  Vector3 mLightDir;
+  Timer mTickTimer;
+
+  CameraActor mCamera3D;
+  CameraActor mReflectionCamera3D;
+  Actor mCenterActor;
+  Actor mCenterHorizActor;
+};
+
+int DALI_EXPORT_API main( int argc, char **argv )
+{
+  Application application = Application::New( &argc, &argv );
+  ReflectionExample test( application );
+  application.MainLoop();
+  return 0;
+}
diff --git a/resources/game/reflection.bin b/resources/game/reflection.bin
new file mode 100644 (file)
index 0000000..510c7af
Binary files /dev/null and b/resources/game/reflection.bin differ
diff --git a/resources/game/reflection.gltf b/resources/game/reflection.gltf
new file mode 100644 (file)
index 0000000..713f818
--- /dev/null
@@ -0,0 +1,1426 @@
+{
+    "asset" : {
+        "generator" : "Khronos glTF Blender I/O v1.1.46",
+        "version" : "2.0"
+    },
+    "scene" : 0,
+    "scenes" : [
+        {
+            "name" : "Scene",
+            "nodes" : [
+                4,
+                26
+            ]
+        }
+    ],
+    "nodes" : [
+        {
+            "mesh" : 0,
+            "name" : "background",
+            "rotation" : [
+                0.2561876177787781,
+                0,
+                0,
+                0.9666270613670349
+            ],
+            "scale" : [
+                41.288753509521484,
+                41.288753509521484,
+                41.288753509521484
+            ],
+            "translation" : [
+                0.6707069277763367,
+                -66.81024932861328,
+                -2.4491214752197266
+            ]
+        },
+        {
+            "camera" : 0,
+            "name" : "CameraReflection_Orientation",
+            "rotation" : [
+                -0.7071067690849304,
+                0,
+                0,
+                0.7071067690849304
+            ]
+        },
+        {
+            "children" : [
+                1
+            ],
+            "name" : "CameraReflection"
+        },
+        {
+            "camera" : 1,
+            "name" : "Camera_Orientation",
+            "rotation" : [
+                -0.7071067690849304,
+                0,
+                0,
+                0.7071067690849304
+            ]
+        },
+        {
+            "children" : [
+                0,
+                2,
+                3
+            ],
+            "name" : "Camera",
+            "rotation" : [
+                0.3552197515964508,
+                0.6114072799682617,
+                -0.3552197515964508,
+                0.6114073395729065
+            ],
+            "translation" : [
+                25.934219360351562,
+                18.4041748046875,
+                0
+            ]
+        },
+        {
+            "mesh" : 1,
+            "name" : "Cylinder",
+            "scale" : [
+                6.3863205909729,
+                1.160239815711975,
+                6.3863205909729
+            ],
+            "translation" : [
+                0,
+                -2.19092059135437,
+                0
+            ]
+        },
+        {
+            "name" : "Empty",
+            "rotation" : [
+                0,
+                -0.3826834261417389,
+                0.9238795042037964,
+                -1.672762550697371e-08
+            ],
+            "scale" : [
+                8.46078109741211,
+                8.46078109741211,
+                8.46078109741211
+            ],
+            "translation" : [
+                -10.59361743927002,
+                -9.98163890838623,
+                -0.23785686492919922
+            ]
+        },
+        {
+            "name" : "Empty.001",
+            "rotation" : [
+                0,
+                1,
+                0,
+                -4.371138828673793e-08
+            ],
+            "scale" : [
+                8.46078109741211,
+                8.46078109741211,
+                8.46078109741211
+            ],
+            "translation" : [
+                -10.59361743927002,
+                -9.98163890838623,
+                -0.23785686492919922
+            ]
+        },
+        {
+            "name" : "Light",
+            "rotation" : [
+                0.16907575726509094,
+                0.7558803558349609,
+                -0.27217137813568115,
+                0.570947527885437
+            ],
+            "translation" : [
+                -1.473832130432129,
+                4.775537014007568,
+                -8.020967483520508
+            ]
+        },
+        {
+            "mesh" : 2,
+            "name" : "Plane",
+            "scale" : [
+                6.463212966918945,
+                1.1742093563079834,
+                6.463212966918945
+            ],
+            "translation" : [
+                0,
+                -2.19092059135437,
+                0
+            ]
+        },
+        {
+            "mesh" : 3,
+            "name" : "column_0.000",
+            "translation" : [
+                4.034352779388428,
+                0,
+                3.406787395477295
+            ]
+        },
+        {
+            "mesh" : 3,
+            "name" : "column_0.001",
+            "translation" : [
+                4.034352779388428,
+                0,
+                -3.546769142150879
+            ]
+        },
+        {
+            "mesh" : 3,
+            "name" : "column_0.002",
+            "translation" : [
+                -0.7669124603271484,
+                0,
+                -5.243768215179443
+            ]
+        },
+        {
+            "mesh" : 3,
+            "name" : "column_0.003",
+            "translation" : [
+                -0.7669124603271484,
+                0,
+                5.600468158721924
+            ]
+        },
+        {
+            "mesh" : 3,
+            "name" : "column_0.004",
+            "translation" : [
+                -5.40261697769165,
+                0,
+                1.7097878456115723
+            ]
+        },
+        {
+            "mesh" : 3,
+            "name" : "column_0.005",
+            "translation" : [
+                -5.40261697769165,
+                0,
+                -2.0567219257354736
+            ]
+        },
+        {
+            "mesh" : 4,
+            "name" : "dome",
+            "translation" : [
+                0,
+                10.846502304077148,
+                0
+            ]
+        },
+        {
+            "mesh" : 5,
+            "name" : "milkyway",
+            "scale" : [
+                5.861281394958496,
+                -0.5747091174125671,
+                5.861281394958496
+            ],
+            "translation" : [
+                0,
+                10.332517623901367,
+                0
+            ]
+        },
+        {
+            "mesh" : 6,
+            "name" : "earth",
+            "scale" : [
+                0.3868779242038727,
+                0.3868779242038727,
+                0.3868779242038727
+            ],
+            "translation" : [
+                0.046428680419921875,
+                4.758791923522949,
+                1.4374182224273682
+            ]
+        },
+        {
+            "mesh" : 7,
+            "name" : "saturn",
+            "scale" : [
+                0.8631472587585449,
+                0.8631472587585449,
+                0.8631472587585449
+            ],
+            "translation" : [
+                0.7704035043716431,
+                4.758791923522949,
+                3.2832906246185303
+            ]
+        },
+        {
+            "mesh" : 8,
+            "name" : "saturn.001",
+            "scale" : [
+                0.630012571811676,
+                0.630012571811676,
+                0.630012571811676
+            ],
+            "translation" : [
+                -2.5262279510498047,
+                3.7848119735717773,
+                2.0928404331207275
+            ]
+        },
+        {
+            "mesh" : 9,
+            "name" : "saturn.002",
+            "scale" : [
+                0.5450682640075684,
+                0.5450682640075684,
+                0.5450682640075684
+            ],
+            "translation" : [
+                0.22096490859985352,
+                5.8246612548828125,
+                -2.989466428756714
+            ]
+        },
+        {
+            "mesh" : 10,
+            "name" : "saturn.003",
+            "scale" : [
+                0.9573714733123779,
+                0.9573714733123779,
+                0.9573714733123779
+            ],
+            "translation" : [
+                2.654115915298462,
+                1.1106147766113281,
+                -0.8603012561798096
+            ]
+        },
+        {
+            "children" : [
+                18,
+                19,
+                20,
+                21,
+                22
+            ],
+            "name" : "solar_root"
+        },
+        {
+            "mesh" : 11,
+            "name" : "sun",
+            "scale" : [
+                1.3610559701919556,
+                1.3610559701919556,
+                1.3610559701919556
+            ],
+            "translation" : [
+                0,
+                2.0611612796783447,
+                0
+            ]
+        },
+        {
+            "children" : [
+                10,
+                11,
+                12,
+                13,
+                14,
+                15,
+                16,
+                17,
+                23,
+                24
+            ],
+            "name" : "RenderTaskSource"
+        },
+        {
+            "children" : [
+                5,
+                6,
+                7,
+                8,
+                9,
+                25
+            ],
+            "name" : "center"
+        }
+    ],
+    "cameras" : [
+        {
+            "name" : "Camera.002",
+            "perspective" : {
+                "yfov" : 0.6911112070083618,
+                "zfar" : 1000,
+                "znear" : 0.0010000000474974513
+            },
+            "type" : "perspective"
+        },
+        {
+            "name" : "Camera.001",
+            "perspective" : {
+                "yfov" : 0.6911112070083618,
+                "zfar" : 1000,
+                "znear" : 0.0010000000474974513
+            },
+            "type" : "perspective"
+        }
+    ],
+    "materials" : [
+        {
+            "doubleSided" : true,
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "background",
+            "pbrMetallicRoughness" : {
+                "baseColorTexture" : {
+                    "index" : 0,
+                    "texCoord" : 0
+                },
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.5
+            }
+        },
+        {
+            "doubleSided" : true,
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "sides",
+            "pbrMetallicRoughness" : {
+                "baseColorTexture" : {
+                    "index" : 1,
+                    "texCoord" : 0
+                },
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.5
+            }
+        },
+        {
+            "doubleSided" : true,
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "top",
+            "pbrMetallicRoughness" : {
+                "baseColorTexture" : {
+                    "index" : 2,
+                    "texCoord" : 0
+                },
+                "metallicFactor" : 0.8947368264198303,
+                "roughnessFactor" : 0.0964912474155426
+            }
+        },
+        {
+            "doubleSided" : true,
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "marble2",
+            "pbrMetallicRoughness" : {
+                "baseColorTexture" : {
+                    "index" : 3,
+                    "texCoord" : 0
+                },
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.5
+            }
+        },
+        {
+            "doubleSided" : true,
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "marble",
+            "pbrMetallicRoughness" : {
+                "baseColorTexture" : {
+                    "index" : 4,
+                    "texCoord" : 0
+                },
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.5
+            }
+        },
+        {
+            "doubleSided" : true,
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "milkyway",
+            "pbrMetallicRoughness" : {
+                "baseColorTexture" : {
+                    "index" : 5,
+                    "texCoord" : 0
+                },
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.5
+            }
+        },
+        {
+            "doubleSided" : true,
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "mat_earth",
+            "pbrMetallicRoughness" : {
+                "baseColorTexture" : {
+                    "index" : 6,
+                    "texCoord" : 0
+                },
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.5
+            }
+        },
+        {
+            "doubleSided" : true,
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "mat_saturn",
+            "pbrMetallicRoughness" : {
+                "baseColorTexture" : {
+                    "index" : 7,
+                    "texCoord" : 0
+                },
+                "metallicFactor" : 0.11403506994247437,
+                "roughnessFactor" : 0.34210526943206787
+            }
+        },
+        {
+            "doubleSided" : true,
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "mat_mars",
+            "pbrMetallicRoughness" : {
+                "baseColorTexture" : {
+                    "index" : 8,
+                    "texCoord" : 0
+                },
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.5
+            }
+        },
+        {
+            "doubleSided" : true,
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "mat_venus",
+            "pbrMetallicRoughness" : {
+                "baseColorTexture" : {
+                    "index" : 9,
+                    "texCoord" : 0
+                },
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.5
+            }
+        },
+        {
+            "doubleSided" : true,
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "mat_jupiter",
+            "pbrMetallicRoughness" : {
+                "baseColorTexture" : {
+                    "index" : 10,
+                    "texCoord" : 0
+                },
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.5
+            }
+        },
+        {
+            "doubleSided" : true,
+            "emissiveFactor" : [
+                1,
+                1,
+                1
+            ],
+            "name" : "mat_sun2",
+            "pbrMetallicRoughness" : {
+                "baseColorTexture" : {
+                    "index" : 11,
+                    "texCoord" : 0
+                },
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.7368420958518982
+            }
+        }
+    ],
+    "meshes" : [
+        {
+            "name" : "Plane",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 0
+                }
+            ]
+        },
+        {
+            "name" : "Cylinder",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 4,
+                        "NORMAL" : 5,
+                        "TEXCOORD_0" : 6
+                    },
+                    "indices" : 7,
+                    "material" : 1
+                }
+            ]
+        },
+        {
+            "name" : "Cylinder.001",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 8,
+                        "NORMAL" : 9,
+                        "TEXCOORD_0" : 10
+                    },
+                    "indices" : 11,
+                    "material" : 2
+                }
+            ]
+        },
+        {
+            "name" : "Cylinder.002",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 12,
+                        "NORMAL" : 13,
+                        "TEXCOORD_0" : 14
+                    },
+                    "indices" : 15,
+                    "material" : 3
+                }
+            ]
+        },
+        {
+            "name" : "Cylinder.003",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 16,
+                        "NORMAL" : 17,
+                        "TEXCOORD_0" : 18
+                    },
+                    "indices" : 19,
+                    "material" : 4
+                }
+            ]
+        },
+        {
+            "name" : "Cylinder.004",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 20,
+                        "NORMAL" : 21,
+                        "TEXCOORD_0" : 22
+                    },
+                    "indices" : 23,
+                    "material" : 5
+                }
+            ]
+        },
+        {
+            "name" : "Sphere",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 24,
+                        "NORMAL" : 25,
+                        "TEXCOORD_0" : 26
+                    },
+                    "indices" : 27,
+                    "material" : 6
+                }
+            ]
+        },
+        {
+            "name" : "Sphere.002",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 28,
+                        "NORMAL" : 29,
+                        "TEXCOORD_0" : 30
+                    },
+                    "indices" : 27,
+                    "material" : 7
+                }
+            ]
+        },
+        {
+            "name" : "Sphere.003",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 31,
+                        "NORMAL" : 32,
+                        "TEXCOORD_0" : 33
+                    },
+                    "indices" : 27,
+                    "material" : 8
+                }
+            ]
+        },
+        {
+            "name" : "Sphere.004",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 34,
+                        "NORMAL" : 35,
+                        "TEXCOORD_0" : 36
+                    },
+                    "indices" : 27,
+                    "material" : 9
+                }
+            ]
+        },
+        {
+            "name" : "Sphere.005",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 37,
+                        "NORMAL" : 38,
+                        "TEXCOORD_0" : 39
+                    },
+                    "indices" : 40,
+                    "material" : 10
+                }
+            ]
+        },
+        {
+            "name" : "Sphere.001",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 41,
+                        "NORMAL" : 42,
+                        "TEXCOORD_0" : 43
+                    },
+                    "indices" : 44,
+                    "material" : 11
+                }
+            ]
+        }
+    ],
+    "textures" : [
+        {
+            "source" : 0
+        },
+        {
+            "source" : 0
+        },
+        {
+            "source" : 0
+        },
+        {
+            "source" : 0
+        },
+        {
+            "source" : 0
+        },
+        {
+            "source" : 0
+        },
+        {
+            "source" : 0
+        },
+        {
+            "source" : 0
+        },
+        {
+            "source" : 0
+        },
+        {
+            "source" : 0
+        },
+        {
+            "source" : 0
+        },
+        {
+            "source" : 0
+        }
+    ],
+    "images" : [
+        {
+            "mimeType" : "image/png",
+            "name" : "texture-atlas",
+            "uri" : "texture-atlas.jpg"
+        }
+    ],
+    "accessors" : [
+        {
+            "bufferView" : 0,
+            "componentType" : 5126,
+            "count" : 4,
+            "max" : [
+                1,
+                0,
+                1
+            ],
+            "min" : [
+                -1,
+                0,
+                -1
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 1,
+            "componentType" : 5126,
+            "count" : 4,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 2,
+            "componentType" : 5126,
+            "count" : 4,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 3,
+            "componentType" : 5123,
+            "count" : 6,
+            "type" : "SCALAR"
+        },
+        {
+            "bufferView" : 4,
+            "componentType" : 5126,
+            "count" : 72,
+            "max" : [
+                1,
+                1,
+                1
+            ],
+            "min" : [
+                -1,
+                -1,
+                -1
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 5,
+            "componentType" : 5126,
+            "count" : 72,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 6,
+            "componentType" : 5126,
+            "count" : 72,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 7,
+            "componentType" : 5123,
+            "count" : 192,
+            "type" : "SCALAR"
+        },
+        {
+            "bufferView" : 8,
+            "componentType" : 5126,
+            "count" : 32,
+            "max" : [
+                1,
+                1,
+                1
+            ],
+            "min" : [
+                -1,
+                1,
+                -1
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 9,
+            "componentType" : 5126,
+            "count" : 32,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 10,
+            "componentType" : 5126,
+            "count" : 32,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 11,
+            "componentType" : 5123,
+            "count" : 90,
+            "type" : "SCALAR"
+        },
+        {
+            "bufferView" : 12,
+            "componentType" : 5126,
+            "count" : 800,
+            "max" : [
+                0.9250403642654419,
+                10.267958641052246,
+                0.9250403642654419
+            ],
+            "min" : [
+                -0.9250403642654419,
+                -0.9530067443847656,
+                -0.9250403642654419
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 13,
+            "componentType" : 5126,
+            "count" : 800,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 14,
+            "componentType" : 5126,
+            "count" : 800,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 15,
+            "componentType" : 5123,
+            "count" : 1428,
+            "type" : "SCALAR"
+        },
+        {
+            "bufferView" : 16,
+            "componentType" : 5126,
+            "count" : 716,
+            "max" : [
+                6.96999979019165,
+                1.0763102769851685,
+                6.67748498916626
+            ],
+            "min" : [
+                -6.970000267028809,
+                -1,
+                -6.677481651306152
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 17,
+            "componentType" : 5126,
+            "count" : 716,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 18,
+            "componentType" : 5126,
+            "count" : 716,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 19,
+            "componentType" : 5123,
+            "count" : 3444,
+            "type" : "SCALAR"
+        },
+        {
+            "bufferView" : 20,
+            "componentType" : 5126,
+            "count" : 89,
+            "max" : [
+                1,
+                1,
+                1
+            ],
+            "min" : [
+                -1,
+                1,
+                -1
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 21,
+            "componentType" : 5126,
+            "count" : 89,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 22,
+            "componentType" : 5126,
+            "count" : 89,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 23,
+            "componentType" : 5123,
+            "count" : 90,
+            "type" : "SCALAR"
+        },
+        {
+            "bufferView" : 24,
+            "componentType" : 5126,
+            "count" : 541,
+            "max" : [
+                1.000000238418579,
+                1,
+                1.0000003576278687
+            ],
+            "min" : [
+                -0.9999998211860657,
+                -1,
+                -1
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 25,
+            "componentType" : 5126,
+            "count" : 541,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 26,
+            "componentType" : 5126,
+            "count" : 541,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 27,
+            "componentType" : 5123,
+            "count" : 2880,
+            "type" : "SCALAR"
+        },
+        {
+            "bufferView" : 28,
+            "componentType" : 5126,
+            "count" : 541,
+            "max" : [
+                1.000000238418579,
+                1,
+                1.0000003576278687
+            ],
+            "min" : [
+                -0.9999998211860657,
+                -1,
+                -1
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 29,
+            "componentType" : 5126,
+            "count" : 541,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 30,
+            "componentType" : 5126,
+            "count" : 541,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 31,
+            "componentType" : 5126,
+            "count" : 541,
+            "max" : [
+                1.000000238418579,
+                1,
+                1.0000003576278687
+            ],
+            "min" : [
+                -0.9999998211860657,
+                -1,
+                -1
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 32,
+            "componentType" : 5126,
+            "count" : 541,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 33,
+            "componentType" : 5126,
+            "count" : 541,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 34,
+            "componentType" : 5126,
+            "count" : 541,
+            "max" : [
+                1.000000238418579,
+                1,
+                1.0000003576278687
+            ],
+            "min" : [
+                -0.9999998211860657,
+                -1,
+                -1
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 35,
+            "componentType" : 5126,
+            "count" : 541,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 36,
+            "componentType" : 5126,
+            "count" : 541,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 37,
+            "componentType" : 5126,
+            "count" : 539,
+            "max" : [
+                1.000000238418579,
+                1,
+                1.0000003576278687
+            ],
+            "min" : [
+                -0.9999998211860657,
+                -1,
+                -1
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 38,
+            "componentType" : 5126,
+            "count" : 539,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 39,
+            "componentType" : 5126,
+            "count" : 539,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 40,
+            "componentType" : 5123,
+            "count" : 2880,
+            "type" : "SCALAR"
+        },
+        {
+            "bufferView" : 41,
+            "componentType" : 5126,
+            "count" : 559,
+            "max" : [
+                1.0000001192092896,
+                1,
+                1.0000001192092896
+            ],
+            "min" : [
+                -0.9999999403953552,
+                -1,
+                -1.000000238418579
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 42,
+            "componentType" : 5126,
+            "count" : 559,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 43,
+            "componentType" : 5126,
+            "count" : 559,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 44,
+            "componentType" : 5123,
+            "count" : 2880,
+            "type" : "SCALAR"
+        }
+    ],
+    "bufferViews" : [
+        {
+            "buffer" : 0,
+            "byteLength" : 48,
+            "byteOffset" : 0
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 48,
+            "byteOffset" : 48
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 32,
+            "byteOffset" : 96
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 12,
+            "byteOffset" : 128
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 864,
+            "byteOffset" : 140
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 864,
+            "byteOffset" : 1004
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 576,
+            "byteOffset" : 1868
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 384,
+            "byteOffset" : 2444
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 384,
+            "byteOffset" : 2828
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 384,
+            "byteOffset" : 3212
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 256,
+            "byteOffset" : 3596
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 180,
+            "byteOffset" : 3852
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 9600,
+            "byteOffset" : 4032
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 9600,
+            "byteOffset" : 13632
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 6400,
+            "byteOffset" : 23232
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 2856,
+            "byteOffset" : 29632
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 8592,
+            "byteOffset" : 32488
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 8592,
+            "byteOffset" : 41080
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 5728,
+            "byteOffset" : 49672
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 6888,
+            "byteOffset" : 55400
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 1068,
+            "byteOffset" : 62288
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 1068,
+            "byteOffset" : 63356
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 712,
+            "byteOffset" : 64424
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 180,
+            "byteOffset" : 65136
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 6492,
+            "byteOffset" : 65316
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 6492,
+            "byteOffset" : 71808
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 4328,
+            "byteOffset" : 78300
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 5760,
+            "byteOffset" : 82628
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 6492,
+            "byteOffset" : 88388
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 6492,
+            "byteOffset" : 94880
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 4328,
+            "byteOffset" : 101372
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 6492,
+            "byteOffset" : 105700
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 6492,
+            "byteOffset" : 112192
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 4328,
+            "byteOffset" : 118684
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 6492,
+            "byteOffset" : 123012
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 6492,
+            "byteOffset" : 129504
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 4328,
+            "byteOffset" : 135996
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 6468,
+            "byteOffset" : 140324
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 6468,
+            "byteOffset" : 146792
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 4312,
+            "byteOffset" : 153260
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 5760,
+            "byteOffset" : 157572
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 6708,
+            "byteOffset" : 163332
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 6708,
+            "byteOffset" : 170040
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 4472,
+            "byteOffset" : 176748
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 5760,
+            "byteOffset" : 181220
+        }
+    ],
+    "buffers" : [
+        {
+            "byteLength" : 186980,
+            "uri" : "reflection.bin"
+        }
+    ]
+}
diff --git a/resources/game/texture-atlas-license.txt b/resources/game/texture-atlas-license.txt
new file mode 100644 (file)
index 0000000..9603efa
--- /dev/null
@@ -0,0 +1,6 @@
+Texture Atlas contains images under Attribution 4.0 International license (CC By 4.0)
+
+https://creativecommons.org/licenses/by/4.0/
+
+https://creativecommons.org/licenses/by/4.0/legalcode
+
diff --git a/resources/game/texture-atlas.jpg b/resources/game/texture-atlas.jpg
new file mode 100644 (file)
index 0000000..21b46a7
Binary files /dev/null and b/resources/game/texture-atlas.jpg differ
index d5c26e5..d36222d 100755 (executable)
@@ -175,6 +175,9 @@ msgstr "Radial Menu"
 msgid "DALI_DEMO_STR_TITLE_REFRACTION"
 msgstr "Refraction"
 
+msgid "DALI_DEMO_STR_TITLE_REFLECTION"
+msgstr "Reflection"
+
 msgid "DALI_DEMO_STR_TITLE_RENDERER_STENCIL"
 msgstr "Renderer Stencil"
 
index c8f0c68..6058031 100755 (executable)
@@ -175,6 +175,9 @@ msgstr "Primitive Shapes"
 msgid "DALI_DEMO_STR_TITLE_RADIAL_MENU"
 msgstr "Radial Menu"
 
+msgid "DALI_DEMO_STR_TITLE_REFLECTION"
+msgstr "Reflection"
+
 msgid "DALI_DEMO_STR_TITLE_REFRACTION"
 msgstr "Refraction"
 
index fbb8155..8d62660 100644 (file)
@@ -96,6 +96,7 @@ extern "C"
 #define DALI_DEMO_STR_TITLE_PRIMITIVE_SHAPES            dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PRIMITIVE_SHAPES")
 #define DALI_DEMO_STR_TITLE_PROGRESS_BAR                dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PROGRESS_BAR")
 #define DALI_DEMO_STR_TITLE_PROPERTY_NOTIFICATION       dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PROPERTY_NOTIFICATION")
+#define DALI_DEMO_STR_TITLE_REFLECTION                  dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_REFLECTION")
 #define DALI_DEMO_STR_TITLE_REFRACTION                  dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_REFRACTION")
 #define DALI_DEMO_STR_TITLE_REMOTE_IMAGE                dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_REMOTE_IMAGE")
 #define DALI_DEMO_STR_TITLE_RENDERING_DRAW_LINE         dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_RENDERING_DRAW_LINE")
@@ -195,6 +196,7 @@ extern "C"
 #define DALI_DEMO_STR_TITLE_PRIMITIVE_SHAPES            "Primitive Shapes"
 #define DALI_DEMO_STR_TITLE_PROGRESS_BAR                "Progress Bar"
 #define DALI_DEMO_STR_TITLE_PROPERTY_NOTIFICATION       "Property Notification"
+#define DALI_DEMO_STR_TITLE_REFLECTION                  "Reflection"
 #define DALI_DEMO_STR_TITLE_REFRACTION                  "Refract Effect"
 #define DALI_DEMO_STR_TITLE_REMOTE_IMAGE                "Remote Image"
 #define DALI_DEMO_STR_TITLE_RENDERING_DRAW_LINE         "Draw Line"