+using ModelPtr = std::unique_ptr<Model>;
+
+using ActorContainer = std::vector<Actor>;
+using CameraContainer = std::vector<CameraActor>;
+using ModelContainer = std::vector<ModelPtr>;
+using TextureSetContainer = std::vector<TextureSet>;
+
+const Vector3 DEFAULT_LIGHT_DIRECTION( 0.5, 0.5, -1 );
+
+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()) );
+}
+
+ModelPtr 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 = VertexBuffer::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 );
+ ModelPtr 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 CreateTextureSetsFromGLTF( glTF* gltf, const std::string& basePath, TextureSetContainer& textureSets )
+{
+ 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 );
+ }
+ textureSets.emplace_back( textureSet );
+ }
+}
+
+
+/**
+ * Creates models from glTF
+ */
+void CreateModelsFromGLTF( glTF* gltf, ModelContainer& models )
+{
+ 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)
+ {
+ models.emplace_back( CreateModel( *gltf, mesh, VERTEX_SHADER, TEXTURED_FRAGMENT_SHADER ) );
+ }
+ else
+ {
+ models.emplace_back( CreateModel( *gltf, mesh, VERTEX_SHADER, FRAGMENT_SHADER ) );
+ }
+ }
+}
+
+Actor CreateSceneFromGLTF(
+ Window window,
+ glTF* gltf,
+ ModelContainer& models,
+ ActorContainer& actors,
+ CameraContainer& cameras,
+ TextureSetContainer& textureSets )
+{
+ const auto& nodes = gltf->GetNodes();
+
+ Vector3 cameraPosition;
+
+ // for each node create nodes and children
+ // resolve parents later
+ actors.reserve( nodes.size() );
+ for( const auto& node : nodes )
+ {
+ auto actor = node.cameraId != 0xffffffff ? CameraActor::New( window.GetSize() ) : Actor::New();
+
+ actor.SetProperty( Actor::Property::SIZE, Vector3( 1, 1, 1 ) );
+ actor.SetProperty( Dali::Actor::Property::NAME, node.name );
+ actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
+ actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
+ actor.SetProperty( Actor::Property::POSITION, Vector3( node.translation[0], node.translation[1], node.translation[2] ));
+ actor.SetProperty( Actor::Property::SCALE, Vector3( node.scale[0], node.scale[1], node.scale[2] ) );
+ actor.SetProperty( Actor::Property::ORIENTATION, 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 = models[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( textureSets[materialId] );
+ }
+ }
+
+ actor.AddRenderer( renderer );
+ }
+
+ // Reset and attach main camera
+ if( node.cameraId != 0xffffffff )
+ {
+ cameraPosition = 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.SetProperty( Actor::Property::ORIENTATION, 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.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
+ cameraActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
+
+ cameras.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;
+ }
+
+ for( auto& actor : actors )
+ {
+ actor.RegisterProperty( "lightDir", DEFAULT_LIGHT_DIRECTION );
+ actor.RegisterProperty( "eyePos", cameraPosition );
+ }
+
+ return actors[0];
+}
+
+} // unnamed namespace