2 * Copyright (c) 2020 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.
17 #include <dali/devel-api/adaptor-framework/file-stream.h>
19 #include "gltf-scene.h"
21 #include "pico-json.h"
25 // string contains enum type index encoded matching glTFAttributeType
26 const std::vector<std::string> GLTF_STR_ATTRIBUTE_TYPE = {
31 const std::vector<std::pair<std::string, uint32_t>> GLTF_STR_COMPONENT_TYPE = {
32 std::make_pair("VEC2", 2),
33 std::make_pair("VEC3", 3),
34 std::make_pair("VEC4", 4),
35 std::make_pair("SCALAR", 1)};
37 glTFAttributeType glTFAttributeTypeStrToEnum(const std::string& name)
40 auto iter = std::find_if(GLTF_STR_ATTRIBUTE_TYPE.begin(), GLTF_STR_ATTRIBUTE_TYPE.end(), [name, &index](const std::string& val) {
44 if(iter == GLTF_STR_ATTRIBUTE_TYPE.end())
46 return glTFAttributeType::UNDEFINED;
49 return static_cast<glTFAttributeType>(index);
52 uint32_t glTFComponentTypeStrToNum(const std::string& name)
54 auto iter = std::find_if(GLTF_STR_COMPONENT_TYPE.begin(), GLTF_STR_COMPONENT_TYPE.end(), [name](const std::pair<std::string, uint32_t>& val) {
55 return val.first == name;
57 if(iter == GLTF_STR_COMPONENT_TYPE.end())
72 return static_cast<T>(result);
77 struct JsonResult<bool>
88 template<class Expected, class Converted = Expected>
89 JsonResult<picojson::value> JsonFindValue(const picojson::object& object, const std::string& name)
91 auto iter = find_if(object.begin(), object.end(), [name](decltype(*object.begin())& item) {
92 return item.first == name;
95 if(iter == object.end())
100 return {true, iter->second};
103 template<class Expected, class Converted = Expected>
104 JsonResult<Converted> JsonGetValue(const picojson::value& val, const std::string& name, const Converted& onFail)
106 if(val.contains(name))
108 return {true, static_cast<Converted>(val.get(name).get<Expected>())};
110 return {false, onFail};
113 template<class Expected, class Converted = Expected>
114 bool JsonGetValueOut(const picojson::value& val, const std::string& name, Converted& writeOut)
116 if(val.contains(name))
118 writeOut = static_cast<Converted>(val.get(name).get<Expected>());
125 * Safe json value accessor
127 template<class Expected, class Converted = Expected>
128 JsonResult<std::vector<Converted>> JsonGetArray(const picojson::value& val, const std::string& name, std::vector<Converted> valueOnFail = {})
130 if(val.contains(name))
132 const auto& array = val.get(name).get<picojson::array>();
133 std::vector<Converted> result{};
134 for(const auto& item : array)
136 result.emplace_back(static_cast<Converted>(item.get<Expected>()));
138 return {true, result};
140 return {false, valueOnFail};
145 glTF::glTF(const std::string& filename)
147 LoadFromFile(filename);
151 void glTF::LoadFromFile(const std::string& filename)
153 std::string jsonFile(filename);
155 std::string binFile(filename);
160 GLTF_LOG("LoadFromFile: %s", binFile.c_str());
161 mBuffer = LoadFile(binFile);
162 jsonBuffer = LoadFile(jsonFile);
167 GLTF_LOG("Error, buffer empty!");
171 GLTF_LOG("GLTF[BIN]: %s loaded, size = %d", binFile.c_str(), int(mBuffer.size()));
173 if(jsonBuffer.empty())
175 GLTF_LOG("Error, buffer GLTF empty!");
179 GLTF_LOG("GLTF: %s loaded, size = %d", binFile.c_str(), int(jsonBuffer.size()));
183 if(jsonBuffer.empty() || mBuffer.empty())
189 auto err = picojson::parse(jsonNode, std::string(reinterpret_cast<char*>(jsonBuffer.data())));
192 GLTF_LOG("GLTF: Error parsing %s, error: %s", jsonFile.c_str(), err.c_str());
197 GLTF_LOG("GLTF: %s loaded, size = %d", jsonFile.c_str(), int(jsonBuffer.size()));
201 bool glTF::ParseJSON()
203 if(!jsonNode.is<picojson::object>())
208 // Add dummy first node to nodes (scene node)
209 mNodes.emplace_back();
211 // Sources for textures to be resolved later
212 std::vector<uint32_t> textureSources{};
213 std::vector<glTF_Texture> images{};
215 for(auto& val : jsonNode.get<picojson::object>())
217 GLTF_LOG("node: %s", val.first.c_str());
220 if(val.first == "bufferViews")
222 auto bufferViews = val.second;
223 for(auto& view : bufferViews.get<picojson::array>())
225 auto bufferIndex = uint32_t(view.get("buffer").get<double>());
226 auto byteLength = uint32_t(view.get("byteLength").get<double>());
227 auto byteOffset = uint32_t(view.get("byteOffset").get<double>());
229 glTF_BufferView bufferView{};
230 bufferView.bufferIndex = bufferIndex;
231 bufferView.byteLength = byteLength;
232 bufferView.byteOffset = byteOffset;
234 mBufferViews.emplace_back(bufferView);
239 else if(val.first == "accessors")
241 for(const auto& accessor : val.second.get<picojson::array>())
243 auto gltfAccessor = glTF_Accessor{};
244 gltfAccessor.bufferView = uint32_t(accessor.get("bufferView").get<double>());
245 gltfAccessor.componentType = uint32_t(accessor.get("componentType").get<double>());
246 gltfAccessor.count = uint32_t(accessor.get("count").get<double>());
247 gltfAccessor.type = accessor.get("type").get<std::string>();
248 gltfAccessor.componentSize = glTFComponentTypeStrToNum(gltfAccessor.type);
249 mAccessors.emplace_back(gltfAccessor);
254 else if(val.first == "meshes")
256 for(const auto& mesh : val.second.get<picojson::array>())
258 glTF_Mesh gltfMesh{};
259 gltfMesh.name = mesh.get("name").get<std::string>();
261 // get primitives (in this implementation assuming single mesh consists of
262 // one and only one primitive)
263 const auto& primitive = mesh.get("primitives").get<picojson::array>()[0];
264 const auto& attrs = primitive.get("attributes").get<picojson::object>();
265 for(const auto& attr : attrs)
267 auto type = glTFAttributeTypeStrToEnum(attr.first);
268 auto bvIndex = uint32_t(attr.second.get<double>());
269 gltfMesh.attributes.emplace_back(std::make_pair(type, bvIndex));
270 GLTF_LOG("GLTF: ATTR: type: %d, index: %d", int(type), int(bvIndex));
273 gltfMesh.indices = uint32_t(primitive.get("indices").get<double>());
274 gltfMesh.material = uint32_t(primitive.get("material").get<double>());
275 mMeshes.emplace_back(gltfMesh);
279 else if(val.first == "cameras")
281 glTF_Camera tgifCamera{};
282 for(const auto& camera : val.second.get<picojson::array>())
284 tgifCamera.name = camera.get("name").to_str();
285 tgifCamera.isPerspective = (camera.get("type").to_str() == "perspective");
286 if(tgifCamera.isPerspective)
288 const auto& perspective = camera.get("perspective");
289 tgifCamera.yfov = static_cast<float>(perspective.get("yfov").get<double>());
290 tgifCamera.zfar = static_cast<float>(perspective.get("zfar").get<double>());
291 tgifCamera.znear = static_cast<float>(perspective.get("znear").get<double>());
294 mCameras.emplace_back(tgifCamera);
298 else if(val.first == "nodes")
301 for(const auto& node : val.second.get<picojson::array>())
303 glTF_Node gltfNode{};
304 gltfNode.name = node.get("name").to_str();
305 auto rotation = JsonGetArray<double, float>(node, "rotation", {0.0f, 0.0f, 0.0f, 1.0f});
306 auto translation = JsonGetArray<double, float>(node, "translation", {0.0f, 0.0f, 0.0f});
307 auto scale = JsonGetArray<double, float>(node, "scale", {1.0f, 1.0f, 1.0f});
308 std::copy(rotation.result.begin(), rotation.result.end(), gltfNode.rotationQuaternion);
309 std::copy(translation.result.begin(), translation.result.end(), gltfNode.translation);
310 std::copy(scale.result.begin(), scale.result.end(), gltfNode.scale);
312 auto children = JsonGetArray<double, uint32_t>(node, "children");
315 gltfNode.children = std::move(children.result);
317 gltfNode.index = nodeIndex;
318 gltfNode.cameraId = 0xffffffff;
319 gltfNode.meshId = 0xffffffff;
321 auto cameraId = JsonGetValue<double, uint32_t>(node, "camera", 0xffffffff);
324 gltfNode.cameraId = cameraId.result;
326 auto meshId = JsonGetValue<double, uint32_t>(node, "mesh", 0xffffffff);
329 gltfNode.meshId = meshId.result;
331 mNodes.emplace_back(gltfNode);
335 // parse scenes, note: only first scene is being parsed
336 else if(val.first == "scenes")
338 auto& sceneNode = mNodes[0];
339 const auto& scene = val.second.get<picojson::array>()[0];
340 sceneNode.name = JsonGetValue<std::string>(scene, "name", std::string());
341 auto result = JsonGetArray<double, uint32_t>(scene, "nodes");
342 sceneNode.children = result.result;
345 else if(val.first == "materials")
347 for(const auto& node : val.second.get<picojson::array>())
349 // Get pbr material, base color texture
350 glTF_Material material{};
351 material.doubleSided = JsonGetValue<bool>(node, "doubleSided", false).result;
352 material.name = JsonGetValue<std::string>(node, "name", std::string()).result;
353 if(node.contains("pbrMetallicRoughness"))
355 const auto& node0 = node.get("pbrMetallicRoughness");
356 if(node0.contains("baseColorTexture"))
358 const auto& node1 = node0.get("baseColorTexture");
359 auto index = uint32_t(node1.get("index").get<double>());
360 auto texCoord = uint32_t(node1.get("texCoord").get<double>());
361 material.pbrMetallicRoughness.enabled = true;
362 material.pbrMetallicRoughness.baseTextureColor.index = index;
363 material.pbrMetallicRoughness.baseTextureColor.texCoord = texCoord;
366 mMaterials.emplace_back(material);
369 else if(val.first == "textures")
371 for(const auto& item : val.second.get<picojson::array>())
373 auto source = JsonGetValue<double, uint32_t>(item, "source", 0xffffffff);
374 textureSources.emplace_back(source.result);
377 else if(val.first == "images")
379 for(const auto& item : val.second.get<picojson::array>())
382 JsonGetValueOut<std::string>(item, "name", tex.name);
383 JsonGetValueOut<std::string>(item, "uri", tex.uri);
384 images.emplace_back(tex);
389 // Resolve cross-referencing
390 for(const auto& source : textureSources)
392 mTextures.emplace_back(images[source]);
398 glTF_Buffer glTF::LoadFile(const std::string& filename)
400 Dali::FileStream fileStream(filename.c_str(), Dali::FileStream::READ | Dali::FileStream::BINARY);
401 FILE* fin = fileStream.GetFile();
402 std::vector<unsigned char> buffer;
405 if(fseek(fin, 0, SEEK_END))
409 auto size = ftell(fin);
410 if(fseek(fin, 0, SEEK_SET))
414 buffer.resize(unsigned(size));
415 auto result = fread(buffer.data(), 1, size_t(size), fin);
416 if(result != size_t(size))
418 GLTF_LOG("LoadFile: Result: %d", int(result));
419 // return empty buffer
425 GLTF_LOG("LoadFile: Can't open file: errno = %d", errno);
431 std::vector<const glTF_Mesh*> glTF::GetMeshes() const
433 std::vector<const glTF_Mesh*> retval;
434 for(auto& mesh : mMeshes)
436 retval.emplace_back(&mesh);
441 std::vector<const glTF_Camera*> glTF::GetCameras()
443 std::vector<const glTF_Camera*> cameras;
444 for(const auto& cam : mCameras)
446 cameras.emplace_back(&cam);
451 std::vector<unsigned char> glTF::GetMeshAttributeBuffer(const glTF_Mesh& mesh, const std::vector<glTFAttributeType>& attrTypes)
456 uint32_t accessorIndex{0u};
457 uint32_t byteStride{0u};
458 char* srcPtr{nullptr};
460 std::vector<Data> data{};
461 for(const auto& attrType : attrTypes)
463 std::find_if(mesh.attributes.begin(), mesh.attributes.end(), [&data, &attrType](const std::pair<glTFAttributeType, uint32_t>& item) {
464 if(item.first == attrType)
467 data.back().accessorIndex = item.second;
478 // number of attributes is same for the whole mesh so using very first
481 glTF_Buffer retval{};
483 // data is interleaved
486 auto attributeCount = mAccessors[data[0].accessorIndex].count; // / mAccessors[data[0].accessorIndex].componentSize;
487 uint32_t attributeStride = 0;
488 // now find buffer view stride for particular accessor
489 for(auto& item : data)
491 auto& accessor = mAccessors[item.accessorIndex];
493 // Update byte stride for this buffer view
494 auto& bufferView = mBufferViews[accessor.bufferView];
495 item.byteStride = bufferView.byteLength / attributeCount;
496 attributeStride += item.byteStride;
497 item.srcPtr = reinterpret_cast<char*>(mBuffer.data()) + bufferView.byteOffset;
500 // now allocate final buffer and interleave data
501 retval.resize(attributeStride * attributeCount);
502 auto* dstPtr = retval.data();
503 for(auto i = 0u; i < attributeCount; ++i)
505 for(auto& item : data)
507 std::copy(item.srcPtr,
508 item.srcPtr + item.byteStride,
509 reinterpret_cast<char*>(dstPtr));
510 dstPtr += item.byteStride;
511 item.srcPtr += item.byteStride;
515 else // copy data directly as single buffer
517 auto& bufferView = mBufferViews[mAccessors[data[0].accessorIndex].bufferView];
518 retval.resize(bufferView.byteLength);
519 std::copy(mBuffer.begin() + bufferView.byteOffset,
520 mBuffer.begin() + bufferView.byteOffset + bufferView.byteLength,
526 const glTF_Mesh* glTF::FindMeshByName(const std::string& name) const
528 for(const auto& mesh : mMeshes)
530 if(mesh.name == name)
536 uint32_t glTF::GetMeshAttributeCount(const glTF_Mesh* mesh) const
538 const auto& accessor = mAccessors[mesh->attributes[0].second];
539 return accessor.count; // / accessor.componentSize;
542 std::vector<uint16_t> glTF::GetMeshIndexBuffer(const glTF_Mesh* mesh) const
544 // check GL component type
545 const auto& accessor = mAccessors[mesh->indices];
546 if(accessor.componentType == 0x1403) // GL_UNSIGNED_SHORT
548 std::vector<uint16_t> retval{};
549 retval.resize(accessor.count);
550 const auto& bufferView = mBufferViews[accessor.bufferView];
551 const auto* srcPtr = reinterpret_cast<const uint16_t*>(reinterpret_cast<const char*>(mBuffer.data()) + bufferView.byteOffset);
552 std::copy(srcPtr, srcPtr + accessor.count, retval.data());
558 const glTF_Node* glTF::FindNodeByName(const std::string& name) const
560 auto iter = std::find_if(mNodes.begin(), mNodes.end(), [name](const glTF_Node& node) {
561 return !name.compare(node.name);
564 if(iter == mNodes.end())
572 const std::vector<glTF_Material>& glTF::GetMaterials() const
577 const std::vector<glTF_Texture>& glTF::GetTextures() const