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);
169 GLTF_LOG("Error, buffer empty!");
173 GLTF_LOG("GLTF[BIN]: %s loaded, size = %d", binFile.c_str(), int(mBuffer.size()));
175 if(jsonBuffer.empty())
177 GLTF_LOG("Error, buffer GLTF empty!");
181 GLTF_LOG("GLTF: %s loaded, size = %d", binFile.c_str(), int(jsonBuffer.size()));
185 if(jsonBuffer.empty() || mBuffer.empty())
191 auto err = picojson::parse(jsonNode, std::string(reinterpret_cast<char*>(jsonBuffer.data())));
194 GLTF_LOG("GLTF: Error parsing %s, error: %s", jsonFile.c_str(), err.c_str());
199 GLTF_LOG("GLTF: %s loaded, size = %d", jsonFile.c_str(), int(jsonBuffer.size()));
203 bool glTF::ParseJSON()
205 if(!jsonNode.is<picojson::object>())
210 // Add dummy first node to nodes (scene node)
211 mNodes.emplace_back();
213 // Sources for textures to be resolved later
214 std::vector<uint32_t> textureSources{};
215 std::vector<glTF_Texture> images{};
217 for(auto& val : jsonNode.get<picojson::object>())
219 GLTF_LOG("node: %s", val.first.c_str());
222 if(val.first == "bufferViews")
224 auto bufferViews = val.second;
225 for(auto& view : bufferViews.get<picojson::array>())
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>());
231 glTF_BufferView bufferView{};
232 bufferView.bufferIndex = bufferIndex;
233 bufferView.byteLength = byteLength;
234 bufferView.byteOffset = byteOffset;
236 mBufferViews.emplace_back(bufferView);
241 else if(val.first == "accessors")
243 for(const auto& accessor : val.second.get<picojson::array>())
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);
256 else if(val.first == "meshes")
258 for(const auto& mesh : val.second.get<picojson::array>())
260 glTF_Mesh gltfMesh{};
261 gltfMesh.name = mesh.get("name").get<std::string>();
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)
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));
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);
281 else if(val.first == "cameras")
283 glTF_Camera tgifCamera{};
284 for(const auto& camera : val.second.get<picojson::array>())
286 tgifCamera.name = camera.get("name").to_str();
287 tgifCamera.isPerspective = (camera.get("type").to_str() == "perspective");
288 if(tgifCamera.isPerspective)
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>());
296 mCameras.emplace_back(tgifCamera);
300 else if(val.first == "nodes")
303 for(const auto& node : val.second.get<picojson::array>())
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);
314 auto children = JsonGetArray<double, uint32_t>(node, "children");
317 gltfNode.children = std::move(children.result);
319 gltfNode.index = nodeIndex;
320 gltfNode.cameraId = 0xffffffff;
321 gltfNode.meshId = 0xffffffff;
323 auto cameraId = JsonGetValue<double, uint32_t>(node, "camera", 0xffffffff);
326 gltfNode.cameraId = cameraId.result;
328 auto meshId = JsonGetValue<double, uint32_t>(node, "mesh", 0xffffffff);
331 gltfNode.meshId = meshId.result;
333 mNodes.emplace_back(gltfNode);
337 // parse scenes, note: only first scene is being parsed
338 else if(val.first == "scenes")
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;
347 else if(val.first == "materials")
349 for(const auto& node : val.second.get<picojson::array>())
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"))
357 const auto& node0 = node.get("pbrMetallicRoughness");
358 if(node0.contains("baseColorTexture"))
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;
368 mMaterials.emplace_back(material);
371 else if(val.first == "textures")
373 for(const auto& item : val.second.get<picojson::array>())
375 auto source = JsonGetValue<double, uint32_t>(item, "source", 0xffffffff);
376 textureSources.emplace_back(source.result);
379 else if(val.first == "images")
381 for(const auto& item : val.second.get<picojson::array>())
384 JsonGetValueOut<std::string>(item, "name", tex.name);
385 JsonGetValueOut<std::string>(item, "uri", tex.uri);
386 images.emplace_back(tex);
391 // Resolve cross-referencing
392 for(const auto& source : textureSources)
394 mTextures.emplace_back(images[source]);
400 glTF_Buffer glTF::LoadFile(const std::string& filename)
402 Dali::FileStream fileStream(filename.c_str(), Dali::FileStream::READ | Dali::FileStream::BINARY);
403 FILE* fin = fileStream.GetFile();
404 std::vector<unsigned char> buffer;
407 if(fseek(fin, 0, SEEK_END))
411 auto size = ftell(fin);
412 if(fseek(fin, 0, SEEK_SET))
416 buffer.resize(unsigned(size));
417 auto result = fread(buffer.data(), 1, size_t(size), fin);
418 if(result != size_t(size))
420 GLTF_LOG("LoadFile: Result: %d", int(result));
421 // return empty buffer
427 GLTF_LOG("LoadFile: Can't open file: errno = %d", errno);
433 std::vector<const glTF_Mesh*> glTF::GetMeshes() const
435 std::vector<const glTF_Mesh*> retval;
436 for(auto& mesh : mMeshes)
438 retval.emplace_back(&mesh);
443 std::vector<const glTF_Camera*> glTF::GetCameras()
445 std::vector<const glTF_Camera*> cameras;
446 for(const auto& cam : mCameras)
448 cameras.emplace_back(&cam);
453 std::vector<unsigned char> glTF::GetMeshAttributeBuffer(const glTF_Mesh& mesh, const std::vector<glTFAttributeType>& attrTypes)
458 uint32_t accessorIndex{0u};
459 uint32_t byteStride{0u};
460 char* srcPtr{nullptr};
462 std::vector<Data> data{};
463 for(const auto& attrType : attrTypes)
465 std::find_if(mesh.attributes.begin(), mesh.attributes.end(), [&data, &attrType](const std::pair<glTFAttributeType, uint32_t>& item) {
466 if(item.first == attrType)
469 data.back().accessorIndex = item.second;
480 // number of attributes is same for the whole mesh so using very first
483 glTF_Buffer retval{};
485 // data is interleaved
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)
493 auto& accessor = mAccessors[item.accessorIndex];
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;
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)
507 for(auto& item : data)
509 std::copy(item.srcPtr,
510 item.srcPtr + item.byteStride,
511 reinterpret_cast<char*>(dstPtr));
512 dstPtr += item.byteStride;
513 item.srcPtr += item.byteStride;
517 else // copy data directly as single buffer
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,
528 const glTF_Mesh* glTF::FindMeshByName(const std::string& name) const
530 for(const auto& mesh : mMeshes)
532 if(mesh.name == name)
538 uint32_t glTF::GetMeshAttributeCount(const glTF_Mesh* mesh) const
540 const auto& accessor = mAccessors[mesh->attributes[0].second];
541 return accessor.count; // / accessor.componentSize;
544 std::vector<uint16_t> glTF::GetMeshIndexBuffer(const glTF_Mesh* mesh) const
546 // check GL component type
547 const auto& accessor = mAccessors[mesh->indices];
548 if(accessor.componentType == 0x1403) // GL_UNSIGNED_SHORT
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());
560 const glTF_Node* glTF::FindNodeByName(const std::string& name) const
562 auto iter = std::find_if(mNodes.begin(), mNodes.end(), [name](const glTF_Node& node) {
563 return !name.compare(node.name);
566 if(iter == mNodes.end())
574 const std::vector<glTF_Material>& glTF::GetMaterials() const
579 const std::vector<glTF_Texture>& glTF::GetTextures() const