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