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 if (aMetalRoughVal == NULL)
550 theMat = new RWGltf_MaterialMetallicRoughness();
551 const RWGltf_JsonValue* aBaseColorFactorVal = findObjectMember (*aMetalRoughVal, "baseColorFactor");
552 const RWGltf_JsonValue* aBaseColorTexVal = findObjectMember (*aMetalRoughVal, "baseColorTexture");
553 const RWGltf_JsonValue* aMetallicFactorVal = findObjectMember (*aMetalRoughVal, "metallicFactor");
554 const RWGltf_JsonValue* aRoughnessFactorVal = findObjectMember (*aMetalRoughVal, "roughnessFactor");
555 const RWGltf_JsonValue* aMetalRoughTexVal = findObjectMember (*aMetalRoughVal, "metallicRoughnessTexture");
557 if (aDoubleSidedVal != NULL
558 && aDoubleSidedVal->IsBool())
560 theMat->IsDoubleSided = aDoubleSidedVal->GetBool();
562 if (anAlphaCutoffVal != NULL
563 && anAlphaCutoffVal->IsNumber())
565 theMat->AlphaCutOff = (float )anAlphaCutoffVal->GetDouble();
567 if (anAlphaModeVal != NULL
568 && anAlphaModeVal->IsString())
570 theMat->AlphaMode = RWGltf_GltfParseAlphaMode (anAlphaModeVal->GetString());
573 if (aBaseColorTexVal != NULL
574 && aBaseColorTexVal->IsObject())
576 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aBaseColorTexVal, "index"))
578 gltfParseTexture (theMat->BaseColorTexture, aTexIndexVal);
582 Graphic3d_Vec4d aBaseColorFactor;
583 if (gltfReadVec4 (aBaseColorFactor, aBaseColorFactorVal)
584 && validateColor4 (aBaseColorFactor))
586 theMat->BaseColor = Quantity_ColorRGBA (Graphic3d_Vec4 (aBaseColorFactor));
589 Graphic3d_Vec3d anEmissiveFactor;
590 if (gltfReadVec3 (anEmissiveFactor, anEmissFactorVal)
591 && validateColor3 (anEmissiveFactor))
593 theMat->EmissiveFactor = Graphic3d_Vec3 (anEmissiveFactor);
596 if (aMetalRoughTexVal != NULL
597 && aMetalRoughTexVal->IsObject())
599 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aMetalRoughTexVal, "index"))
601 gltfParseTexture (theMat->MetallicRoughnessTexture, aTexIndexVal);
605 if (aMetallicFactorVal != NULL
606 && aMetallicFactorVal->IsNumber())
608 theMat->Metallic = (float )aMetallicFactorVal->GetDouble();
611 if (aRoughnessFactorVal != NULL
612 && aRoughnessFactorVal->IsNumber())
614 theMat->Roughness = (float )aRoughnessFactorVal->GetDouble();
617 if (aNormTexVal != NULL
618 && aNormTexVal->IsObject())
620 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aNormTexVal, "index"))
622 gltfParseTexture (theMat->NormalTexture, aTexIndexVal);
626 if (anEmissTexVal != NULL
627 && anEmissTexVal->IsObject())
629 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anEmissTexVal, "index"))
631 gltfParseTexture (theMat->EmissiveTexture, aTexIndexVal);
635 if (anOcclusionTexVal != NULL
636 && anOcclusionTexVal->IsObject())
638 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anOcclusionTexVal, "index"))
640 gltfParseTexture (theMat->OcclusionTexture, aTexIndexVal);
646 // =======================================================================
647 // function : gltfParseCommonMaterial
649 // =======================================================================
650 bool RWGltf_GltfJsonParser::gltfParseCommonMaterial (Handle(RWGltf_MaterialCommon)& theMat,
651 const RWGltf_JsonValue& theMatNode)
653 const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions");
654 if (anExtVal == NULL)
659 const RWGltf_JsonValue* aMatCommon = findObjectMember (*anExtVal, THE_KHR_materials_common);
660 if (aMatCommon == NULL)
665 if (!gltfParseStdMaterial (theMat, *aMatCommon))
672 // =======================================================================
673 // function : gltfParseTexture
675 // =======================================================================
676 bool RWGltf_GltfJsonParser::gltfParseTexture (Handle(Image_Texture)& theTexture,
677 const RWGltf_JsonValue* theTextureId)
679 if (theTextureId == NULL
680 || myGltfRoots[RWGltf_GltfRootElement_Textures].IsNull()
681 || myGltfRoots[RWGltf_GltfRootElement_Images].IsNull())
686 const TCollection_AsciiString aTextureId = getKeyString (*theTextureId);
687 const RWGltf_JsonValue* aTexNode = myGltfRoots[RWGltf_GltfRootElement_Textures].FindChild (*theTextureId);
688 if (aTexNode == NULL)
690 reportGltfWarning ("Texture node '" + aTextureId + "' is not found.");
694 const RWGltf_JsonValue* aSrcVal = findObjectMember (*aTexNode, "source");
695 const RWGltf_JsonValue* aTargVal = findObjectMember (*aTexNode, "target");
698 reportGltfWarning ("Invalid texture node '" + aTextureId + "' without a 'source' property.");
702 && aTargVal->IsNumber()
703 && aTargVal->GetInt() != 3553) // GL_TEXTURE_2D
708 const RWGltf_JsonValue* anImgNode = myGltfRoots[RWGltf_GltfRootElement_Images].FindChild (*aSrcVal);
709 if (anImgNode == NULL)
711 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to non-existing image '" + getKeyString (*aSrcVal) + "'.");
717 const RWGltf_JsonValue* aBinVal = NULL;
718 const RWGltf_JsonValue* aBufferViewName = findObjectMember (*anImgNode, "bufferView");
719 if (aBufferViewName != NULL)
725 const RWGltf_JsonValue* anExtVal = findObjectMember (*anImgNode, "extensions");
726 if (anExtVal != NULL)
728 aBinVal = findObjectMember (*anExtVal, THE_KHR_binary_glTF);
731 aBufferViewName = findObjectMember (*aBinVal, "bufferView");
738 //const RWGltf_JsonValue* aMimeTypeVal = findObjectMember (*aBinVal, "mimeType");
739 //const RWGltf_JsonValue* aWidthVal = findObjectMember (*aBinVal, "width");
740 //const RWGltf_JsonValue* aHeightVal = findObjectMember (*aBinVal, "height");
741 if (aBufferViewName == NULL)
743 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
747 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
748 if (aBufferView == NULL
749 || !aBufferView->IsObject())
751 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid buffer view '" + getKeyString (*aBufferViewName) + "'.");
755 const RWGltf_JsonValue* aBufferName = findObjectMember (*aBufferView, "buffer");
756 const RWGltf_JsonValue* aByteLength = findObjectMember (*aBufferView, "byteLength");
757 const RWGltf_JsonValue* aByteOffset = findObjectMember (*aBufferView, "byteOffset");
758 if (aBufferName != NULL
759 && aBufferName->IsString()
760 && !IsEqual (aBufferName->GetString(), "binary_glTF"))
762 reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' does not define binary_glTF buffer.");
766 RWGltf_GltfBufferView aBuffView;
767 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
768 ? (int64_t )aByteOffset->GetDouble()
770 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
771 ? (int64_t )aByteLength->GetDouble()
773 if (aBuffView.ByteLength < 0)
775 reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' defines invalid byteLength.");
778 else if (aBuffView.ByteOffset < 0)
780 reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' defines invalid byteOffset.");
785 const int64_t anOffset = myBinBodyOffset + aBuffView.ByteOffset;
786 theTexture = new Image_Texture (myFilePath, anOffset, aBuffView.ByteLength);
791 const RWGltf_JsonValue* anUriVal = findObjectMember (*anImgNode, "uri");
793 || !anUriVal->IsString())
798 const char* anUriData = anUriVal->GetString();
799 if (::strncmp (anUriData, "data:", 5) == 0) // data:image/png;base64
801 // uncompressing base64 here is inefficient, because the same image can be shared by several nodes
802 const char* aDataStart = anUriData + 5;
803 for (const char* aDataIter = aDataStart; *aDataIter != '\0'; ++aDataIter)
805 if (::memcmp (aDataIter, ";base64,", 8) == 0)
807 const char* aBase64End = anUriData + anUriVal->GetStringLength();
808 const char* aBase64Data = aDataIter + 8;
809 const size_t aBase64Len = size_t(aBase64End - aBase64Data);
810 //const TCollection_AsciiString aMime (aDataStart, aDataIter - aDataStart);
811 Handle(NCollection_Buffer) aData = FSD_Base64Decoder::Decode ((const Standard_Byte* )aBase64Data, aBase64Len);
812 theTexture = new Image_Texture (aData, myFilePath + "@" + getKeyString (*aSrcVal));
816 Message::DefaultMessenger()->Send ("glTF reader - embedded image has been skipped", Message_Warning);
820 TCollection_AsciiString anImageFile = myFolder + anUriVal->GetString();
821 theTexture = new Image_Texture (anImageFile);
822 if (myExternalFiles != NULL)
824 myExternalFiles->Add (anImageFile);
829 // =======================================================================
830 // function : gltfParseScene
832 // =======================================================================
833 bool RWGltf_GltfJsonParser::gltfParseScene (const Handle(Message_ProgressIndicator)& theProgress)
835 // search default scene
836 const RWGltf_JsonValue* aDefScene = myGltfRoots[RWGltf_GltfRootElement_Scenes].FindChild (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root());
837 if (aDefScene == NULL)
839 reportGltfError ("Default scene is not found.");
843 const RWGltf_JsonValue* aSceneNodes = findObjectMember (*aDefScene, "nodes");
844 if (aSceneNodes == NULL
845 || !aSceneNodes->IsArray())
847 reportGltfError ("Empty scene '" + getKeyString (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root()) + "'.");
851 return gltfParseSceneNodes (*myRootShapes, *aSceneNodes, theProgress);
854 // =======================================================================
855 // function : gltfParseSceneNodes
857 // =======================================================================
858 bool RWGltf_GltfJsonParser::gltfParseSceneNodes (TopTools_SequenceOfShape& theShapeSeq,
859 const RWGltf_JsonValue& theSceneNodes,
860 const Handle(Message_ProgressIndicator)& theProgress)
862 if (!theSceneNodes.IsArray())
864 reportGltfError ("Scene nodes is not array.");
868 Message_ProgressSentry aPSentry (theProgress, "Reading scene nodes", 0, theSceneNodes.Size(), 1);
869 for (rapidjson::Value::ConstValueIterator aSceneNodeIter = theSceneNodes.Begin();
870 aSceneNodeIter != theSceneNodes.End() && aPSentry.More(); ++aSceneNodeIter, aPSentry.Next())
872 const RWGltf_JsonValue* aSceneNode = myGltfRoots[RWGltf_GltfRootElement_Nodes].FindChild (*aSceneNodeIter);
873 if (aSceneNode == NULL)
875 reportGltfWarning ("Scene refers to non-existing node '" + getKeyString (*aSceneNodeIter) + "'.");
879 TopoDS_Shape aNodeShape;
880 if (!gltfParseSceneNode (aNodeShape, getKeyString (*aSceneNodeIter), *aSceneNode, theProgress))
885 if (aNodeShape.IsNull())
889 else if (myToSkipEmptyNodes
890 && !TopExp_Explorer (aNodeShape, TopAbs_FACE).More())
895 theShapeSeq.Append (aNodeShape);
900 // =======================================================================
901 // function : gltfParseSceneNode
903 // =======================================================================
904 bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
905 const TCollection_AsciiString& theSceneNodeId,
906 const RWGltf_JsonValue& theSceneNode,
907 const Handle(Message_ProgressIndicator)& theProgress)
909 const RWGltf_JsonValue* aName = findObjectMember (theSceneNode, "name");
910 //const RWGltf_JsonValue* aJointName = findObjectMember (theSceneNode, "jointName");
911 const RWGltf_JsonValue* aChildren = findObjectMember (theSceneNode, "children");
912 const RWGltf_JsonValue* aMeshes_1 = findObjectMember (theSceneNode, "meshes");
913 const RWGltf_JsonValue* aMesh_2 = findObjectMember (theSceneNode, "mesh");
914 //const RWGltf_JsonValue* aCamera = findObjectMember (theSceneNode, "camera");
915 const RWGltf_JsonValue* aTrsfMatVal = findObjectMember (theSceneNode, "matrix");
916 const RWGltf_JsonValue* aTrsfRotVal = findObjectMember (theSceneNode, "rotation");
917 const RWGltf_JsonValue* aTrsfScaleVal = findObjectMember (theSceneNode, "scale");
918 const RWGltf_JsonValue* aTrsfTransVal = findObjectMember (theSceneNode, "translation");
919 if (findNodeShape (theNodeShape, theSceneNodeId))
924 TopLoc_Location aNodeLoc;
925 const bool hasTrs = aTrsfRotVal != NULL
926 || aTrsfScaleVal != NULL
927 || aTrsfTransVal != NULL;
928 if (aTrsfMatVal != NULL)
932 reportGltfError ("Scene node '" + theSceneNodeId + "' defines ambiguous transformation.");
935 else if (!aTrsfMatVal->IsArray()
936 || aTrsfMatVal->Size() != 16)
938 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix array.");
942 Graphic3d_Mat4d aMat4;
943 for (int aColIter = 0; aColIter < 4; ++aColIter)
945 for (int aRowIter = 0; aRowIter < 4; ++aRowIter)
947 const RWGltf_JsonValue& aGenVal = (*aTrsfMatVal)[aColIter * 4 + aRowIter];
948 if (!aGenVal.IsNumber())
950 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix.");
953 aMat4.SetValue (aRowIter, aColIter, aGenVal.GetDouble());
957 if (!aMat4.IsIdentity())
960 aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
961 aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
962 aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
963 myCSTrsf.TransformTransformation (aTrsf);
964 if (aTrsf.Form() != gp_Identity)
966 aNodeLoc = TopLoc_Location (aTrsf);
973 if (aTrsfRotVal != NULL)
975 if (!aTrsfRotVal->IsArray()
976 || aTrsfRotVal->Size() != 4)
978 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation quaternion.");
982 Graphic3d_Vec4d aRotVec4;
983 for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
985 const RWGltf_JsonValue& aGenVal = (*aTrsfRotVal)[aCompIter];
986 if (!aGenVal.IsNumber())
988 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation.");
991 aRotVec4[aCompIter] = aGenVal.GetDouble();
993 const gp_Quaternion aQuaternion (aRotVec4.x(), aRotVec4.y(), aRotVec4.z(), aRotVec4.w());
994 if (Abs (aQuaternion.X()) > gp::Resolution()
995 || Abs (aQuaternion.Y()) > gp::Resolution()
996 || Abs (aQuaternion.Z()) > gp::Resolution()
997 || Abs (aQuaternion.W() - 1.0) > gp::Resolution())
999 aTrsf.SetRotation (aQuaternion);
1003 if (aTrsfTransVal != NULL)
1005 if (!aTrsfTransVal->IsArray()
1006 || aTrsfTransVal->Size() != 3)
1008 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation vector.");
1013 for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
1015 const RWGltf_JsonValue& aGenVal = (*aTrsfTransVal)[aCompIter];
1016 if (!aGenVal.IsNumber())
1018 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation.");
1021 aTransVec.SetCoord (aCompIter + 1, aGenVal.GetDouble());
1023 aTrsf.SetTranslationPart (aTransVec);
1026 if (aTrsfScaleVal != NULL)
1028 Graphic3d_Vec3d aScaleVec;
1029 if (!aTrsfScaleVal->IsArray()
1030 || aTrsfScaleVal->Size() != 3)
1032 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale vector.");
1035 for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
1037 const RWGltf_JsonValue& aGenVal = (*aTrsfScaleVal)[aCompIter];
1038 if (!aGenVal.IsNumber())
1040 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
1043 aScaleVec[aCompIter] = aGenVal.GetDouble();
1044 if (Abs (aScaleVec[aCompIter]) <= gp::Resolution())
1046 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
1051 if (Abs (aScaleVec.x() - aScaleVec.y()) > Precision::Confusion()
1052 || Abs (aScaleVec.y() - aScaleVec.z()) > Precision::Confusion()
1053 || Abs (aScaleVec.x() - aScaleVec.z()) > Precision::Confusion())
1055 Graphic3d_Mat4d aScaleMat;
1056 aScaleMat.SetDiagonal (aScaleVec);
1058 Graphic3d_Mat4d aMat4;
1059 aTrsf.GetMat4 (aMat4);
1061 aMat4 = aMat4 * aScaleMat;
1063 aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
1064 aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
1065 aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
1067 Message::DefaultMessenger()->Send (TCollection_AsciiString ("glTF reader, scene node '")
1068 + theSceneNodeId + "' defines unsupported scaling "
1069 + aScaleVec.x() + " " + aScaleVec.y() + " " + aScaleVec.z(), Message_Warning);
1071 else if (Abs (aScaleVec.x() - 1.0) > Precision::Confusion())
1073 aTrsf.SetScaleFactor (aScaleVec.x());
1077 myCSTrsf.TransformTransformation (aTrsf);
1078 if (aTrsf.Form() != gp_Identity)
1080 aNodeLoc = TopLoc_Location (aTrsf);
1084 BRep_Builder aBuilder;
1085 TopoDS_Compound aNodeShape;
1086 aBuilder.MakeCompound (aNodeShape);
1087 TopTools_SequenceOfShape aChildShapes;
1088 int aNbSubShapes = 0;
1089 if (aChildren != NULL
1090 && !gltfParseSceneNodes (aChildShapes, *aChildren, theProgress))
1092 theNodeShape = aNodeShape;
1093 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1096 for (TopTools_SequenceOfShape::Iterator aChildShapeIter (aChildShapes); aChildShapeIter.More(); aChildShapeIter.Next())
1098 aBuilder.Add (aNodeShape, aChildShapeIter.Value());
1102 if (aMeshes_1 != NULL
1103 && aMeshes_1->IsArray())
1106 Message_ProgressSentry aPSentry (theProgress, "Reading scene meshes", 0, aMeshes_1->Size(), 1);
1107 for (rapidjson::Value::ConstValueIterator aMeshIter = aMeshes_1->Begin();
1108 aMeshIter != aMeshes_1->End() && aPSentry.More(); ++aMeshIter, aPSentry.Next())
1110 const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMeshIter);
1113 theNodeShape = aNodeShape;
1114 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1115 reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1119 TopoDS_Shape aMeshShape;
1120 if (!gltfParseMesh (aMeshShape, getKeyString (*aMeshIter), *aMesh, theProgress))
1122 theNodeShape = aNodeShape;
1123 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1126 if (!aMeshShape.IsNull())
1128 aBuilder.Add (aNodeShape, aMeshShape);
1133 if (aMesh_2 != NULL)
1136 const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMesh_2);
1139 theNodeShape = aNodeShape;
1140 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1141 reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1145 TopoDS_Shape aMeshShape;
1146 if (!gltfParseMesh (aMeshShape, getKeyString (*aMesh_2), *aMesh, theProgress))
1148 theNodeShape = aNodeShape;
1149 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1152 if (!aMeshShape.IsNull())
1154 aBuilder.Add (aNodeShape, aMeshShape);
1159 if (aNbSubShapes == 1)
1161 theNodeShape = TopoDS_Iterator (aNodeShape).Value();
1165 theNodeShape = aNodeShape;
1167 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1171 // =======================================================================
1172 // function : gltfParseMesh
1174 // =======================================================================
1175 bool RWGltf_GltfJsonParser::gltfParseMesh (TopoDS_Shape& theMeshShape,
1176 const TCollection_AsciiString& theMeshId,
1177 const RWGltf_JsonValue& theMesh,
1178 const Handle(Message_ProgressIndicator)& theProgress)
1180 const RWGltf_JsonValue* aName = findObjectMember (theMesh, "name");
1181 const RWGltf_JsonValue* aPrims = findObjectMember (theMesh, "primitives");
1182 if (!aPrims->IsArray())
1184 reportGltfError ("Primitive array attributes within Mesh '" + theMeshId + "' is not an array.");
1188 if (findMeshShape (theMeshShape, theMeshId))
1193 BRep_Builder aBuilder;
1194 TopoDS_Compound aMeshShape;
1196 for (rapidjson::Value::ConstValueIterator aPrimArrIter = aPrims->Begin();
1197 aPrimArrIter != aPrims->End(); ++aPrimArrIter)
1199 TCollection_AsciiString aUserName;
1201 && aName->IsString())
1203 aUserName = aName->GetString();
1206 Handle(RWGltf_GltfLatePrimitiveArray) aMeshData = new RWGltf_GltfLatePrimitiveArray (theMeshId, aUserName);
1207 if (!gltfParsePrimArray (aMeshData, theMeshId, *aPrimArrIter, theProgress))
1212 if (!aMeshData->Data().IsEmpty())
1214 if (aMeshShape.IsNull())
1216 aBuilder.MakeCompound (aMeshShape);
1220 aBuilder.MakeFace (aFace, aMeshData);
1221 aBuilder.Add (aMeshShape, aFace);
1222 if (myAttribMap != NULL
1223 && aMeshData->HasStyle())
1225 RWMesh_NodeAttributes aShapeAttribs;
1226 aShapeAttribs.RawName = aUserName;
1228 // assign material and not color
1229 //aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor());
1231 Handle(XCAFDoc_VisMaterial) aMat;
1232 myMaterials.Find (!aMeshData->MaterialPbr().IsNull() ? aMeshData->MaterialPbr()->Id : aMeshData->MaterialCommon()->Id, aMat);
1233 aShapeAttribs.Style.SetMaterial (aMat);
1235 myAttribMap->Bind (aFace, aShapeAttribs);
1237 myFaceList.Append (aFace);
1244 theMeshShape = TopoDS_Iterator (aMeshShape).Value();
1248 theMeshShape = aMeshShape;
1250 bindMeshShape (theMeshShape, theMeshId, aName);
1254 // =======================================================================
1255 // function : gltfParsePrimArray
1257 // =======================================================================
1258 bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1259 const TCollection_AsciiString& theMeshId,
1260 const RWGltf_JsonValue& thePrimArray,
1261 const Handle(Message_ProgressIndicator)& /*theProgress*/)
1263 const RWGltf_JsonValue* anAttribs = findObjectMember (thePrimArray, "attributes");
1264 const RWGltf_JsonValue* anIndices = findObjectMember (thePrimArray, "indices");
1265 const RWGltf_JsonValue* aMaterial = findObjectMember (thePrimArray, "material");
1266 const RWGltf_JsonValue* aModeVal = findObjectMember (thePrimArray, "mode");
1267 RWGltf_GltfPrimitiveMode aMode = RWGltf_GltfPrimitiveMode_Triangles;
1268 if (anAttribs == NULL
1269 || !anAttribs->IsObject())
1271 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' defines no attributes.");
1274 else if (aModeVal != NULL)
1276 aMode = RWGltf_GltfPrimitiveMode_UNKNOWN;
1277 if (aModeVal->IsInt())
1279 aMode = (RWGltf_GltfPrimitiveMode )aModeVal->GetInt();
1281 if (aMode < RWGltf_GltfPrimitiveMode_Points
1282 || aMode > RWGltf_GltfPrimitiveMode_TriangleFan)
1284 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' has unknown mode.");
1288 if (aMode != RWGltf_GltfPrimitiveMode_Triangles)
1290 Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Primitive array within Mesh '"
1291 + theMeshId + "' skipped due to unsupported mode.", Message_Warning);
1294 theMeshData->SetPrimitiveMode (aMode);
1297 if (aMaterial != NULL)
1299 Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
1300 if (myMaterialsPbr.Find (getKeyString (*aMaterial), aMatPbr))
1302 theMeshData->SetMaterialPbr (aMatPbr);
1305 Handle(RWGltf_MaterialCommon) aMatCommon;
1306 if (myMaterialsCommon.Find (getKeyString (*aMaterial), aMatCommon))
1308 theMeshData->SetMaterialCommon (aMatCommon);
1312 bool hasPositions = false;
1313 for (rapidjson::Value::ConstMemberIterator anAttribIter = anAttribs->MemberBegin();
1314 anAttribIter != anAttribs->MemberEnd(); ++anAttribIter)
1316 const TCollection_AsciiString anAttribId = getKeyString (anAttribIter->value);
1317 if (anAttribId.IsEmpty())
1319 reportGltfError ("Primitive array attribute accessor key within Mesh '" + theMeshId + "' is not a string.");
1323 RWGltf_GltfArrayType aType = RWGltf_GltfParseAttribType (anAttribIter->name.GetString());
1324 if (aType == RWGltf_GltfArrayType_UNKNOWN)
1326 // just ignore unknown attributes
1330 const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (anAttribIter->value);
1331 if (anAccessor == NULL
1332 || !anAccessor->IsObject())
1334 reportGltfError ("Primitive array attribute accessor key '" + anAttribId + "' points to non-existing object.");
1337 else if (!gltfParseAccessor (theMeshData, anAttribId, *anAccessor, aType))
1341 else if (aType == RWGltf_GltfArrayType_Position)
1343 hasPositions = true;
1348 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' does not define vertex positions.");
1352 if (anIndices != NULL)
1354 const TCollection_AsciiString anIndicesId = getKeyString (*anIndices);
1355 const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (*anIndices);
1356 if (anAccessor == NULL
1357 || !anAccessor->IsObject())
1359 reportGltfError ("Primitive array indices accessor key '" + anIndicesId + "' points to non-existing object.");
1362 else if (!gltfParseAccessor (theMeshData, anIndicesId, *anAccessor, RWGltf_GltfArrayType_Indices))
1371 // =======================================================================
1372 // function : gltfParseAccessor
1374 // =======================================================================
1375 bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1376 const TCollection_AsciiString& theName,
1377 const RWGltf_JsonValue& theAccessor,
1378 const RWGltf_GltfArrayType theType)
1380 RWGltf_GltfAccessor aStruct;
1381 const RWGltf_JsonValue* aTypeStr = findObjectMember (theAccessor, "type");
1382 const RWGltf_JsonValue* aBufferViewName = findObjectMember (theAccessor, "bufferView");
1383 const RWGltf_JsonValue* aByteOffset = findObjectMember (theAccessor, "byteOffset");
1384 const RWGltf_JsonValue* aByteStride = findObjectMember (theAccessor, "byteStride");
1385 const RWGltf_JsonValue* aCompType = findObjectMember (theAccessor, "componentType");
1386 const RWGltf_JsonValue* aCount = findObjectMember (theAccessor, "count");
1387 if (aTypeStr == NULL
1388 || !aTypeStr->IsString())
1390 reportGltfError ("Accessor '" + theName + "' does not define type.");
1393 aStruct.Type = RWGltf_GltfParseAccessorType (aTypeStr->GetString());
1394 if (aStruct.Type == RWGltf_GltfAccessorLayout_UNKNOWN)
1396 reportGltfError ("Accessor '" + theName + "' has invalid type.");
1400 if (aBufferViewName == NULL)
1402 reportGltfError ("Accessor '" + theName + "' does not define bufferView.");
1405 if (aCompType == NULL
1406 || !aCompType->IsInt())
1408 reportGltfError ("Accessor '" + theName + "' does not define componentType.");
1411 aStruct.ComponentType = (RWGltf_GltfAccessorCompType )aCompType->GetInt();
1412 if (aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int8
1413 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt8
1414 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int16
1415 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt16
1416 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt32
1417 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Float32)
1419 reportGltfError ("Accessor '" + theName + "' defines invalid componentType value.");
1424 || !aCount->IsNumber())
1426 reportGltfError ("Accessor '" + theName + "' does not define count.");
1430 aStruct.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1431 ? (int64_t )aByteOffset->GetDouble()
1433 aStruct.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1434 ? aByteStride->GetInt()
1436 aStruct.Count = (int64_t )aCount->GetDouble();
1438 if (aStruct.ByteOffset < 0)
1440 reportGltfError ("Accessor '" + theName + "' defines invalid byteOffset.");
1443 else if (aStruct.ByteStride < 0
1444 || aStruct.ByteStride > 255)
1446 reportGltfError ("Accessor '" + theName + "' defines invalid byteStride.");
1449 else if (aStruct.Count < 1)
1451 reportGltfError ("Accessor '" + theName + "' defines invalid count.");
1455 // Read Min/Max values for POSITION type. It is used for bounding boxes
1456 if (theType == RWGltf_GltfArrayType_Position)
1458 const RWGltf_JsonValue* aMin = findObjectMember (theAccessor, "min");
1459 const RWGltf_JsonValue* aMax = findObjectMember (theAccessor, "max");
1460 if (aMin != NULL && aMax != NULL)
1462 // Note: Min/Max values can be not defined in glTF file.
1463 // In this case it is not used only.
1464 if (!aMin->IsArray() || !aMax->IsArray() ||
1465 aMin->Size() != 3 || aMax->Size() != 3)
1467 reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max values.");
1471 bool isValidMinMax = true;
1472 gp_Pnt aMinPnt, aMaxPnt;
1473 for (int anIter = 0; anIter < 3; ++anIter)
1475 const RWGltf_JsonValue& aMinVal = (*aMin)[anIter];
1476 const RWGltf_JsonValue& aMaxVal = (*aMax)[anIter];
1477 if (!aMinVal.IsNumber() || !aMaxVal.IsNumber())
1479 reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max value.");
1480 isValidMinMax = false;
1483 aMinPnt.SetCoord (anIter + 1, aMinVal.GetDouble());
1484 aMinPnt.SetCoord (anIter + 1, aMaxVal.GetDouble());
1488 myCSTrsf.TransformPosition (aMinPnt.ChangeCoord());
1489 myCSTrsf.TransformPosition (aMaxPnt.ChangeCoord());
1495 theMeshData->SetBoundingBox (aBox);
1501 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
1502 if (aBufferView == NULL
1503 || !aBufferView->IsObject())
1505 reportGltfError ("Accessor '" + theName + "' refers to non-existing bufferView.");
1509 return gltfParseBufferView (theMeshData, getKeyString (*aBufferViewName), *aBufferView, aStruct, theType);
1512 // =======================================================================
1513 // function : gltfParseBufferView
1515 // =======================================================================
1516 bool RWGltf_GltfJsonParser::gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1517 const TCollection_AsciiString& theName,
1518 const RWGltf_JsonValue& theBufferView,
1519 const RWGltf_GltfAccessor& theAccessor,
1520 const RWGltf_GltfArrayType theType)
1522 RWGltf_GltfBufferView aBuffView;
1523 const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
1524 const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
1525 const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
1526 const RWGltf_JsonValue* aTarget = findObjectMember (theBufferView, "target");
1527 if (aBufferName == NULL)
1529 reportGltfError ("BufferView '" + theName + "' does not define buffer.");
1533 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1534 ? (int64_t )aByteOffset->GetDouble()
1536 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
1537 ? (int64_t )aByteLength->GetDouble()
1539 if (aTarget != NULL && aTarget->IsInt())
1541 aBuffView.Target = (RWGltf_GltfBufferViewTarget )aTarget->GetInt();
1542 if (aBuffView.Target != RWGltf_GltfBufferViewTarget_ARRAY_BUFFER
1543 && aBuffView.Target != RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER)
1545 reportGltfError ("BufferView '" + theName + "' defines invalid target.");
1550 if (aBuffView.ByteLength < 0)
1552 reportGltfError ("BufferView '" + theName + "' defines invalid byteLength.");
1555 else if (aBuffView.ByteOffset < 0)
1557 reportGltfError ("BufferView '" + theName + "' defines invalid byteOffset.");
1561 const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
1563 || !aBuffer->IsObject())
1565 reportGltfError ("BufferView '" + theName + "' refers to non-existing buffer.");
1569 return gltfParseBuffer (theMeshData, getKeyString (*aBufferName), *aBuffer, theAccessor, aBuffView, theType);
1572 // =======================================================================
1573 // function : gltfParseBuffer
1575 // =======================================================================
1576 bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1577 const TCollection_AsciiString& theName,
1578 const RWGltf_JsonValue& theBuffer,
1579 const RWGltf_GltfAccessor& theAccessor,
1580 const RWGltf_GltfBufferView& theView,
1581 const RWGltf_GltfArrayType theType)
1583 //const RWGltf_JsonValue* aType = findObjectMember (theBuffer, "type");
1584 //const RWGltf_JsonValue* aByteLength = findObjectMember (theBuffer, "byteLength");
1585 const RWGltf_JsonValue* anUriVal = findObjectMember (theBuffer, "uri");
1587 int64_t anOffset = theView.ByteOffset + theAccessor.ByteOffset;
1588 bool isBinary = false;
1591 isBinary = IsEqual ("binary_glTF", theName) // glTF 1.0
1592 || anUriVal == NULL; // glTF 2.0
1596 anOffset += myBinBodyOffset;
1598 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1599 aData.Accessor = theAccessor;
1600 aData.StreamOffset = anOffset;
1601 aData.StreamUri = myFilePath;
1605 if (anUriVal == NULL
1606 || !anUriVal->IsString())
1608 reportGltfError ("Buffer '" + theName + "' does not define uri.");
1612 const char* anUriData = anUriVal->GetString();
1613 if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
1615 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1616 aData.Accessor = theAccessor;
1617 aData.StreamOffset = anOffset;
1618 if (!myDecodedBuffers.Find (theName, aData.StreamData))
1620 // it is better decoding in multiple threads
1621 aData.StreamData = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
1622 myDecodedBuffers.Bind (theName, aData.StreamData);
1628 TCollection_AsciiString anUri = anUriData;
1629 if (anUri.IsEmpty())
1631 reportGltfError ("Buffer '" + theName + "' does not define uri.");
1635 TCollection_AsciiString aPath = myFolder + anUri;
1636 bool isFileExist = false;
1637 if (!myProbedFiles.Find (aPath, isFileExist))
1639 isFileExist = OSD_File (aPath).Exists();
1640 myProbedFiles.Bind (aPath, isFileExist);
1644 reportGltfError ("Buffer '" + theName + "' refers to non-existing file '" + anUri + "'.");
1648 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1649 aData.Accessor = theAccessor;
1650 aData.StreamOffset = anOffset;
1651 aData.StreamUri = myFolder + anUri;
1652 if (myExternalFiles != NULL)
1654 myExternalFiles->Add (aData.StreamUri);
1660 // =======================================================================
1661 // function : bindNamedShape
1663 // =======================================================================
1664 void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape,
1665 ShapeMapGroup theGroup,
1666 const TopLoc_Location& theLoc,
1667 const TCollection_AsciiString& theId,
1668 const RWGltf_JsonValue* theUserName)
1670 if (theShape.IsNull())
1675 if (!theLoc.IsIdentity())
1677 theShape.Location (theLoc);
1680 TCollection_AsciiString aUserName;
1681 if (theUserName != NULL
1682 && theUserName->IsString())
1684 aUserName = theUserName->GetString();
1691 myShapeMap[theGroup].Bind (theId, theShape);
1692 if (myAttribMap != NULL)
1694 RWMesh_NodeAttributes aShapeAttribs;
1695 aShapeAttribs.Name = aUserName;
1696 aShapeAttribs.RawName = theId;
1697 if (theShape.ShapeType() == TopAbs_FACE)
1699 TopLoc_Location aDummy;
1700 if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (theShape), aDummy)))
1702 if (aLateData->HasStyle())
1704 // assign material and not color
1705 //aShapeAttribs.Style.SetColorSurf (aLateData->BaseColor());
1707 Handle(XCAFDoc_VisMaterial) aMat;
1708 myMaterials.Find (!aLateData->MaterialPbr().IsNull() ? aLateData->MaterialPbr()->Id : aLateData->MaterialCommon()->Id, aMat);
1709 aShapeAttribs.Style.SetMaterial (aMat);
1711 if (aShapeAttribs.Name.IsEmpty()
1712 && myUseMeshNameAsFallback)
1714 // fallback using Mesh name
1715 aShapeAttribs.Name = aLateData->Name();
1719 else if (aShapeAttribs.Name.IsEmpty()
1720 && myUseMeshNameAsFallback)
1722 // fallback using Mesh name
1723 TopLoc_Location aDummy;
1724 TCollection_AsciiString aMeshName;
1725 for (TopExp_Explorer aFaceIter (theShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
1727 if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (aFaceIter.Value()), aDummy)))
1729 if (aLateData->Name().IsEmpty())
1734 else if (aMeshName.IsEmpty())
1736 aMeshName = aLateData->Name();
1738 else if (!aMeshName.IsEqual (aLateData->Name()))
1745 if (!aMeshName.IsEmpty())
1747 aShapeAttribs.Name = aMeshName;
1750 myAttribMap->Bind (theShape, aShapeAttribs);
1755 // =======================================================================
1758 // =======================================================================
1759 bool RWGltf_GltfJsonParser::Parse (const Handle(Message_ProgressIndicator)& theProgress)
1761 Message_ProgressSentry aPSentry (theProgress, "Reading Gltf", 0, 2, 1);
1762 #ifdef HAVE_RAPIDJSON
1764 if (!gltfParseRoots())
1770 gltfParseMaterials();
1771 if (!gltfParseScene (theProgress))
1777 if (!aPSentry.More())
1783 Message::DefaultMessenger()->Send ("Error: glTF reader is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined].", Message_Fail);