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