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