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