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.
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())
73 return static_cast<T>(result);
78 struct JsonResult<bool>
89 template<class Expected, class Converted = Expected>
90 JsonResult<picojson::value> JsonFindValue(const picojson::object& object, const std::string& name)
92 auto iter = find_if(object.begin(), object.end(), [name](decltype(*object.begin())& item) {
93 return item.first == name;
96 if(iter == object.end())
101 return {true, iter->second};
104 template<class Expected, class Converted = Expected>
105 JsonResult<Converted> JsonGetValue(const picojson::value& val, const std::string& name, const Converted& onFail)
107 if(val.contains(name))
109 return {true, static_cast<Converted>(val.get(name).get<Expected>())};
111 return {false, onFail};
114 template<class Expected, class Converted = Expected>
115 bool JsonGetValueOut(const picojson::value& val, const std::string& name, Converted& writeOut)
117 if(val.contains(name))
119 writeOut = static_cast<Converted>(val.get(name).get<Expected>());
126 * Safe json value accessor
128 template<class Expected, class Converted = Expected>
129 JsonResult<std::vector<Converted>> JsonGetArray(const picojson::value& val, const std::string& name, std::vector<Converted> valueOnFail = {})
131 if(val.contains(name))
133 const auto& array = val.get(name).get<picojson::array>();
134 std::vector<Converted> result{};
135 for(const auto& item : array)
137 result.emplace_back(static_cast<Converted>(item.get<Expected>()));
139 return {true, result};
141 return {false, valueOnFail};
146 glTF::glTF(const std::string& filename)
148 LoadFromFile(filename);
152 void glTF::LoadFromFile(const std::string& filename)
154 std::string jsonFile(filename);
156 std::string binFile(filename);
161 GLTF_LOG("LoadFromFile: %s", binFile.c_str());
162 mBuffer = LoadFile(binFile);
163 jsonBuffer = LoadFile(jsonFile);
168 GLTF_LOG("Error, buffer empty!");
172 GLTF_LOG("GLTF[BIN]: %s loaded, size = %d", binFile.c_str(), int(mBuffer.size()));
174 if(jsonBuffer.empty())
176 GLTF_LOG("Error, buffer GLTF empty!");
180 GLTF_LOG("GLTF: %s loaded, size = %d", binFile.c_str(), int(jsonBuffer.size()));
184 if(jsonBuffer.empty() || mBuffer.empty())
190 auto err = picojson::parse(jsonNode, std::string(reinterpret_cast<char*>(jsonBuffer.data())));
193 GLTF_LOG("GLTF: Error parsing %s, error: %s", jsonFile.c_str(), err.c_str());
198 GLTF_LOG("GLTF: %s loaded, size = %d", jsonFile.c_str(), int(jsonBuffer.size()));
202 bool glTF::ParseJSON()
204 if(!jsonNode.is<picojson::object>())
209 // Add dummy first node to nodes (scene node)
210 mNodes.emplace_back();
212 // Sources for textures to be resolved later
213 std::vector<uint32_t> textureSources{};
214 std::vector<glTF_Texture> images{};
216 for(auto& val : jsonNode.get<picojson::object>())
218 GLTF_LOG("node: %s", val.first.c_str());
221 if(val.first == "bufferViews")
223 auto bufferViews = val.second;
224 for(auto& view : bufferViews.get<picojson::array>())
226 auto bufferIndex = uint32_t(view.get("buffer").get<double>());
227 auto byteLength = uint32_t(view.get("byteLength").get<double>());
228 auto byteOffset = uint32_t(view.get("byteOffset").get<double>());
230 glTF_BufferView bufferView{};
231 bufferView.bufferIndex = bufferIndex;
232 bufferView.byteLength = byteLength;
233 bufferView.byteOffset = byteOffset;
235 mBufferViews.emplace_back(bufferView);
240 else if(val.first == "accessors")
242 for(const auto& accessor : val.second.get<picojson::array>())
244 auto gltfAccessor = glTF_Accessor{};
245 gltfAccessor.bufferView = uint32_t(accessor.get("bufferView").get<double>());
246 gltfAccessor.componentType = uint32_t(accessor.get("componentType").get<double>());
247 gltfAccessor.count = uint32_t(accessor.get("count").get<double>());
248 gltfAccessor.type = accessor.get("type").get<std::string>();
249 gltfAccessor.componentSize = glTFComponentTypeStrToNum(gltfAccessor.type);
250 mAccessors.emplace_back(gltfAccessor);
255 else if(val.first == "meshes")
257 for(const auto& mesh : val.second.get<picojson::array>())
259 glTF_Mesh gltfMesh{};
260 gltfMesh.name = mesh.get("name").get<std::string>();
262 // get primitives (in this implementation assuming single mesh consists of
263 // one and only one primitive)
264 const auto& primitive = mesh.get("primitives").get<picojson::array>()[0];
265 const auto& attrs = primitive.get("attributes").get<picojson::object>();
266 for(const auto& attr : attrs)
268 auto type = glTFAttributeTypeStrToEnum(attr.first);
269 auto bvIndex = uint32_t(attr.second.get<double>());
270 gltfMesh.attributes.emplace_back(std::make_pair(type, bvIndex));
271 GLTF_LOG("GLTF: ATTR: type: %d, index: %d", int(type), int(bvIndex));
274 gltfMesh.indices = uint32_t(primitive.get("indices").get<double>());
275 gltfMesh.material = uint32_t(primitive.get("material").get<double>());
276 mMeshes.emplace_back(gltfMesh);
280 else if(val.first == "cameras")
282 glTF_Camera tgifCamera{};
283 for(const auto& camera : val.second.get<picojson::array>())
285 tgifCamera.name = camera.get("name").to_str();
286 tgifCamera.isPerspective = (camera.get("type").to_str() == "perspective");
287 if(tgifCamera.isPerspective)
289 const auto& perspective = camera.get("perspective");
290 tgifCamera.yfov = static_cast<float>(perspective.get("yfov").get<double>());
291 tgifCamera.zfar = static_cast<float>(perspective.get("zfar").get<double>());
292 tgifCamera.znear = static_cast<float>(perspective.get("znear").get<double>());
295 mCameras.emplace_back(tgifCamera);
299 else if(val.first == "nodes")
302 for(const auto& node : val.second.get<picojson::array>())
304 glTF_Node gltfNode{};
305 gltfNode.name = node.get("name").to_str();
306 auto rotation = JsonGetArray<double, float>(node, "rotation", {0.0f, 0.0f, 0.0f, 1.0f});
307 auto translation = JsonGetArray<double, float>(node, "translation", {0.0f, 0.0f, 0.0f});
308 auto scale = JsonGetArray<double, float>(node, "scale", {1.0f, 1.0f, 1.0f});
309 std::copy(rotation.result.begin(), rotation.result.end(), gltfNode.rotationQuaternion);
310 std::copy(translation.result.begin(), translation.result.end(), gltfNode.translation);
311 std::copy(scale.result.begin(), scale.result.end(), gltfNode.scale);
313 auto children = JsonGetArray<double, uint32_t>(node, "children");
316 gltfNode.children = std::move(children.result);
318 gltfNode.index = nodeIndex;
319 gltfNode.cameraId = 0xffffffff;
320 gltfNode.meshId = 0xffffffff;
322 auto cameraId = JsonGetValue<double, uint32_t>(node, "camera", 0xffffffff);
325 gltfNode.cameraId = cameraId.result;
327 auto meshId = JsonGetValue<double, uint32_t>(node, "mesh", 0xffffffff);
330 gltfNode.meshId = meshId.result;
332 mNodes.emplace_back(gltfNode);
336 // parse scenes, note: only first scene is being parsed
337 else if(val.first == "scenes")
339 auto& sceneNode = mNodes[0];
340 const auto& scene = val.second.get<picojson::array>()[0];
341 sceneNode.name = JsonGetValue<std::string>(scene, "name", std::string());
342 auto result = JsonGetArray<double, uint32_t>(scene, "nodes");
343 sceneNode.children = result.result;
346 else if(val.first == "materials")
348 for(const auto& node : val.second.get<picojson::array>())
350 // Get pbr material, base color texture
351 glTF_Material material{};
352 material.doubleSided = JsonGetValue<bool>(node, "doubleSided", false).result;
353 material.name = JsonGetValue<std::string>(node, "name", std::string()).result;
354 if(node.contains("pbrMetallicRoughness"))
356 const auto& node0 = node.get("pbrMetallicRoughness");
357 if(node0.contains("baseColorTexture"))
359 const auto& node1 = node0.get("baseColorTexture");
360 auto index = uint32_t(node1.get("index").get<double>());
361 auto texCoord = uint32_t(node1.get("texCoord").get<double>());
362 material.pbrMetallicRoughness.enabled = true;
363 material.pbrMetallicRoughness.baseTextureColor.index = index;
364 material.pbrMetallicRoughness.baseTextureColor.texCoord = texCoord;
367 mMaterials.emplace_back(material);
370 else if(val.first == "textures")
372 for(const auto& item : val.second.get<picojson::array>())
374 auto source = JsonGetValue<double, uint32_t>(item, "source", 0xffffffff);
375 textureSources.emplace_back(source.result);
378 else if(val.first == "images")
380 for(const auto& item : val.second.get<picojson::array>())
383 JsonGetValueOut<std::string>(item, "name", tex.name);
384 JsonGetValueOut<std::string>(item, "uri", tex.uri);
385 images.emplace_back(tex);
390 // Resolve cross-referencing
391 for(const auto& source : textureSources)
393 mTextures.emplace_back(images[source]);
399 glTF_Buffer glTF::LoadFile(const std::string& filename)
401 Dali::FileStream fileStream(filename.c_str(), Dali::FileStream::READ | Dali::FileStream::BINARY);
402 FILE* fin = fileStream.GetFile();
403 std::vector<unsigned char> buffer;
406 if(fseek(fin, 0, SEEK_END))
410 auto size = ftell(fin);
411 if(fseek(fin, 0, SEEK_SET))
415 buffer.resize(unsigned(size));
416 auto result = fread(buffer.data(), 1, size_t(size), fin);
417 if(result != size_t(size))
419 GLTF_LOG("LoadFile: Result: %d", int(result));
420 // return empty buffer
426 GLTF_LOG("LoadFile: Can't open file: errno = %d", errno);
432 std::vector<const glTF_Mesh*> glTF::GetMeshes() const
434 std::vector<const glTF_Mesh*> retval;
435 for(auto& mesh : mMeshes)
437 retval.emplace_back(&mesh);
442 std::vector<const glTF_Camera*> glTF::GetCameras()
444 std::vector<const glTF_Camera*> cameras;
445 for(const auto& cam : mCameras)
447 cameras.emplace_back(&cam);
452 std::vector<unsigned char> glTF::GetMeshAttributeBuffer(const glTF_Mesh& mesh, const std::vector<glTFAttributeType>& attrTypes)
457 uint32_t accessorIndex{0u};
458 uint32_t byteStride{0u};
459 char* srcPtr{nullptr};
461 std::vector<Data> data{};
462 for(const auto& attrType : attrTypes)
464 std::find_if(mesh.attributes.begin(), mesh.attributes.end(), [&data, &attrType](const std::pair<glTFAttributeType, uint32_t>& item) {
465 if(item.first == attrType)
468 data.back().accessorIndex = item.second;
479 // number of attributes is same for the whole mesh so using very first
482 glTF_Buffer retval{};
484 // data is interleaved
487 auto attributeCount = mAccessors[data[0].accessorIndex].count; // / mAccessors[data[0].accessorIndex].componentSize;
488 uint32_t attributeStride = 0;
489 // now find buffer view stride for particular accessor
490 for(auto& item : data)
492 auto& accessor = mAccessors[item.accessorIndex];
494 // Update byte stride for this buffer view
495 auto& bufferView = mBufferViews[accessor.bufferView];
496 item.byteStride = bufferView.byteLength / attributeCount;
497 attributeStride += item.byteStride;
498 item.srcPtr = reinterpret_cast<char*>(mBuffer.data()) + bufferView.byteOffset;
501 // now allocate final buffer and interleave data
502 retval.resize(attributeStride * attributeCount);
503 auto* dstPtr = retval.data();
504 for(auto i = 0u; i < attributeCount; ++i)
506 for(auto& item : data)
508 std::copy(item.srcPtr,
509 item.srcPtr + item.byteStride,
510 reinterpret_cast<char*>(dstPtr));
511 dstPtr += item.byteStride;
512 item.srcPtr += item.byteStride;
516 else // copy data directly as single buffer
518 auto& bufferView = mBufferViews[mAccessors[data[0].accessorIndex].bufferView];
519 retval.resize(bufferView.byteLength);
520 std::copy(mBuffer.begin() + bufferView.byteOffset,
521 mBuffer.begin() + bufferView.byteOffset + bufferView.byteLength,
527 const glTF_Mesh* glTF::FindMeshByName(const std::string& name) const
529 for(const auto& mesh : mMeshes)
531 if(mesh.name == name)
537 uint32_t glTF::GetMeshAttributeCount(const glTF_Mesh* mesh) const
539 const auto& accessor = mAccessors[mesh->attributes[0].second];
540 return accessor.count; // / accessor.componentSize;
543 std::vector<uint16_t> glTF::GetMeshIndexBuffer(const glTF_Mesh* mesh) const
545 // check GL component type
546 const auto& accessor = mAccessors[mesh->indices];
547 if(accessor.componentType == 0x1403) // GL_UNSIGNED_SHORT
549 std::vector<uint16_t> retval{};
550 retval.resize(accessor.count);
551 const auto& bufferView = mBufferViews[accessor.bufferView];
552 const auto* srcPtr = reinterpret_cast<const uint16_t*>(reinterpret_cast<const char*>(mBuffer.data()) + bufferView.byteOffset);
553 std::copy(srcPtr, srcPtr + accessor.count, retval.data());
559 const glTF_Node* glTF::FindNodeByName(const std::string& name) const
561 auto iter = std::find_if(mNodes.begin(), mNodes.end(), [name](const glTF_Node& node) {
562 return !name.compare(node.name);
565 if(iter == mNodes.end())
573 const std::vector<glTF_Material>& glTF::GetMaterials() const
578 const std::vector<glTF_Texture>& glTF::GetTextures() const