1 // Author: Kirill Gavrilov
2 // Copyright (c) 2016-2019 OPEN CASCADE SAS
4 // This file is part of Open CASCADE Technology software library.
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
15 #include "RWGltf_GltfJsonParser.pxx"
17 #include <BRep_Builder.hxx>
18 #include <gp_Quaternion.hxx>
19 #include <Message.hxx>
20 #include <Message_Messenger.hxx>
21 #include <Message_ProgressSentry.hxx>
22 #include <OSD_File.hxx>
23 #include <OSD_OpenFile.hxx>
24 #include <OSD_Path.hxx>
25 #include <OSD_ThreadPool.hxx>
26 #include <Precision.hxx>
27 #include <FSD_Base64Decoder.hxx>
28 #include <TopExp_Explorer.hxx>
30 #include <TopoDS_Iterator.hxx>
37 //! Material extension.
38 const char THE_KHR_materials_common[] = "KHR_materials_common";
39 const char THE_KHR_binary_glTF[] = "KHR_binary_glTF";
42 //! Find member of the object in a safe way.
43 inline const RWGltf_JsonValue* findObjectMember (const RWGltf_JsonValue& theObject,
44 const RWGltf_JsonValue& theName)
46 if (!theObject.IsObject()
47 || !theName.IsString())
52 rapidjson::Document::ConstMemberIterator anIter = theObject.FindMember (theName);
53 return anIter != theObject.MemberEnd()
58 //! Find member of the object in a safe way.
59 inline const RWGltf_JsonValue* findObjectMember (const RWGltf_JsonValue& theObject,
62 if (!theObject.IsObject())
67 rapidjson::Document::ConstMemberIterator anIter = theObject.FindMember (theName);
68 return anIter != theObject.MemberEnd()
73 // =======================================================================
74 // function : RWGltf_GltfJsonParser::FormatParseError
76 // =======================================================================
77 const char* RWGltf_GltfJsonParser::FormatParseError (rapidjson::ParseErrorCode theCode)
81 case rapidjson::kParseErrorNone: return "";
82 case rapidjson::kParseErrorDocumentEmpty: return "Empty Document";
83 case rapidjson::kParseErrorDocumentRootNotSingular: return "The document root must not follow by other values";
84 case rapidjson::kParseErrorValueInvalid: return "Invalid value";
85 case rapidjson::kParseErrorObjectMissName: return "Missing a name for object member";
86 case rapidjson::kParseErrorObjectMissColon: return "Missing a colon after a name of object member";
87 case rapidjson::kParseErrorObjectMissCommaOrCurlyBracket: return "Missing a comma or '}' after an object member";
88 case rapidjson::kParseErrorArrayMissCommaOrSquareBracket: return "Missing a comma or ']' after an array element";
89 case rapidjson::kParseErrorStringUnicodeEscapeInvalidHex: return "Incorrect hex digit after \\u escape in string";
90 case rapidjson::kParseErrorStringUnicodeSurrogateInvalid: return "The surrogate pair in string is invalid";
91 case rapidjson::kParseErrorStringEscapeInvalid: return "Invalid escape character in string";
92 case rapidjson::kParseErrorStringMissQuotationMark: return "Missing a closing quotation mark in string";
93 case rapidjson::kParseErrorStringInvalidEncoding: return "Invalid encoding in string";
94 case rapidjson::kParseErrorNumberTooBig: return "Number is too big to be stored in double";
95 case rapidjson::kParseErrorNumberMissFraction: return "Miss fraction part in number";
96 case rapidjson::kParseErrorNumberMissExponent: return "Miss exponent in number";
97 case rapidjson::kParseErrorTermination: return "Parsing was terminated";
98 case rapidjson::kParseErrorUnspecificSyntaxError: return "Unspecific syntax error";
100 return "UNKOWN syntax error";
103 // =======================================================================
104 // function : GltfElementMap::Init
106 // =======================================================================
107 void RWGltf_GltfJsonParser::GltfElementMap::Init (const TCollection_AsciiString& theRootName,
108 const RWGltf_JsonValue* theRoot)
117 if (theRoot->IsObject())
120 for (ConstMemberIterator aChildIter = theRoot->MemberBegin(); aChildIter != theRoot->MemberEnd(); ++aChildIter)
122 if (!aChildIter->name.IsString())
127 const TCollection_AsciiString aKey (aChildIter->name.GetString());
128 if (!myChildren.Bind (aKey, &aChildIter->value))
130 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Invalid glTF syntax - key '")
131 + aKey + "' is already defined in '" + theRootName + "'.", Message_Warning);
135 else if (theRoot->IsArray())
139 for (rapidjson::Value::ConstValueIterator aChildIter = theRoot->Begin(); aChildIter != theRoot->End(); ++aChildIter, ++aChildIndex)
141 myChildren.Bind (TCollection_AsciiString (aChildIndex), aChildIter);
147 // Auxiliary macros for formatting message.
148 #define reportGltfError(theMsg) reportGltfSyntaxProblem(TCollection_AsciiString() + theMsg, Message_Fail);
149 #define reportGltfWarning(theMsg) reportGltfSyntaxProblem(TCollection_AsciiString() + theMsg, Message_Warning);
151 // =======================================================================
152 // function : reportGltfSyntaxProblem
154 // =======================================================================
155 void RWGltf_GltfJsonParser::reportGltfSyntaxProblem (const TCollection_AsciiString& theMsg,
156 Message_Gravity theGravity)
158 Message::DefaultMessenger()->Send (myErrorPrefix + theMsg, theGravity);
161 // =======================================================================
162 // function : RWGltf_GltfJsonParser
164 // =======================================================================
165 RWGltf_GltfJsonParser::RWGltf_GltfJsonParser (TopTools_SequenceOfShape& theRootShapes)
166 : myRootShapes(&theRootShapes),
168 myExternalFiles (NULL),
173 myToSkipEmptyNodes (true),
174 myUseMeshNameAsFallback (true),
175 myToProbeHeader (false)
177 myCSTrsf.SetInputLengthUnit (1.0); // meters
178 myCSTrsf.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
181 // =======================================================================
182 // function : SetFilePath
184 // =======================================================================
185 void RWGltf_GltfJsonParser::SetFilePath (const TCollection_AsciiString& theFilePath)
187 myFilePath = theFilePath;
188 // determine file location to load associated files
189 TCollection_AsciiString aFileName;
190 OSD_Path::FolderAndFileFromPath (theFilePath, myFolder, aFileName);
193 #ifdef HAVE_RAPIDJSON
194 // =======================================================================
195 // function : gltfParseRoots
197 // =======================================================================
198 bool RWGltf_GltfJsonParser::gltfParseRoots()
200 // find glTF root elements for smooth navigation
201 RWGltf_JsonValue aNames[RWGltf_GltfRootElement_NB];
202 for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
204 aNames[aRootNameIter] = rapidjson::StringRef (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter));
207 for (ConstMemberIterator aRootIter = MemberBegin();
208 aRootIter != MemberEnd(); ++aRootIter)
210 for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
212 if (myGltfRoots[aRootNameIter].IsNull()
213 && aNames[aRootNameIter] == aRootIter->name)
215 // we will not modify JSON document, thus it is OK to keep the pointers
216 myGltfRoots[aRootNameIter].Init (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter), &aRootIter->value);
222 for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB_MANDATORY; ++aRootNameIter)
224 if (myGltfRoots[aRootNameIter].IsNull())
226 reportGltfError ("Member '" + RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter) + "' is not found.");
233 // =======================================================================
234 // function : gltfParseAsset
236 // =======================================================================
237 void RWGltf_GltfJsonParser::gltfParseAsset()
239 const RWGltf_JsonValue* anAsset = myGltfRoots[RWGltf_GltfRootElement_Asset].Root();
245 if (const RWGltf_JsonValue* aVersion = findObjectMember (*anAsset, "version"))
247 if (aVersion->IsString())
249 TCollection_AsciiString aVerStr (aVersion->GetString());
250 myIsGltf1 = aVerStr.StartsWith ("1.");
254 if (myMetadata == NULL)
259 if (const RWGltf_JsonValue* aGenerator = findObjectMember (*anAsset, "generator"))
261 if (aGenerator->IsString())
263 myMetadata->Add ("generator", aGenerator->GetString());
266 if (const RWGltf_JsonValue* aCopyRight = findObjectMember (*anAsset, "copyright"))
268 if (aCopyRight->IsString())
270 myMetadata->Add ("copyright", aCopyRight->GetString());
275 // =======================================================================
276 // function : gltfParseMaterials
278 // =======================================================================
279 void RWGltf_GltfJsonParser::gltfParseMaterials()
281 const RWGltf_JsonValue* aMatList = myGltfRoots[RWGltf_GltfRootElement_Materials].Root();
282 if (aMatList == NULL)
286 else if (aMatList->IsObject())
289 for (ConstMemberIterator aMatIter = aMatList->MemberBegin();
290 aMatIter != aMatList->MemberEnd(); ++aMatIter)
292 Handle(RWGltf_MaterialCommon) aMat;
293 const RWGltf_JsonValue& aMatNode = aMatIter->value;
294 const RWGltf_JsonValue& aMatId = aMatIter->name;
295 const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
296 if (!gltfParseCommonMaterial (aMat, aMatNode))
298 if (!gltfParseStdMaterial (aMat, aMatNode))
305 && aNameVal->IsString())
307 aMat->Name = aNameVal->GetString();
309 aMat->Id = aMatId.GetString();
310 myMaterialsCommon.Bind (aMat->Id, aMat);
313 else if (aMatList->IsArray())
317 for (rapidjson::Value::ConstValueIterator aMatIter = aMatList->Begin(); aMatIter != aMatList->End(); ++aMatIter, ++aMatIndex)
319 Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
320 const RWGltf_JsonValue& aMatNode = *aMatIter;
321 const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
322 if (gltfParsePbrMaterial (aMatPbr, aMatNode))
325 && aNameVal->IsString())
327 aMatPbr->Name = aNameVal->GetString();
329 aMatPbr->Id = TCollection_AsciiString ("mat_") + aMatIndex;
330 myMaterialsPbr.Bind (TCollection_AsciiString (aMatIndex), aMatPbr);
333 Handle(RWGltf_MaterialCommon) aMatCommon;
334 if (gltfParseCommonMaterial(aMatCommon, aMatNode)
335 || gltfParseStdMaterial (aMatCommon, aMatNode))
338 && aNameVal->IsString())
340 aMatCommon->Name = aNameVal->GetString();
342 aMatCommon->Id = TCollection_AsciiString ("mat_") + aMatIndex;
343 myMaterialsCommon.Bind (TCollection_AsciiString (aMatIndex), aMatCommon);
349 // =======================================================================
350 // function : gltfParseStdMaterial
352 // =======================================================================
353 bool RWGltf_GltfJsonParser::gltfParseStdMaterial (Handle(RWGltf_MaterialCommon)& theMat,
354 const RWGltf_JsonValue& theMatNode)
356 //const RWGltf_JsonValue* aTechVal = findObjectMember (theMatNode, "technique");
357 const RWGltf_JsonValue* aValues = findObjectMember (theMatNode, "values");
363 const RWGltf_JsonValue* anAmbVal = findObjectMember (*aValues, "ambient");
364 const RWGltf_JsonValue* aDiffVal = findObjectMember (*aValues, "diffuse");
365 const RWGltf_JsonValue* anEmiVal = findObjectMember (*aValues, "emission");
366 const RWGltf_JsonValue* aSpecVal = findObjectMember (*aValues, "specular");
367 const RWGltf_JsonValue* aShinVal = findObjectMember (*aValues, "shininess");
377 theMat = new RWGltf_MaterialCommon();
379 Graphic3d_Vec4d anAmb, aDiff, anEmi, aSpec;
381 && anAmbVal->IsString())
383 gltfParseTexture (theMat->AmbientTexture, anAmbVal);
385 else if (gltfReadVec4 (anAmb, anAmbVal)
386 && validateColor4 (anAmb))
388 theMat->AmbientColor = Quantity_Color (anAmb.r(), anAmb.g(), anAmb.b(), Quantity_TOC_sRGB);
392 && aDiffVal->IsString())
394 gltfParseTexture (theMat->DiffuseTexture, aDiffVal);
396 else if (gltfReadVec4 (aDiff, aDiffVal)
397 && validateColor4 (aDiff))
399 theMat->DiffuseColor = Quantity_Color (aDiff.r(), aDiff.g(), aDiff.b(), Quantity_TOC_sRGB);
400 theMat->Transparency = float(1.0 - aDiff.a());
403 if (gltfReadVec4 (anEmi, anEmiVal)
404 && validateColor4 (anEmi))
406 theMat->EmissiveColor = Quantity_Color (anEmi.r(), anEmi.g(), anEmi.b(), Quantity_TOC_sRGB);
410 && aSpecVal->IsString())
412 gltfParseTexture (theMat->SpecularTexture, aSpecVal);
414 if (gltfReadVec4 (aSpec, aSpecVal)
415 && validateColor4 (aSpec))
417 theMat->SpecularColor = Quantity_Color (aSpec.r(), aSpec.g(), aSpec.b(), Quantity_TOC_sRGB);
421 && aShinVal->IsNumber())
423 const double aSpecular = aShinVal->GetDouble();
426 theMat->Shininess = (float )Min (aSpecular / 1000.0, 1.0);
432 // =======================================================================
433 // function : gltfParsePbrMaterial
435 // =======================================================================
436 bool RWGltf_GltfJsonParser::gltfParsePbrMaterial (Handle(RWGltf_MaterialMetallicRoughness)& theMat,
437 const RWGltf_JsonValue& theMatNode)
439 /*if (const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions"))
441 if (const RWGltf_JsonValue* anExtDefVal = findObjectMember (*anExtVal, "KHR_materials_pbrSpecularGlossiness"))
443 const RWGltf_JsonValue* aDiffTexVal = findObjectMember (*anExtDefVal, "diffuseTexture");
444 const RWGltf_JsonValue* aSpecTexVal = findObjectMember (*anExtDefVal, "specularGlossinessTexture");
448 const RWGltf_JsonValue* aMetalRoughVal = findObjectMember (theMatNode, "pbrMetallicRoughness");
449 const RWGltf_JsonValue* aNormTexVal = findObjectMember (theMatNode, "normalTexture");
450 const RWGltf_JsonValue* anEmissFactorVal = findObjectMember (theMatNode, "emissiveFactor");
451 const RWGltf_JsonValue* anEmissTexVal = findObjectMember (theMatNode, "emissiveTexture");
452 const RWGltf_JsonValue* anOcclusionTexVal = findObjectMember (theMatNode, "occlusionTexture");
453 if (aMetalRoughVal == NULL)
458 theMat = new RWGltf_MaterialMetallicRoughness();
459 const RWGltf_JsonValue* aBaseColorFactorVal = findObjectMember (*aMetalRoughVal, "baseColorFactor");
460 const RWGltf_JsonValue* aBaseColorTexVal = findObjectMember (*aMetalRoughVal, "baseColorTexture");
461 const RWGltf_JsonValue* aMetallicFactorVal = findObjectMember (*aMetalRoughVal, "metallicFactor");
462 const RWGltf_JsonValue* aRoughnessFactorVal = findObjectMember (*aMetalRoughVal, "roughnessFactor");
463 const RWGltf_JsonValue* aMetalRoughTexVal = findObjectMember (*aMetalRoughVal, "metallicRoughnessTexture");
465 if (aBaseColorTexVal != NULL
466 && aBaseColorTexVal->IsObject())
468 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aBaseColorTexVal, "index"))
470 gltfParseTexture (theMat->BaseColorTexture, aTexIndexVal);
474 Graphic3d_Vec4d aBaseColorFactor;
475 if (gltfReadVec4 (aBaseColorFactor, aBaseColorFactorVal)
476 && validateColor4 (aBaseColorFactor))
478 theMat->BaseColor = Quantity_ColorRGBA (Graphic3d_Vec4 (aBaseColorFactor));
481 Graphic3d_Vec3d anEmissiveFactor;
482 if (gltfReadVec3 (anEmissiveFactor, anEmissFactorVal)
483 && validateColor3 (anEmissiveFactor))
485 theMat->EmissiveFactor = Graphic3d_Vec3 (anEmissiveFactor);
488 if (aMetalRoughTexVal != NULL
489 && aMetalRoughTexVal->IsObject())
491 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aMetalRoughTexVal, "index"))
493 gltfParseTexture (theMat->MetallicRoughnessTexture, aTexIndexVal);
497 if (aMetallicFactorVal != NULL
498 && aMetallicFactorVal->IsNumber())
500 theMat->Metallic = (float )aMetallicFactorVal->GetDouble();
503 if (aRoughnessFactorVal != NULL
504 && aRoughnessFactorVal->IsNumber())
506 theMat->Roughness = (float )aRoughnessFactorVal->GetDouble();
509 if (aNormTexVal != NULL
510 && aNormTexVal->IsObject())
512 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aNormTexVal, "index"))
514 gltfParseTexture (theMat->NormalTexture, aTexIndexVal);
518 if (anEmissTexVal != NULL
519 && anEmissTexVal->IsObject())
521 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anEmissTexVal, "index"))
523 gltfParseTexture (theMat->EmissiveTexture, aTexIndexVal);
527 if (anOcclusionTexVal != NULL
528 && anOcclusionTexVal->IsObject())
530 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anOcclusionTexVal, "index"))
532 gltfParseTexture (theMat->OcclusionTexture, aTexIndexVal);
538 // =======================================================================
539 // function : gltfParseCommonMaterial
541 // =======================================================================
542 bool RWGltf_GltfJsonParser::gltfParseCommonMaterial (Handle(RWGltf_MaterialCommon)& theMat,
543 const RWGltf_JsonValue& theMatNode)
545 const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions");
546 if (anExtVal == NULL)
551 const RWGltf_JsonValue* aMatCommon = findObjectMember (*anExtVal, THE_KHR_materials_common);
552 if (aMatCommon == NULL)
557 if (!gltfParseStdMaterial (theMat, *aMatCommon))
564 // =======================================================================
565 // function : gltfParseTexture
567 // =======================================================================
568 bool RWGltf_GltfJsonParser::gltfParseTexture (Handle(Image_Texture)& theTexture,
569 const RWGltf_JsonValue* theTextureId)
571 if (theTextureId == NULL
572 || myGltfRoots[RWGltf_GltfRootElement_Textures].IsNull()
573 || myGltfRoots[RWGltf_GltfRootElement_Images].IsNull())
578 const TCollection_AsciiString aTextureId = getKeyString (*theTextureId);
579 const RWGltf_JsonValue* aTexNode = myGltfRoots[RWGltf_GltfRootElement_Textures].FindChild (*theTextureId);
580 if (aTexNode == NULL)
582 reportGltfWarning ("Texture node '" + aTextureId + "' is not found.");
586 const RWGltf_JsonValue* aSrcVal = findObjectMember (*aTexNode, "source");
587 const RWGltf_JsonValue* aTargVal = findObjectMember (*aTexNode, "target");
590 reportGltfWarning ("Invalid texture node '" + aTextureId + "' without a 'source' property.");
594 && aTargVal->IsNumber()
595 && aTargVal->GetInt() != 3553) // GL_TEXTURE_2D
600 const RWGltf_JsonValue* anImgNode = myGltfRoots[RWGltf_GltfRootElement_Images].FindChild (*aSrcVal);
601 if (anImgNode == NULL)
603 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to non-existing image '" + getKeyString (*aSrcVal) + "'.");
609 const RWGltf_JsonValue* aBinVal = NULL;
610 const RWGltf_JsonValue* aBufferViewName = findObjectMember (*anImgNode, "bufferView");
611 if (aBufferViewName != NULL)
617 const RWGltf_JsonValue* anExtVal = findObjectMember (*anImgNode, "extensions");
618 if (anExtVal != NULL)
620 aBinVal = findObjectMember (*anExtVal, THE_KHR_binary_glTF);
623 aBufferViewName = findObjectMember (*aBinVal, "bufferView");
630 //const RWGltf_JsonValue* aMimeTypeVal = findObjectMember (*aBinVal, "mimeType");
631 //const RWGltf_JsonValue* aWidthVal = findObjectMember (*aBinVal, "width");
632 //const RWGltf_JsonValue* aHeightVal = findObjectMember (*aBinVal, "height");
633 if (aBufferViewName == NULL)
635 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
639 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
640 if (aBufferView == NULL
641 || !aBufferView->IsObject())
643 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid buffer view '" + getKeyString (*aBufferViewName) + "'.");
647 const RWGltf_JsonValue* aBufferName = findObjectMember (*aBufferView, "buffer");
648 const RWGltf_JsonValue* aByteLength = findObjectMember (*aBufferView, "byteLength");
649 const RWGltf_JsonValue* aByteOffset = findObjectMember (*aBufferView, "byteOffset");
650 if (aBufferName != NULL
651 && aBufferName->IsString()
652 && !IsEqual (aBufferName->GetString(), "binary_glTF"))
654 reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' does not define binary_glTF buffer.");
658 RWGltf_GltfBufferView aBuffView;
659 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
660 ? (int64_t )aByteOffset->GetDouble()
662 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
663 ? (int64_t )aByteLength->GetDouble()
665 if (aBuffView.ByteLength < 0)
667 reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' defines invalid byteLength.");
670 else if (aBuffView.ByteOffset < 0)
672 reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' defines invalid byteOffset.");
677 const int64_t anOffset = myBinBodyOffset + aBuffView.ByteOffset;
678 theTexture = new Image_Texture (myFilePath, anOffset, aBuffView.ByteLength);
683 const RWGltf_JsonValue* anUriVal = findObjectMember (*anImgNode, "uri");
685 || !anUriVal->IsString())
690 const char* anUriData = anUriVal->GetString();
691 if (::strncmp (anUriData, "data:", 5) == 0) // data:image/png;base64
693 // uncompressing base64 here is inefficient, because the same image can be shared by several nodes
694 const char* aDataStart = anUriData + 5;
695 for (const char* aDataIter = aDataStart; *aDataIter != '\0'; ++aDataIter)
697 if (::memcmp (aDataIter, ";base64,", 8) == 0)
699 const char* aBase64End = anUriData + anUriVal->GetStringLength();
700 const char* aBase64Data = aDataIter + 8;
701 const size_t aBase64Len = size_t(aBase64End - aBase64Data);
702 //const TCollection_AsciiString aMime (aDataStart, aDataIter - aDataStart);
703 Handle(NCollection_Buffer) aData = FSD_Base64Decoder::Decode ((const Standard_Byte* )aBase64Data, aBase64Len);
704 theTexture = new Image_Texture (aData, myFilePath + "@" + getKeyString (*aSrcVal));
708 Message::DefaultMessenger()->Send ("glTF reader - embedded image has been skipped", Message_Warning);
712 TCollection_AsciiString anImageFile = myFolder + anUriVal->GetString();
713 theTexture = new Image_Texture (anImageFile);
714 if (myExternalFiles != NULL)
716 myExternalFiles->Add (anImageFile);
721 // =======================================================================
722 // function : gltfParseScene
724 // =======================================================================
725 bool RWGltf_GltfJsonParser::gltfParseScene (const Handle(Message_ProgressIndicator)& theProgress)
727 // search default scene
728 const RWGltf_JsonValue* aDefScene = myGltfRoots[RWGltf_GltfRootElement_Scenes].FindChild (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root());
729 if (aDefScene == NULL)
731 reportGltfError ("Default scene is not found.");
735 const RWGltf_JsonValue* aSceneNodes = findObjectMember (*aDefScene, "nodes");
736 if (aSceneNodes == NULL
737 || !aSceneNodes->IsArray())
739 reportGltfError ("Empty scene '" + getKeyString (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root()) + "'.");
743 return gltfParseSceneNodes (*myRootShapes, *aSceneNodes, theProgress);
746 // =======================================================================
747 // function : gltfParseSceneNodes
749 // =======================================================================
750 bool RWGltf_GltfJsonParser::gltfParseSceneNodes (TopTools_SequenceOfShape& theShapeSeq,
751 const RWGltf_JsonValue& theSceneNodes,
752 const Handle(Message_ProgressIndicator)& theProgress)
754 if (!theSceneNodes.IsArray())
756 reportGltfError ("Scene nodes is not array.");
760 Message_ProgressSentry aPSentry (theProgress, "Reading scene nodes", 0, theSceneNodes.Size(), 1);
761 for (rapidjson::Value::ConstValueIterator aSceneNodeIter = theSceneNodes.Begin();
762 aSceneNodeIter != theSceneNodes.End() && aPSentry.More(); ++aSceneNodeIter, aPSentry.Next())
764 const RWGltf_JsonValue* aSceneNode = myGltfRoots[RWGltf_GltfRootElement_Nodes].FindChild (*aSceneNodeIter);
765 if (aSceneNode == NULL)
767 reportGltfWarning ("Scene refers to non-existing node '" + getKeyString (*aSceneNodeIter) + "'.");
771 TopoDS_Shape aNodeShape;
772 if (!gltfParseSceneNode (aNodeShape, getKeyString (*aSceneNodeIter), *aSceneNode, theProgress))
777 if (aNodeShape.IsNull())
781 else if (myToSkipEmptyNodes
782 && !TopExp_Explorer (aNodeShape, TopAbs_FACE).More())
787 theShapeSeq.Append (aNodeShape);
792 // =======================================================================
793 // function : gltfParseSceneNode
795 // =======================================================================
796 bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
797 const TCollection_AsciiString& theSceneNodeId,
798 const RWGltf_JsonValue& theSceneNode,
799 const Handle(Message_ProgressIndicator)& theProgress)
801 const RWGltf_JsonValue* aName = findObjectMember (theSceneNode, "name");
802 //const RWGltf_JsonValue* aJointName = findObjectMember (theSceneNode, "jointName");
803 const RWGltf_JsonValue* aChildren = findObjectMember (theSceneNode, "children");
804 const RWGltf_JsonValue* aMeshes_1 = findObjectMember (theSceneNode, "meshes");
805 const RWGltf_JsonValue* aMesh_2 = findObjectMember (theSceneNode, "mesh");
806 //const RWGltf_JsonValue* aCamera = findObjectMember (theSceneNode, "camera");
807 const RWGltf_JsonValue* aTrsfMatVal = findObjectMember (theSceneNode, "matrix");
808 const RWGltf_JsonValue* aTrsfRotVal = findObjectMember (theSceneNode, "rotation");
809 const RWGltf_JsonValue* aTrsfScaleVal = findObjectMember (theSceneNode, "scale");
810 const RWGltf_JsonValue* aTrsfTransVal = findObjectMember (theSceneNode, "translation");
811 if (findNodeShape (theNodeShape, theSceneNodeId))
816 TopLoc_Location aNodeLoc;
817 const bool hasTrs = aTrsfRotVal != NULL
818 || aTrsfScaleVal != NULL
819 || aTrsfTransVal != NULL;
820 if (aTrsfMatVal != NULL)
824 reportGltfError ("Scene node '" + theSceneNodeId + "' defines ambiguous transformation.");
827 else if (!aTrsfMatVal->IsArray()
828 || aTrsfMatVal->Size() != 16)
830 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix array.");
834 Graphic3d_Mat4d aMat4;
835 for (int aColIter = 0; aColIter < 4; ++aColIter)
837 for (int aRowIter = 0; aRowIter < 4; ++aRowIter)
839 const RWGltf_JsonValue& aGenVal = (*aTrsfMatVal)[aColIter * 4 + aRowIter];
840 if (!aGenVal.IsNumber())
842 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix.");
845 aMat4.SetValue (aRowIter, aColIter, aGenVal.GetDouble());
849 if (!aMat4.IsIdentity())
852 aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
853 aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
854 aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
855 myCSTrsf.TransformTransformation (aTrsf);
856 if (aTrsf.Form() != gp_Identity)
858 aNodeLoc = TopLoc_Location (aTrsf);
865 if (aTrsfRotVal != NULL)
867 if (!aTrsfRotVal->IsArray()
868 || aTrsfRotVal->Size() != 4)
870 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation quaternion.");
874 Graphic3d_Vec4d aRotVec4;
875 for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
877 const RWGltf_JsonValue& aGenVal = (*aTrsfRotVal)[aCompIter];
878 if (!aGenVal.IsNumber())
880 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation.");
883 aRotVec4[aCompIter] = aGenVal.GetDouble();
885 const gp_Quaternion aQuaternion (aRotVec4.x(), aRotVec4.y(), aRotVec4.z(), aRotVec4.w());
886 if (Abs (aQuaternion.X()) > gp::Resolution()
887 || Abs (aQuaternion.Y()) > gp::Resolution()
888 || Abs (aQuaternion.Z()) > gp::Resolution()
889 || Abs (aQuaternion.W() - 1.0) > gp::Resolution())
891 aTrsf.SetRotation (aQuaternion);
895 if (aTrsfTransVal != NULL)
897 if (!aTrsfTransVal->IsArray()
898 || aTrsfTransVal->Size() != 3)
900 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation vector.");
905 for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
907 const RWGltf_JsonValue& aGenVal = (*aTrsfTransVal)[aCompIter];
908 if (!aGenVal.IsNumber())
910 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation.");
913 aTransVec.SetCoord (aCompIter + 1, aGenVal.GetDouble());
915 aTrsf.SetTranslationPart (aTransVec);
918 if (aTrsfScaleVal != NULL)
920 Graphic3d_Vec3d aScaleVec;
921 if (!aTrsfScaleVal->IsArray()
922 || aTrsfScaleVal->Size() != 3)
924 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale vector.");
927 for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
929 const RWGltf_JsonValue& aGenVal = (*aTrsfScaleVal)[aCompIter];
930 if (!aGenVal.IsNumber())
932 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
935 aScaleVec[aCompIter] = aGenVal.GetDouble();
936 if (Abs (aScaleVec[aCompIter]) <= gp::Resolution())
938 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
943 if (Abs (aScaleVec.x() - aScaleVec.y()) > Precision::Confusion()
944 || Abs (aScaleVec.y() - aScaleVec.z()) > Precision::Confusion()
945 || Abs (aScaleVec.x() - aScaleVec.z()) > Precision::Confusion())
947 Graphic3d_Mat4d aScaleMat;
948 aScaleMat.SetDiagonal (aScaleVec);
950 Graphic3d_Mat4d aMat4;
951 aTrsf.GetMat4 (aMat4);
953 aMat4 = aMat4 * aScaleMat;
955 aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
956 aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
957 aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
959 Message::DefaultMessenger()->Send (TCollection_AsciiString ("glTF reader, scene node '")
960 + theSceneNodeId + "' defines unsupported scaling "
961 + aScaleVec.x() + " " + aScaleVec.y() + " " + aScaleVec.z(), Message_Warning);
963 else if (Abs (aScaleVec.x() - 1.0) > Precision::Confusion())
965 aTrsf.SetScaleFactor (aScaleVec.x());
969 myCSTrsf.TransformTransformation (aTrsf);
970 if (aTrsf.Form() != gp_Identity)
972 aNodeLoc = TopLoc_Location (aTrsf);
976 BRep_Builder aBuilder;
977 TopoDS_Compound aNodeShape;
978 aBuilder.MakeCompound (aNodeShape);
979 TopTools_SequenceOfShape aChildShapes;
980 int aNbSubShapes = 0;
981 if (aChildren != NULL
982 && !gltfParseSceneNodes (aChildShapes, *aChildren, theProgress))
984 theNodeShape = aNodeShape;
985 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
988 for (TopTools_SequenceOfShape::Iterator aChildShapeIter (aChildShapes); aChildShapeIter.More(); aChildShapeIter.Next())
990 aBuilder.Add (aNodeShape, aChildShapeIter.Value());
994 if (aMeshes_1 != NULL
995 && aMeshes_1->IsArray())
998 Message_ProgressSentry aPSentry (theProgress, "Reading scene meshes", 0, aMeshes_1->Size(), 1);
999 for (rapidjson::Value::ConstValueIterator aMeshIter = aMeshes_1->Begin();
1000 aMeshIter != aMeshes_1->End() && aPSentry.More(); ++aMeshIter, aPSentry.Next())
1002 const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMeshIter);
1005 theNodeShape = aNodeShape;
1006 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1007 reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1011 TopoDS_Shape aMeshShape;
1012 if (!gltfParseMesh (aMeshShape, getKeyString (*aMeshIter), *aMesh, theProgress))
1014 theNodeShape = aNodeShape;
1015 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1018 if (!aMeshShape.IsNull())
1020 aBuilder.Add (aNodeShape, aMeshShape);
1025 if (aMesh_2 != NULL)
1028 const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMesh_2);
1031 theNodeShape = aNodeShape;
1032 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1033 reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1037 TopoDS_Shape aMeshShape;
1038 if (!gltfParseMesh (aMeshShape, getKeyString (*aMesh_2), *aMesh, theProgress))
1040 theNodeShape = aNodeShape;
1041 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1044 if (!aMeshShape.IsNull())
1046 aBuilder.Add (aNodeShape, aMeshShape);
1051 if (aNbSubShapes == 1)
1053 theNodeShape = TopoDS_Iterator (aNodeShape).Value();
1057 theNodeShape = aNodeShape;
1059 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1063 // =======================================================================
1064 // function : gltfParseMesh
1066 // =======================================================================
1067 bool RWGltf_GltfJsonParser::gltfParseMesh (TopoDS_Shape& theMeshShape,
1068 const TCollection_AsciiString& theMeshId,
1069 const RWGltf_JsonValue& theMesh,
1070 const Handle(Message_ProgressIndicator)& theProgress)
1072 const RWGltf_JsonValue* aName = findObjectMember (theMesh, "name");
1073 const RWGltf_JsonValue* aPrims = findObjectMember (theMesh, "primitives");
1074 if (!aPrims->IsArray())
1076 reportGltfError ("Primitive array attributes within Mesh '" + theMeshId + "' is not an array.");
1080 if (findMeshShape (theMeshShape, theMeshId))
1085 BRep_Builder aBuilder;
1086 TopoDS_Compound aMeshShape;
1088 for (rapidjson::Value::ConstValueIterator aPrimArrIter = aPrims->Begin();
1089 aPrimArrIter != aPrims->End(); ++aPrimArrIter)
1091 TCollection_AsciiString aUserName;
1093 && aName->IsString())
1095 aUserName = aName->GetString();
1098 Handle(RWGltf_GltfLatePrimitiveArray) aMeshData = new RWGltf_GltfLatePrimitiveArray (theMeshId, aUserName);
1099 if (!gltfParsePrimArray (aMeshData, theMeshId, *aPrimArrIter, theProgress))
1104 if (!aMeshData->Data().IsEmpty())
1106 if (aMeshShape.IsNull())
1108 aBuilder.MakeCompound (aMeshShape);
1112 aBuilder.MakeFace (aFace, aMeshData);
1113 aBuilder.Add (aMeshShape, aFace);
1114 if (myAttribMap != NULL
1115 && aMeshData->HasStyle())
1117 RWMesh_NodeAttributes aShapeAttribs;
1118 aShapeAttribs.RawName = aUserName;
1119 aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor());
1120 myAttribMap->Bind (aFace, aShapeAttribs);
1122 myFaceList.Append (aFace);
1129 theMeshShape = TopoDS_Iterator (aMeshShape).Value();
1133 theMeshShape = aMeshShape;
1135 bindMeshShape (theMeshShape, theMeshId, aName);
1139 // =======================================================================
1140 // function : gltfParsePrimArray
1142 // =======================================================================
1143 bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1144 const TCollection_AsciiString& theMeshId,
1145 const RWGltf_JsonValue& thePrimArray,
1146 const Handle(Message_ProgressIndicator)& /*theProgress*/)
1148 const RWGltf_JsonValue* anAttribs = findObjectMember (thePrimArray, "attributes");
1149 const RWGltf_JsonValue* anIndices = findObjectMember (thePrimArray, "indices");
1150 const RWGltf_JsonValue* aMaterial = findObjectMember (thePrimArray, "material");
1151 const RWGltf_JsonValue* aModeVal = findObjectMember (thePrimArray, "mode");
1152 RWGltf_GltfPrimitiveMode aMode = RWGltf_GltfPrimitiveMode_Triangles;
1153 if (anAttribs == NULL
1154 || !anAttribs->IsObject())
1156 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' defines no attributes.");
1159 else if (aModeVal != NULL)
1161 aMode = RWGltf_GltfPrimitiveMode_UNKNOWN;
1162 if (aModeVal->IsInt())
1164 aMode = (RWGltf_GltfPrimitiveMode )aModeVal->GetInt();
1166 if (aMode < RWGltf_GltfPrimitiveMode_Points
1167 || aMode > RWGltf_GltfPrimitiveMode_TriangleFan)
1169 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' has unknown mode.");
1173 if (aMode != RWGltf_GltfPrimitiveMode_Triangles)
1175 Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Primitive array within Mesh '"
1176 + theMeshId + "' skipped due to unsupported mode.", Message_Warning);
1179 theMeshData->SetPrimitiveMode (aMode);
1182 if (aMaterial != NULL)
1184 Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
1185 if (myMaterialsPbr.Find (getKeyString (*aMaterial), aMatPbr))
1187 theMeshData->SetMaterialPbr (aMatPbr);
1190 Handle(RWGltf_MaterialCommon) aMatCommon;
1191 if (myMaterialsCommon.Find (getKeyString (*aMaterial), aMatCommon))
1193 theMeshData->SetMaterialCommon (aMatCommon);
1197 bool hasPositions = false;
1198 for (rapidjson::Value::ConstMemberIterator anAttribIter = anAttribs->MemberBegin();
1199 anAttribIter != anAttribs->MemberEnd(); ++anAttribIter)
1201 const TCollection_AsciiString anAttribId = getKeyString (anAttribIter->value);
1202 if (anAttribId.IsEmpty())
1204 reportGltfError ("Primitive array attribute accessor key within Mesh '" + theMeshId + "' is not a string.");
1208 RWGltf_GltfArrayType aType = RWGltf_GltfParseAttribType (anAttribIter->name.GetString());
1209 if (aType == RWGltf_GltfArrayType_UNKNOWN)
1211 // just ignore unknown attributes
1215 const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (anAttribIter->value);
1216 if (anAccessor == NULL
1217 || !anAccessor->IsObject())
1219 reportGltfError ("Primitive array attribute accessor key '" + anAttribId + "' points to non-existing object.");
1222 else if (!gltfParseAccessor (theMeshData, anAttribId, *anAccessor, aType))
1226 else if (aType == RWGltf_GltfArrayType_Position)
1228 hasPositions = true;
1233 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' does not define vertex positions.");
1237 if (anIndices != NULL)
1239 const TCollection_AsciiString anIndicesId = getKeyString (*anIndices);
1240 const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (*anIndices);
1241 if (anAccessor == NULL
1242 || !anAccessor->IsObject())
1244 reportGltfError ("Primitive array indices accessor key '" + anIndicesId + "' points to non-existing object.");
1247 else if (!gltfParseAccessor (theMeshData, anIndicesId, *anAccessor, RWGltf_GltfArrayType_Indices))
1256 // =======================================================================
1257 // function : gltfParseAccessor
1259 // =======================================================================
1260 bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1261 const TCollection_AsciiString& theName,
1262 const RWGltf_JsonValue& theAccessor,
1263 const RWGltf_GltfArrayType theType)
1265 RWGltf_GltfAccessor aStruct;
1266 const RWGltf_JsonValue* aTypeStr = findObjectMember (theAccessor, "type");
1267 const RWGltf_JsonValue* aBufferViewName = findObjectMember (theAccessor, "bufferView");
1268 const RWGltf_JsonValue* aByteOffset = findObjectMember (theAccessor, "byteOffset");
1269 const RWGltf_JsonValue* aByteStride = findObjectMember (theAccessor, "byteStride");
1270 const RWGltf_JsonValue* aCompType = findObjectMember (theAccessor, "componentType");
1271 const RWGltf_JsonValue* aCount = findObjectMember (theAccessor, "count");
1272 if (aTypeStr == NULL
1273 || !aTypeStr->IsString())
1275 reportGltfError ("Accessor '" + theName + "' does not define type.");
1278 aStruct.Type = RWGltf_GltfParseAccessorType (aTypeStr->GetString());
1279 if (aStruct.Type == RWGltf_GltfAccessorLayout_UNKNOWN)
1281 reportGltfError ("Accessor '" + theName + "' has invalid type.");
1285 if (aBufferViewName == NULL)
1287 reportGltfError ("Accessor '" + theName + "' does not define bufferView.");
1290 if (aCompType == NULL
1291 || !aCompType->IsInt())
1293 reportGltfError ("Accessor '" + theName + "' does not define componentType.");
1296 aStruct.ComponentType = (RWGltf_GltfAccessorCompType )aCompType->GetInt();
1297 if (aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int8
1298 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt8
1299 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int16
1300 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt16
1301 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt32
1302 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Float32)
1304 reportGltfError ("Accessor '" + theName + "' defines invalid componentType value.");
1309 || !aCount->IsNumber())
1311 reportGltfError ("Accessor '" + theName + "' does not define count.");
1315 aStruct.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1316 ? (int64_t )aByteOffset->GetDouble()
1318 aStruct.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1319 ? aByteStride->GetInt()
1321 aStruct.Count = (int64_t )aCount->GetDouble();
1323 if (aStruct.ByteOffset < 0)
1325 reportGltfError ("Accessor '" + theName + "' defines invalid byteOffset.");
1328 else if (aStruct.ByteStride < 0
1329 || aStruct.ByteStride > 255)
1331 reportGltfError ("Accessor '" + theName + "' defines invalid byteStride.");
1334 else if (aStruct.Count < 1)
1336 reportGltfError ("Accessor '" + theName + "' defines invalid count.");
1340 // Read Min/Max values for POSITION type. It is used for bounding boxes
1341 if (theType == RWGltf_GltfArrayType_Position)
1343 const RWGltf_JsonValue* aMin = findObjectMember (theAccessor, "min");
1344 const RWGltf_JsonValue* aMax = findObjectMember (theAccessor, "max");
1345 if (aMin != NULL && aMax != NULL)
1347 // Note: Min/Max values can be not defined in glTF file.
1348 // In this case it is not used only.
1349 if (!aMin->IsArray() || !aMax->IsArray() ||
1350 aMin->Size() != 3 || aMax->Size() != 3)
1352 reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max values.");
1356 bool isValidMinMax = true;
1357 gp_Pnt aMinPnt, aMaxPnt;
1358 for (int anIter = 0; anIter < 3; ++anIter)
1360 const RWGltf_JsonValue& aMinVal = (*aMin)[anIter];
1361 const RWGltf_JsonValue& aMaxVal = (*aMax)[anIter];
1362 if (!aMinVal.IsNumber() || !aMaxVal.IsNumber())
1364 reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max value.");
1365 isValidMinMax = false;
1368 aMinPnt.SetCoord (anIter + 1, aMinVal.GetDouble());
1369 aMinPnt.SetCoord (anIter + 1, aMaxVal.GetDouble());
1373 myCSTrsf.TransformPosition (aMinPnt.ChangeCoord());
1374 myCSTrsf.TransformPosition (aMaxPnt.ChangeCoord());
1380 theMeshData->SetBoundingBox (aBox);
1386 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
1387 if (aBufferView == NULL
1388 || !aBufferView->IsObject())
1390 reportGltfError ("Accessor '" + theName + "' refers to non-existing bufferView.");
1394 return gltfParseBufferView (theMeshData, getKeyString (*aBufferViewName), *aBufferView, aStruct, theType);
1397 // =======================================================================
1398 // function : gltfParseBufferView
1400 // =======================================================================
1401 bool RWGltf_GltfJsonParser::gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1402 const TCollection_AsciiString& theName,
1403 const RWGltf_JsonValue& theBufferView,
1404 const RWGltf_GltfAccessor& theAccessor,
1405 const RWGltf_GltfArrayType theType)
1407 RWGltf_GltfBufferView aBuffView;
1408 const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
1409 const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
1410 const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
1411 const RWGltf_JsonValue* aTarget = findObjectMember (theBufferView, "target");
1412 if (aBufferName == NULL)
1414 reportGltfError ("BufferView '" + theName + "' does not define buffer.");
1418 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1419 ? (int64_t )aByteOffset->GetDouble()
1421 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
1422 ? (int64_t )aByteLength->GetDouble()
1424 if (aTarget != NULL && aTarget->IsInt())
1426 aBuffView.Target = (RWGltf_GltfBufferViewTarget )aTarget->GetInt();
1427 if (aBuffView.Target != RWGltf_GltfBufferViewTarget_ARRAY_BUFFER
1428 && aBuffView.Target != RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER)
1430 reportGltfError ("BufferView '" + theName + "' defines invalid target.");
1435 if (aBuffView.ByteLength < 0)
1437 reportGltfError ("BufferView '" + theName + "' defines invalid byteLength.");
1440 else if (aBuffView.ByteOffset < 0)
1442 reportGltfError ("BufferView '" + theName + "' defines invalid byteOffset.");
1446 const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
1448 || !aBuffer->IsObject())
1450 reportGltfError ("BufferView '" + theName + "' refers to non-existing buffer.");
1454 return gltfParseBuffer (theMeshData, getKeyString (*aBufferName), *aBuffer, theAccessor, aBuffView, theType);
1457 // =======================================================================
1458 // function : gltfParseBuffer
1460 // =======================================================================
1461 bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1462 const TCollection_AsciiString& theName,
1463 const RWGltf_JsonValue& theBuffer,
1464 const RWGltf_GltfAccessor& theAccessor,
1465 const RWGltf_GltfBufferView& theView,
1466 const RWGltf_GltfArrayType theType)
1468 //const RWGltf_JsonValue* aType = findObjectMember (theBuffer, "type");
1469 //const RWGltf_JsonValue* aByteLength = findObjectMember (theBuffer, "byteLength");
1470 const RWGltf_JsonValue* anUriVal = findObjectMember (theBuffer, "uri");
1472 int64_t anOffset = theView.ByteOffset + theAccessor.ByteOffset;
1473 bool isBinary = false;
1476 isBinary = IsEqual ("binary_glTF", theName) // glTF 1.0
1477 || anUriVal == NULL; // glTF 2.0
1481 anOffset += myBinBodyOffset;
1483 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1484 aData.Accessor = theAccessor;
1485 aData.StreamOffset = anOffset;
1486 aData.StreamUri = myFilePath;
1490 if (anUriVal == NULL
1491 || !anUriVal->IsString())
1493 reportGltfError ("Buffer '" + theName + "' does not define uri.");
1497 const char* anUriData = anUriVal->GetString();
1498 if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
1500 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1501 aData.Accessor = theAccessor;
1502 aData.StreamOffset = anOffset;
1503 if (!myDecodedBuffers.Find (theName, aData.StreamData))
1505 // it is better decoding in multiple threads
1506 aData.StreamData = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
1507 myDecodedBuffers.Bind (theName, aData.StreamData);
1513 TCollection_AsciiString anUri = anUriData;
1514 if (anUri.IsEmpty())
1516 reportGltfError ("Buffer '" + theName + "' does not define uri.");
1520 TCollection_AsciiString aPath = myFolder + anUri;
1521 bool isFileExist = false;
1522 if (!myProbedFiles.Find (aPath, isFileExist))
1524 isFileExist = OSD_File (aPath).Exists();
1525 myProbedFiles.Bind (aPath, isFileExist);
1529 reportGltfError ("Buffer '" + theName + "' refers to non-existing file '" + anUri + "'.");
1533 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1534 aData.Accessor = theAccessor;
1535 aData.StreamOffset = anOffset;
1536 aData.StreamUri = myFolder + anUri;
1537 if (myExternalFiles != NULL)
1539 myExternalFiles->Add (aData.StreamUri);
1545 // =======================================================================
1546 // function : bindNamedShape
1548 // =======================================================================
1549 void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape,
1550 ShapeMapGroup theGroup,
1551 const TopLoc_Location& theLoc,
1552 const TCollection_AsciiString& theId,
1553 const RWGltf_JsonValue* theUserName)
1555 if (theShape.IsNull())
1560 if (!theLoc.IsIdentity())
1562 theShape.Location (theLoc);
1565 TCollection_AsciiString aUserName;
1566 if (theUserName != NULL
1567 && theUserName->IsString())
1569 aUserName = theUserName->GetString();
1576 myShapeMap[theGroup].Bind (theId, theShape);
1577 if (myAttribMap != NULL)
1579 RWMesh_NodeAttributes aShapeAttribs;
1580 aShapeAttribs.Name = aUserName;
1581 aShapeAttribs.RawName = theId;
1582 if (theShape.ShapeType() == TopAbs_FACE)
1584 TopLoc_Location aDummy;
1585 if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (theShape), aDummy)))
1587 if (aLateData->HasStyle())
1589 aShapeAttribs.Style.SetColorSurf (aLateData->BaseColor());
1591 if (aShapeAttribs.Name.IsEmpty()
1592 && myUseMeshNameAsFallback)
1594 // fallback using Mesh name
1595 aShapeAttribs.Name = aLateData->Name();
1599 else if (aShapeAttribs.Name.IsEmpty()
1600 && myUseMeshNameAsFallback)
1602 // fallback using Mesh name
1603 TopLoc_Location aDummy;
1604 TCollection_AsciiString aMeshName;
1605 for (TopExp_Explorer aFaceIter (theShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
1607 if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (aFaceIter.Value()), aDummy)))
1609 if (aLateData->Name().IsEmpty())
1614 else if (aMeshName.IsEmpty())
1616 aMeshName = aLateData->Name();
1618 else if (!aMeshName.IsEqual (aLateData->Name()))
1625 if (!aMeshName.IsEmpty())
1627 aShapeAttribs.Name = aMeshName;
1630 myAttribMap->Bind (theShape, aShapeAttribs);
1635 // =======================================================================
1638 // =======================================================================
1639 bool RWGltf_GltfJsonParser::Parse (const Handle(Message_ProgressIndicator)& theProgress)
1641 Message_ProgressSentry aPSentry (theProgress, "Reading Gltf", 0, 2, 1);
1642 #ifdef HAVE_RAPIDJSON
1644 if (!gltfParseRoots())
1650 gltfParseMaterials();
1651 if (!gltfParseScene (theProgress))
1657 if (!aPSentry.More())
1663 Message::DefaultMessenger()->Send ("Error: glTF reader is unavailable - OCCT has been built without RapidJSON support.", Message_Fail);