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 myToProbeHeader (false)
176 myCSTrsf.SetInputLengthUnit (1.0); // meters
177 myCSTrsf.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
180 // =======================================================================
181 // function : SetFilePath
183 // =======================================================================
184 void RWGltf_GltfJsonParser::SetFilePath (const TCollection_AsciiString& theFilePath)
186 myFilePath = theFilePath;
187 // determine file location to load associated files
188 TCollection_AsciiString aFileName;
189 OSD_Path::FolderAndFileFromPath (theFilePath, myFolder, aFileName);
192 #ifdef HAVE_RAPIDJSON
193 // =======================================================================
194 // function : gltfParseRoots
196 // =======================================================================
197 bool RWGltf_GltfJsonParser::gltfParseRoots()
199 // find glTF root elements for smooth navigation
200 RWGltf_JsonValue aNames[RWGltf_GltfRootElement_NB];
201 for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
203 aNames[aRootNameIter] = rapidjson::StringRef (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter));
206 for (ConstMemberIterator aRootIter = MemberBegin();
207 aRootIter != MemberEnd(); ++aRootIter)
209 for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
211 if (myGltfRoots[aRootNameIter].IsNull()
212 && aNames[aRootNameIter] == aRootIter->name)
214 // we will not modify JSON document, thus it is OK to keep the pointers
215 myGltfRoots[aRootNameIter].Init (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter), &aRootIter->value);
221 for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB_MANDATORY; ++aRootNameIter)
223 if (myGltfRoots[aRootNameIter].IsNull())
225 reportGltfError ("Member '" + RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter) + "' is not found.");
232 // =======================================================================
233 // function : gltfParseAsset
235 // =======================================================================
236 void RWGltf_GltfJsonParser::gltfParseAsset()
238 const RWGltf_JsonValue* anAsset = myGltfRoots[RWGltf_GltfRootElement_Asset].Root();
244 if (const RWGltf_JsonValue* aVersion = findObjectMember (*anAsset, "version"))
246 if (aVersion->IsString())
248 TCollection_AsciiString aVerStr (aVersion->GetString());
249 myIsGltf1 = aVerStr.StartsWith ("1.");
253 if (myMetadata == NULL)
258 if (const RWGltf_JsonValue* aGenerator = findObjectMember (*anAsset, "generator"))
260 if (aGenerator->IsString())
262 myMetadata->Add ("generator", aGenerator->GetString());
265 if (const RWGltf_JsonValue* aCopyRight = findObjectMember (*anAsset, "copyright"))
267 if (aCopyRight->IsString())
269 myMetadata->Add ("copyright", aCopyRight->GetString());
274 // =======================================================================
275 // function : gltfParseMaterials
277 // =======================================================================
278 void RWGltf_GltfJsonParser::gltfParseMaterials()
280 const RWGltf_JsonValue* aMatList = myGltfRoots[RWGltf_GltfRootElement_Materials].Root();
281 if (aMatList == NULL)
285 else if (aMatList->IsObject())
288 for (ConstMemberIterator aMatIter = aMatList->MemberBegin();
289 aMatIter != aMatList->MemberEnd(); ++aMatIter)
291 Handle(RWGltf_MaterialCommon) aMat;
292 const RWGltf_JsonValue& aMatNode = aMatIter->value;
293 const RWGltf_JsonValue& aMatId = aMatIter->name;
294 const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
295 if (!gltfParseCommonMaterial (aMat, aMatNode))
297 if (!gltfParseStdMaterial (aMat, aMatNode))
304 && aNameVal->IsString())
306 aMat->Name = aNameVal->GetString();
308 aMat->Id = aMatId.GetString();
309 myMaterialsCommon.Bind (aMat->Id, aMat);
312 else if (aMatList->IsArray())
316 for (rapidjson::Value::ConstValueIterator aMatIter = aMatList->Begin(); aMatIter != aMatList->End(); ++aMatIter, ++aMatIndex)
318 Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
319 const RWGltf_JsonValue& aMatNode = *aMatIter;
320 const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
321 if (gltfParsePbrMaterial (aMatPbr, aMatNode))
324 && aNameVal->IsString())
326 aMatPbr->Name = aNameVal->GetString();
328 aMatPbr->Id = TCollection_AsciiString ("mat_") + aMatIndex;
329 myMaterialsPbr.Bind (TCollection_AsciiString (aMatIndex), aMatPbr);
332 Handle(RWGltf_MaterialCommon) aMatCommon;
333 if (gltfParseCommonMaterial(aMatCommon, aMatNode)
334 || gltfParseStdMaterial (aMatCommon, aMatNode))
337 && aNameVal->IsString())
339 aMatCommon->Name = aNameVal->GetString();
341 aMatCommon->Id = TCollection_AsciiString ("mat_") + aMatIndex;
342 myMaterialsCommon.Bind (TCollection_AsciiString (aMatIndex), aMatCommon);
348 // =======================================================================
349 // function : gltfParseStdMaterial
351 // =======================================================================
352 bool RWGltf_GltfJsonParser::gltfParseStdMaterial (Handle(RWGltf_MaterialCommon)& theMat,
353 const RWGltf_JsonValue& theMatNode)
355 //const RWGltf_JsonValue* aTechVal = findObjectMember (theMatNode, "technique");
356 const RWGltf_JsonValue* aValues = findObjectMember (theMatNode, "values");
362 const RWGltf_JsonValue* anAmbVal = findObjectMember (*aValues, "ambient");
363 const RWGltf_JsonValue* aDiffVal = findObjectMember (*aValues, "diffuse");
364 const RWGltf_JsonValue* anEmiVal = findObjectMember (*aValues, "emission");
365 const RWGltf_JsonValue* aSpecVal = findObjectMember (*aValues, "specular");
366 const RWGltf_JsonValue* aShinVal = findObjectMember (*aValues, "shininess");
376 theMat = new RWGltf_MaterialCommon();
378 Graphic3d_Vec4d anAmb, aDiff, anEmi, aSpec;
380 && anAmbVal->IsString())
382 gltfParseTexture (theMat->AmbientTexture, anAmbVal);
384 else if (gltfReadVec4 (anAmb, anAmbVal)
385 && validateColor4 (anAmb))
387 theMat->AmbientColor = Quantity_Color (anAmb.r(), anAmb.g(), anAmb.b(), Quantity_TOC_RGB);
391 && aDiffVal->IsString())
393 gltfParseTexture (theMat->DiffuseTexture, aDiffVal);
395 else if (gltfReadVec4 (aDiff, aDiffVal)
396 && validateColor4 (aDiff))
398 theMat->DiffuseColor = Quantity_Color (aDiff.r(), aDiff.g(), aDiff.b(), Quantity_TOC_RGB);
399 theMat->Transparency = float(1.0 - aDiff.a());
402 if (gltfReadVec4 (anEmi, anEmiVal)
403 && validateColor4 (anEmi))
405 theMat->EmissiveColor = Quantity_Color (anEmi.r(), anEmi.g(), anEmi.b(), Quantity_TOC_RGB);
409 && aSpecVal->IsString())
411 gltfParseTexture (theMat->SpecularTexture, aSpecVal);
413 if (gltfReadVec4 (aSpec, aSpecVal)
414 && validateColor4 (aSpec))
416 theMat->SpecularColor = Quantity_Color (aSpec.r(), aSpec.g(), aSpec.b(), Quantity_TOC_RGB);
420 && aShinVal->IsNumber())
422 const double aSpecular = aShinVal->GetDouble();
425 theMat->Shininess = (float )Min (aSpecular / 1000.0, 1.0);
431 // =======================================================================
432 // function : gltfParsePbrMaterial
434 // =======================================================================
435 bool RWGltf_GltfJsonParser::gltfParsePbrMaterial (Handle(RWGltf_MaterialMetallicRoughness)& theMat,
436 const RWGltf_JsonValue& theMatNode)
438 /*if (const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions"))
440 if (const RWGltf_JsonValue* anExtDefVal = findObjectMember (*anExtVal, "KHR_materials_pbrSpecularGlossiness"))
442 const RWGltf_JsonValue* aDiffTexVal = findObjectMember (*anExtDefVal, "diffuseTexture");
443 const RWGltf_JsonValue* aSpecTexVal = findObjectMember (*anExtDefVal, "specularGlossinessTexture");
447 const RWGltf_JsonValue* aMetalRoughVal = findObjectMember (theMatNode, "pbrMetallicRoughness");
448 const RWGltf_JsonValue* aNormTexVal = findObjectMember (theMatNode, "normalTexture");
449 const RWGltf_JsonValue* anEmissFactorVal = findObjectMember (theMatNode, "emissiveFactor");
450 const RWGltf_JsonValue* anEmissTexVal = findObjectMember (theMatNode, "emissiveTexture");
451 const RWGltf_JsonValue* anOcclusionTexVal = findObjectMember (theMatNode, "occlusionTexture");
452 if (aMetalRoughVal == NULL)
457 theMat = new RWGltf_MaterialMetallicRoughness();
458 const RWGltf_JsonValue* aBaseColorFactorVal = findObjectMember (*aMetalRoughVal, "baseColorFactor");
459 const RWGltf_JsonValue* aBaseColorTexVal = findObjectMember (*aMetalRoughVal, "baseColorTexture");
460 const RWGltf_JsonValue* aMetallicFactorVal = findObjectMember (*aMetalRoughVal, "metallicFactor");
461 const RWGltf_JsonValue* aRoughnessFactorVal = findObjectMember (*aMetalRoughVal, "roughnessFactor");
462 const RWGltf_JsonValue* aMetalRoughTexVal = findObjectMember (*aMetalRoughVal, "metallicRoughnessTexture");
464 if (aBaseColorTexVal != NULL
465 && aBaseColorTexVal->IsObject())
467 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aBaseColorTexVal, "index"))
469 gltfParseTexture (theMat->BaseColorTexture, aTexIndexVal);
473 Graphic3d_Vec4d aBaseColorFactor;
474 if (gltfReadVec4 (aBaseColorFactor, aBaseColorFactorVal)
475 && validateColor4 (aBaseColorFactor))
477 theMat->BaseColor = Quantity_ColorRGBA (Graphic3d_Vec4 (aBaseColorFactor));
480 Graphic3d_Vec3d anEmissiveFactor;
481 if (gltfReadVec3 (anEmissiveFactor, anEmissFactorVal)
482 && validateColor3 (anEmissiveFactor))
484 theMat->EmissiveFactor = Graphic3d_Vec3 (anEmissiveFactor);
487 if (aMetalRoughTexVal != NULL
488 && aMetalRoughTexVal->IsObject())
490 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aMetalRoughTexVal, "index"))
492 gltfParseTexture (theMat->MetallicRoughnessTexture, aTexIndexVal);
496 if (aMetallicFactorVal != NULL
497 && aMetallicFactorVal->IsNumber())
499 theMat->Metallic = (float )aMetallicFactorVal->GetDouble();
502 if (aRoughnessFactorVal != NULL
503 && aRoughnessFactorVal->IsNumber())
505 theMat->Roughness = (float )aRoughnessFactorVal->GetDouble();
508 if (aNormTexVal != NULL
509 && aNormTexVal->IsObject())
511 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aNormTexVal, "index"))
513 gltfParseTexture (theMat->NormalTexture, aTexIndexVal);
517 if (anEmissTexVal != NULL
518 && anEmissTexVal->IsObject())
520 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anEmissTexVal, "index"))
522 gltfParseTexture (theMat->EmissiveTexture, aTexIndexVal);
526 if (anOcclusionTexVal != NULL
527 && anOcclusionTexVal->IsObject())
529 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anOcclusionTexVal, "index"))
531 gltfParseTexture (theMat->OcclusionTexture, aTexIndexVal);
537 // =======================================================================
538 // function : gltfParseCommonMaterial
540 // =======================================================================
541 bool RWGltf_GltfJsonParser::gltfParseCommonMaterial (Handle(RWGltf_MaterialCommon)& theMat,
542 const RWGltf_JsonValue& theMatNode)
544 const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions");
545 if (anExtVal == NULL)
550 const RWGltf_JsonValue* aMatCommon = findObjectMember (*anExtVal, THE_KHR_materials_common);
551 if (aMatCommon == NULL)
556 if (!gltfParseStdMaterial (theMat, *aMatCommon))
563 // =======================================================================
564 // function : gltfParseTexture
566 // =======================================================================
567 bool RWGltf_GltfJsonParser::gltfParseTexture (Handle(Image_Texture)& theTexture,
568 const RWGltf_JsonValue* theTextureId)
570 if (theTextureId == NULL
571 || myGltfRoots[RWGltf_GltfRootElement_Textures].IsNull()
572 || myGltfRoots[RWGltf_GltfRootElement_Images].IsNull())
577 const TCollection_AsciiString aTextureId = getKeyString (*theTextureId);
578 const RWGltf_JsonValue* aTexNode = myGltfRoots[RWGltf_GltfRootElement_Textures].FindChild (*theTextureId);
579 if (aTexNode == NULL)
581 reportGltfWarning ("Texture node '" + aTextureId + "' is not found.");
585 const RWGltf_JsonValue* aSrcVal = findObjectMember (*aTexNode, "source");
586 const RWGltf_JsonValue* aTargVal = findObjectMember (*aTexNode, "target");
589 reportGltfWarning ("Invalid texture node '" + aTextureId + "' without a 'source' property.");
593 && aTargVal->IsNumber()
594 && aTargVal->GetInt() != 3553) // GL_TEXTURE_2D
599 const RWGltf_JsonValue* anImgNode = myGltfRoots[RWGltf_GltfRootElement_Images].FindChild (*aSrcVal);
600 if (anImgNode == NULL)
602 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to non-existing image '" + getKeyString (*aSrcVal) + "'.");
608 const RWGltf_JsonValue* aBinVal = NULL;
609 const RWGltf_JsonValue* aBufferViewName = findObjectMember (*anImgNode, "bufferView");
610 if (aBufferViewName != NULL)
616 const RWGltf_JsonValue* anExtVal = findObjectMember (*anImgNode, "extensions");
617 if (anExtVal != NULL)
619 aBinVal = findObjectMember (*anExtVal, THE_KHR_binary_glTF);
622 aBufferViewName = findObjectMember (*aBinVal, "bufferView");
629 //const RWGltf_JsonValue* aMimeTypeVal = findObjectMember (*aBinVal, "mimeType");
630 //const RWGltf_JsonValue* aWidthVal = findObjectMember (*aBinVal, "width");
631 //const RWGltf_JsonValue* aHeightVal = findObjectMember (*aBinVal, "height");
632 if (aBufferViewName == NULL)
634 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
638 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
639 if (aBufferView == NULL
640 || !aBufferView->IsObject())
642 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid buffer view '" + getKeyString (*aBufferViewName) + "'.");
646 const RWGltf_JsonValue* aBufferName = findObjectMember (*aBufferView, "buffer");
647 const RWGltf_JsonValue* aByteLength = findObjectMember (*aBufferView, "byteLength");
648 const RWGltf_JsonValue* aByteOffset = findObjectMember (*aBufferView, "byteOffset");
649 if (aBufferName != NULL
650 && aBufferName->IsString()
651 && !IsEqual (aBufferName->GetString(), "binary_glTF"))
653 reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' does not define binary_glTF buffer.");
657 RWGltf_GltfBufferView aBuffView;
658 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
659 ? (int64_t )aByteOffset->GetDouble()
661 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
662 ? (int64_t )aByteLength->GetDouble()
664 if (aBuffView.ByteLength < 0)
666 reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' defines invalid byteLength.");
669 else if (aBuffView.ByteOffset < 0)
671 reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' defines invalid byteOffset.");
676 const int64_t anOffset = myBinBodyOffset + aBuffView.ByteOffset;
677 theTexture = new Image_Texture (myFilePath, anOffset, aBuffView.ByteLength);
682 const RWGltf_JsonValue* anUriVal = findObjectMember (*anImgNode, "uri");
684 || !anUriVal->IsString())
689 const char* anUriData = anUriVal->GetString();
690 if (::strncmp (anUriData, "data:", 5) == 0) // data:image/png;base64
692 // uncompressing base64 here is inefficient, because the same image can be shared by several nodes
693 const char* aDataStart = anUriData + 5;
694 for (const char* aDataIter = aDataStart; *aDataIter != '\0'; ++aDataIter)
696 if (::memcmp (aDataIter, ";base64,", 8) == 0)
698 const char* aBase64End = anUriData + anUriVal->GetStringLength();
699 const char* aBase64Data = aDataIter + 8;
700 const size_t aBase64Len = size_t(aBase64End - aBase64Data);
701 //const TCollection_AsciiString aMime (aDataStart, aDataIter - aDataStart);
702 Handle(NCollection_Buffer) aData = FSD_Base64Decoder::Decode ((const Standard_Byte* )aBase64Data, aBase64Len);
703 theTexture = new Image_Texture (aData, myFilePath + "@" + getKeyString (*aSrcVal));
707 Message::DefaultMessenger()->Send ("glTF reader - embedded image has been skipped", Message_Warning);
711 TCollection_AsciiString anImageFile = myFolder + anUriVal->GetString();
712 theTexture = new Image_Texture (anImageFile);
713 if (myExternalFiles != NULL)
715 myExternalFiles->Add (anImageFile);
720 // =======================================================================
721 // function : gltfParseScene
723 // =======================================================================
724 bool RWGltf_GltfJsonParser::gltfParseScene (const Handle(Message_ProgressIndicator)& theProgress)
726 // search default scene
727 const RWGltf_JsonValue* aDefScene = myGltfRoots[RWGltf_GltfRootElement_Scenes].FindChild (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root());
728 if (aDefScene == NULL)
730 reportGltfError ("Default scene is not found.");
734 const RWGltf_JsonValue* aSceneNodes = findObjectMember (*aDefScene, "nodes");
735 if (aSceneNodes == NULL
736 || !aSceneNodes->IsArray())
738 reportGltfError ("Empty scene '" + getKeyString (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root()) + "'.");
742 return gltfParseSceneNodes (*myRootShapes, *aSceneNodes, theProgress);
745 // =======================================================================
746 // function : gltfParseSceneNodes
748 // =======================================================================
749 bool RWGltf_GltfJsonParser::gltfParseSceneNodes (TopTools_SequenceOfShape& theShapeSeq,
750 const RWGltf_JsonValue& theSceneNodes,
751 const Handle(Message_ProgressIndicator)& theProgress)
753 if (!theSceneNodes.IsArray())
755 reportGltfError ("Scene nodes is not array.");
759 Message_ProgressSentry aPSentry (theProgress, "Reading scene nodes", 0, theSceneNodes.Size(), 1);
760 for (rapidjson::Value::ConstValueIterator aSceneNodeIter = theSceneNodes.Begin();
761 aSceneNodeIter != theSceneNodes.End() && aPSentry.More(); ++aSceneNodeIter, aPSentry.Next())
763 const RWGltf_JsonValue* aSceneNode = myGltfRoots[RWGltf_GltfRootElement_Nodes].FindChild (*aSceneNodeIter);
764 if (aSceneNode == NULL)
766 reportGltfWarning ("Scene refers to non-existing node '" + getKeyString (*aSceneNodeIter) + "'.");
770 TopoDS_Shape aNodeShape;
771 if (!gltfParseSceneNode (aNodeShape, getKeyString (*aSceneNodeIter), *aSceneNode, theProgress))
776 if (aNodeShape.IsNull())
780 else if (myToSkipEmptyNodes
781 && !TopExp_Explorer (aNodeShape, TopAbs_FACE).More())
786 theShapeSeq.Append (aNodeShape);
791 // =======================================================================
792 // function : gltfParseSceneNode
794 // =======================================================================
795 bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
796 const TCollection_AsciiString& theSceneNodeId,
797 const RWGltf_JsonValue& theSceneNode,
798 const Handle(Message_ProgressIndicator)& theProgress)
800 const RWGltf_JsonValue* aName = findObjectMember (theSceneNode, "name");
801 //const RWGltf_JsonValue* aJointName = findObjectMember (theSceneNode, "jointName");
802 const RWGltf_JsonValue* aChildren = findObjectMember (theSceneNode, "children");
803 const RWGltf_JsonValue* aMeshes_1 = findObjectMember (theSceneNode, "meshes");
804 const RWGltf_JsonValue* aMesh_2 = findObjectMember (theSceneNode, "mesh");
805 //const RWGltf_JsonValue* aCamera = findObjectMember (theSceneNode, "camera");
806 const RWGltf_JsonValue* aTrsfMatVal = findObjectMember (theSceneNode, "matrix");
807 const RWGltf_JsonValue* aTrsfRotVal = findObjectMember (theSceneNode, "rotation");
808 const RWGltf_JsonValue* aTrsfScaleVal = findObjectMember (theSceneNode, "scale");
809 const RWGltf_JsonValue* aTrsfTransVal = findObjectMember (theSceneNode, "translation");
810 if (findNodeShape (theNodeShape, theSceneNodeId))
815 TopLoc_Location aNodeLoc;
816 const bool hasTrs = aTrsfRotVal != NULL
817 || aTrsfScaleVal != NULL
818 || aTrsfTransVal != NULL;
819 if (aTrsfMatVal != NULL)
823 reportGltfError ("Scene node '" + theSceneNodeId + "' defines ambiguous transformation.");
826 else if (!aTrsfMatVal->IsArray()
827 || aTrsfMatVal->Size() != 16)
829 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix array.");
833 Graphic3d_Mat4d aMat4;
834 for (int aColIter = 0; aColIter < 4; ++aColIter)
836 for (int aRowIter = 0; aRowIter < 4; ++aRowIter)
838 const RWGltf_JsonValue& aGenVal = (*aTrsfMatVal)[aColIter * 4 + aRowIter];
839 if (!aGenVal.IsNumber())
841 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix.");
844 aMat4.SetValue (aRowIter, aColIter, aGenVal.GetDouble());
848 if (!aMat4.IsIdentity())
851 aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
852 aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
853 aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
854 myCSTrsf.TransformTransformation (aTrsf);
855 if (aTrsf.Form() != gp_Identity)
857 aNodeLoc = TopLoc_Location (aTrsf);
864 if (aTrsfRotVal != NULL)
866 if (!aTrsfRotVal->IsArray()
867 || aTrsfRotVal->Size() != 4)
869 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation quaternion.");
873 Graphic3d_Vec4d aRotVec4;
874 for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
876 const RWGltf_JsonValue& aGenVal = (*aTrsfRotVal)[aCompIter];
877 if (!aGenVal.IsNumber())
879 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation.");
882 aRotVec4[aCompIter] = aGenVal.GetDouble();
884 const gp_Quaternion aQuaternion (aRotVec4.x(), aRotVec4.y(), aRotVec4.z(), aRotVec4.w());
885 if (Abs (aQuaternion.X()) > gp::Resolution()
886 || Abs (aQuaternion.Y()) > gp::Resolution()
887 || Abs (aQuaternion.Z()) > gp::Resolution()
888 || Abs (aQuaternion.W() - 1.0) > gp::Resolution())
890 aTrsf.SetRotation (aQuaternion);
894 if (aTrsfTransVal != NULL)
896 if (!aTrsfTransVal->IsArray()
897 || aTrsfTransVal->Size() != 3)
899 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation vector.");
904 for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
906 const RWGltf_JsonValue& aGenVal = (*aTrsfTransVal)[aCompIter];
907 if (!aGenVal.IsNumber())
909 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation.");
912 aTransVec.SetCoord (aCompIter + 1, aGenVal.GetDouble());
914 aTrsf.SetTranslationPart (aTransVec);
917 if (aTrsfScaleVal != NULL)
919 Graphic3d_Vec3d aScaleVec;
920 if (!aTrsfScaleVal->IsArray()
921 || aTrsfScaleVal->Size() != 3)
923 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale vector.");
926 for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
928 const RWGltf_JsonValue& aGenVal = (*aTrsfScaleVal)[aCompIter];
929 if (!aGenVal.IsNumber())
931 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
934 aScaleVec[aCompIter] = aGenVal.GetDouble();
935 if (Abs (aScaleVec[aCompIter]) <= gp::Resolution())
937 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
942 if (Abs (aScaleVec.x() - aScaleVec.y()) > Precision::Confusion()
943 || Abs (aScaleVec.y() - aScaleVec.z()) > Precision::Confusion()
944 || Abs (aScaleVec.x() - aScaleVec.z()) > Precision::Confusion())
946 Graphic3d_Mat4d aScaleMat;
947 aScaleMat.SetDiagonal (aScaleVec);
949 Graphic3d_Mat4d aMat4;
950 aTrsf.GetMat4 (aMat4);
952 aMat4 = aMat4 * aScaleMat;
954 aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
955 aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
956 aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
958 Message::DefaultMessenger()->Send (TCollection_AsciiString ("glTF reader, scene node '")
959 + theSceneNodeId + "' defines unsupported scaling "
960 + aScaleVec.x() + " " + aScaleVec.y() + " " + aScaleVec.z(), Message_Warning);
962 else if (Abs (aScaleVec.x() - 1.0) > Precision::Confusion())
964 aTrsf.SetScaleFactor (aScaleVec.x());
968 myCSTrsf.TransformTransformation (aTrsf);
969 if (aTrsf.Form() != gp_Identity)
971 aNodeLoc = TopLoc_Location (aTrsf);
975 BRep_Builder aBuilder;
976 TopoDS_Compound aNodeShape;
977 aBuilder.MakeCompound (aNodeShape);
978 TopTools_SequenceOfShape aChildShapes;
979 int aNbSubShapes = 0;
980 if (aChildren != NULL
981 && !gltfParseSceneNodes (aChildShapes, *aChildren, theProgress))
983 theNodeShape = aNodeShape;
984 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
987 for (TopTools_SequenceOfShape::Iterator aChildShapeIter (aChildShapes); aChildShapeIter.More(); aChildShapeIter.Next())
989 aBuilder.Add (aNodeShape, aChildShapeIter.Value());
993 if (aMeshes_1 != NULL
994 && aMeshes_1->IsArray())
997 Message_ProgressSentry aPSentry (theProgress, "Reading scene meshes", 0, aMeshes_1->Size(), 1);
998 for (rapidjson::Value::ConstValueIterator aMeshIter = aMeshes_1->Begin();
999 aMeshIter != aMeshes_1->End() && aPSentry.More(); ++aMeshIter, aPSentry.Next())
1001 const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMeshIter);
1004 theNodeShape = aNodeShape;
1005 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1006 reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1010 TopoDS_Shape aMeshShape;
1011 if (!gltfParseMesh (aMeshShape, getKeyString (*aMeshIter), *aMesh, theProgress))
1013 theNodeShape = aNodeShape;
1014 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1017 if (!aMeshShape.IsNull())
1019 aBuilder.Add (aNodeShape, aMeshShape);
1024 if (aMesh_2 != NULL)
1027 const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMesh_2);
1030 theNodeShape = aNodeShape;
1031 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1032 reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1036 TopoDS_Shape aMeshShape;
1037 if (!gltfParseMesh (aMeshShape, getKeyString (*aMesh_2), *aMesh, theProgress))
1039 theNodeShape = aNodeShape;
1040 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1043 if (!aMeshShape.IsNull())
1045 aBuilder.Add (aNodeShape, aMeshShape);
1050 if (aNbSubShapes == 1)
1052 theNodeShape = TopoDS_Iterator (aNodeShape).Value();
1056 theNodeShape = aNodeShape;
1058 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1062 // =======================================================================
1063 // function : gltfParseMesh
1065 // =======================================================================
1066 bool RWGltf_GltfJsonParser::gltfParseMesh (TopoDS_Shape& theMeshShape,
1067 const TCollection_AsciiString& theMeshId,
1068 const RWGltf_JsonValue& theMesh,
1069 const Handle(Message_ProgressIndicator)& theProgress)
1071 const RWGltf_JsonValue* aName = findObjectMember (theMesh, "name");
1072 const RWGltf_JsonValue* aPrims = findObjectMember (theMesh, "primitives");
1073 if (!aPrims->IsArray())
1075 reportGltfError ("Primitive array attributes within Mesh '" + theMeshId + "' is not an array.");
1079 if (findMeshShape (theMeshShape, theMeshId))
1084 BRep_Builder aBuilder;
1085 TopoDS_Compound aMeshShape;
1087 for (rapidjson::Value::ConstValueIterator aPrimArrIter = aPrims->Begin();
1088 aPrimArrIter != aPrims->End(); ++aPrimArrIter)
1090 TCollection_AsciiString aUserName;
1092 && aName->IsString())
1094 aUserName = aName->GetString();
1097 Handle(RWGltf_GltfLatePrimitiveArray) aMeshData = new RWGltf_GltfLatePrimitiveArray (theMeshId, aUserName);
1098 if (!gltfParsePrimArray (aMeshData, theMeshId, *aPrimArrIter, theProgress))
1103 if (!aMeshData->Data().IsEmpty())
1105 if (aMeshShape.IsNull())
1107 aBuilder.MakeCompound (aMeshShape);
1111 aBuilder.MakeFace (aFace, aMeshData);
1112 aBuilder.Add (aMeshShape, aFace);
1113 if (myAttribMap != NULL
1114 && aMeshData->HasStyle())
1116 RWMesh_NodeAttributes aShapeAttribs;
1117 aShapeAttribs.RawName = aUserName;
1118 aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor());
1119 myAttribMap->Bind (aFace, aShapeAttribs);
1121 myFaceList.Append (aFace);
1128 theMeshShape = TopoDS_Iterator (aMeshShape).Value();
1132 theMeshShape = aMeshShape;
1134 bindMeshShape (theMeshShape, theMeshId, aName);
1138 // =======================================================================
1139 // function : gltfParsePrimArray
1141 // =======================================================================
1142 bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1143 const TCollection_AsciiString& theMeshId,
1144 const RWGltf_JsonValue& thePrimArray,
1145 const Handle(Message_ProgressIndicator)& /*theProgress*/)
1147 const RWGltf_JsonValue* anAttribs = findObjectMember (thePrimArray, "attributes");
1148 const RWGltf_JsonValue* anIndices = findObjectMember (thePrimArray, "indices");
1149 const RWGltf_JsonValue* aMaterial = findObjectMember (thePrimArray, "material");
1150 const RWGltf_JsonValue* aModeVal = findObjectMember (thePrimArray, "mode");
1151 RWGltf_GltfPrimitiveMode aMode = RWGltf_GltfPrimitiveMode_Triangles;
1152 if (anAttribs == NULL
1153 || !anAttribs->IsObject())
1155 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' defines no attributes.");
1158 else if (aModeVal != NULL)
1160 aMode = RWGltf_GltfPrimitiveMode_UNKNOWN;
1161 if (aModeVal->IsInt())
1163 aMode = (RWGltf_GltfPrimitiveMode )aModeVal->GetInt();
1165 if (aMode < RWGltf_GltfPrimitiveMode_Points
1166 || aMode > RWGltf_GltfPrimitiveMode_TriangleFan)
1168 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' has unknown mode.");
1172 if (aMode != RWGltf_GltfPrimitiveMode_Triangles)
1174 Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Primitive array within Mesh '"
1175 + theMeshId + "' skipped due to unsupported mode.", Message_Warning);
1178 theMeshData->SetPrimitiveMode (aMode);
1181 if (aMaterial != NULL)
1183 Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
1184 if (myMaterialsPbr.Find (getKeyString (*aMaterial), aMatPbr))
1186 theMeshData->SetMaterialPbr (aMatPbr);
1189 Handle(RWGltf_MaterialCommon) aMatCommon;
1190 if (myMaterialsCommon.Find (getKeyString (*aMaterial), aMatCommon))
1192 theMeshData->SetMaterialCommon (aMatCommon);
1196 bool hasPositions = false;
1197 for (rapidjson::Value::ConstMemberIterator anAttribIter = anAttribs->MemberBegin();
1198 anAttribIter != anAttribs->MemberEnd(); ++anAttribIter)
1200 const TCollection_AsciiString anAttribId = getKeyString (anAttribIter->value);
1201 if (anAttribId.IsEmpty())
1203 reportGltfError ("Primitive array attribute accessor key within Mesh '" + theMeshId + "' is not a string.");
1207 RWGltf_GltfArrayType aType = RWGltf_GltfParseAttribType (anAttribIter->name.GetString());
1208 if (aType == RWGltf_GltfArrayType_UNKNOWN)
1210 // just ignore unknown attributes
1214 const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (anAttribIter->value);
1215 if (anAccessor == NULL
1216 || !anAccessor->IsObject())
1218 reportGltfError ("Primitive array attribute accessor key '" + anAttribId + "' points to non-existing object.");
1221 else if (!gltfParseAccessor (theMeshData, anAttribId, *anAccessor, aType))
1225 else if (aType == RWGltf_GltfArrayType_Position)
1227 hasPositions = true;
1232 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' does not define vertex positions.");
1236 if (anIndices != NULL)
1238 const TCollection_AsciiString anIndicesId = getKeyString (*anIndices);
1239 const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (*anIndices);
1240 if (anAccessor == NULL
1241 || !anAccessor->IsObject())
1243 reportGltfError ("Primitive array indices accessor key '" + anIndicesId + "' points to non-existing object.");
1246 else if (!gltfParseAccessor (theMeshData, anIndicesId, *anAccessor, RWGltf_GltfArrayType_Indices))
1255 // =======================================================================
1256 // function : gltfParseAccessor
1258 // =======================================================================
1259 bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1260 const TCollection_AsciiString& theName,
1261 const RWGltf_JsonValue& theAccessor,
1262 const RWGltf_GltfArrayType theType)
1264 RWGltf_GltfAccessor aStruct;
1265 const RWGltf_JsonValue* aTypeStr = findObjectMember (theAccessor, "type");
1266 const RWGltf_JsonValue* aBufferViewName = findObjectMember (theAccessor, "bufferView");
1267 const RWGltf_JsonValue* aByteOffset = findObjectMember (theAccessor, "byteOffset");
1268 const RWGltf_JsonValue* aByteStride = findObjectMember (theAccessor, "byteStride");
1269 const RWGltf_JsonValue* aCompType = findObjectMember (theAccessor, "componentType");
1270 const RWGltf_JsonValue* aCount = findObjectMember (theAccessor, "count");
1271 if (aTypeStr == NULL
1272 || !aTypeStr->IsString())
1274 reportGltfError ("Accessor '" + theName + "' does not define type.");
1277 aStruct.Type = RWGltf_GltfParseAccessorType (aTypeStr->GetString());
1278 if (aStruct.Type == RWGltf_GltfAccessorLayout_UNKNOWN)
1280 reportGltfError ("Accessor '" + theName + "' has invalid type.");
1284 if (aBufferViewName == NULL)
1286 reportGltfError ("Accessor '" + theName + "' does not define bufferView.");
1289 if (aCompType == NULL
1290 || !aCompType->IsInt())
1292 reportGltfError ("Accessor '" + theName + "' does not define componentType.");
1295 aStruct.ComponentType = (RWGltf_GltfAccessorCompType )aCompType->GetInt();
1296 if (aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int8
1297 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt8
1298 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int16
1299 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt16
1300 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt32
1301 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Float32)
1303 reportGltfError ("Accessor '" + theName + "' defines invalid componentType value.");
1308 || !aCount->IsNumber())
1310 reportGltfError ("Accessor '" + theName + "' does not define count.");
1314 aStruct.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1315 ? (int64_t )aByteOffset->GetDouble()
1317 aStruct.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1318 ? aByteStride->GetInt()
1320 aStruct.Count = (int64_t )aCount->GetDouble();
1322 if (aStruct.ByteOffset < 0)
1324 reportGltfError ("Accessor '" + theName + "' defines invalid byteOffset.");
1327 else if (aStruct.ByteStride < 0
1328 || aStruct.ByteStride > 255)
1330 reportGltfError ("Accessor '" + theName + "' defines invalid byteStride.");
1333 else if (aStruct.Count < 1)
1335 reportGltfError ("Accessor '" + theName + "' defines invalid count.");
1339 // Read Min/Max values for POSITION type. It is used for bounding boxes
1340 if (theType == RWGltf_GltfArrayType_Position)
1342 const RWGltf_JsonValue* aMin = findObjectMember (theAccessor, "min");
1343 const RWGltf_JsonValue* aMax = findObjectMember (theAccessor, "max");
1344 if (aMin != NULL && aMax != NULL)
1346 // Note: Min/Max values can be not defined in glTF file.
1347 // In this case it is not used only.
1348 if (!aMin->IsArray() || !aMax->IsArray() ||
1349 aMin->Size() != 3 || aMax->Size() != 3)
1351 reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max values.");
1355 bool isValidMinMax = true;
1356 gp_Pnt aMinPnt, aMaxPnt;
1357 for (int anIter = 0; anIter < 3; ++anIter)
1359 const RWGltf_JsonValue& aMinVal = (*aMin)[anIter];
1360 const RWGltf_JsonValue& aMaxVal = (*aMax)[anIter];
1361 if (!aMinVal.IsNumber() || !aMaxVal.IsNumber())
1363 reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max value.");
1364 isValidMinMax = false;
1367 aMinPnt.SetCoord (anIter + 1, aMinVal.GetDouble());
1368 aMinPnt.SetCoord (anIter + 1, aMaxVal.GetDouble());
1372 myCSTrsf.TransformPosition (aMinPnt.ChangeCoord());
1373 myCSTrsf.TransformPosition (aMaxPnt.ChangeCoord());
1379 theMeshData->SetBoundingBox (aBox);
1385 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
1386 if (aBufferView == NULL
1387 || !aBufferView->IsObject())
1389 reportGltfError ("Accessor '" + theName + "' refers to non-existing bufferView.");
1393 return gltfParseBufferView (theMeshData, getKeyString (*aBufferViewName), *aBufferView, aStruct, theType);
1396 // =======================================================================
1397 // function : gltfParseBufferView
1399 // =======================================================================
1400 bool RWGltf_GltfJsonParser::gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1401 const TCollection_AsciiString& theName,
1402 const RWGltf_JsonValue& theBufferView,
1403 const RWGltf_GltfAccessor& theAccessor,
1404 const RWGltf_GltfArrayType theType)
1406 RWGltf_GltfBufferView aBuffView;
1407 const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
1408 const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
1409 const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
1410 const RWGltf_JsonValue* aTarget = findObjectMember (theBufferView, "target");
1411 if (aBufferName == NULL)
1413 reportGltfError ("BufferView '" + theName + "' does not define buffer.");
1417 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1418 ? (int64_t )aByteOffset->GetDouble()
1420 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
1421 ? (int64_t )aByteLength->GetDouble()
1423 if (aTarget != NULL && aTarget->IsInt())
1425 aBuffView.Target = (RWGltf_GltfBufferViewTarget )aTarget->GetInt();
1426 if (aBuffView.Target != RWGltf_GltfBufferViewTarget_ARRAY_BUFFER
1427 && aBuffView.Target != RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER)
1429 reportGltfError ("BufferView '" + theName + "' defines invalid target.");
1434 if (aBuffView.ByteLength < 0)
1436 reportGltfError ("BufferView '" + theName + "' defines invalid byteLength.");
1439 else if (aBuffView.ByteOffset < 0)
1441 reportGltfError ("BufferView '" + theName + "' defines invalid byteOffset.");
1445 const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
1447 || !aBuffer->IsObject())
1449 reportGltfError ("BufferView '" + theName + "' refers to non-existing buffer.");
1453 return gltfParseBuffer (theMeshData, getKeyString (*aBufferName), *aBuffer, theAccessor, aBuffView, theType);
1456 // =======================================================================
1457 // function : gltfParseBuffer
1459 // =======================================================================
1460 bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1461 const TCollection_AsciiString& theName,
1462 const RWGltf_JsonValue& theBuffer,
1463 const RWGltf_GltfAccessor& theAccessor,
1464 const RWGltf_GltfBufferView& theView,
1465 const RWGltf_GltfArrayType theType)
1467 //const RWGltf_JsonValue* aType = findObjectMember (theBuffer, "type");
1468 //const RWGltf_JsonValue* aByteLength = findObjectMember (theBuffer, "byteLength");
1469 const RWGltf_JsonValue* anUriVal = findObjectMember (theBuffer, "uri");
1471 int64_t anOffset = theView.ByteOffset + theAccessor.ByteOffset;
1472 bool isBinary = false;
1475 isBinary = IsEqual ("binary_glTF", theName) // glTF 1.0
1476 || anUriVal == NULL; // glTF 2.0
1480 anOffset += myBinBodyOffset;
1482 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1483 aData.Accessor = theAccessor;
1484 aData.StreamOffset = anOffset;
1485 aData.StreamUri = myFilePath;
1489 if (anUriVal == NULL
1490 || !anUriVal->IsString())
1492 reportGltfError ("Buffer '" + theName + "' does not define uri.");
1496 const char* anUriData = anUriVal->GetString();
1497 if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
1499 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1500 aData.Accessor = theAccessor;
1501 aData.StreamOffset = anOffset;
1502 if (!myDecodedBuffers.Find (theName, aData.StreamData))
1504 // it is better decoding in multiple threads
1505 aData.StreamData = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
1506 myDecodedBuffers.Bind (theName, aData.StreamData);
1512 TCollection_AsciiString anUri = anUriData;
1513 if (anUri.IsEmpty())
1515 reportGltfError ("Buffer '" + theName + "' does not define uri.");
1519 TCollection_AsciiString aPath = myFolder + anUri;
1520 bool isFileExist = false;
1521 if (!myProbedFiles.Find (aPath, isFileExist))
1523 isFileExist = OSD_File (aPath).Exists();
1524 myProbedFiles.Bind (aPath, isFileExist);
1528 reportGltfError ("Buffer '" + theName + "' refers to non-existing file '" + anUri + "'.");
1532 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1533 aData.Accessor = theAccessor;
1534 aData.StreamOffset = anOffset;
1535 aData.StreamUri = myFolder + anUri;
1536 if (myExternalFiles != NULL)
1538 myExternalFiles->Add (aData.StreamUri);
1544 // =======================================================================
1545 // function : bindNamedShape
1547 // =======================================================================
1548 void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape,
1549 ShapeMapGroup theGroup,
1550 const TopLoc_Location& theLoc,
1551 const TCollection_AsciiString& theId,
1552 const RWGltf_JsonValue* theUserName)
1554 if (theShape.IsNull())
1559 if (!theLoc.IsIdentity())
1561 theShape.Location (theLoc);
1564 TCollection_AsciiString aUserName;
1565 if (theUserName != NULL
1566 && theUserName->IsString())
1568 aUserName = theUserName->GetString();
1575 myShapeMap[theGroup].Bind (theId, theShape);
1576 if (myAttribMap != NULL)
1578 RWMesh_NodeAttributes aShapeAttribs;
1579 aShapeAttribs.Name = aUserName;
1580 aShapeAttribs.RawName = theId;
1581 if (theShape.ShapeType() == TopAbs_FACE)
1583 TopLoc_Location aDummy;
1584 if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (theShape), aDummy)))
1586 if (aLateData->HasStyle())
1588 aShapeAttribs.Style.SetColorSurf (aLateData->BaseColor());
1592 myAttribMap->Bind (theShape, aShapeAttribs);
1597 // =======================================================================
1600 // =======================================================================
1601 bool RWGltf_GltfJsonParser::Parse (const Handle(Message_ProgressIndicator)& theProgress)
1603 Message_ProgressSentry aPSentry (theProgress, "Reading Gltf", 0, 2, 1);
1604 #ifdef HAVE_RAPIDJSON
1606 if (!gltfParseRoots())
1612 gltfParseMaterials();
1613 if (!gltfParseScene (theProgress))
1619 if (!aPSentry.More())
1625 Message::DefaultMessenger()->Send ("Error: glTF reader is unavailable - OCCT has been built without RapidJSON support.", Message_Fail);