[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / internal / loader / glb-loader-impl.cpp
1 /*
2  * Copyright (c) 2023 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 // FILE HEADER
19 #include <dali-scene3d/internal/loader/glb-loader-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/adaptor-framework/file-stream.h>
23 #include <dali/integration-api/debug.h>
24
25 // INTERNAL INCLUDES
26 #include <dali-scene3d/internal/loader/gltf2-util.h>
27 #include <dali-scene3d/public-api/loader/load-result.h>
28 #include <dali-scene3d/public-api/loader/utils.h>
29
30 namespace gt = gltf2;
31 namespace js = json;
32
33 namespace Dali::Scene3D::Loader::Internal
34 {
35 namespace
36 {
37 static constexpr uint32_t GLB_MAGIC       = 0x46546C67;
38 static constexpr uint32_t JSON_CHUNK_TYPE = 0x4E4F534A;
39 static constexpr uint32_t DATA_CHUNK_TYPE = 0x004E4942;
40
41 struct GlbHeader
42 {
43   uint32_t magic;
44   uint32_t version;
45   uint32_t length;
46 };
47
48 struct ChunkHeader
49 {
50   uint32_t chunkLength;
51   uint32_t chunkType;
52 };
53
54 } // namespace
55
56 bool GlbLoaderImpl::LoadModel(const std::string& url, Dali::Scene3D::Loader::LoadResult& result)
57 {
58   Dali::FileStream fileStream(url, FileStream::READ | FileStream::BINARY);
59   auto&            stream = fileStream.GetStream();
60   if(!stream.rdbuf()->in_avail())
61   {
62     DALI_LOG_ERROR("Load Model file is failed, url : %s\n", url.c_str());
63     return false;
64   }
65
66   GlbHeader glbHeader;
67   stream.clear();
68   stream.seekg(0u, stream.beg);
69   stream.read(reinterpret_cast<char*>(&glbHeader), sizeof(GlbHeader));
70
71   if(glbHeader.magic != GLB_MAGIC)
72   {
73     DALI_LOG_ERROR("Wrong file format, url : %s\n", url.c_str());
74     return false;
75   }
76
77   ChunkHeader jsonChunkHeader;
78   stream.read(reinterpret_cast<char*>(&jsonChunkHeader), sizeof(ChunkHeader));
79
80   if(jsonChunkHeader.chunkType != JSON_CHUNK_TYPE)
81   {
82     DALI_LOG_ERROR("Glb files first chunk is not a json chunk.\n");
83     return false;
84   }
85
86   std::vector<uint8_t> jsonChunkData;
87   jsonChunkData.resize(jsonChunkHeader.chunkLength);
88   stream.read(reinterpret_cast<char*>(&jsonChunkData[0]), static_cast<std::streamsize>(static_cast<size_t>(jsonChunkHeader.chunkLength)));
89   std::string gltfText(jsonChunkData.begin(), jsonChunkData.end());
90
91   uint32_t             binaryChunkOffset = sizeof(GlbHeader) + sizeof(ChunkHeader) + jsonChunkHeader.chunkLength;
92   std::vector<uint8_t> binaryChunkData;
93   if(glbHeader.length > binaryChunkOffset)
94   {
95     ChunkHeader binaryChunkHeader;
96     stream.read(reinterpret_cast<char*>(&binaryChunkHeader), sizeof(ChunkHeader));
97
98     if(binaryChunkHeader.chunkType != DATA_CHUNK_TYPE)
99     {
100       DALI_LOG_ERROR("Glb files has wrong binary chunk data.\n");
101       return false;
102     }
103
104     binaryChunkData.resize(binaryChunkHeader.chunkLength);
105     stream.read(reinterpret_cast<char*>(&binaryChunkData[0]), static_cast<std::streamsize>(static_cast<size_t>(binaryChunkHeader.chunkLength)));
106   }
107
108   json::unique_ptr root(json_parse(gltfText.c_str(), gltfText.size()));
109   if(!root)
110   {
111     DALI_LOG_ERROR("Failed to parse %s\n", url.c_str());
112     return false;
113   }
114
115   gt::Document document;
116
117   bool isMRendererModel(false);
118   if(!Gltf2Util::GenerateDocument(root, document, isMRendererModel))
119   {
120     DALI_LOG_ERROR("Failed to parse %s\n", url.c_str());
121     return false;
122   }
123
124   auto                         path = url.substr(0, url.rfind('/') + 1);
125   Gltf2Util::ConversionContext context{result, path, INVALID_INDEX};
126
127   auto& outBuffers = context.mOutput.mResources.mBuffers;
128   outBuffers.reserve(document.mBuffers.size());
129   if(!binaryChunkData.empty())
130   {
131     BufferDefinition dataBuffer(binaryChunkData);
132     outBuffers.emplace_back(std::move(dataBuffer));
133   }
134
135   Gltf2Util::ConvertGltfToContext(document, context, isMRendererModel);
136
137   return true;
138 }
139
140 } // namespace Dali::Scene3D::Loader::Internal