46e39cf5de9a98f505ea839e9f596c65c7970ec2
[platform/core/uifw/dali-demo.git] / examples / reflection-demo / gltf-scene.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 #include <dali/devel-api/adaptor-framework/file-stream.h>
18
19 #include "gltf-scene.h"
20
21 #include "pico-json.h"
22
23 namespace
24 {
25 // string contains enum type index encoded matching glTFAttributeType
26 const std::vector<std::string> GLTF_STR_ATTRIBUTE_TYPE = {
27   "POSITION",
28   "NORMAL",
29   "TEXCOORD_0"
30 };
31
32 const std::vector<std::pair<std::string, uint32_t>> GLTF_STR_COMPONENT_TYPE = {
33   std::make_pair( "VEC2", 2 ),
34   std::make_pair( "VEC3", 3 ),
35   std::make_pair( "VEC4", 4 ),
36   std::make_pair( "SCALAR", 1 )
37 };
38
39 glTFAttributeType glTFAttributeTypeStrToEnum( const std::string& name )
40 {
41   int index = -1;
42   auto iter = std::find_if( GLTF_STR_ATTRIBUTE_TYPE.begin(), GLTF_STR_ATTRIBUTE_TYPE.end(),
43   [name, &index]( const std::string& val )
44   {
45     index++;
46     return val == name;
47   });
48   if( iter == GLTF_STR_ATTRIBUTE_TYPE.end() )
49   {
50     return glTFAttributeType::UNDEFINED;
51   }
52
53   return static_cast<glTFAttributeType>(index);
54 }
55
56 uint32_t glTFComponentTypeStrToNum( const std::string& name )
57 {
58   auto iter = std::find_if( GLTF_STR_COMPONENT_TYPE.begin(), GLTF_STR_COMPONENT_TYPE.end(),
59                             [name]( const std::pair<std::string, uint32_t>& val )
60                             {
61                               return val.first == name;
62                             });
63   if( iter == GLTF_STR_COMPONENT_TYPE.end() )
64   {
65     return 0;
66   }
67
68   return iter->second;
69 }
70
71 template<class T>
72 struct JsonResult
73 {
74   bool success;
75   T    result;
76   operator T() const
77   {
78     return static_cast<T>(result);
79   }
80 };
81
82 template<>
83 struct JsonResult<bool>
84 {
85   bool success;
86   bool result;
87
88   operator bool() const
89   {
90     return result;
91   }
92 };
93
94 template<class Expected, class Converted = Expected>
95 JsonResult<picojson::value> JsonFindValue( const picojson::object& object, const std::string& name )
96 {
97   auto iter = find_if(object.begin(), object.end(), [name](decltype(*object.begin())& item)
98   {
99     return item.first == name;
100   });
101
102   if(iter == object.end())
103   {
104     return {false};
105   }
106
107   return {true, iter->second};
108 };
109
110
111 template<class Expected, class Converted = Expected>
112 JsonResult<Converted> JsonGetValue( const picojson::value& val, const std::string& name, const Converted& onFail )
113 {
114   if( val.contains(name) )
115   {
116     return {true, static_cast<Converted>(val.get(name).get<Expected>())};
117   }
118   return {false, onFail};
119 }
120
121 template<class Expected, class Converted = Expected>
122 bool JsonGetValueOut( const picojson::value& val, const std::string& name, Converted& writeOut )
123 {
124   if( val.contains(name) )
125   {
126     writeOut = static_cast<Converted>(val.get(name).get<Expected>());
127     return true;
128   }
129   return false;
130 }
131
132
133 /**
134  * Safe json value accessor
135  */
136 template<class Expected, class Converted = Expected>
137 JsonResult<std::vector<Converted>> JsonGetArray( const picojson::value& val, const std::string& name, std::vector<Converted> valueOnFail = {} )
138 {
139   if(val.contains(name) )
140   {
141     const auto& array = val.get(name).get<picojson::array>();
142     std::vector<Converted> result{};
143     for( const auto& item : array )
144     {
145       result.emplace_back( static_cast<Converted>(item.get<Expected>()) );
146     }
147     return {true, result};
148   }
149   return {false, valueOnFail};
150 }
151
152 }
153
154 glTF::glTF(const std::string& filename)
155 {
156   LoadFromFile(filename);
157   ParseJSON();
158 }
159
160 void glTF::LoadFromFile( const std::string& filename )
161 {
162   std::string jsonFile( filename );
163   jsonFile += ".gltf";
164   std::string binFile( filename );
165   binFile += ".bin";
166
167   // load binary
168
169   GLTF_LOG("LoadFromFile: %s", binFile.c_str());
170   mBuffer = LoadFile(binFile);
171   jsonBuffer = LoadFile(jsonFile);
172
173   // Log errors
174   if(mBuffer.empty())
175   {
176     GLTF_LOG("Error, buffer empty!");
177   }
178   else
179   {
180     GLTF_LOG( "GLTF[BIN]: %s loaded, size = %d", binFile.c_str(), int(mBuffer.size()));
181   }
182   if(jsonBuffer.empty())
183   {
184     GLTF_LOG("Error, buffer GLTF empty!");
185   }
186   else
187   {
188     GLTF_LOG( "GLTF: %s loaded, size = %d", binFile.c_str(), int(jsonBuffer.size()));
189   }
190
191   // Abort if errors
192   if(jsonBuffer.empty() || mBuffer.empty())
193   {
194     return;
195   }
196
197   // parse json
198   auto err = picojson::parse(jsonNode, std::string(reinterpret_cast<char*>(jsonBuffer.data())));
199   if(!err.empty())
200   {
201     GLTF_LOG( "GLTF: Error parsing %s, error: %s", jsonFile.c_str(), err.c_str());
202     return;
203   }
204   else
205   {
206     GLTF_LOG( "GLTF: %s loaded, size = %d", jsonFile.c_str(), int(jsonBuffer.size()));
207   }
208 }
209
210 bool glTF::ParseJSON()
211 {
212   if (!jsonNode.is<picojson::object>())
213   {
214     return false;
215   }
216
217   // Add dummy first node to nodes (scene node)
218   mNodes.emplace_back();
219
220   // Sources for textures to be resolved later
221   std::vector<uint32_t> textureSources{};
222   std::vector<glTF_Texture> images{};
223
224   for( auto& val : jsonNode.get<picojson::object>() )
225   {
226     GLTF_LOG( "node: %s", val.first.c_str());
227
228     // Parse bufferviews
229     if( val.first == "bufferViews" )
230     {
231       auto bufferViews = val.second;
232       for( auto& view : bufferViews.get<picojson::array>() )
233       {
234         auto bufferIndex = uint32_t(view.get("buffer").get<double>());
235         auto byteLength = uint32_t(view.get("byteLength").get<double>());
236         auto byteOffset = uint32_t(view.get("byteOffset").get<double>());
237
238         glTF_BufferView bufferView{};
239         bufferView.bufferIndex = bufferIndex;
240         bufferView.byteLength = byteLength;
241         bufferView.byteOffset = byteOffset;
242
243         mBufferViews.emplace_back( bufferView );
244       }
245     }
246
247     // parse accessors
248     else if( val.first == "accessors" )
249     {
250       for( const auto& accessor : val.second.get<picojson::array>() )
251       {
252         auto gltfAccessor = glTF_Accessor{};
253         gltfAccessor.bufferView = uint32_t(accessor.get( "bufferView" ).get<double>());
254         gltfAccessor.componentType = uint32_t(accessor.get( "componentType" ).get<double>());
255         gltfAccessor.count = uint32_t(accessor.get( "count" ).get<double>());
256         gltfAccessor.type = accessor.get( "type" ).get<std::string>();
257         gltfAccessor.componentSize = glTFComponentTypeStrToNum( gltfAccessor.type );
258         mAccessors.emplace_back( gltfAccessor );
259       }
260     }
261
262     // parse meshes
263     else if( val.first == "meshes" )
264     {
265       for( const auto& mesh : val.second.get<picojson::array>() )
266       {
267         glTF_Mesh gltfMesh{};
268         gltfMesh.name = mesh.get( "name" ).get<std::string>();
269
270         // get primitives (in this implementation assuming single mesh consists of
271         // one and only one primitive)
272         const auto& primitive = mesh.get("primitives").get<picojson::array>()[0];
273         const auto& attrs = primitive.get("attributes").get<picojson::object>();
274         for( const auto& attr : attrs )
275         {
276           auto type = glTFAttributeTypeStrToEnum( attr.first );
277           auto bvIndex = uint32_t(attr.second.get<double>());
278           gltfMesh.attributes.emplace_back( std::make_pair( type, bvIndex ) );
279           GLTF_LOG("GLTF: ATTR: type: %d, index: %d", int(type), int(bvIndex));
280         }
281
282         gltfMesh.indices = uint32_t(primitive.get("indices").get<double>());
283         gltfMesh.material = uint32_t(primitive.get("material").get<double>());
284         mMeshes.emplace_back( gltfMesh );
285       }
286     }
287     // parse cameras
288     else if( val.first == "cameras" )
289     {
290       glTF_Camera tgifCamera{};
291       for( const auto& camera : val.second.get<picojson::array>() )
292       {
293         tgifCamera.name = camera.get( "name" ).to_str();
294         tgifCamera.isPerspective = (camera.get( "type" ).to_str() == "perspective" );
295         if(tgifCamera.isPerspective)
296         {
297           const auto& perspective = camera.get( "perspective" );
298           tgifCamera.yfov = static_cast<float>(perspective.get( "yfov" ).get<double>());
299           tgifCamera.zfar = static_cast<float>(perspective.get( "zfar" ).get<double>());
300           tgifCamera.znear = static_cast<float>(perspective.get( "znear" ).get<double>());
301         }
302
303         mCameras.emplace_back( tgifCamera );
304       }
305     }
306     // parse nodes
307     else if( val.first == "nodes" )
308     {
309       auto nodeIndex = 1u;
310       for( const auto& node : val.second.get<picojson::array>() )
311       {
312         glTF_Node gltfNode{};
313         gltfNode.name = node.get( "name" ).to_str();
314         auto rotation = JsonGetArray<double, float>( node, "rotation", {0.0f, 0.0f, 0.0f, 1.0f} );
315         auto translation = JsonGetArray<double, float>( node, "translation", {0.0f, 0.0f, 0.0f} );
316         auto scale = JsonGetArray<double, float>( node, "scale", {1.0f, 1.0f, 1.0f} );
317         std::copy( rotation.result.begin(), rotation.result.end(), gltfNode.rotationQuaternion );
318         std::copy( translation.result.begin(), translation.result.end(), gltfNode.translation );
319         std::copy( scale.result.begin(), scale.result.end(), gltfNode.scale );
320
321         auto children = JsonGetArray<double, uint32_t>( node, "children" );
322         if(children.success)
323         {
324           gltfNode.children = std::move(children.result);
325         }
326         gltfNode.index = nodeIndex;
327         gltfNode.cameraId = 0xffffffff;
328         gltfNode.meshId = 0xffffffff;
329
330         auto cameraId = JsonGetValue<double, uint32_t>( node, "camera", 0xffffffff );
331         if( cameraId.success )
332         {
333           gltfNode.cameraId = cameraId.result;
334         }
335         auto meshId = JsonGetValue<double, uint32_t>( node, "mesh",  0xffffffff );
336         if( meshId.success )
337         {
338           gltfNode.meshId = meshId.result;
339         }
340         mNodes.emplace_back( gltfNode );
341         ++nodeIndex;
342       }
343
344     }
345     // parse scenes, note: only first scene is being parsed
346     else if( val.first == "scenes" )
347     {
348       auto& sceneNode = mNodes[0];
349       const auto& scene = val.second.get<picojson::array>()[0];
350       sceneNode.name = JsonGetValue<std::string>( scene, "name", std::string() );
351       auto result = JsonGetArray<double, uint32_t>( scene, "nodes" );
352       sceneNode.children = result.result;
353       sceneNode.index = 0;
354     }
355     else if( val.first == "materials" )
356     {
357       for( const auto& node : val.second.get<picojson::array>() )
358       {
359         // Get pbr material, base color texture
360         glTF_Material material{};
361         material.doubleSided = JsonGetValue<bool>( node, "doubleSided", false ).result;
362         material.name = JsonGetValue<std::string>( node, "name", std::string() ).result;
363         if( node.contains("pbrMetallicRoughness") )
364         {
365           const auto& node0 = node.get("pbrMetallicRoughness");
366           if(node0.contains("baseColorTexture"))
367           {
368             const auto& node1 = node0.get("baseColorTexture");
369             auto index = uint32_t(node1.get("index").get<double>());
370             auto texCoord = uint32_t(node1.get("texCoord").get<double>());
371             material.pbrMetallicRoughness.enabled = true;
372             material.pbrMetallicRoughness.baseTextureColor.index = index;
373             material.pbrMetallicRoughness.baseTextureColor.texCoord = texCoord;
374           }
375         }
376         mMaterials.emplace_back( material );
377       }
378     }
379     else if( val.first == "textures" )
380     {
381       for(const auto& item : val.second.get<picojson::array>() )
382       {
383         auto source = JsonGetValue<double, uint32_t>( item, "source", 0xffffffff );
384         textureSources.emplace_back( source.result );
385       }
386     }
387     else if( val.first == "images" )
388     {
389       for(const auto& item : val.second.get<picojson::array>() )
390       {
391         glTF_Texture tex{};
392         JsonGetValueOut<std::string>( item, "name", tex.name );
393         JsonGetValueOut<std::string>( item, "uri", tex.uri );
394         images.emplace_back( tex );
395       }
396     }
397   }
398
399   // Resolve cross-referencing
400   for( const auto& source : textureSources )
401   {
402     mTextures.emplace_back( images[source] );
403   }
404
405   return true;
406 }
407
408 glTF_Buffer glTF::LoadFile( const std::string& filename )
409 {
410   Dali::FileStream fileStream( filename.c_str(), Dali::FileStream::READ | Dali::FileStream::BINARY );
411   FILE* fin = fileStream.GetFile();
412   std::vector<unsigned char> buffer;
413   if( fin )
414   {
415     if( fseek( fin, 0, SEEK_END ) )
416     {
417       return {};
418     }
419     auto size = ftell(fin);
420     if( fseek( fin, 0, SEEK_SET ) )
421     {
422       return {};
423     }
424     buffer.resize(unsigned(size));
425     auto result = fread( buffer.data(), 1, size_t(size), fin );
426     if( result != size_t(size) )
427     {
428       GLTF_LOG("LoadFile: Result: %d", int(result));
429       // return empty buffer
430       return {};
431     }
432   }
433   else
434   {
435     GLTF_LOG("LoadFile: Can't open file: errno = %d", errno);
436   }
437
438   return buffer;
439 }
440
441 std::vector<const glTF_Mesh*> glTF::GetMeshes() const
442 {
443   std::vector<const glTF_Mesh*> retval;
444   for( auto& mesh : mMeshes )
445   {
446     retval.emplace_back( &mesh );
447   }
448   return retval;
449 }
450
451 std::vector<const glTF_Camera*> glTF::GetCameras()
452 {
453   std::vector<const glTF_Camera*> cameras;
454   for( const auto& cam : mCameras )
455   {
456     cameras.emplace_back( &cam );
457   }
458   return cameras;
459 }
460
461 std::vector<unsigned char> glTF::GetMeshAttributeBuffer( const glTF_Mesh& mesh,  const std::vector<glTFAttributeType>& attrTypes )
462 {
463   // find buffer views
464   struct Data
465   {
466     uint32_t accessorIndex{0u};
467     uint32_t byteStride{0u};
468     char*    srcPtr{ nullptr };
469   };
470   std::vector<Data> data{};
471   for( const auto& attrType : attrTypes )
472   {
473     std::find_if( mesh.attributes.begin(), mesh.attributes.end(), [&data, &attrType]( const std::pair<glTFAttributeType, uint32_t>& item ){
474       if( item.first == attrType )
475       {
476         data.emplace_back();
477         data.back().accessorIndex = item.second;
478       }
479       return false;
480     });
481   }
482
483   if(data.empty())
484   {
485     return {};
486   }
487
488   // number of attributes is same for the whole mesh so using very first
489   // accessor
490
491   glTF_Buffer retval{};
492
493   // data is interleaved
494   if( data.size() > 1 )
495   {
496     auto attributeCount = mAccessors[data[0].accessorIndex].count;// / mAccessors[data[0].accessorIndex].componentSize;
497     uint32_t attributeStride = 0;
498     // now find buffer view stride for particular accessor
499     for( auto& item : data )
500     {
501       auto& accessor = mAccessors[item.accessorIndex];
502
503       // Update byte stride for this buffer view
504       auto& bufferView = mBufferViews[accessor.bufferView];
505       item.byteStride = bufferView.byteLength / attributeCount;
506       attributeStride += item.byteStride;
507       item.srcPtr = reinterpret_cast<char*>(mBuffer.data()) + bufferView.byteOffset;
508     }
509
510     // now allocate final buffer and interleave data
511     retval.resize( attributeStride * attributeCount );
512     auto* dstPtr = retval.data();
513     for( auto i = 0u; i < attributeCount; ++i )
514     {
515       for(auto& item : data )
516       {
517         std::copy( item.srcPtr,
518                    item.srcPtr + item.byteStride,
519                    reinterpret_cast<char*>(dstPtr) );
520         dstPtr += item.byteStride;
521         item.srcPtr += item.byteStride;
522       }
523     }
524   }
525   else // copy data directly as single buffer
526   {
527     auto& bufferView = mBufferViews[mAccessors[data[0].accessorIndex].bufferView];
528     retval.resize( bufferView.byteLength );
529     std::copy( mBuffer.begin() + bufferView.byteOffset,
530                mBuffer.begin() + bufferView.byteOffset + bufferView.byteLength,
531                retval.begin());
532
533   }
534   return retval;
535 }
536
537
538 const glTF_Mesh* glTF::FindMeshByName( const std::string& name ) const
539 {
540   for( const auto& mesh : mMeshes )
541   {
542     if(mesh.name == name)
543       return &mesh;
544   }
545   return nullptr;
546 }
547
548 uint32_t glTF::GetMeshAttributeCount( const glTF_Mesh* mesh ) const
549 {
550   const auto& accessor = mAccessors[mesh->attributes[0].second];
551   return accessor.count;// / accessor.componentSize;
552 }
553
554 std::vector<uint16_t> glTF::GetMeshIndexBuffer( const glTF_Mesh* mesh ) const
555 {
556   // check GL component type
557   const auto& accessor = mAccessors[mesh->indices];
558   if( accessor.componentType == 0x1403 ) // GL_UNSIGNED_SHORT
559   {
560     std::vector<uint16_t> retval{};
561     retval.resize( accessor.count );
562     const auto& bufferView = mBufferViews[accessor.bufferView];
563     const auto* srcPtr = reinterpret_cast<const uint16_t*>
564                 (reinterpret_cast<const char*>(mBuffer.data()) + bufferView.byteOffset);
565     std::copy( srcPtr, srcPtr + accessor.count, retval.data() );
566     return retval;
567   }
568   return {};
569 }
570
571 const glTF_Node* glTF::FindNodeByName( const std::string& name ) const
572 {
573   auto iter = std::find_if( mNodes.begin(), mNodes.end(), [name]( const glTF_Node& node )
574   {
575     return !name.compare( node.name );
576   });
577
578   if(iter == mNodes.end())
579   {
580     return nullptr;
581   }
582
583   return &*iter;
584 }
585
586 const std::vector<glTF_Material>& glTF::GetMaterials() const
587 {
588   return mMaterials;
589 }
590
591 const std::vector<glTF_Texture>& glTF::GetTextures() const
592 {
593   return mTextures;
594 }