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 = {
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 )
39 glTFAttributeType glTFAttributeTypeStrToEnum( const std::string& name )
42 auto iter = std::find_if( GLTF_STR_ATTRIBUTE_TYPE.begin(), GLTF_STR_ATTRIBUTE_TYPE.end(),
43 [name, &index]( const std::string& val )
48 if( iter == GLTF_STR_ATTRIBUTE_TYPE.end() )
50 return glTFAttributeType::UNDEFINED;
53 return static_cast<glTFAttributeType>(index);
56 uint32_t glTFComponentTypeStrToNum( const std::string& name )
58 auto iter = std::find_if( GLTF_STR_COMPONENT_TYPE.begin(), GLTF_STR_COMPONENT_TYPE.end(),
59 [name]( const std::pair<std::string, uint32_t>& val )
61 return val.first == name;
63 if( iter == GLTF_STR_COMPONENT_TYPE.end() )
78 return static_cast<T>(result);
83 struct JsonResult<bool>
94 template<class Expected, class Converted = Expected>
95 JsonResult<picojson::value> JsonFindValue( const picojson::object& object, const std::string& name )
97 auto iter = find_if(object.begin(), object.end(), [name](decltype(*object.begin())& item)
99 return item.first == name;
102 if(iter == object.end())
107 return {true, iter->second};
111 template<class Expected, class Converted = Expected>
112 JsonResult<Converted> JsonGetValue( const picojson::value& val, const std::string& name, const Converted& onFail )
114 if( val.contains(name) )
116 return {true, static_cast<Converted>(val.get(name).get<Expected>())};
118 return {false, onFail};
121 template<class Expected, class Converted = Expected>
122 bool JsonGetValueOut( const picojson::value& val, const std::string& name, Converted& writeOut )
124 if( val.contains(name) )
126 writeOut = static_cast<Converted>(val.get(name).get<Expected>());
134 * Safe json value accessor
136 template<class Expected, class Converted = Expected>
137 JsonResult<std::vector<Converted>> JsonGetArray( const picojson::value& val, const std::string& name, std::vector<Converted> valueOnFail = {} )
139 if(val.contains(name) )
141 const auto& array = val.get(name).get<picojson::array>();
142 std::vector<Converted> result{};
143 for( const auto& item : array )
145 result.emplace_back( static_cast<Converted>(item.get<Expected>()) );
147 return {true, result};
149 return {false, valueOnFail};
154 glTF::glTF(const std::string& filename)
156 LoadFromFile(filename);
160 void glTF::LoadFromFile( const std::string& filename )
162 std::string jsonFile( filename );
164 std::string binFile( filename );
169 GLTF_LOG("LoadFromFile: %s", binFile.c_str());
170 mBuffer = LoadFile(binFile);
171 jsonBuffer = LoadFile(jsonFile);
176 GLTF_LOG("Error, buffer empty!");
180 GLTF_LOG( "GLTF[BIN]: %s loaded, size = %d", binFile.c_str(), int(mBuffer.size()));
182 if(jsonBuffer.empty())
184 GLTF_LOG("Error, buffer GLTF empty!");
188 GLTF_LOG( "GLTF: %s loaded, size = %d", binFile.c_str(), int(mBuffer.size()));
192 if(jsonBuffer.empty() || mBuffer.empty())
198 auto err = picojson::parse(jsonNode, std::string(reinterpret_cast<char*>(jsonBuffer.data())));
201 GLTF_LOG( "GLTF: Error parsing %s, error: %s", jsonFile.c_str(), err.c_str());
206 GLTF_LOG( "GLTF: %s loaded, size = %d", jsonFile.c_str(), int(jsonBuffer.size()));
210 bool glTF::ParseJSON()
212 if (!jsonNode.is<picojson::object>())
217 // Add dummy first node to nodes (scene node)
218 mNodes.emplace_back();
220 // Sources for textures to be resolved later
221 std::vector<uint32_t> textureSources{};
222 std::vector<glTF_Texture> images{};
224 for( auto& val : jsonNode.get<picojson::object>() )
226 GLTF_LOG( "node: %s", val.first.c_str());
229 if( val.first == "bufferViews" )
231 auto bufferViews = val.second;
232 for( auto& view : bufferViews.get<picojson::array>() )
234 auto bufferIndex = uint32_t(view.get("buffer").get<double>());
235 auto byteLength = uint32_t(view.get("byteLength").get<double>());
236 auto byteOffset = uint32_t(view.get("byteOffset").get<double>());
238 glTF_BufferView bufferView{};
239 bufferView.bufferIndex = bufferIndex;
240 bufferView.byteLength = byteLength;
241 bufferView.byteOffset = byteOffset;
243 mBufferViews.emplace_back( bufferView );
248 else if( val.first == "accessors" )
250 for( const auto& accessor : val.second.get<picojson::array>() )
252 auto gltfAccessor = glTF_Accessor{};
253 gltfAccessor.bufferView = uint32_t(accessor.get( "bufferView" ).get<double>());
254 gltfAccessor.componentType = uint32_t(accessor.get( "componentType" ).get<double>());
255 gltfAccessor.count = uint32_t(accessor.get( "count" ).get<double>());
256 gltfAccessor.type = accessor.get( "type" ).get<std::string>();
257 gltfAccessor.componentSize = glTFComponentTypeStrToNum( gltfAccessor.type );
258 mAccessors.emplace_back( gltfAccessor );
263 else if( val.first == "meshes" )
265 for( const auto& mesh : val.second.get<picojson::array>() )
267 glTF_Mesh gltfMesh{};
268 gltfMesh.name = mesh.get( "name" ).get<std::string>();
270 // get primitives (in this implementation assuming single mesh consists of
271 // one and only one primitive)
272 const auto& primitive = mesh.get("primitives").get<picojson::array>()[0];
273 const auto& attrs = primitive.get("attributes").get<picojson::object>();
274 for( const auto& attr : attrs )
276 auto type = glTFAttributeTypeStrToEnum( attr.first );
277 auto bvIndex = uint32_t(attr.second.get<double>());
278 gltfMesh.attributes.emplace_back( std::make_pair( type, bvIndex ) );
279 GLTF_LOG("GLTF: ATTR: type: %d, index: %d", int(type), int(bvIndex));
282 gltfMesh.indices = uint32_t(primitive.get("indices").get<double>());
283 gltfMesh.material = uint32_t(primitive.get("material").get<double>());
284 mMeshes.emplace_back( gltfMesh );
288 else if( val.first == "cameras" )
290 glTF_Camera tgifCamera{};
291 for( const auto& camera : val.second.get<picojson::array>() )
293 tgifCamera.name = camera.get( "name" ).to_str();
294 tgifCamera.isPerspective = (camera.get( "type" ).to_str() == "perspective" );
295 if(tgifCamera.isPerspective)
297 const auto& perspective = camera.get( "perspective" );
298 tgifCamera.yfov = static_cast<float>(perspective.get( "yfov" ).get<double>());
299 tgifCamera.zfar = static_cast<float>(perspective.get( "zfar" ).get<double>());
300 tgifCamera.znear = static_cast<float>(perspective.get( "znear" ).get<double>());
303 mCameras.emplace_back( tgifCamera );
307 else if( val.first == "nodes" )
310 for( const auto& node : val.second.get<picojson::array>() )
312 glTF_Node gltfNode{};
313 gltfNode.name = node.get( "name" ).to_str();
314 auto rotation = JsonGetArray<double, float>( node, "rotation", {0.0f, 0.0f, 0.0f, 1.0f} );
315 auto translation = JsonGetArray<double, float>( node, "translation", {0.0f, 0.0f, 0.0f} );
316 auto scale = JsonGetArray<double, float>( node, "scale", {1.0f, 1.0f, 1.0f} );
317 std::copy( rotation.result.begin(), rotation.result.end(), gltfNode.rotationQuaternion );
318 std::copy( translation.result.begin(), translation.result.end(), gltfNode.translation );
319 std::copy( scale.result.begin(), scale.result.end(), gltfNode.scale );
321 auto children = JsonGetArray<double, uint32_t>( node, "children" );
324 gltfNode.children = std::move(children.result);
326 gltfNode.index = nodeIndex;
327 gltfNode.cameraId = 0xffffffff;
328 gltfNode.meshId = 0xffffffff;
330 auto cameraId = JsonGetValue<double, uint32_t>( node, "camera", 0xffffffff );
331 if( cameraId.success )
333 gltfNode.cameraId = cameraId.result;
335 auto meshId = JsonGetValue<double, uint32_t>( node, "mesh", 0xffffffff );
338 gltfNode.meshId = meshId.result;
340 mNodes.emplace_back( gltfNode );
345 // parse scenes, note: only first scene is being parsed
346 else if( val.first == "scenes" )
348 auto& sceneNode = mNodes[0];
349 const auto& scene = val.second.get<picojson::array>()[0];
350 sceneNode.name = JsonGetValue<std::string>( scene, "name", std::string() );
351 auto result = JsonGetArray<double, uint32_t>( scene, "nodes" );
352 sceneNode.children = result.result;
355 else if( val.first == "materials" )
357 for( const auto& node : val.second.get<picojson::array>() )
359 // Get pbr material, base color texture
360 glTF_Material material{};
361 material.doubleSided = JsonGetValue<bool>( node, "doubleSided", false ).result;
362 material.name = JsonGetValue<std::string>( node, "name", std::string() ).result;
363 if( node.contains("pbrMetallicRoughness") )
365 const auto& node0 = node.get("pbrMetallicRoughness");
366 if(node0.contains("baseColorTexture"))
368 const auto& node1 = node0.get("baseColorTexture");
369 auto index = uint32_t(node1.get("index").get<double>());
370 auto texCoord = uint32_t(node1.get("texCoord").get<double>());
371 material.pbrMetallicRoughness.enabled = true;
372 material.pbrMetallicRoughness.baseTextureColor.index = index;
373 material.pbrMetallicRoughness.baseTextureColor.texCoord = texCoord;
376 mMaterials.emplace_back( material );
379 else if( val.first == "textures" )
381 for(const auto& item : val.second.get<picojson::array>() )
383 auto source = JsonGetValue<double, uint32_t>( item, "source", 0xffffffff );
384 textureSources.emplace_back( source.result );
387 else if( val.first == "images" )
389 for(const auto& item : val.second.get<picojson::array>() )
392 JsonGetValueOut<std::string>( item, "name", tex.name );
393 JsonGetValueOut<std::string>( item, "uri", tex.uri );
394 images.emplace_back( tex );
399 // Resolve cross-referencing
400 for( const auto& source : textureSources )
402 mTextures.emplace_back( images[source] );
408 glTF_Buffer glTF::LoadFile( const std::string& filename )
410 Dali::FileStream fileStream( filename.c_str(), Dali::FileStream::READ | Dali::FileStream::BINARY );
411 FILE* fin = fileStream.GetFile();
412 std::vector<unsigned char> buffer;
415 fseek( fin, 0, SEEK_END );
416 auto size = ftell(fin);
417 fseek( fin, 0, SEEK_SET );
418 buffer.resize(unsigned(size));
419 auto result = fread( buffer.data(), 1, size_t(size), fin );
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
488 if( data.size() > 1 )
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,
532 const glTF_Mesh* glTF::FindMeshByName( const std::string& name ) const
534 for( const auto& mesh : mMeshes )
536 if(mesh.name == name)
542 uint32_t glTF::GetMeshAttributeCount( const glTF_Mesh* mesh ) const
544 const auto& accessor = mAccessors[mesh->attributes[0].second];
545 return accessor.count;// / accessor.componentSize;
548 std::vector<uint16_t> glTF::GetMeshIndexBuffer( const glTF_Mesh* mesh ) const
550 // check GL component type
551 const auto& accessor = mAccessors[mesh->indices];
552 if( accessor.componentType == 0x1403 ) // GL_UNSIGNED_SHORT
554 std::vector<uint16_t> retval{};
555 retval.resize( accessor.count );
556 const auto& bufferView = mBufferViews[accessor.bufferView];
557 const auto* srcPtr = reinterpret_cast<const uint16_t*>
558 (reinterpret_cast<const char*>(mBuffer.data()) + bufferView.byteOffset);
559 std::copy( srcPtr, srcPtr + accessor.count, retval.data() );
565 const glTF_Node* glTF::FindNodeByName( const std::string& name ) const
567 auto iter = std::find_if( mNodes.begin(), mNodes.end(), [name]( const glTF_Node& node )
569 return !name.compare( node.name );
572 if(iter == mNodes.end())
580 const std::vector<glTF_Material>& glTF::GetMaterials() const
585 const std::vector<glTF_Texture>& glTF::GetTextures() const