2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "gltf-scene.h"
22 #include <dali/devel-api/adaptor-framework/file-stream.h>
26 // string contains enum type index encoded matching glTFAttributeType
27 const std::vector<std::string> GLTF_STR_ATTRIBUTE_TYPE = {
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)};
38 glTFAttributeType glTFAttributeTypeStrToEnum(const std::string& name)
41 auto iter = std::find_if(GLTF_STR_ATTRIBUTE_TYPE.begin(), GLTF_STR_ATTRIBUTE_TYPE.end(), [name, &index](const std::string& val) {
45 if(iter == GLTF_STR_ATTRIBUTE_TYPE.end())
47 return glTFAttributeType::UNDEFINED;
50 return static_cast<glTFAttributeType>(index);
53 uint32_t glTFComponentTypeStrToNum(const std::string& name)
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;
58 if(iter == GLTF_STR_COMPONENT_TYPE.end())
74 return static_cast<T>(result);
79 struct JsonResult<bool>
90 template<class Expected, class Converted = Expected>
91 JsonResult<picojson::value> JsonFindValue(const picojson::object& object, const std::string& name)
93 auto iter = find_if(object.begin(), object.end(), [name](decltype(*object.begin())& item) {
94 return item.first == name;
97 if(iter == object.end())
102 return {true, iter->second};
105 template<class Expected, class Converted = Expected>
106 JsonResult<Converted> JsonGetValue(const picojson::value& val, const std::string& name, const Converted& onFail)
108 if(val.contains(name))
110 return {true, static_cast<Converted>(val.get(name).get<Expected>())};
112 return {false, onFail};
115 template<class Expected, class Converted = Expected>
116 bool JsonGetValueOut(const picojson::value& val, const std::string& name, Converted& writeOut)
118 if(val.contains(name))
120 writeOut = static_cast<Converted>(val.get(name).get<Expected>());
127 * Safe json value accessor
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 = {})
132 if(val.contains(name))
134 const auto& array = val.get(name).get<picojson::array>();
135 std::vector<Converted> result{};
136 for(const auto& item : array)
138 result.emplace_back(static_cast<Converted>(item.get<Expected>()));
140 return {true, result};
142 return {false, valueOnFail};
147 glTF::glTF(const std::string& filename)
149 LoadFromFile(filename);
153 void glTF::LoadFromFile(const std::string& filename)
155 std::string jsonFile(filename);
157 std::string binFile(filename);
162 GLTF_LOG("LoadFromFile: %s", binFile.c_str());
163 mBuffer = LoadFile(binFile);
164 jsonBuffer = LoadFile(jsonFile);
166 jsonBuffer.push_back('\0'); // It should be null terminated.
171 GLTF_LOG("Error, buffer empty!");
175 GLTF_LOG("GLTF[BIN]: %s loaded, size = %d", binFile.c_str(), int(mBuffer.size()));
177 if(jsonBuffer.empty())
179 GLTF_LOG("Error, buffer GLTF empty!");
183 GLTF_LOG("GLTF: %s loaded, size = %d", binFile.c_str(), int(jsonBuffer.size()));
187 if(jsonBuffer.empty() || mBuffer.empty())
193 auto err = picojson::parse(jsonNode, std::string(reinterpret_cast<char*>(jsonBuffer.data())));
196 GLTF_LOG("GLTF: Error parsing %s, error: %s", jsonFile.c_str(), err.c_str());
201 GLTF_LOG("GLTF: %s loaded, size = %d", jsonFile.c_str(), int(jsonBuffer.size()));
205 bool glTF::ParseJSON()
207 if(!jsonNode.is<picojson::object>())
212 // Add dummy first node to nodes (scene node)
213 mNodes.emplace_back();
215 // Sources for textures to be resolved later
216 std::vector<uint32_t> textureSources{};
217 std::vector<glTF_Texture> images{};
219 for(auto& val : jsonNode.get<picojson::object>())
221 GLTF_LOG("node: %s", val.first.c_str());
224 if(val.first == "bufferViews")
226 auto bufferViews = val.second;
227 for(auto& view : bufferViews.get<picojson::array>())
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>());
233 glTF_BufferView bufferView{};
234 bufferView.bufferIndex = bufferIndex;
235 bufferView.byteLength = byteLength;
236 bufferView.byteOffset = byteOffset;
238 mBufferViews.emplace_back(bufferView);
243 else if(val.first == "accessors")
245 for(const auto& accessor : val.second.get<picojson::array>())
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);
258 else if(val.first == "meshes")
260 for(const auto& mesh : val.second.get<picojson::array>())
262 glTF_Mesh gltfMesh{};
263 gltfMesh.name = mesh.get("name").get<std::string>();
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)
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));
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);
283 else if(val.first == "cameras")
285 glTF_Camera tgifCamera{};
286 for(const auto& camera : val.second.get<picojson::array>())
288 tgifCamera.name = camera.get("name").to_str();
289 tgifCamera.isPerspective = (camera.get("type").to_str() == "perspective");
290 if(tgifCamera.isPerspective)
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>());
298 mCameras.emplace_back(tgifCamera);
302 else if(val.first == "nodes")
305 for(const auto& node : val.second.get<picojson::array>())
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);
316 auto children = JsonGetArray<double, uint32_t>(node, "children");
319 gltfNode.children = std::move(children.result);
321 gltfNode.index = nodeIndex;
322 gltfNode.cameraId = 0xffffffff;
323 gltfNode.meshId = 0xffffffff;
325 auto cameraId = JsonGetValue<double, uint32_t>(node, "camera", 0xffffffff);
328 gltfNode.cameraId = cameraId.result;
330 auto meshId = JsonGetValue<double, uint32_t>(node, "mesh", 0xffffffff);
333 gltfNode.meshId = meshId.result;
335 mNodes.emplace_back(gltfNode);
339 // parse scenes, note: only first scene is being parsed
340 else if(val.first == "scenes")
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;
349 else if(val.first == "materials")
351 for(const auto& node : val.second.get<picojson::array>())
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"))
359 const auto& node0 = node.get("pbrMetallicRoughness");
360 if(node0.contains("baseColorTexture"))
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;
370 mMaterials.emplace_back(material);
373 else if(val.first == "textures")
375 for(const auto& item : val.second.get<picojson::array>())
377 auto source = JsonGetValue<double, uint32_t>(item, "source", 0xffffffff);
378 textureSources.emplace_back(source.result);
381 else if(val.first == "images")
383 for(const auto& item : val.second.get<picojson::array>())
386 JsonGetValueOut<std::string>(item, "name", tex.name);
387 JsonGetValueOut<std::string>(item, "uri", tex.uri);
388 images.emplace_back(tex);
393 // Resolve cross-referencing
394 for(const auto& source : textureSources)
396 mTextures.emplace_back(images[source]);
402 glTF_Buffer glTF::LoadFile(const std::string& filename)
404 Dali::FileStream fileStream(filename.c_str(), Dali::FileStream::READ | Dali::FileStream::BINARY);
405 FILE* fin = fileStream.GetFile();
406 std::vector<unsigned char> buffer;
409 if(fseek(fin, 0, SEEK_END))
413 auto size = ftell(fin);
414 if(fseek(fin, 0, SEEK_SET))
418 buffer.resize(unsigned(size));
419 auto result = fread(buffer.data(), 1, size_t(size), fin);
420 if(result != size_t(size))
422 GLTF_LOG("LoadFile: Result: %d", int(result));
423 // return empty buffer
429 GLTF_LOG("LoadFile: Can't open file: errno = %d", errno);
435 std::vector<const glTF_Mesh*> glTF::GetMeshes() const
437 std::vector<const glTF_Mesh*> retval;
438 for(auto& mesh : mMeshes)
440 retval.emplace_back(&mesh);
445 std::vector<const glTF_Camera*> glTF::GetCameras()
447 std::vector<const glTF_Camera*> cameras;
448 for(const auto& cam : mCameras)
450 cameras.emplace_back(&cam);
455 std::vector<unsigned char> glTF::GetMeshAttributeBuffer(const glTF_Mesh& mesh, const std::vector<glTFAttributeType>& attrTypes)
460 uint32_t accessorIndex{0u};
461 uint32_t byteStride{0u};
462 char* srcPtr{nullptr};
464 std::vector<Data> data{};
465 for(const auto& attrType : attrTypes)
467 std::find_if(mesh.attributes.begin(), mesh.attributes.end(), [&data, &attrType](const std::pair<glTFAttributeType, uint32_t>& item) {
468 if(item.first == attrType)
471 data.back().accessorIndex = item.second;
482 // number of attributes is same for the whole mesh so using very first
485 glTF_Buffer retval{};
487 // data is interleaved
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)
495 auto& accessor = mAccessors[item.accessorIndex];
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;
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)
509 for(auto& item : data)
511 std::copy(item.srcPtr,
512 item.srcPtr + item.byteStride,
513 reinterpret_cast<char*>(dstPtr));
514 dstPtr += item.byteStride;
515 item.srcPtr += item.byteStride;
519 else // copy data directly as single buffer
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,
530 const glTF_Mesh* glTF::FindMeshByName(const std::string& name) const
532 for(const auto& mesh : mMeshes)
534 if(mesh.name == name)
540 uint32_t glTF::GetMeshAttributeCount(const glTF_Mesh* mesh) const
542 const auto& accessor = mAccessors[mesh->attributes[0].second];
543 return accessor.count; // / accessor.componentSize;
546 std::vector<uint16_t> glTF::GetMeshIndexBuffer(const glTF_Mesh* mesh) const
548 // check GL component type
549 const auto& accessor = mAccessors[mesh->indices];
550 if(accessor.componentType == 0x1403) // GL_UNSIGNED_SHORT
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());
562 const glTF_Node* glTF::FindNodeByName(const std::string& name) const
564 auto iter = std::find_if(mNodes.begin(), mNodes.end(), [name](const glTF_Node& node) {
565 return !name.compare(node.name);
568 if(iter == mNodes.end())
576 const std::vector<glTF_Material>& glTF::GetMaterials() const
581 const std::vector<glTF_Texture>& glTF::GetTextures() const