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);
311 gltfBindMaterial (Handle(RWGltf_MaterialMetallicRoughness)(), aMat);
314 else if (aMatList->IsArray())
318 for (rapidjson::Value::ConstValueIterator aMatIter = aMatList->Begin(); aMatIter != aMatList->End(); ++aMatIter, ++aMatIndex)
320 Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
321 const RWGltf_JsonValue& aMatNode = *aMatIter;
322 const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
323 if (gltfParsePbrMaterial (aMatPbr, aMatNode))
326 && aNameVal->IsString())
328 aMatPbr->Name = aNameVal->GetString();
330 aMatPbr->Id = TCollection_AsciiString ("mat_") + aMatIndex;
331 myMaterialsPbr.Bind (TCollection_AsciiString (aMatIndex), aMatPbr);
334 Handle(RWGltf_MaterialCommon) aMatCommon;
335 if (gltfParseCommonMaterial(aMatCommon, aMatNode)
336 || gltfParseStdMaterial (aMatCommon, aMatNode))
339 && aNameVal->IsString())
341 aMatCommon->Name = aNameVal->GetString();
343 aMatCommon->Id = TCollection_AsciiString ("mat_") + aMatIndex;
344 myMaterialsCommon.Bind (TCollection_AsciiString (aMatIndex), aMatCommon);
347 gltfBindMaterial (aMatPbr, aMatCommon);
352 // =======================================================================
353 // function : gltfBindMaterial
355 // =======================================================================
356 void RWGltf_GltfJsonParser::gltfBindMaterial (const Handle(RWGltf_MaterialMetallicRoughness)& theMatPbr,
357 const Handle(RWGltf_MaterialCommon)& theMatCommon)
359 if (theMatPbr.IsNull()
360 && theMatCommon.IsNull())
365 Handle(XCAFDoc_VisMaterial) aMat = new XCAFDoc_VisMaterial();
366 if (!theMatCommon.IsNull())
368 XCAFDoc_VisMaterialCommon aMatXde;
369 aMatXde.IsDefined = true;
370 aMatXde.AmbientColor = theMatCommon->AmbientColor;
371 aMatXde.DiffuseColor = theMatCommon->DiffuseColor;
372 aMatXde.SpecularColor = theMatCommon->SpecularColor;
373 aMatXde.EmissiveColor = theMatCommon->EmissiveColor;
374 aMatXde.Shininess = theMatCommon->Shininess;
375 aMatXde.Transparency = theMatCommon->Transparency;
376 aMatXde.DiffuseTexture = theMatCommon->DiffuseTexture;
377 if (aMatXde.DiffuseTexture.IsNull()
378 && !theMatCommon->AmbientTexture.IsNull())
380 aMatXde.DiffuseTexture = theMatCommon->AmbientTexture;
382 aMat->SetCommonMaterial (aMatXde);
383 if (!theMatCommon->Name.IsEmpty())
385 aMat->SetRawName (new TCollection_HAsciiString (theMatCommon->Name));
388 if (!theMatPbr.IsNull())
390 XCAFDoc_VisMaterialPBR aMatXde;
391 aMatXde.IsDefined = true;
392 aMatXde.MetallicRoughnessTexture = theMatPbr->MetallicRoughnessTexture;
393 aMatXde.BaseColorTexture = theMatPbr->BaseColorTexture;
394 aMatXde.EmissiveTexture = theMatPbr->EmissiveTexture;
395 aMatXde.OcclusionTexture = theMatPbr->OcclusionTexture;
396 aMatXde.NormalTexture = theMatPbr->NormalTexture;
397 aMatXde.BaseColor = theMatPbr->BaseColor;
398 aMatXde.EmissiveFactor = theMatPbr->EmissiveFactor;
399 aMatXde.Metallic = theMatPbr->Metallic;
400 aMatXde.Roughness = theMatPbr->Roughness;
401 aMat->SetPbrMaterial (aMatXde);
403 Graphic3d_AlphaMode anAlphaMode = Graphic3d_AlphaMode_BlendAuto;
404 switch (theMatPbr->AlphaMode)
406 case RWGltf_GltfAlphaMode_Opaque:
408 anAlphaMode = Graphic3d_AlphaMode_Opaque;
409 if (aMatXde.BaseColor.Alpha() < 1.0f)
411 Message::DefaultMessenger()->Send ("glTF reader - material with non-zero Transparency specifies Opaque AlphaMode", Message_Warning);
415 case RWGltf_GltfAlphaMode_Mask:
417 anAlphaMode = Graphic3d_AlphaMode_Mask;
420 case RWGltf_GltfAlphaMode_Blend:
422 anAlphaMode = Graphic3d_AlphaMode_Blend;
426 aMat->SetAlphaMode (anAlphaMode, theMatPbr->AlphaCutOff);
427 aMat->SetDoubleSided (theMatPbr->IsDoubleSided);
429 if (!theMatPbr->Name.IsEmpty())
431 aMat->SetRawName (new TCollection_HAsciiString (theMatPbr->Name));
435 myMaterials.Bind (!theMatPbr.IsNull() ? theMatPbr->Id : theMatCommon->Id, aMat);
438 // =======================================================================
439 // function : gltfParseStdMaterial
441 // =======================================================================
442 bool RWGltf_GltfJsonParser::gltfParseStdMaterial (Handle(RWGltf_MaterialCommon)& theMat,
443 const RWGltf_JsonValue& theMatNode)
445 //const RWGltf_JsonValue* aTechVal = findObjectMember (theMatNode, "technique");
446 const RWGltf_JsonValue* aValues = findObjectMember (theMatNode, "values");
452 const RWGltf_JsonValue* anAmbVal = findObjectMember (*aValues, "ambient");
453 const RWGltf_JsonValue* aDiffVal = findObjectMember (*aValues, "diffuse");
454 const RWGltf_JsonValue* anEmiVal = findObjectMember (*aValues, "emission");
455 const RWGltf_JsonValue* aSpecVal = findObjectMember (*aValues, "specular");
456 const RWGltf_JsonValue* aShinVal = findObjectMember (*aValues, "shininess");
466 theMat = new RWGltf_MaterialCommon();
468 Graphic3d_Vec4d anAmb, aDiff, anEmi, aSpec;
470 && anAmbVal->IsString())
472 gltfParseTexture (theMat->AmbientTexture, anAmbVal);
474 else if (gltfReadVec4 (anAmb, anAmbVal)
475 && validateColor4 (anAmb))
477 theMat->AmbientColor = Quantity_Color (anAmb.r(), anAmb.g(), anAmb.b(), Quantity_TOC_sRGB);
481 && aDiffVal->IsString())
483 gltfParseTexture (theMat->DiffuseTexture, aDiffVal);
485 else if (gltfReadVec4 (aDiff, aDiffVal)
486 && validateColor4 (aDiff))
488 theMat->DiffuseColor = Quantity_Color (aDiff.r(), aDiff.g(), aDiff.b(), Quantity_TOC_sRGB);
489 theMat->Transparency = float(1.0 - aDiff.a());
492 if (gltfReadVec4 (anEmi, anEmiVal)
493 && validateColor4 (anEmi))
495 theMat->EmissiveColor = Quantity_Color (anEmi.r(), anEmi.g(), anEmi.b(), Quantity_TOC_sRGB);
499 && aSpecVal->IsString())
501 gltfParseTexture (theMat->SpecularTexture, aSpecVal);
503 if (gltfReadVec4 (aSpec, aSpecVal)
504 && validateColor4 (aSpec))
506 theMat->SpecularColor = Quantity_Color (aSpec.r(), aSpec.g(), aSpec.b(), Quantity_TOC_sRGB);
510 && aShinVal->IsNumber())
512 const double aSpecular = aShinVal->GetDouble();
515 theMat->Shininess = (float )Min (aSpecular / 1000.0, 1.0);
521 // =======================================================================
522 // function : gltfParsePbrMaterial
524 // =======================================================================
525 bool RWGltf_GltfJsonParser::gltfParsePbrMaterial (Handle(RWGltf_MaterialMetallicRoughness)& theMat,
526 const RWGltf_JsonValue& theMatNode)
528 /*if (const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions"))
530 if (const RWGltf_JsonValue* anExtDefVal = findObjectMember (*anExtVal, "KHR_materials_pbrSpecularGlossiness"))
532 const RWGltf_JsonValue* aDiffTexVal = findObjectMember (*anExtDefVal, "diffuseTexture");
533 const RWGltf_JsonValue* aSpecTexVal = findObjectMember (*anExtDefVal, "specularGlossinessTexture");
537 const RWGltf_JsonValue* aMetalRoughVal = findObjectMember (theMatNode, "pbrMetallicRoughness");
538 const RWGltf_JsonValue* aNormTexVal = findObjectMember (theMatNode, "normalTexture");
539 const RWGltf_JsonValue* anEmissFactorVal = findObjectMember (theMatNode, "emissiveFactor");
540 const RWGltf_JsonValue* anEmissTexVal = findObjectMember (theMatNode, "emissiveTexture");
541 const RWGltf_JsonValue* anOcclusionTexVal = findObjectMember (theMatNode, "occlusionTexture");
542 const RWGltf_JsonValue* aDoubleSidedVal = findObjectMember (theMatNode, "doubleSided");
543 const RWGltf_JsonValue* anAlphaModeVal = findObjectMember (theMatNode, "alphaMode");
544 const RWGltf_JsonValue* anAlphaCutoffVal = findObjectMember (theMatNode, "alphaCutoff");
545 // TODO ADOBE_materials_thin_transparency extension can be used to read IOR (Index of Refraction for transparent materials)
546 if (aMetalRoughVal == NULL)
551 theMat = new RWGltf_MaterialMetallicRoughness();
552 const RWGltf_JsonValue* aBaseColorFactorVal = findObjectMember (*aMetalRoughVal, "baseColorFactor");
553 const RWGltf_JsonValue* aBaseColorTexVal = findObjectMember (*aMetalRoughVal, "baseColorTexture");
554 const RWGltf_JsonValue* aMetallicFactorVal = findObjectMember (*aMetalRoughVal, "metallicFactor");
555 const RWGltf_JsonValue* aRoughnessFactorVal = findObjectMember (*aMetalRoughVal, "roughnessFactor");
556 const RWGltf_JsonValue* aMetalRoughTexVal = findObjectMember (*aMetalRoughVal, "metallicRoughnessTexture");
558 if (aDoubleSidedVal != NULL
559 && aDoubleSidedVal->IsBool())
561 theMat->IsDoubleSided = aDoubleSidedVal->GetBool();
563 if (anAlphaCutoffVal != NULL
564 && anAlphaCutoffVal->IsNumber())
566 theMat->AlphaCutOff = (float )anAlphaCutoffVal->GetDouble();
568 if (anAlphaModeVal != NULL
569 && anAlphaModeVal->IsString())
571 theMat->AlphaMode = RWGltf_GltfParseAlphaMode (anAlphaModeVal->GetString());
574 if (aBaseColorTexVal != NULL
575 && aBaseColorTexVal->IsObject())
577 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aBaseColorTexVal, "index"))
579 gltfParseTexture (theMat->BaseColorTexture, aTexIndexVal);
583 Graphic3d_Vec4d aBaseColorFactor;
584 if (gltfReadVec4 (aBaseColorFactor, aBaseColorFactorVal)
585 && validateColor4 (aBaseColorFactor))
587 theMat->BaseColor = Quantity_ColorRGBA (Graphic3d_Vec4 (aBaseColorFactor));
590 Graphic3d_Vec3d anEmissiveFactor;
591 if (gltfReadVec3 (anEmissiveFactor, anEmissFactorVal)
592 && validateColor3 (anEmissiveFactor))
594 theMat->EmissiveFactor = Graphic3d_Vec3 (anEmissiveFactor);
597 if (aMetalRoughTexVal != NULL
598 && aMetalRoughTexVal->IsObject())
600 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aMetalRoughTexVal, "index"))
602 gltfParseTexture (theMat->MetallicRoughnessTexture, aTexIndexVal);
606 if (aMetallicFactorVal != NULL
607 && aMetallicFactorVal->IsNumber())
609 theMat->Metallic = (float )aMetallicFactorVal->GetDouble();
612 if (aRoughnessFactorVal != NULL
613 && aRoughnessFactorVal->IsNumber())
615 theMat->Roughness = (float )aRoughnessFactorVal->GetDouble();
618 if (aNormTexVal != NULL
619 && aNormTexVal->IsObject())
621 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aNormTexVal, "index"))
623 gltfParseTexture (theMat->NormalTexture, aTexIndexVal);
627 if (anEmissTexVal != NULL
628 && anEmissTexVal->IsObject())
630 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anEmissTexVal, "index"))
632 gltfParseTexture (theMat->EmissiveTexture, aTexIndexVal);
636 if (anOcclusionTexVal != NULL
637 && anOcclusionTexVal->IsObject())
639 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anOcclusionTexVal, "index"))
641 gltfParseTexture (theMat->OcclusionTexture, aTexIndexVal);
647 // =======================================================================
648 // function : gltfParseCommonMaterial
650 // =======================================================================
651 bool RWGltf_GltfJsonParser::gltfParseCommonMaterial (Handle(RWGltf_MaterialCommon)& theMat,
652 const RWGltf_JsonValue& theMatNode)
654 const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions");
655 if (anExtVal == NULL)
660 const RWGltf_JsonValue* aMatCommon = findObjectMember (*anExtVal, THE_KHR_materials_common);
661 if (aMatCommon == NULL)
666 if (!gltfParseStdMaterial (theMat, *aMatCommon))
673 // =======================================================================
674 // function : gltfParseTexture
676 // =======================================================================
677 bool RWGltf_GltfJsonParser::gltfParseTexture (Handle(Image_Texture)& theTexture,
678 const RWGltf_JsonValue* theTextureId)
680 if (theTextureId == NULL
681 || myGltfRoots[RWGltf_GltfRootElement_Textures].IsNull()
682 || myGltfRoots[RWGltf_GltfRootElement_Images].IsNull())
687 const TCollection_AsciiString aTextureId = getKeyString (*theTextureId);
688 const RWGltf_JsonValue* aTexNode = myGltfRoots[RWGltf_GltfRootElement_Textures].FindChild (*theTextureId);
689 if (aTexNode == NULL)
691 reportGltfWarning ("Texture node '" + aTextureId + "' is not found.");
695 const RWGltf_JsonValue* aSrcVal = findObjectMember (*aTexNode, "source");
696 const RWGltf_JsonValue* aTargVal = findObjectMember (*aTexNode, "target");
699 reportGltfWarning ("Invalid texture node '" + aTextureId + "' without a 'source' property.");
703 && aTargVal->IsNumber()
704 && aTargVal->GetInt() != 3553) // GL_TEXTURE_2D
709 const RWGltf_JsonValue* anImgNode = myGltfRoots[RWGltf_GltfRootElement_Images].FindChild (*aSrcVal);
710 if (anImgNode == NULL)
712 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to non-existing image '" + getKeyString (*aSrcVal) + "'.");
718 const RWGltf_JsonValue* aBinVal = NULL;
719 const RWGltf_JsonValue* aBufferViewName = findObjectMember (*anImgNode, "bufferView");
720 if (aBufferViewName != NULL)
726 const RWGltf_JsonValue* anExtVal = findObjectMember (*anImgNode, "extensions");
727 if (anExtVal != NULL)
729 aBinVal = findObjectMember (*anExtVal, THE_KHR_binary_glTF);
732 aBufferViewName = findObjectMember (*aBinVal, "bufferView");
739 //const RWGltf_JsonValue* aMimeTypeVal = findObjectMember (*aBinVal, "mimeType");
740 //const RWGltf_JsonValue* aWidthVal = findObjectMember (*aBinVal, "width");
741 //const RWGltf_JsonValue* aHeightVal = findObjectMember (*aBinVal, "height");
742 if (aBufferViewName == NULL)
744 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
748 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
749 if (aBufferView == NULL
750 || !aBufferView->IsObject())
752 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid buffer view '" + getKeyString (*aBufferViewName) + "'.");
756 const RWGltf_JsonValue* aBufferName = findObjectMember (*aBufferView, "buffer");
757 const RWGltf_JsonValue* aByteLength = findObjectMember (*aBufferView, "byteLength");
758 const RWGltf_JsonValue* aByteOffset = findObjectMember (*aBufferView, "byteOffset");
759 if (aBufferName != NULL
760 && aBufferName->IsString()
761 && !IsEqual (aBufferName->GetString(), "binary_glTF"))
763 reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' does not define binary_glTF buffer.");
767 RWGltf_GltfBufferView aBuffView;
768 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
769 ? (int64_t )aByteOffset->GetDouble()
771 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
772 ? (int64_t )aByteLength->GetDouble()
774 if (aBuffView.ByteLength < 0)
776 reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' defines invalid byteLength.");
779 else if (aBuffView.ByteOffset < 0)
781 reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' defines invalid byteOffset.");
786 const int64_t anOffset = myBinBodyOffset + aBuffView.ByteOffset;
787 theTexture = new Image_Texture (myFilePath, anOffset, aBuffView.ByteLength);
792 const RWGltf_JsonValue* anUriVal = findObjectMember (*anImgNode, "uri");
794 || !anUriVal->IsString())
799 const char* anUriData = anUriVal->GetString();
800 if (::strncmp (anUriData, "data:", 5) == 0) // data:image/png;base64
802 // uncompressing base64 here is inefficient, because the same image can be shared by several nodes
803 const char* aDataStart = anUriData + 5;
804 for (const char* aDataIter = aDataStart; *aDataIter != '\0'; ++aDataIter)
806 if (::memcmp (aDataIter, ";base64,", 8) == 0)
808 const char* aBase64End = anUriData + anUriVal->GetStringLength();
809 const char* aBase64Data = aDataIter + 8;
810 const size_t aBase64Len = size_t(aBase64End - aBase64Data);
811 //const TCollection_AsciiString aMime (aDataStart, aDataIter - aDataStart);
812 Handle(NCollection_Buffer) aData = FSD_Base64Decoder::Decode ((const Standard_Byte* )aBase64Data, aBase64Len);
813 theTexture = new Image_Texture (aData, myFilePath + "@" + getKeyString (*aSrcVal));
817 Message::DefaultMessenger()->Send ("glTF reader - embedded image has been skipped", Message_Warning);
821 TCollection_AsciiString anImageFile = myFolder + anUriVal->GetString();
822 theTexture = new Image_Texture (anImageFile);
823 if (myExternalFiles != NULL)
825 myExternalFiles->Add (anImageFile);
830 // =======================================================================
831 // function : gltfParseScene
833 // =======================================================================
834 bool RWGltf_GltfJsonParser::gltfParseScene (const Handle(Message_ProgressIndicator)& theProgress)
836 // search default scene
837 const RWGltf_JsonValue* aDefScene = myGltfRoots[RWGltf_GltfRootElement_Scenes].FindChild (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root());
838 if (aDefScene == NULL)
840 reportGltfError ("Default scene is not found.");
844 const RWGltf_JsonValue* aSceneNodes = findObjectMember (*aDefScene, "nodes");
845 if (aSceneNodes == NULL
846 || !aSceneNodes->IsArray())
848 reportGltfError ("Empty scene '" + getKeyString (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root()) + "'.");
852 return gltfParseSceneNodes (*myRootShapes, *aSceneNodes, theProgress);
855 // =======================================================================
856 // function : gltfParseSceneNodes
858 // =======================================================================
859 bool RWGltf_GltfJsonParser::gltfParseSceneNodes (TopTools_SequenceOfShape& theShapeSeq,
860 const RWGltf_JsonValue& theSceneNodes,
861 const Handle(Message_ProgressIndicator)& theProgress)
863 if (!theSceneNodes.IsArray())
865 reportGltfError ("Scene nodes is not array.");
869 Message_ProgressSentry aPSentry (theProgress, "Reading scene nodes", 0, theSceneNodes.Size(), 1);
870 for (rapidjson::Value::ConstValueIterator aSceneNodeIter = theSceneNodes.Begin();
871 aSceneNodeIter != theSceneNodes.End() && aPSentry.More(); ++aSceneNodeIter, aPSentry.Next())
873 const RWGltf_JsonValue* aSceneNode = myGltfRoots[RWGltf_GltfRootElement_Nodes].FindChild (*aSceneNodeIter);
874 if (aSceneNode == NULL)
876 reportGltfWarning ("Scene refers to non-existing node '" + getKeyString (*aSceneNodeIter) + "'.");
880 TopoDS_Shape aNodeShape;
881 if (!gltfParseSceneNode (aNodeShape, getKeyString (*aSceneNodeIter), *aSceneNode, theProgress))
886 if (aNodeShape.IsNull())
890 else if (myToSkipEmptyNodes
891 && !TopExp_Explorer (aNodeShape, TopAbs_FACE).More())
896 theShapeSeq.Append (aNodeShape);
901 // =======================================================================
902 // function : gltfParseSceneNode
904 // =======================================================================
905 bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
906 const TCollection_AsciiString& theSceneNodeId,
907 const RWGltf_JsonValue& theSceneNode,
908 const Handle(Message_ProgressIndicator)& theProgress)
910 const RWGltf_JsonValue* aName = findObjectMember (theSceneNode, "name");
911 //const RWGltf_JsonValue* aJointName = findObjectMember (theSceneNode, "jointName");
912 const RWGltf_JsonValue* aChildren = findObjectMember (theSceneNode, "children");
913 const RWGltf_JsonValue* aMeshes_1 = findObjectMember (theSceneNode, "meshes");
914 const RWGltf_JsonValue* aMesh_2 = findObjectMember (theSceneNode, "mesh");
915 //const RWGltf_JsonValue* aCamera = findObjectMember (theSceneNode, "camera");
916 const RWGltf_JsonValue* aTrsfMatVal = findObjectMember (theSceneNode, "matrix");
917 const RWGltf_JsonValue* aTrsfRotVal = findObjectMember (theSceneNode, "rotation");
918 const RWGltf_JsonValue* aTrsfScaleVal = findObjectMember (theSceneNode, "scale");
919 const RWGltf_JsonValue* aTrsfTransVal = findObjectMember (theSceneNode, "translation");
920 if (findNodeShape (theNodeShape, theSceneNodeId))
925 TopLoc_Location aNodeLoc;
926 const bool hasTrs = aTrsfRotVal != NULL
927 || aTrsfScaleVal != NULL
928 || aTrsfTransVal != NULL;
929 if (aTrsfMatVal != NULL)
933 reportGltfError ("Scene node '" + theSceneNodeId + "' defines ambiguous transformation.");
936 else if (!aTrsfMatVal->IsArray()
937 || aTrsfMatVal->Size() != 16)
939 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix array.");
943 Graphic3d_Mat4d aMat4;
944 for (int aColIter = 0; aColIter < 4; ++aColIter)
946 for (int aRowIter = 0; aRowIter < 4; ++aRowIter)
948 const RWGltf_JsonValue& aGenVal = (*aTrsfMatVal)[aColIter * 4 + aRowIter];
949 if (!aGenVal.IsNumber())
951 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix.");
954 aMat4.SetValue (aRowIter, aColIter, aGenVal.GetDouble());
958 if (!aMat4.IsIdentity())
961 aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
962 aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
963 aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
964 myCSTrsf.TransformTransformation (aTrsf);
965 if (aTrsf.Form() != gp_Identity)
967 aNodeLoc = TopLoc_Location (aTrsf);
974 if (aTrsfRotVal != NULL)
976 if (!aTrsfRotVal->IsArray()
977 || aTrsfRotVal->Size() != 4)
979 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation quaternion.");
983 Graphic3d_Vec4d aRotVec4;
984 for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
986 const RWGltf_JsonValue& aGenVal = (*aTrsfRotVal)[aCompIter];
987 if (!aGenVal.IsNumber())
989 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation.");
992 aRotVec4[aCompIter] = aGenVal.GetDouble();
994 const gp_Quaternion aQuaternion (aRotVec4.x(), aRotVec4.y(), aRotVec4.z(), aRotVec4.w());
995 if (Abs (aQuaternion.X()) > gp::Resolution()
996 || Abs (aQuaternion.Y()) > gp::Resolution()
997 || Abs (aQuaternion.Z()) > gp::Resolution()
998 || Abs (aQuaternion.W() - 1.0) > gp::Resolution())
1000 aTrsf.SetRotation (aQuaternion);
1004 if (aTrsfTransVal != NULL)
1006 if (!aTrsfTransVal->IsArray()
1007 || aTrsfTransVal->Size() != 3)
1009 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation vector.");
1014 for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
1016 const RWGltf_JsonValue& aGenVal = (*aTrsfTransVal)[aCompIter];
1017 if (!aGenVal.IsNumber())
1019 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation.");
1022 aTransVec.SetCoord (aCompIter + 1, aGenVal.GetDouble());
1024 aTrsf.SetTranslationPart (aTransVec);
1027 if (aTrsfScaleVal != NULL)
1029 Graphic3d_Vec3d aScaleVec;
1030 if (!aTrsfScaleVal->IsArray()
1031 || aTrsfScaleVal->Size() != 3)
1033 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale vector.");
1036 for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
1038 const RWGltf_JsonValue& aGenVal = (*aTrsfScaleVal)[aCompIter];
1039 if (!aGenVal.IsNumber())
1041 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
1044 aScaleVec[aCompIter] = aGenVal.GetDouble();
1045 if (Abs (aScaleVec[aCompIter]) <= gp::Resolution())
1047 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
1052 if (Abs (aScaleVec.x() - aScaleVec.y()) > Precision::Confusion()
1053 || Abs (aScaleVec.y() - aScaleVec.z()) > Precision::Confusion()
1054 || Abs (aScaleVec.x() - aScaleVec.z()) > Precision::Confusion())
1056 Graphic3d_Mat4d aScaleMat;
1057 aScaleMat.SetDiagonal (aScaleVec);
1059 Graphic3d_Mat4d aMat4;
1060 aTrsf.GetMat4 (aMat4);
1062 aMat4 = aMat4 * aScaleMat;
1064 aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
1065 aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
1066 aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
1068 Message::DefaultMessenger()->Send (TCollection_AsciiString ("glTF reader, scene node '")
1069 + theSceneNodeId + "' defines unsupported scaling "
1070 + aScaleVec.x() + " " + aScaleVec.y() + " " + aScaleVec.z(), Message_Warning);
1072 else if (Abs (aScaleVec.x() - 1.0) > Precision::Confusion())
1074 aTrsf.SetScaleFactor (aScaleVec.x());
1078 myCSTrsf.TransformTransformation (aTrsf);
1079 if (aTrsf.Form() != gp_Identity)
1081 aNodeLoc = TopLoc_Location (aTrsf);
1085 BRep_Builder aBuilder;
1086 TopoDS_Compound aNodeShape;
1087 aBuilder.MakeCompound (aNodeShape);
1088 TopTools_SequenceOfShape aChildShapes;
1089 int aNbSubShapes = 0;
1090 if (aChildren != NULL
1091 && !gltfParseSceneNodes (aChildShapes, *aChildren, theProgress))
1093 theNodeShape = aNodeShape;
1094 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1097 for (TopTools_SequenceOfShape::Iterator aChildShapeIter (aChildShapes); aChildShapeIter.More(); aChildShapeIter.Next())
1099 aBuilder.Add (aNodeShape, aChildShapeIter.Value());
1103 if (aMeshes_1 != NULL
1104 && aMeshes_1->IsArray())
1107 Message_ProgressSentry aPSentry (theProgress, "Reading scene meshes", 0, aMeshes_1->Size(), 1);
1108 for (rapidjson::Value::ConstValueIterator aMeshIter = aMeshes_1->Begin();
1109 aMeshIter != aMeshes_1->End() && aPSentry.More(); ++aMeshIter, aPSentry.Next())
1111 const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMeshIter);
1114 theNodeShape = aNodeShape;
1115 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1116 reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1120 TopoDS_Shape aMeshShape;
1121 if (!gltfParseMesh (aMeshShape, getKeyString (*aMeshIter), *aMesh, theProgress))
1123 theNodeShape = aNodeShape;
1124 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1127 if (!aMeshShape.IsNull())
1129 aBuilder.Add (aNodeShape, aMeshShape);
1134 if (aMesh_2 != NULL)
1137 const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMesh_2);
1140 theNodeShape = aNodeShape;
1141 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1142 reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1146 TopoDS_Shape aMeshShape;
1147 if (!gltfParseMesh (aMeshShape, getKeyString (*aMesh_2), *aMesh, theProgress))
1149 theNodeShape = aNodeShape;
1150 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1153 if (!aMeshShape.IsNull())
1155 aBuilder.Add (aNodeShape, aMeshShape);
1160 if (aNbSubShapes == 1)
1162 theNodeShape = TopoDS_Iterator (aNodeShape).Value();
1166 theNodeShape = aNodeShape;
1168 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1172 // =======================================================================
1173 // function : gltfParseMesh
1175 // =======================================================================
1176 bool RWGltf_GltfJsonParser::gltfParseMesh (TopoDS_Shape& theMeshShape,
1177 const TCollection_AsciiString& theMeshId,
1178 const RWGltf_JsonValue& theMesh,
1179 const Handle(Message_ProgressIndicator)& theProgress)
1181 const RWGltf_JsonValue* aName = findObjectMember (theMesh, "name");
1182 const RWGltf_JsonValue* aPrims = findObjectMember (theMesh, "primitives");
1183 if (!aPrims->IsArray())
1185 reportGltfError ("Primitive array attributes within Mesh '" + theMeshId + "' is not an array.");
1189 if (findMeshShape (theMeshShape, theMeshId))
1194 BRep_Builder aBuilder;
1195 TopoDS_Compound aMeshShape;
1197 for (rapidjson::Value::ConstValueIterator aPrimArrIter = aPrims->Begin();
1198 aPrimArrIter != aPrims->End(); ++aPrimArrIter)
1200 TCollection_AsciiString aUserName;
1202 && aName->IsString())
1204 aUserName = aName->GetString();
1207 Handle(RWGltf_GltfLatePrimitiveArray) aMeshData = new RWGltf_GltfLatePrimitiveArray (theMeshId, aUserName);
1208 if (!gltfParsePrimArray (aMeshData, theMeshId, *aPrimArrIter, theProgress))
1213 if (!aMeshData->Data().IsEmpty())
1215 if (aMeshShape.IsNull())
1217 aBuilder.MakeCompound (aMeshShape);
1221 aBuilder.MakeFace (aFace, aMeshData);
1222 aBuilder.Add (aMeshShape, aFace);
1223 if (myAttribMap != NULL
1224 && aMeshData->HasStyle())
1226 RWMesh_NodeAttributes aShapeAttribs;
1227 aShapeAttribs.RawName = aUserName;
1229 // assign material and not color
1230 //aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor());
1232 Handle(XCAFDoc_VisMaterial) aMat;
1233 myMaterials.Find (!aMeshData->MaterialPbr().IsNull() ? aMeshData->MaterialPbr()->Id : aMeshData->MaterialCommon()->Id, aMat);
1234 aShapeAttribs.Style.SetMaterial (aMat);
1236 myAttribMap->Bind (aFace, aShapeAttribs);
1238 myFaceList.Append (aFace);
1245 theMeshShape = TopoDS_Iterator (aMeshShape).Value();
1249 theMeshShape = aMeshShape;
1251 bindMeshShape (theMeshShape, theMeshId, aName);
1255 // =======================================================================
1256 // function : gltfParsePrimArray
1258 // =======================================================================
1259 bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1260 const TCollection_AsciiString& theMeshId,
1261 const RWGltf_JsonValue& thePrimArray,
1262 const Handle(Message_ProgressIndicator)& /*theProgress*/)
1264 const RWGltf_JsonValue* anAttribs = findObjectMember (thePrimArray, "attributes");
1265 const RWGltf_JsonValue* anIndices = findObjectMember (thePrimArray, "indices");
1266 const RWGltf_JsonValue* aMaterial = findObjectMember (thePrimArray, "material");
1267 const RWGltf_JsonValue* aModeVal = findObjectMember (thePrimArray, "mode");
1268 RWGltf_GltfPrimitiveMode aMode = RWGltf_GltfPrimitiveMode_Triangles;
1269 if (anAttribs == NULL
1270 || !anAttribs->IsObject())
1272 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' defines no attributes.");
1275 else if (aModeVal != NULL)
1277 aMode = RWGltf_GltfPrimitiveMode_UNKNOWN;
1278 if (aModeVal->IsInt())
1280 aMode = (RWGltf_GltfPrimitiveMode )aModeVal->GetInt();
1282 if (aMode < RWGltf_GltfPrimitiveMode_Points
1283 || aMode > RWGltf_GltfPrimitiveMode_TriangleFan)
1285 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' has unknown mode.");
1289 if (aMode != RWGltf_GltfPrimitiveMode_Triangles)
1291 Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Primitive array within Mesh '"
1292 + theMeshId + "' skipped due to unsupported mode.", Message_Warning);
1295 theMeshData->SetPrimitiveMode (aMode);
1298 if (aMaterial != NULL)
1300 Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
1301 if (myMaterialsPbr.Find (getKeyString (*aMaterial), aMatPbr))
1303 theMeshData->SetMaterialPbr (aMatPbr);
1306 Handle(RWGltf_MaterialCommon) aMatCommon;
1307 if (myMaterialsCommon.Find (getKeyString (*aMaterial), aMatCommon))
1309 theMeshData->SetMaterialCommon (aMatCommon);
1313 bool hasPositions = false;
1314 for (rapidjson::Value::ConstMemberIterator anAttribIter = anAttribs->MemberBegin();
1315 anAttribIter != anAttribs->MemberEnd(); ++anAttribIter)
1317 const TCollection_AsciiString anAttribId = getKeyString (anAttribIter->value);
1318 if (anAttribId.IsEmpty())
1320 reportGltfError ("Primitive array attribute accessor key within Mesh '" + theMeshId + "' is not a string.");
1324 RWGltf_GltfArrayType aType = RWGltf_GltfParseAttribType (anAttribIter->name.GetString());
1325 if (aType == RWGltf_GltfArrayType_UNKNOWN)
1327 // just ignore unknown attributes
1331 const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (anAttribIter->value);
1332 if (anAccessor == NULL
1333 || !anAccessor->IsObject())
1335 reportGltfError ("Primitive array attribute accessor key '" + anAttribId + "' points to non-existing object.");
1338 else if (!gltfParseAccessor (theMeshData, anAttribId, *anAccessor, aType))
1342 else if (aType == RWGltf_GltfArrayType_Position)
1344 hasPositions = true;
1349 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' does not define vertex positions.");
1353 if (anIndices != NULL)
1355 const TCollection_AsciiString anIndicesId = getKeyString (*anIndices);
1356 const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (*anIndices);
1357 if (anAccessor == NULL
1358 || !anAccessor->IsObject())
1360 reportGltfError ("Primitive array indices accessor key '" + anIndicesId + "' points to non-existing object.");
1363 else if (!gltfParseAccessor (theMeshData, anIndicesId, *anAccessor, RWGltf_GltfArrayType_Indices))
1372 // =======================================================================
1373 // function : gltfParseAccessor
1375 // =======================================================================
1376 bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1377 const TCollection_AsciiString& theName,
1378 const RWGltf_JsonValue& theAccessor,
1379 const RWGltf_GltfArrayType theType)
1381 RWGltf_GltfAccessor aStruct;
1382 const RWGltf_JsonValue* aTypeStr = findObjectMember (theAccessor, "type");
1383 const RWGltf_JsonValue* aBufferViewName = findObjectMember (theAccessor, "bufferView");
1384 const RWGltf_JsonValue* aByteOffset = findObjectMember (theAccessor, "byteOffset");
1385 const RWGltf_JsonValue* aByteStride = findObjectMember (theAccessor, "byteStride");
1386 const RWGltf_JsonValue* aCompType = findObjectMember (theAccessor, "componentType");
1387 const RWGltf_JsonValue* aCount = findObjectMember (theAccessor, "count");
1388 if (aTypeStr == NULL
1389 || !aTypeStr->IsString())
1391 reportGltfError ("Accessor '" + theName + "' does not define type.");
1394 aStruct.Type = RWGltf_GltfParseAccessorType (aTypeStr->GetString());
1395 if (aStruct.Type == RWGltf_GltfAccessorLayout_UNKNOWN)
1397 reportGltfError ("Accessor '" + theName + "' has invalid type.");
1401 if (aBufferViewName == NULL)
1403 reportGltfError ("Accessor '" + theName + "' does not define bufferView.");
1406 if (aCompType == NULL
1407 || !aCompType->IsInt())
1409 reportGltfError ("Accessor '" + theName + "' does not define componentType.");
1412 aStruct.ComponentType = (RWGltf_GltfAccessorCompType )aCompType->GetInt();
1413 if (aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int8
1414 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt8
1415 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int16
1416 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt16
1417 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt32
1418 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Float32)
1420 reportGltfError ("Accessor '" + theName + "' defines invalid componentType value.");
1425 || !aCount->IsNumber())
1427 reportGltfError ("Accessor '" + theName + "' does not define count.");
1431 aStruct.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1432 ? (int64_t )aByteOffset->GetDouble()
1434 aStruct.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1435 ? aByteStride->GetInt()
1437 aStruct.Count = (int64_t )aCount->GetDouble();
1439 if (aStruct.ByteOffset < 0)
1441 reportGltfError ("Accessor '" + theName + "' defines invalid byteOffset.");
1444 else if (aStruct.ByteStride < 0
1445 || aStruct.ByteStride > 255)
1447 reportGltfError ("Accessor '" + theName + "' defines invalid byteStride.");
1450 else if (aStruct.Count < 1)
1452 reportGltfError ("Accessor '" + theName + "' defines invalid count.");
1456 // Read Min/Max values for POSITION type. It is used for bounding boxes
1457 if (theType == RWGltf_GltfArrayType_Position)
1459 const RWGltf_JsonValue* aMin = findObjectMember (theAccessor, "min");
1460 const RWGltf_JsonValue* aMax = findObjectMember (theAccessor, "max");
1461 if (aMin != NULL && aMax != NULL)
1463 // Note: Min/Max values can be not defined in glTF file.
1464 // In this case it is not used only.
1465 if (!aMin->IsArray() || !aMax->IsArray() ||
1466 aMin->Size() != 3 || aMax->Size() != 3)
1468 reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max values.");
1472 bool isValidMinMax = true;
1473 gp_Pnt aMinPnt, aMaxPnt;
1474 for (int anIter = 0; anIter < 3; ++anIter)
1476 const RWGltf_JsonValue& aMinVal = (*aMin)[anIter];
1477 const RWGltf_JsonValue& aMaxVal = (*aMax)[anIter];
1478 if (!aMinVal.IsNumber() || !aMaxVal.IsNumber())
1480 reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max value.");
1481 isValidMinMax = false;
1484 aMinPnt.SetCoord (anIter + 1, aMinVal.GetDouble());
1485 aMinPnt.SetCoord (anIter + 1, aMaxVal.GetDouble());
1489 myCSTrsf.TransformPosition (aMinPnt.ChangeCoord());
1490 myCSTrsf.TransformPosition (aMaxPnt.ChangeCoord());
1496 theMeshData->SetBoundingBox (aBox);
1502 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
1503 if (aBufferView == NULL
1504 || !aBufferView->IsObject())
1506 reportGltfError ("Accessor '" + theName + "' refers to non-existing bufferView.");
1510 return gltfParseBufferView (theMeshData, getKeyString (*aBufferViewName), *aBufferView, aStruct, theType);
1513 // =======================================================================
1514 // function : gltfParseBufferView
1516 // =======================================================================
1517 bool RWGltf_GltfJsonParser::gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1518 const TCollection_AsciiString& theName,
1519 const RWGltf_JsonValue& theBufferView,
1520 const RWGltf_GltfAccessor& theAccessor,
1521 const RWGltf_GltfArrayType theType)
1523 RWGltf_GltfBufferView aBuffView;
1524 const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
1525 const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
1526 const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
1527 const RWGltf_JsonValue* aTarget = findObjectMember (theBufferView, "target");
1528 if (aBufferName == NULL)
1530 reportGltfError ("BufferView '" + theName + "' does not define buffer.");
1534 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1535 ? (int64_t )aByteOffset->GetDouble()
1537 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
1538 ? (int64_t )aByteLength->GetDouble()
1540 if (aTarget != NULL && aTarget->IsInt())
1542 aBuffView.Target = (RWGltf_GltfBufferViewTarget )aTarget->GetInt();
1543 if (aBuffView.Target != RWGltf_GltfBufferViewTarget_ARRAY_BUFFER
1544 && aBuffView.Target != RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER)
1546 reportGltfError ("BufferView '" + theName + "' defines invalid target.");
1551 if (aBuffView.ByteLength < 0)
1553 reportGltfError ("BufferView '" + theName + "' defines invalid byteLength.");
1556 else if (aBuffView.ByteOffset < 0)
1558 reportGltfError ("BufferView '" + theName + "' defines invalid byteOffset.");
1562 const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
1564 || !aBuffer->IsObject())
1566 reportGltfError ("BufferView '" + theName + "' refers to non-existing buffer.");
1570 return gltfParseBuffer (theMeshData, getKeyString (*aBufferName), *aBuffer, theAccessor, aBuffView, theType);
1573 // =======================================================================
1574 // function : gltfParseBuffer
1576 // =======================================================================
1577 bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1578 const TCollection_AsciiString& theName,
1579 const RWGltf_JsonValue& theBuffer,
1580 const RWGltf_GltfAccessor& theAccessor,
1581 const RWGltf_GltfBufferView& theView,
1582 const RWGltf_GltfArrayType theType)
1584 //const RWGltf_JsonValue* aType = findObjectMember (theBuffer, "type");
1585 //const RWGltf_JsonValue* aByteLength = findObjectMember (theBuffer, "byteLength");
1586 const RWGltf_JsonValue* anUriVal = findObjectMember (theBuffer, "uri");
1588 int64_t anOffset = theView.ByteOffset + theAccessor.ByteOffset;
1589 bool isBinary = false;
1592 isBinary = IsEqual ("binary_glTF", theName) // glTF 1.0
1593 || anUriVal == NULL; // glTF 2.0
1597 anOffset += myBinBodyOffset;
1599 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1600 aData.Accessor = theAccessor;
1601 aData.StreamOffset = anOffset;
1602 aData.StreamUri = myFilePath;
1606 if (anUriVal == NULL
1607 || !anUriVal->IsString())
1609 reportGltfError ("Buffer '" + theName + "' does not define uri.");
1613 const char* anUriData = anUriVal->GetString();
1614 if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
1616 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1617 aData.Accessor = theAccessor;
1618 aData.StreamOffset = anOffset;
1619 if (!myDecodedBuffers.Find (theName, aData.StreamData))
1621 // it is better decoding in multiple threads
1622 aData.StreamData = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
1623 myDecodedBuffers.Bind (theName, aData.StreamData);
1629 TCollection_AsciiString anUri = anUriData;
1630 if (anUri.IsEmpty())
1632 reportGltfError ("Buffer '" + theName + "' does not define uri.");
1636 TCollection_AsciiString aPath = myFolder + anUri;
1637 bool isFileExist = false;
1638 if (!myProbedFiles.Find (aPath, isFileExist))
1640 isFileExist = OSD_File (aPath).Exists();
1641 myProbedFiles.Bind (aPath, isFileExist);
1645 reportGltfError ("Buffer '" + theName + "' refers to non-existing file '" + anUri + "'.");
1649 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1650 aData.Accessor = theAccessor;
1651 aData.StreamOffset = anOffset;
1652 aData.StreamUri = myFolder + anUri;
1653 if (myExternalFiles != NULL)
1655 myExternalFiles->Add (aData.StreamUri);
1661 // =======================================================================
1662 // function : bindNamedShape
1664 // =======================================================================
1665 void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape,
1666 ShapeMapGroup theGroup,
1667 const TopLoc_Location& theLoc,
1668 const TCollection_AsciiString& theId,
1669 const RWGltf_JsonValue* theUserName)
1671 if (theShape.IsNull())
1676 if (!theLoc.IsIdentity())
1678 theShape.Location (theLoc);
1681 TCollection_AsciiString aUserName;
1682 if (theUserName != NULL
1683 && theUserName->IsString())
1685 aUserName = theUserName->GetString();
1692 myShapeMap[theGroup].Bind (theId, theShape);
1693 if (myAttribMap != NULL)
1695 RWMesh_NodeAttributes aShapeAttribs;
1696 aShapeAttribs.Name = aUserName;
1697 aShapeAttribs.RawName = theId;
1698 if (theShape.ShapeType() == TopAbs_FACE)
1700 TopLoc_Location aDummy;
1701 if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (theShape), aDummy)))
1703 if (aLateData->HasStyle())
1705 // assign material and not color
1706 //aShapeAttribs.Style.SetColorSurf (aLateData->BaseColor());
1708 Handle(XCAFDoc_VisMaterial) aMat;
1709 myMaterials.Find (!aLateData->MaterialPbr().IsNull() ? aLateData->MaterialPbr()->Id : aLateData->MaterialCommon()->Id, aMat);
1710 aShapeAttribs.Style.SetMaterial (aMat);
1712 if (aShapeAttribs.Name.IsEmpty()
1713 && myUseMeshNameAsFallback)
1715 // fallback using Mesh name
1716 aShapeAttribs.Name = aLateData->Name();
1720 else if (aShapeAttribs.Name.IsEmpty()
1721 && myUseMeshNameAsFallback)
1723 // fallback using Mesh name
1724 TopLoc_Location aDummy;
1725 TCollection_AsciiString aMeshName;
1726 for (TopExp_Explorer aFaceIter (theShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
1728 if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (aFaceIter.Value()), aDummy)))
1730 if (aLateData->Name().IsEmpty())
1735 else if (aMeshName.IsEmpty())
1737 aMeshName = aLateData->Name();
1739 else if (!aMeshName.IsEqual (aLateData->Name()))
1746 if (!aMeshName.IsEmpty())
1748 aShapeAttribs.Name = aMeshName;
1751 myAttribMap->Bind (theShape, aShapeAttribs);
1756 // =======================================================================
1759 // =======================================================================
1760 bool RWGltf_GltfJsonParser::Parse (const Handle(Message_ProgressIndicator)& theProgress)
1762 Message_ProgressSentry aPSentry (theProgress, "Reading Gltf", 0, 2, 1);
1763 #ifdef HAVE_RAPIDJSON
1765 if (!gltfParseRoots())
1771 gltfParseMaterials();
1772 if (!gltfParseScene (theProgress))
1778 if (!aPSentry.More())
1784 Message::DefaultMessenger()->Send ("Error: glTF reader is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined].", Message_Fail);