1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following conditions are met:
12
13* Redistributions of source code must retain the above
14copyright notice, this list of conditions and the
15following disclaimer.
16
17* Redistributions in binary form must reproduce the above
18copyright notice, this list of conditions and the
19following disclaimer in the documentation and/or other
20materials provided with the distribution.
21
22* Neither the name of the assimp team, nor the names of its
23contributors may be used to endorse or promote products
24derived from this software without specific prior
25written permission of the assimp team.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39----------------------------------------------------------------------
40*/
41
42#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
43
44#include "glTF2Importer.h"
45#include "StringComparison.h"
46#include "StringUtils.h"
47
48#include <assimp/Importer.hpp>
49#include <assimp/scene.h>
50#include <assimp/ai_assert.h>
51#include <assimp/DefaultLogger.hpp>
52#include <assimp/importerdesc.h>
53
54#include <memory>
55
56#include "MakeVerboseFormat.h"
57
58#include "glTF2Asset.h"
59// This is included here so WriteLazyDict<T>'s definition is found.
60#include "glTF2AssetWriter.h"
61#include <rapidjson/document.h>
62#include <rapidjson/rapidjson.h>
63
64using namespace Assimp;
65using namespace glTF2;
66
67
68//
69// glTF2Importer
70//
71
72static const aiImporterDesc desc = {
73 "glTF2 Importer",
74 "",
75 "",
76 "",
77 aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
78 0,
79 0,
80 0,
81 0,
82 "gltf glb"
83};
84
85glTF2Importer::glTF2Importer()
86: BaseImporter()
87, meshOffsets()
88, embeddedTexIdxs()
89, mScene( NULL ) {
90 // empty
91}
92
93glTF2Importer::~glTF2Importer() {
94 // empty
95}
96
97const aiImporterDesc* glTF2Importer::GetInfo() const
98{
99 return &desc;
100}
101
102bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
103{
104 const std::string &extension = GetExtension(pFile);
105
106 if (extension != "gltf" && extension != "glb")
107 return false;
108
109 if (checkSig && pIOHandler) {
110 glTF2::Asset asset(pIOHandler);
111 try {
112 asset.Load(pFile, extension == "glb");
113 std::string version = asset.asset.version;
114 return !version.empty() && version[0] == '2';
115 } catch (...) {
116 return false;
117 }
118 }
119
120 return false;
121}
122
123
124//static void CopyValue(const glTF2::vec3& v, aiColor3D& out)
125//{
126// out.r = v[0]; out.g = v[1]; out.b = v[2];
127//}
128
129static void CopyValue(const glTF2::vec4& v, aiColor4D& out)
130{
131 out.r = v[0]; out.g = v[1]; out.b = v[2]; out.a = v[3];
132}
133
134/*static void CopyValue(const glTF2::vec4& v, aiColor3D& out)
135{
136 out.r = v[0]; out.g = v[1]; out.b = v[2];
137}*/
138
139static void CopyValue(const glTF2::vec3& v, aiColor4D& out)
140{
141 out.r = v[0]; out.g = v[1]; out.b = v[2]; out.a = 1.0;
142}
143
144static void CopyValue(const glTF2::vec3& v, aiVector3D& out)
145{
146 out.x = v[0]; out.y = v[1]; out.z = v[2];
147}
148
149static void CopyValue(const glTF2::vec4& v, aiQuaternion& out)
150{
151 out.x = v[0]; out.y = v[1]; out.z = v[2]; out.w = v[3];
152}
153
154static void CopyValue(const glTF2::mat4& v, aiMatrix4x4& o)
155{
156 o.a1 = v[ 0]; o.b1 = v[ 1]; o.c1 = v[ 2]; o.d1 = v[ 3];
157 o.a2 = v[ 4]; o.b2 = v[ 5]; o.c2 = v[ 6]; o.d2 = v[ 7];
158 o.a3 = v[ 8]; o.b3 = v[ 9]; o.c3 = v[10]; o.d3 = v[11];
159 o.a4 = v[12]; o.b4 = v[13]; o.c4 = v[14]; o.d4 = v[15];
160}
161
162inline void SetMaterialColorProperty(Asset& /*r*/, vec4& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx)
163{
164 aiColor4D col;
165 CopyValue(prop, col);
166 mat->AddProperty(&col, 1, pKey, type, idx);
167}
168
169inline void SetMaterialColorProperty(Asset& /*r*/, vec3& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx)
170{
171 aiColor4D col;
172 CopyValue(prop, col);
173 mat->AddProperty(&col, 1, pKey, type, idx);
174}
175
176inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset& /*r*/, glTF2::TextureInfo prop, aiMaterial* mat, aiTextureType texType, unsigned int texSlot = 0)
177{
178 if (prop.texture && prop.texture->source) {
179 aiString uri(prop.texture->source->uri);
180
181 int texIdx = embeddedTexIdxs[prop.texture->source.GetIndex()];
182 if (texIdx != -1) { // embedded
183 // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
184 uri.data[0] = '*';
185 uri.length = 1 + ASSIMP_itoa10(uri.data + 1, MAXLEN - 1, texIdx);
186 }
187
188 mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot));
189 mat->AddProperty(&prop.texCoord, 1, _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, texType, texSlot);
190
191 if (prop.texture->sampler) {
192 Ref<Sampler> sampler = prop.texture->sampler;
193
194 aiString name(sampler->name);
195 aiString id(sampler->id);
196
197 mat->AddProperty(&name, AI_MATKEY_GLTF_MAPPINGNAME(texType, texSlot));
198 mat->AddProperty(&id, AI_MATKEY_GLTF_MAPPINGID(texType, texSlot));
199
200 mat->AddProperty(&sampler->wrapS, 1, AI_MATKEY_MAPPINGMODE_U(texType, texSlot));
201 mat->AddProperty(&sampler->wrapT, 1, AI_MATKEY_MAPPINGMODE_V(texType, texSlot));
202
203 if (sampler->magFilter != SamplerMagFilter::UNSET) {
204 mat->AddProperty(&sampler->magFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(texType, texSlot));
205 }
206
207 if (sampler->minFilter != SamplerMinFilter::UNSET) {
208 mat->AddProperty(&sampler->minFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(texType, texSlot));
209 }
210 }
211 }
212}
213
214void glTF2Importer::ImportMaterials(glTF2::Asset& r)
215{
216 mScene->mNumMaterials = unsigned(r.materials.Size());
217 mScene->mMaterials = new aiMaterial*[mScene->mNumMaterials];
218
219 for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) {
220 aiMaterial* aimat = mScene->mMaterials[i] = new aiMaterial();
221
222 Material& mat = r.materials[i];
223
224 if (!mat.name.empty()) {
225 aiString str(mat.name);
226
227 aimat->AddProperty(&str, AI_MATKEY_NAME);
228 }
229
230 SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE);
231 SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR);
232
233 SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE);
234 SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE);
235
236 SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
237
238 aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR);
239 aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR);
240
241 float roughnessAsShininess = (1 - mat.pbrMetallicRoughness.roughnessFactor) * 1000;
242 aimat->AddProperty(&roughnessAsShininess, 1, AI_MATKEY_SHININESS);
243
244 SetMaterialTextureProperty(embeddedTexIdxs, r, mat.normalTexture, aimat, aiTextureType_NORMALS);
245 SetMaterialTextureProperty(embeddedTexIdxs, r, mat.occlusionTexture, aimat, aiTextureType_LIGHTMAP);
246 SetMaterialTextureProperty(embeddedTexIdxs, r, mat.emissiveTexture, aimat, aiTextureType_EMISSIVE);
247 SetMaterialColorProperty(r, mat.emissiveFactor, aimat, AI_MATKEY_COLOR_EMISSIVE);
248
249 aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED);
250
251 aiString alphaMode(mat.alphaMode);
252 aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE);
253 aimat->AddProperty(&mat.alphaCutoff, 1, AI_MATKEY_GLTF_ALPHACUTOFF);
254
255 //pbrSpecularGlossiness
256 if (mat.pbrSpecularGlossiness.isPresent) {
257 PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value;
258
259 aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS);
260 SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE);
261 SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_COLOR_SPECULAR);
262
263 float glossinessAsShininess = pbrSG.glossinessFactor * 1000.0f;
264 aimat->AddProperty(&glossinessAsShininess, 1, AI_MATKEY_SHININESS);
265 aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR);
266
267 SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, aiTextureType_DIFFUSE);
268
269 SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, aiTextureType_SPECULAR);
270 }
271 }
272}
273
274
275static inline void SetFace(aiFace& face, int a)
276{
277 face.mNumIndices = 1;
278 face.mIndices = new unsigned int[1];
279 face.mIndices[0] = a;
280}
281
282static inline void SetFace(aiFace& face, int a, int b)
283{
284 face.mNumIndices = 2;
285 face.mIndices = new unsigned int[2];
286 face.mIndices[0] = a;
287 face.mIndices[1] = b;
288}
289
290static inline void SetFace(aiFace& face, int a, int b, int c)
291{
292 face.mNumIndices = 3;
293 face.mIndices = new unsigned int[3];
294 face.mIndices[0] = a;
295 face.mIndices[1] = b;
296 face.mIndices[2] = c;
297}
298
299#ifdef ASSIMP_BUILD_DEBUG
300static inline bool CheckValidFacesIndices(aiFace* faces, unsigned nFaces, unsigned nVerts)
301{
302 for (unsigned i = 0; i < nFaces; ++i) {
303 for (unsigned j = 0; j < faces[i].mNumIndices; ++j) {
304 unsigned idx = faces[i].mIndices[j];
305 if (idx >= nVerts)
306 return false;
307 }
308 }
309 return true;
310}
311#endif // ASSIMP_BUILD_DEBUG
312
313void glTF2Importer::ImportMeshes(glTF2::Asset& r)
314{
315 std::vector<aiMesh*> meshes;
316
317 unsigned int k = 0;
318
319 for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
320 Mesh& mesh = r.meshes[m];
321
322 meshOffsets.push_back(k);
323 k += unsigned(mesh.primitives.size());
324
325 for (unsigned int p = 0; p < mesh.primitives.size(); ++p) {
326 Mesh::Primitive& prim = mesh.primitives[p];
327
328 aiMesh* aim = new aiMesh();
329 meshes.push_back(aim);
330
331 aim->mName = mesh.name.empty() ? mesh.id : mesh.name;
332
333 if (mesh.primitives.size() > 1) {
334 size_t& len = aim->mName.length;
335 aim->mName.data[len] = '-';
336 len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(MAXLEN - len - 1), p);
337 }
338
339 switch (prim.mode) {
340 case PrimitiveMode_POINTS:
341 aim->mPrimitiveTypes |= aiPrimitiveType_POINT;
342 break;
343
344 case PrimitiveMode_LINES:
345 case PrimitiveMode_LINE_LOOP:
346 case PrimitiveMode_LINE_STRIP:
347 aim->mPrimitiveTypes |= aiPrimitiveType_LINE;
348 break;
349
350 case PrimitiveMode_TRIANGLES:
351 case PrimitiveMode_TRIANGLE_STRIP:
352 case PrimitiveMode_TRIANGLE_FAN:
353 aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
354 break;
355
356 }
357
358 Mesh::Primitive::Attributes& attr = prim.attributes;
359
360 if (attr.position.size() > 0 && attr.position[0]) {
361 aim->mNumVertices = attr.position[0]->count;
362 attr.position[0]->ExtractData(aim->mVertices);
363 }
364
365 if (attr.normal.size() > 0 && attr.normal[0]) {
366 attr.normal[0]->ExtractData(aim->mNormals);
367
368 // only extract tangents if normals are present
369 if (attr.tangent.size() > 0 && attr.tangent[0]) {
370 // generate bitangents from normals and tangents according to spec
371 struct Tangent
372 {
373 aiVector3D xyz;
374 ai_real w;
375 } *tangents = nullptr;
376
377 attr.tangent[0]->ExtractData(tangents);
378
379 aim->mTangents = new aiVector3D[aim->mNumVertices];
380 aim->mBitangents = new aiVector3D[aim->mNumVertices];
381
382 for (unsigned int i = 0; i < aim->mNumVertices; ++i) {
383 aim->mTangents[i] = tangents[i].xyz;
384 aim->mBitangents[i] = (aim->mNormals[i] ^ tangents[i].xyz) * tangents[i].w;
385 }
386
387 delete tangents;
388 }
389 }
390
391 for (size_t tc = 0; tc < attr.texcoord.size() && tc < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) {
392 attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]);
393 aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents();
394
395 aiVector3D* values = aim->mTextureCoords[tc];
396 for (unsigned int i = 0; i < aim->mNumVertices; ++i) {
397 values[i].y = 1 - values[i].y; // Flip Y coords
398 }
399 }
400
401
402 if (prim.indices) {
403 aiFace* faces = 0;
404 unsigned int nFaces = 0;
405
406 unsigned int count = prim.indices->count;
407
408 Accessor::Indexer data = prim.indices->GetIndexer();
409 ai_assert(data.IsValid());
410
411 switch (prim.mode) {
412 case PrimitiveMode_POINTS: {
413 nFaces = count;
414 faces = new aiFace[nFaces];
415 for (unsigned int i = 0; i < count; ++i) {
416 SetFace(faces[i], data.GetUInt(i));
417 }
418 break;
419 }
420
421 case PrimitiveMode_LINES: {
422 nFaces = count / 2;
423 faces = new aiFace[nFaces];
424 for (unsigned int i = 0; i < count; i += 2) {
425 SetFace(faces[i / 2], data.GetUInt(i), data.GetUInt(i + 1));
426 }
427 break;
428 }
429
430 case PrimitiveMode_LINE_LOOP:
431 case PrimitiveMode_LINE_STRIP: {
432 nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
433 faces = new aiFace[nFaces];
434 SetFace(faces[0], data.GetUInt(0), data.GetUInt(1));
435 for (unsigned int i = 2; i < count; ++i) {
436 SetFace(faces[i - 1], faces[i - 2].mIndices[1], data.GetUInt(i));
437 }
438 if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
439 SetFace(faces[count - 1], faces[count - 2].mIndices[1], faces[0].mIndices[0]);
440 }
441 break;
442 }
443
444 case PrimitiveMode_TRIANGLES: {
445 nFaces = count / 3;
446 faces = new aiFace[nFaces];
447 for (unsigned int i = 0; i < count; i += 3) {
448 SetFace(faces[i / 3], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
449 }
450 break;
451 }
452 case PrimitiveMode_TRIANGLE_STRIP: {
453 nFaces = count - 2;
454 faces = new aiFace[nFaces];
455 SetFace(faces[0], data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
456 for (unsigned int i = 3; i < count; ++i) {
457 SetFace(faces[i - 2], faces[i - 1].mIndices[1], faces[i - 1].mIndices[2], data.GetUInt(i));
458 }
459 break;
460 }
461 case PrimitiveMode_TRIANGLE_FAN:
462 nFaces = count - 2;
463 faces = new aiFace[nFaces];
464 SetFace(faces[0], data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
465 for (unsigned int i = 3; i < count; ++i) {
466 SetFace(faces[i - 2], faces[0].mIndices[0], faces[i - 1].mIndices[2], data.GetUInt(i));
467 }
468 break;
469 }
470
471 if (faces) {
472 aim->mFaces = faces;
473 aim->mNumFaces = nFaces;
474 ai_assert(CheckValidFacesIndices(faces, nFaces, aim->mNumVertices));
475 }
476 }
477
478
479 if (prim.material) {
480 aim->mMaterialIndex = prim.material.GetIndex();
481 }
482 }
483 }
484
485 meshOffsets.push_back(k);
486
487 CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes);
488}
489
490void glTF2Importer::ImportCameras(glTF2::Asset& r)
491{
492 if (!r.cameras.Size()) return;
493
494 mScene->mNumCameras = r.cameras.Size();
495 mScene->mCameras = new aiCamera*[r.cameras.Size()];
496
497 for (size_t i = 0; i < r.cameras.Size(); ++i) {
498 Camera& cam = r.cameras[i];
499
500 aiCamera* aicam = mScene->mCameras[i] = new aiCamera();
501
502 if (cam.type == Camera::Perspective) {
503
504 aicam->mAspect = cam.cameraProperties.perspective.aspectRatio;
505 aicam->mHorizontalFOV = cam.cameraProperties.perspective.yfov * aicam->mAspect;
506 aicam->mClipPlaneFar = cam.cameraProperties.perspective.zfar;
507 aicam->mClipPlaneNear = cam.cameraProperties.perspective.znear;
508 }
509 else {
510 // assimp does not support orthographic cameras
511 }
512 }
513}
514
515aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>& meshOffsets, glTF2::Ref<glTF2::Node>& ptr)
516{
517 Node& node = *ptr;
518
519 std::string nameOrId = node.name.empty() ? node.id : node.name;
520
521 aiNode* ainode = new aiNode(nameOrId);
522
523 if (!node.children.empty()) {
524 ainode->mNumChildren = unsigned(node.children.size());
525 ainode->mChildren = new aiNode*[ainode->mNumChildren];
526
527 for (unsigned int i = 0; i < ainode->mNumChildren; ++i) {
528 aiNode* child = ImportNode(pScene, r, meshOffsets, node.children[i]);
529 child->mParent = ainode;
530 ainode->mChildren[i] = child;
531 }
532 }
533
534 aiMatrix4x4& matrix = ainode->mTransformation;
535 if (node.matrix.isPresent) {
536 CopyValue(node.matrix.value, matrix);
537 }
538 else {
539 if (node.translation.isPresent) {
540 aiVector3D trans;
541 CopyValue(node.translation.value, trans);
542 aiMatrix4x4 t;
543 aiMatrix4x4::Translation(trans, t);
544 matrix = matrix * t;
545 }
546
547 if (node.rotation.isPresent) {
548 aiQuaternion rot;
549 CopyValue(node.rotation.value, rot);
550 matrix = matrix * aiMatrix4x4(rot.GetMatrix());
551 }
552
553 if (node.scale.isPresent) {
554 aiVector3D scal(1.f);
555 CopyValue(node.scale.value, scal);
556 aiMatrix4x4 s;
557 aiMatrix4x4::Scaling(scal, s);
558 matrix = matrix * s;
559 }
560 }
561
562 if (!node.meshes.empty()) {
563 int count = 0;
564 for (size_t i = 0; i < node.meshes.size(); ++i) {
565 int idx = node.meshes[i].GetIndex();
566 count += meshOffsets[idx + 1] - meshOffsets[idx];
567 }
568 ainode->mNumMeshes = count;
569
570 ainode->mMeshes = new unsigned int[count];
571
572 int k = 0;
573 for (size_t i = 0; i < node.meshes.size(); ++i) {
574 int idx = node.meshes[i].GetIndex();
575 for (unsigned int j = meshOffsets[idx]; j < meshOffsets[idx + 1]; ++j, ++k) {
576 ainode->mMeshes[k] = j;
577 }
578 }
579 }
580
581 if (node.camera) {
582 pScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName;
583 }
584
585 return ainode;
586}
587
588void glTF2Importer::ImportNodes(glTF2::Asset& r)
589{
590 if (!r.scene) return;
591
592 std::vector< Ref<Node> > rootNodes = r.scene->nodes;
593
594 // The root nodes
595 unsigned int numRootNodes = unsigned(rootNodes.size());
596 if (numRootNodes == 1) { // a single root node: use it
597 mScene->mRootNode = ImportNode(mScene, r, meshOffsets, rootNodes[0]);
598 }
599 else if (numRootNodes > 1) { // more than one root node: create a fake root
600 aiNode* root = new aiNode("ROOT");
601 root->mChildren = new aiNode*[numRootNodes];
602 for (unsigned int i = 0; i < numRootNodes; ++i) {
603 aiNode* node = ImportNode(mScene, r, meshOffsets, rootNodes[i]);
604 node->mParent = root;
605 root->mChildren[root->mNumChildren++] = node;
606 }
607 mScene->mRootNode = root;
608 }
609
610 //if (!mScene->mRootNode) {
611 // mScene->mRootNode = new aiNode("EMPTY");
612 //}
613}
614
615void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset& r)
616{
617 embeddedTexIdxs.resize(r.images.Size(), -1);
618
619 int numEmbeddedTexs = 0;
620 for (size_t i = 0; i < r.images.Size(); ++i) {
621 if (r.images[i].HasData())
622 numEmbeddedTexs += 1;
623 }
624
625 if (numEmbeddedTexs == 0)
626 return;
627
628 mScene->mTextures = new aiTexture*[numEmbeddedTexs];
629
630 // Add the embedded textures
631 for (size_t i = 0; i < r.images.Size(); ++i) {
632 Image img = r.images[i];
633 if (!img.HasData()) continue;
634
635 int idx = mScene->mNumTextures++;
636 embeddedTexIdxs[i] = idx;
637
638 aiTexture* tex = mScene->mTextures[idx] = new aiTexture();
639
640 size_t length = img.GetDataLength();
641 void* data = img.StealData();
642
643 tex->mWidth = static_cast<unsigned int>(length);
644 tex->mHeight = 0;
645 tex->pcData = reinterpret_cast<aiTexel*>(data);
646
647 if (!img.mimeType.empty()) {
648 const char* ext = strchr(img.mimeType.c_str(), '/') + 1;
649 if (ext) {
650 if (strcmp(ext, "jpeg") == 0) ext = "jpg";
651
652 size_t len = strlen(ext);
653 if (len <= 3) {
654 strcpy(tex->achFormatHint, ext);
655 }
656 }
657 }
658 }
659}
660
661void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) {
662
663 this->mScene = pScene;
664
665 // read the asset file
666 glTF2::Asset asset(pIOHandler);
667 asset.Load(pFile, GetExtension(pFile) == "glb");
668
669 //
670 // Copy the data out
671 //
672
673 ImportEmbeddedTextures(asset);
674 ImportMaterials(asset);
675
676 ImportMeshes(asset);
677
678 ImportCameras(asset);
679
680 ImportNodes(asset);
681
682 // TODO: it does not split the loaded vertices, should it?
683 //pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
684 MakeVerboseFormatProcess process;
685 process.Execute(pScene);
686
687
688 if (pScene->mNumMeshes == 0) {
689 pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
690 }
691}
692
693#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
694
695