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";
41 //! Data buffer referring to a portion of another buffer.
42 class RWGltf_SubBuffer : public NCollection_Buffer
45 RWGltf_SubBuffer (const Handle(NCollection_Buffer)& theBase,
46 Standard_Size theOffset,
47 Standard_Size theLength)
48 : NCollection_Buffer (Handle(NCollection_BaseAllocator)(), theLength, theBase->ChangeData() + theOffset),
49 myBaseBuffer (theBase) {}
52 Handle(NCollection_Buffer) myBaseBuffer;
56 //! Find member of the object in a safe way.
57 inline const RWGltf_JsonValue* findObjectMember (const RWGltf_JsonValue& theObject,
58 const RWGltf_JsonValue& theName)
60 if (!theObject.IsObject()
61 || !theName.IsString())
66 rapidjson::Document::ConstMemberIterator anIter = theObject.FindMember (theName);
67 return anIter != theObject.MemberEnd()
72 //! Find member of the object in a safe way.
73 inline const RWGltf_JsonValue* findObjectMember (const RWGltf_JsonValue& theObject,
76 if (!theObject.IsObject())
81 rapidjson::Document::ConstMemberIterator anIter = theObject.FindMember (theName);
82 return anIter != theObject.MemberEnd()
87 // =======================================================================
88 // function : RWGltf_GltfJsonParser::FormatParseError
90 // =======================================================================
91 const char* RWGltf_GltfJsonParser::FormatParseError (rapidjson::ParseErrorCode theCode)
95 case rapidjson::kParseErrorNone: return "";
96 case rapidjson::kParseErrorDocumentEmpty: return "Empty Document";
97 case rapidjson::kParseErrorDocumentRootNotSingular: return "The document root must not follow by other values";
98 case rapidjson::kParseErrorValueInvalid: return "Invalid value";
99 case rapidjson::kParseErrorObjectMissName: return "Missing a name for object member";
100 case rapidjson::kParseErrorObjectMissColon: return "Missing a colon after a name of object member";
101 case rapidjson::kParseErrorObjectMissCommaOrCurlyBracket: return "Missing a comma or '}' after an object member";
102 case rapidjson::kParseErrorArrayMissCommaOrSquareBracket: return "Missing a comma or ']' after an array element";
103 case rapidjson::kParseErrorStringUnicodeEscapeInvalidHex: return "Incorrect hex digit after \\u escape in string";
104 case rapidjson::kParseErrorStringUnicodeSurrogateInvalid: return "The surrogate pair in string is invalid";
105 case rapidjson::kParseErrorStringEscapeInvalid: return "Invalid escape character in string";
106 case rapidjson::kParseErrorStringMissQuotationMark: return "Missing a closing quotation mark in string";
107 case rapidjson::kParseErrorStringInvalidEncoding: return "Invalid encoding in string";
108 case rapidjson::kParseErrorNumberTooBig: return "Number is too big to be stored in double";
109 case rapidjson::kParseErrorNumberMissFraction: return "Miss fraction part in number";
110 case rapidjson::kParseErrorNumberMissExponent: return "Miss exponent in number";
111 case rapidjson::kParseErrorTermination: return "Parsing was terminated";
112 case rapidjson::kParseErrorUnspecificSyntaxError: return "Unspecific syntax error";
114 return "UNKOWN syntax error";
117 // =======================================================================
118 // function : GltfElementMap::Init
120 // =======================================================================
121 void RWGltf_GltfJsonParser::GltfElementMap::Init (const TCollection_AsciiString& theRootName,
122 const RWGltf_JsonValue* theRoot)
131 if (theRoot->IsObject())
134 for (ConstMemberIterator aChildIter = theRoot->MemberBegin(); aChildIter != theRoot->MemberEnd(); ++aChildIter)
136 if (!aChildIter->name.IsString())
141 const TCollection_AsciiString aKey (aChildIter->name.GetString());
142 if (!myChildren.Bind (aKey, &aChildIter->value))
144 Message::SendWarning (TCollection_AsciiString ("Invalid glTF syntax - key '") + aKey + "' is already defined in '" + theRootName + "'.");
148 else if (theRoot->IsArray())
152 for (rapidjson::Value::ConstValueIterator aChildIter = theRoot->Begin(); aChildIter != theRoot->End(); ++aChildIter, ++aChildIndex)
154 myChildren.Bind (TCollection_AsciiString (aChildIndex), aChildIter);
160 // Auxiliary macros for formatting message.
161 #define reportGltfError(theMsg) reportGltfSyntaxProblem(TCollection_AsciiString() + theMsg, Message_Fail);
162 #define reportGltfWarning(theMsg) reportGltfSyntaxProblem(TCollection_AsciiString() + theMsg, Message_Warning);
164 // =======================================================================
165 // function : reportGltfSyntaxProblem
167 // =======================================================================
168 void RWGltf_GltfJsonParser::reportGltfSyntaxProblem (const TCollection_AsciiString& theMsg,
169 Message_Gravity theGravity)
171 Message::Send (myErrorPrefix + theMsg, theGravity);
174 // =======================================================================
175 // function : RWGltf_GltfJsonParser
177 // =======================================================================
178 RWGltf_GltfJsonParser::RWGltf_GltfJsonParser (TopTools_SequenceOfShape& theRootShapes)
179 : myRootShapes(&theRootShapes),
181 myExternalFiles (NULL),
187 myToSkipEmptyNodes (true),
188 myUseMeshNameAsFallback (true),
189 myToProbeHeader (false)
191 myCSTrsf.SetInputLengthUnit (1.0); // meters
192 myCSTrsf.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
195 // =======================================================================
196 // function : SetFilePath
198 // =======================================================================
199 void RWGltf_GltfJsonParser::SetFilePath (const TCollection_AsciiString& theFilePath)
201 myFilePath = theFilePath;
202 // determine file location to load associated files
203 TCollection_AsciiString aFileName;
204 OSD_Path::FolderAndFileFromPath (theFilePath, myFolder, aFileName);
207 #ifdef HAVE_RAPIDJSON
208 // =======================================================================
209 // function : gltfParseRoots
211 // =======================================================================
212 bool RWGltf_GltfJsonParser::gltfParseRoots()
214 // find glTF root elements for smooth navigation
215 RWGltf_JsonValue aNames[RWGltf_GltfRootElement_NB];
216 for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
218 aNames[aRootNameIter] = rapidjson::StringRef (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter));
221 for (ConstMemberIterator aRootIter = MemberBegin();
222 aRootIter != MemberEnd(); ++aRootIter)
224 for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
226 if (myGltfRoots[aRootNameIter].IsNull()
227 && aNames[aRootNameIter] == aRootIter->name)
229 // we will not modify JSON document, thus it is OK to keep the pointers
230 myGltfRoots[aRootNameIter].Init (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter), &aRootIter->value);
236 for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB_MANDATORY; ++aRootNameIter)
238 if (myGltfRoots[aRootNameIter].IsNull())
240 reportGltfError ("Member '" + RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter) + "' is not found.");
247 // =======================================================================
248 // function : gltfParseAsset
250 // =======================================================================
251 void RWGltf_GltfJsonParser::gltfParseAsset()
253 const RWGltf_JsonValue* anAsset = myGltfRoots[RWGltf_GltfRootElement_Asset].Root();
259 if (const RWGltf_JsonValue* aVersion = findObjectMember (*anAsset, "version"))
261 if (aVersion->IsString())
263 TCollection_AsciiString aVerStr (aVersion->GetString());
264 myIsGltf1 = aVerStr.StartsWith ("1.");
268 if (myMetadata == NULL)
273 if (const RWGltf_JsonValue* aGenerator = findObjectMember (*anAsset, "generator"))
275 if (aGenerator->IsString())
277 myMetadata->Add ("generator", aGenerator->GetString());
280 if (const RWGltf_JsonValue* aCopyRight = findObjectMember (*anAsset, "copyright"))
282 if (aCopyRight->IsString())
284 myMetadata->Add ("copyright", aCopyRight->GetString());
289 // =======================================================================
290 // function : gltfParseMaterials
292 // =======================================================================
293 void RWGltf_GltfJsonParser::gltfParseMaterials()
295 const RWGltf_JsonValue* aMatList = myGltfRoots[RWGltf_GltfRootElement_Materials].Root();
296 if (aMatList == NULL)
300 else if (aMatList->IsObject())
303 for (ConstMemberIterator aMatIter = aMatList->MemberBegin();
304 aMatIter != aMatList->MemberEnd(); ++aMatIter)
306 Handle(RWGltf_MaterialCommon) aMat;
307 const RWGltf_JsonValue& aMatNode = aMatIter->value;
308 const RWGltf_JsonValue& aMatId = aMatIter->name;
309 const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
310 if (!gltfParseCommonMaterial (aMat, aMatNode))
312 if (!gltfParseStdMaterial (aMat, aMatNode))
319 && aNameVal->IsString())
321 aMat->Name = aNameVal->GetString();
323 aMat->Id = aMatId.GetString();
324 myMaterialsCommon.Bind (aMat->Id, aMat);
325 gltfBindMaterial (Handle(RWGltf_MaterialMetallicRoughness)(), aMat);
328 else if (aMatList->IsArray())
332 for (rapidjson::Value::ConstValueIterator aMatIter = aMatList->Begin(); aMatIter != aMatList->End(); ++aMatIter, ++aMatIndex)
334 Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
335 const RWGltf_JsonValue& aMatNode = *aMatIter;
336 const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
337 if (gltfParsePbrMaterial (aMatPbr, aMatNode))
340 && aNameVal->IsString())
342 aMatPbr->Name = aNameVal->GetString();
344 aMatPbr->Id = TCollection_AsciiString ("mat_") + aMatIndex;
345 myMaterialsPbr.Bind (TCollection_AsciiString (aMatIndex), aMatPbr);
348 Handle(RWGltf_MaterialCommon) aMatCommon;
349 if (gltfParseCommonMaterial(aMatCommon, aMatNode)
350 || gltfParseStdMaterial (aMatCommon, aMatNode))
353 && aNameVal->IsString())
355 aMatCommon->Name = aNameVal->GetString();
357 aMatCommon->Id = TCollection_AsciiString ("mat_") + aMatIndex;
358 myMaterialsCommon.Bind (TCollection_AsciiString (aMatIndex), aMatCommon);
361 gltfBindMaterial (aMatPbr, aMatCommon);
366 // =======================================================================
367 // function : gltfBindMaterial
369 // =======================================================================
370 void RWGltf_GltfJsonParser::gltfBindMaterial (const Handle(RWGltf_MaterialMetallicRoughness)& theMatPbr,
371 const Handle(RWGltf_MaterialCommon)& theMatCommon)
373 if (theMatPbr.IsNull()
374 && theMatCommon.IsNull())
379 Handle(XCAFDoc_VisMaterial) aMat = new XCAFDoc_VisMaterial();
380 if (!theMatCommon.IsNull())
382 XCAFDoc_VisMaterialCommon aMatXde;
383 aMatXde.IsDefined = true;
384 aMatXde.AmbientColor = theMatCommon->AmbientColor;
385 aMatXde.DiffuseColor = theMatCommon->DiffuseColor;
386 aMatXde.SpecularColor = theMatCommon->SpecularColor;
387 aMatXde.EmissiveColor = theMatCommon->EmissiveColor;
388 aMatXde.Shininess = theMatCommon->Shininess;
389 aMatXde.Transparency = theMatCommon->Transparency;
390 aMatXde.DiffuseTexture = theMatCommon->DiffuseTexture;
391 if (aMatXde.DiffuseTexture.IsNull()
392 && !theMatCommon->AmbientTexture.IsNull())
394 aMatXde.DiffuseTexture = theMatCommon->AmbientTexture;
396 aMat->SetCommonMaterial (aMatXde);
397 if (!theMatCommon->Name.IsEmpty())
399 aMat->SetRawName (new TCollection_HAsciiString (theMatCommon->Name));
402 if (!theMatPbr.IsNull())
404 XCAFDoc_VisMaterialPBR aMatXde;
405 aMatXde.IsDefined = true;
406 aMatXde.MetallicRoughnessTexture = theMatPbr->MetallicRoughnessTexture;
407 aMatXde.BaseColorTexture = theMatPbr->BaseColorTexture;
408 aMatXde.EmissiveTexture = theMatPbr->EmissiveTexture;
409 aMatXde.OcclusionTexture = theMatPbr->OcclusionTexture;
410 aMatXde.NormalTexture = theMatPbr->NormalTexture;
411 aMatXde.BaseColor = theMatPbr->BaseColor;
412 aMatXde.EmissiveFactor = theMatPbr->EmissiveFactor;
413 aMatXde.Metallic = theMatPbr->Metallic;
414 aMatXde.Roughness = theMatPbr->Roughness;
415 aMat->SetPbrMaterial (aMatXde);
417 Graphic3d_AlphaMode anAlphaMode = Graphic3d_AlphaMode_BlendAuto;
418 switch (theMatPbr->AlphaMode)
420 case RWGltf_GltfAlphaMode_Opaque:
422 anAlphaMode = Graphic3d_AlphaMode_Opaque;
423 if (aMatXde.BaseColor.Alpha() < 1.0f)
425 Message::SendWarning ("glTF reader - material with non-zero Transparency specifies Opaque AlphaMode");
429 case RWGltf_GltfAlphaMode_Mask:
431 anAlphaMode = Graphic3d_AlphaMode_Mask;
434 case RWGltf_GltfAlphaMode_Blend:
436 anAlphaMode = Graphic3d_AlphaMode_Blend;
440 aMat->SetAlphaMode (anAlphaMode, theMatPbr->AlphaCutOff);
441 aMat->SetDoubleSided (theMatPbr->IsDoubleSided);
443 if (!theMatPbr->Name.IsEmpty())
445 aMat->SetRawName (new TCollection_HAsciiString (theMatPbr->Name));
449 myMaterials.Bind (!theMatPbr.IsNull() ? theMatPbr->Id : theMatCommon->Id, aMat);
452 // =======================================================================
453 // function : gltfParseStdMaterial
455 // =======================================================================
456 bool RWGltf_GltfJsonParser::gltfParseStdMaterial (Handle(RWGltf_MaterialCommon)& theMat,
457 const RWGltf_JsonValue& theMatNode)
459 //const RWGltf_JsonValue* aTechVal = findObjectMember (theMatNode, "technique");
460 const RWGltf_JsonValue* aValues = findObjectMember (theMatNode, "values");
466 const RWGltf_JsonValue* anAmbVal = findObjectMember (*aValues, "ambient");
467 const RWGltf_JsonValue* aDiffVal = findObjectMember (*aValues, "diffuse");
468 const RWGltf_JsonValue* anEmiVal = findObjectMember (*aValues, "emission");
469 const RWGltf_JsonValue* aSpecVal = findObjectMember (*aValues, "specular");
470 const RWGltf_JsonValue* aShinVal = findObjectMember (*aValues, "shininess");
480 theMat = new RWGltf_MaterialCommon();
482 Graphic3d_Vec4d anAmb, aDiff, anEmi, aSpec;
484 && anAmbVal->IsString())
486 gltfParseTexture (theMat->AmbientTexture, anAmbVal);
488 else if (gltfReadVec4 (anAmb, anAmbVal)
489 && validateColor4 (anAmb))
491 theMat->AmbientColor = Quantity_Color (anAmb.r(), anAmb.g(), anAmb.b(), Quantity_TOC_sRGB);
495 && aDiffVal->IsString())
497 gltfParseTexture (theMat->DiffuseTexture, aDiffVal);
499 else if (gltfReadVec4 (aDiff, aDiffVal)
500 && validateColor4 (aDiff))
502 theMat->DiffuseColor = Quantity_Color (aDiff.r(), aDiff.g(), aDiff.b(), Quantity_TOC_sRGB);
503 theMat->Transparency = float(1.0 - aDiff.a());
506 if (gltfReadVec4 (anEmi, anEmiVal)
507 && validateColor4 (anEmi))
509 theMat->EmissiveColor = Quantity_Color (anEmi.r(), anEmi.g(), anEmi.b(), Quantity_TOC_sRGB);
513 && aSpecVal->IsString())
515 gltfParseTexture (theMat->SpecularTexture, aSpecVal);
517 if (gltfReadVec4 (aSpec, aSpecVal)
518 && validateColor4 (aSpec))
520 theMat->SpecularColor = Quantity_Color (aSpec.r(), aSpec.g(), aSpec.b(), Quantity_TOC_sRGB);
524 && aShinVal->IsNumber())
526 const double aSpecular = aShinVal->GetDouble();
529 theMat->Shininess = (float )Min (aSpecular / 1000.0, 1.0);
535 // =======================================================================
536 // function : gltfParsePbrMaterial
538 // =======================================================================
539 bool RWGltf_GltfJsonParser::gltfParsePbrMaterial (Handle(RWGltf_MaterialMetallicRoughness)& theMat,
540 const RWGltf_JsonValue& theMatNode)
542 /*if (const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions"))
544 if (const RWGltf_JsonValue* anExtDefVal = findObjectMember (*anExtVal, "KHR_materials_pbrSpecularGlossiness"))
546 const RWGltf_JsonValue* aDiffTexVal = findObjectMember (*anExtDefVal, "diffuseTexture");
547 const RWGltf_JsonValue* aSpecTexVal = findObjectMember (*anExtDefVal, "specularGlossinessTexture");
551 const RWGltf_JsonValue* aMetalRoughVal = findObjectMember (theMatNode, "pbrMetallicRoughness");
552 const RWGltf_JsonValue* aNormTexVal = findObjectMember (theMatNode, "normalTexture");
553 const RWGltf_JsonValue* anEmissFactorVal = findObjectMember (theMatNode, "emissiveFactor");
554 const RWGltf_JsonValue* anEmissTexVal = findObjectMember (theMatNode, "emissiveTexture");
555 const RWGltf_JsonValue* anOcclusionTexVal = findObjectMember (theMatNode, "occlusionTexture");
556 const RWGltf_JsonValue* aDoubleSidedVal = findObjectMember (theMatNode, "doubleSided");
557 const RWGltf_JsonValue* anAlphaModeVal = findObjectMember (theMatNode, "alphaMode");
558 const RWGltf_JsonValue* anAlphaCutoffVal = findObjectMember (theMatNode, "alphaCutoff");
559 // TODO ADOBE_materials_thin_transparency extension can be used to read IOR (Index of Refraction for transparent materials)
560 if (aMetalRoughVal == NULL)
565 theMat = new RWGltf_MaterialMetallicRoughness();
566 const RWGltf_JsonValue* aBaseColorFactorVal = findObjectMember (*aMetalRoughVal, "baseColorFactor");
567 const RWGltf_JsonValue* aBaseColorTexVal = findObjectMember (*aMetalRoughVal, "baseColorTexture");
568 const RWGltf_JsonValue* aMetallicFactorVal = findObjectMember (*aMetalRoughVal, "metallicFactor");
569 const RWGltf_JsonValue* aRoughnessFactorVal = findObjectMember (*aMetalRoughVal, "roughnessFactor");
570 const RWGltf_JsonValue* aMetalRoughTexVal = findObjectMember (*aMetalRoughVal, "metallicRoughnessTexture");
572 if (aDoubleSidedVal != NULL
573 && aDoubleSidedVal->IsBool())
575 theMat->IsDoubleSided = aDoubleSidedVal->GetBool();
577 if (anAlphaCutoffVal != NULL
578 && anAlphaCutoffVal->IsNumber())
580 theMat->AlphaCutOff = (float )anAlphaCutoffVal->GetDouble();
582 if (anAlphaModeVal != NULL
583 && anAlphaModeVal->IsString())
585 theMat->AlphaMode = RWGltf_GltfParseAlphaMode (anAlphaModeVal->GetString());
588 if (aBaseColorTexVal != NULL
589 && aBaseColorTexVal->IsObject())
591 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aBaseColorTexVal, "index"))
593 gltfParseTexture (theMat->BaseColorTexture, aTexIndexVal);
597 Graphic3d_Vec4d aBaseColorFactor;
598 if (gltfReadVec4 (aBaseColorFactor, aBaseColorFactorVal)
599 && validateColor4 (aBaseColorFactor))
601 theMat->BaseColor = Quantity_ColorRGBA (Graphic3d_Vec4 (aBaseColorFactor));
604 Graphic3d_Vec3d anEmissiveFactor;
605 if (gltfReadVec3 (anEmissiveFactor, anEmissFactorVal)
606 && validateColor3 (anEmissiveFactor))
608 theMat->EmissiveFactor = Graphic3d_Vec3 (anEmissiveFactor);
611 if (aMetalRoughTexVal != NULL
612 && aMetalRoughTexVal->IsObject())
614 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aMetalRoughTexVal, "index"))
616 gltfParseTexture (theMat->MetallicRoughnessTexture, aTexIndexVal);
620 if (aMetallicFactorVal != NULL
621 && aMetallicFactorVal->IsNumber())
623 theMat->Metallic = (float )aMetallicFactorVal->GetDouble();
626 if (aRoughnessFactorVal != NULL
627 && aRoughnessFactorVal->IsNumber())
629 theMat->Roughness = (float )aRoughnessFactorVal->GetDouble();
632 if (aNormTexVal != NULL
633 && aNormTexVal->IsObject())
635 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aNormTexVal, "index"))
637 gltfParseTexture (theMat->NormalTexture, aTexIndexVal);
641 if (anEmissTexVal != NULL
642 && anEmissTexVal->IsObject())
644 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anEmissTexVal, "index"))
646 gltfParseTexture (theMat->EmissiveTexture, aTexIndexVal);
650 if (anOcclusionTexVal != NULL
651 && anOcclusionTexVal->IsObject())
653 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anOcclusionTexVal, "index"))
655 gltfParseTexture (theMat->OcclusionTexture, aTexIndexVal);
661 // =======================================================================
662 // function : gltfParseCommonMaterial
664 // =======================================================================
665 bool RWGltf_GltfJsonParser::gltfParseCommonMaterial (Handle(RWGltf_MaterialCommon)& theMat,
666 const RWGltf_JsonValue& theMatNode)
668 const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions");
669 if (anExtVal == NULL)
674 const RWGltf_JsonValue* aMatCommon = findObjectMember (*anExtVal, THE_KHR_materials_common);
675 if (aMatCommon == NULL)
680 if (!gltfParseStdMaterial (theMat, *aMatCommon))
687 // =======================================================================
688 // function : gltfParseTexture
690 // =======================================================================
691 bool RWGltf_GltfJsonParser::gltfParseTexture (Handle(Image_Texture)& theTexture,
692 const RWGltf_JsonValue* theTextureId)
694 if (theTextureId == NULL
695 || myGltfRoots[RWGltf_GltfRootElement_Textures].IsNull()
696 || myGltfRoots[RWGltf_GltfRootElement_Images].IsNull())
701 const TCollection_AsciiString aTextureId = getKeyString (*theTextureId);
702 const RWGltf_JsonValue* aTexNode = myGltfRoots[RWGltf_GltfRootElement_Textures].FindChild (*theTextureId);
703 if (aTexNode == NULL)
705 reportGltfWarning ("Texture node '" + aTextureId + "' is not found.");
709 const RWGltf_JsonValue* aSrcVal = findObjectMember (*aTexNode, "source");
710 const RWGltf_JsonValue* aTargVal = findObjectMember (*aTexNode, "target");
713 reportGltfWarning ("Invalid texture node '" + aTextureId + "' without a 'source' property.");
717 && aTargVal->IsNumber()
718 && aTargVal->GetInt() != 3553) // GL_TEXTURE_2D
723 const RWGltf_JsonValue* anImgNode = myGltfRoots[RWGltf_GltfRootElement_Images].FindChild (*aSrcVal);
724 if (anImgNode == NULL)
726 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to non-existing image '" + getKeyString (*aSrcVal) + "'.");
732 const RWGltf_JsonValue* aBinVal = NULL;
733 const RWGltf_JsonValue* aBufferViewName = findObjectMember (*anImgNode, "bufferView");
734 if (aBufferViewName != NULL)
740 const RWGltf_JsonValue* anExtVal = findObjectMember (*anImgNode, "extensions");
741 if (anExtVal != NULL)
743 aBinVal = findObjectMember (*anExtVal, THE_KHR_binary_glTF);
746 aBufferViewName = findObjectMember (*aBinVal, "bufferView");
753 if (aBufferViewName == NULL)
755 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
758 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
759 if (aBufferView == NULL
760 || !aBufferView->IsObject())
762 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid buffer view '" + getKeyString (*aBufferViewName) + "'.");
765 return gltfParseTexturInGlbBuffer (theTexture, *aBinVal, getKeyString (*aBufferViewName), *aBufferView);
769 const RWGltf_JsonValue* anUriVal = findObjectMember (*anImgNode, "uri");
770 if (anUriVal == NULL)
772 const RWGltf_JsonValue* aBufferViewName = findObjectMember (*anImgNode, "bufferView");
773 if (aBufferViewName == NULL)
775 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
779 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
780 if (aBufferView == NULL
781 || !aBufferView->IsObject())
783 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid buffer view '" + getKeyString (*aBufferViewName) + "'.");
786 return gltfParseTextureInBufferView (theTexture, getKeyString (*aSrcVal), getKeyString (*aBufferViewName), *aBufferView);
789 if (!anUriVal->IsString())
791 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
795 const char* anUriData = anUriVal->GetString();
796 if (::strncmp (anUriData, "data:", 5) == 0) // data:image/png;base64
798 // uncompressing base64 here is inefficient, because the same image can be shared by several nodes
799 const char* aDataStart = anUriData + 5;
800 for (const char* aDataIter = aDataStart; *aDataIter != '\0'; ++aDataIter)
802 if (::memcmp (aDataIter, ";base64,", 8) == 0)
804 const char* aBase64End = anUriData + anUriVal->GetStringLength();
805 const char* aBase64Data = aDataIter + 8;
806 const size_t aBase64Len = size_t(aBase64End - aBase64Data);
807 //const TCollection_AsciiString aMime (aDataStart, aDataIter - aDataStart);
808 Handle(NCollection_Buffer) aData = FSD_Base64Decoder::Decode ((const Standard_Byte* )aBase64Data, aBase64Len);
809 theTexture = new Image_Texture (aData, myFilePath + "@" + getKeyString (*aSrcVal));
813 Message::SendWarning ("glTF reader - embedded image has been skipped");
817 TCollection_AsciiString anImageFile = myFolder + anUriVal->GetString();
818 theTexture = new Image_Texture (anImageFile);
819 if (myExternalFiles != NULL)
821 myExternalFiles->Add (anImageFile);
826 // =======================================================================
827 // function : gltfParseTexturInGlbBuffer
829 // =======================================================================
830 bool RWGltf_GltfJsonParser::gltfParseTexturInGlbBuffer (Handle(Image_Texture)& theTexture,
831 const RWGltf_JsonValue& theBinVal,
832 const TCollection_AsciiString& theBufferViewId,
833 const RWGltf_JsonValue& theBufferView)
835 const RWGltf_JsonValue* aMimeTypeVal = findObjectMember (theBinVal, "mimeType");
836 //const RWGltf_JsonValue* aWidthVal = findObjectMember (theBinVal, "width");
837 //const RWGltf_JsonValue* aHeightVal = findObjectMember (theBinVal, "height");
840 const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
841 const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
842 const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
843 if (aBufferName != NULL
844 && aBufferName->IsString()
845 && !IsEqual (aBufferName->GetString(), "binary_glTF"))
847 reportGltfError ("BufferView '" + theBufferViewId + "' does not define binary_glTF buffer.");
851 RWGltf_GltfBufferView aBuffView;
852 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
853 ? (int64_t )aByteOffset->GetDouble()
855 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
856 ? (int64_t )aByteLength->GetDouble()
858 if (aBuffView.ByteLength <= 0)
860 reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteLength.");
863 else if (aBuffView.ByteOffset < 0)
865 reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteOffset.");
869 const int64_t anOffset = myBinBodyOffset + aBuffView.ByteOffset;
870 theTexture = new Image_Texture (myFilePath, anOffset, aBuffView.ByteLength);
874 // =======================================================================
875 // function : gltfParseTextureInBufferView
877 // =======================================================================
878 bool RWGltf_GltfJsonParser::gltfParseTextureInBufferView (Handle(Image_Texture)& theTexture,
879 const TCollection_AsciiString& theSourceId,
880 const TCollection_AsciiString& theBufferViewId,
881 const RWGltf_JsonValue& theBufferView)
883 const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
884 const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
885 const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
886 if (aBufferName == NULL)
888 reportGltfError ("BufferView '" + theBufferViewId + "' does not define buffer.");
892 const TCollection_AsciiString aBufferId = getKeyString (*aBufferName);
893 const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
895 || !aBuffer->IsObject())
897 reportGltfError ("BufferView '" + theBufferViewId + "' refers to non-existing buffer.");
901 RWGltf_GltfBufferView aBuffView;
902 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
903 ? (int64_t )aByteOffset->GetDouble()
905 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
906 ? (int64_t )aByteLength->GetDouble()
908 if (aBuffView.ByteLength <= 0)
910 reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteLength.");
913 else if (aBuffView.ByteOffset < 0)
915 reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteOffset.");
919 const RWGltf_JsonValue* anUriVal = findObjectMember (*aBuffer, "uri");
921 || !anUriVal->IsString())
923 reportGltfError ("Buffer '" + aBufferId + "' does not define uri.");
927 const char* anUriData = anUriVal->GetString();
928 if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
930 Handle(NCollection_Buffer) aBaseBuffer;
931 if (!myDecodedBuffers.Find (aBufferId, aBaseBuffer))
933 aBaseBuffer = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
934 myDecodedBuffers.Bind (aBufferId, aBaseBuffer);
937 Handle(RWGltf_SubBuffer) aSubBuffer = new RWGltf_SubBuffer (aBaseBuffer, (Standard_Size )aBuffView.ByteOffset, (Standard_Size )aBuffView.ByteLength);
938 theTexture = new Image_Texture (aSubBuffer, myFilePath + "@" + theSourceId);
942 const TCollection_AsciiString anUri (anUriData);
945 reportGltfError ("Buffer '" + aBufferId + "' does not define uri.");
949 const TCollection_AsciiString aPath = myFolder + anUri;
950 bool isFileExist = false;
951 if (!myProbedFiles.Find (aPath, isFileExist))
953 isFileExist = OSD_File (aPath).Exists();
954 myProbedFiles.Bind (aPath, isFileExist);
958 reportGltfError ("Buffer '" + aBufferId + "' refers to non-existing file '" + anUri + "'.");
962 theTexture = new Image_Texture (aPath, aBuffView.ByteOffset, aBuffView.ByteLength);
963 if (myExternalFiles != NULL)
965 myExternalFiles->Add (aPath);
970 // =======================================================================
971 // function : gltfParseScene
973 // =======================================================================
974 bool RWGltf_GltfJsonParser::gltfParseScene (const Handle(Message_ProgressIndicator)& theProgress)
976 // search default scene
977 const RWGltf_JsonValue* aDefScene = myGltfRoots[RWGltf_GltfRootElement_Scenes].FindChild (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root());
978 if (aDefScene == NULL)
980 reportGltfError ("Default scene is not found.");
984 const RWGltf_JsonValue* aSceneNodes = findObjectMember (*aDefScene, "nodes");
985 if (aSceneNodes == NULL
986 || !aSceneNodes->IsArray())
988 reportGltfError ("Empty scene '" + getKeyString (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root()) + "'.");
992 return gltfParseSceneNodes (*myRootShapes, *aSceneNodes, theProgress);
995 // =======================================================================
996 // function : gltfParseSceneNodes
998 // =======================================================================
999 bool RWGltf_GltfJsonParser::gltfParseSceneNodes (TopTools_SequenceOfShape& theShapeSeq,
1000 const RWGltf_JsonValue& theSceneNodes,
1001 const Handle(Message_ProgressIndicator)& theProgress)
1003 if (!theSceneNodes.IsArray())
1005 reportGltfError ("Scene nodes is not array.");
1009 Message_ProgressSentry aPSentry (theProgress, "Reading scene nodes", 0, theSceneNodes.Size(), 1);
1010 for (rapidjson::Value::ConstValueIterator aSceneNodeIter = theSceneNodes.Begin();
1011 aSceneNodeIter != theSceneNodes.End() && aPSentry.More(); ++aSceneNodeIter, aPSentry.Next())
1013 const RWGltf_JsonValue* aSceneNode = myGltfRoots[RWGltf_GltfRootElement_Nodes].FindChild (*aSceneNodeIter);
1014 if (aSceneNode == NULL)
1016 reportGltfWarning ("Scene refers to non-existing node '" + getKeyString (*aSceneNodeIter) + "'.");
1020 TopoDS_Shape aNodeShape;
1021 if (!gltfParseSceneNode (aNodeShape, getKeyString (*aSceneNodeIter), *aSceneNode, theProgress))
1026 if (aNodeShape.IsNull())
1030 else if (myToSkipEmptyNodes
1031 && !TopExp_Explorer (aNodeShape, TopAbs_FACE).More())
1036 theShapeSeq.Append (aNodeShape);
1041 // =======================================================================
1042 // function : gltfParseSceneNode
1044 // =======================================================================
1045 bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
1046 const TCollection_AsciiString& theSceneNodeId,
1047 const RWGltf_JsonValue& theSceneNode,
1048 const Handle(Message_ProgressIndicator)& theProgress)
1050 const RWGltf_JsonValue* aName = findObjectMember (theSceneNode, "name");
1051 //const RWGltf_JsonValue* aJointName = findObjectMember (theSceneNode, "jointName");
1052 const RWGltf_JsonValue* aChildren = findObjectMember (theSceneNode, "children");
1053 const RWGltf_JsonValue* aMeshes_1 = findObjectMember (theSceneNode, "meshes");
1054 const RWGltf_JsonValue* aMesh_2 = findObjectMember (theSceneNode, "mesh");
1055 //const RWGltf_JsonValue* aCamera = findObjectMember (theSceneNode, "camera");
1056 const RWGltf_JsonValue* aTrsfMatVal = findObjectMember (theSceneNode, "matrix");
1057 const RWGltf_JsonValue* aTrsfRotVal = findObjectMember (theSceneNode, "rotation");
1058 const RWGltf_JsonValue* aTrsfScaleVal = findObjectMember (theSceneNode, "scale");
1059 const RWGltf_JsonValue* aTrsfTransVal = findObjectMember (theSceneNode, "translation");
1060 if (findNodeShape (theNodeShape, theSceneNodeId))
1065 TopLoc_Location aNodeLoc;
1066 const bool hasTrs = aTrsfRotVal != NULL
1067 || aTrsfScaleVal != NULL
1068 || aTrsfTransVal != NULL;
1069 if (aTrsfMatVal != NULL)
1073 reportGltfError ("Scene node '" + theSceneNodeId + "' defines ambiguous transformation.");
1076 else if (!aTrsfMatVal->IsArray()
1077 || aTrsfMatVal->Size() != 16)
1079 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix array.");
1083 Graphic3d_Mat4d aMat4;
1084 for (int aColIter = 0; aColIter < 4; ++aColIter)
1086 for (int aRowIter = 0; aRowIter < 4; ++aRowIter)
1088 const RWGltf_JsonValue& aGenVal = (*aTrsfMatVal)[aColIter * 4 + aRowIter];
1089 if (!aGenVal.IsNumber())
1091 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix.");
1094 aMat4.SetValue (aRowIter, aColIter, aGenVal.GetDouble());
1098 if (!aMat4.IsIdentity())
1101 aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
1102 aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
1103 aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
1104 myCSTrsf.TransformTransformation (aTrsf);
1105 if (aTrsf.Form() != gp_Identity)
1107 aNodeLoc = TopLoc_Location (aTrsf);
1114 if (aTrsfRotVal != NULL)
1116 if (!aTrsfRotVal->IsArray()
1117 || aTrsfRotVal->Size() != 4)
1119 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation quaternion.");
1123 Graphic3d_Vec4d aRotVec4;
1124 for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
1126 const RWGltf_JsonValue& aGenVal = (*aTrsfRotVal)[aCompIter];
1127 if (!aGenVal.IsNumber())
1129 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation.");
1132 aRotVec4[aCompIter] = aGenVal.GetDouble();
1134 const gp_Quaternion aQuaternion (aRotVec4.x(), aRotVec4.y(), aRotVec4.z(), aRotVec4.w());
1135 if (Abs (aQuaternion.X()) > gp::Resolution()
1136 || Abs (aQuaternion.Y()) > gp::Resolution()
1137 || Abs (aQuaternion.Z()) > gp::Resolution()
1138 || Abs (aQuaternion.W() - 1.0) > gp::Resolution())
1140 aTrsf.SetRotation (aQuaternion);
1144 if (aTrsfTransVal != NULL)
1146 if (!aTrsfTransVal->IsArray()
1147 || aTrsfTransVal->Size() != 3)
1149 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation vector.");
1154 for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
1156 const RWGltf_JsonValue& aGenVal = (*aTrsfTransVal)[aCompIter];
1157 if (!aGenVal.IsNumber())
1159 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation.");
1162 aTransVec.SetCoord (aCompIter + 1, aGenVal.GetDouble());
1164 aTrsf.SetTranslationPart (aTransVec);
1167 if (aTrsfScaleVal != NULL)
1169 Graphic3d_Vec3d aScaleVec;
1170 if (!aTrsfScaleVal->IsArray()
1171 || aTrsfScaleVal->Size() != 3)
1173 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale vector.");
1176 for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
1178 const RWGltf_JsonValue& aGenVal = (*aTrsfScaleVal)[aCompIter];
1179 if (!aGenVal.IsNumber())
1181 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
1184 aScaleVec[aCompIter] = aGenVal.GetDouble();
1185 if (Abs (aScaleVec[aCompIter]) <= gp::Resolution())
1187 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
1192 if (Abs (aScaleVec.x() - aScaleVec.y()) > Precision::Confusion()
1193 || Abs (aScaleVec.y() - aScaleVec.z()) > Precision::Confusion()
1194 || Abs (aScaleVec.x() - aScaleVec.z()) > Precision::Confusion())
1196 Graphic3d_Mat4d aScaleMat;
1197 aScaleMat.SetDiagonal (aScaleVec);
1199 Graphic3d_Mat4d aMat4;
1200 aTrsf.GetMat4 (aMat4);
1202 aMat4 = aMat4 * aScaleMat;
1204 aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
1205 aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
1206 aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
1208 Message::SendWarning (TCollection_AsciiString ("glTF reader, scene node '")
1209 + theSceneNodeId + "' defines unsupported scaling " + aScaleVec.x() + " " + aScaleVec.y() + " " + aScaleVec.z());
1211 else if (Abs (aScaleVec.x() - 1.0) > Precision::Confusion())
1213 aTrsf.SetScaleFactor (aScaleVec.x());
1217 myCSTrsf.TransformTransformation (aTrsf);
1218 if (aTrsf.Form() != gp_Identity)
1220 aNodeLoc = TopLoc_Location (aTrsf);
1224 BRep_Builder aBuilder;
1225 TopoDS_Compound aNodeShape;
1226 aBuilder.MakeCompound (aNodeShape);
1227 TopTools_SequenceOfShape aChildShapes;
1228 int aNbSubShapes = 0;
1229 if (aChildren != NULL
1230 && !gltfParseSceneNodes (aChildShapes, *aChildren, theProgress))
1232 theNodeShape = aNodeShape;
1233 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1236 for (TopTools_SequenceOfShape::Iterator aChildShapeIter (aChildShapes); aChildShapeIter.More(); aChildShapeIter.Next())
1238 aBuilder.Add (aNodeShape, aChildShapeIter.Value());
1242 if (aMeshes_1 != NULL
1243 && aMeshes_1->IsArray())
1246 Message_ProgressSentry aPSentry (theProgress, "Reading scene meshes", 0, aMeshes_1->Size(), 1);
1247 for (rapidjson::Value::ConstValueIterator aMeshIter = aMeshes_1->Begin();
1248 aMeshIter != aMeshes_1->End() && aPSentry.More(); ++aMeshIter, aPSentry.Next())
1250 const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMeshIter);
1253 theNodeShape = aNodeShape;
1254 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1255 reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1259 TopoDS_Shape aMeshShape;
1260 if (!gltfParseMesh (aMeshShape, getKeyString (*aMeshIter), *aMesh, theProgress))
1262 theNodeShape = aNodeShape;
1263 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1266 if (!aMeshShape.IsNull())
1268 aBuilder.Add (aNodeShape, aMeshShape);
1273 if (aMesh_2 != NULL)
1276 const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMesh_2);
1279 theNodeShape = aNodeShape;
1280 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1281 reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1285 TopoDS_Shape aMeshShape;
1286 if (!gltfParseMesh (aMeshShape, getKeyString (*aMesh_2), *aMesh, theProgress))
1288 theNodeShape = aNodeShape;
1289 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1292 if (!aMeshShape.IsNull())
1294 aBuilder.Add (aNodeShape, aMeshShape);
1299 if (aNbSubShapes == 1)
1301 theNodeShape = TopoDS_Iterator (aNodeShape).Value();
1305 theNodeShape = aNodeShape;
1307 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1311 // =======================================================================
1312 // function : gltfParseMesh
1314 // =======================================================================
1315 bool RWGltf_GltfJsonParser::gltfParseMesh (TopoDS_Shape& theMeshShape,
1316 const TCollection_AsciiString& theMeshId,
1317 const RWGltf_JsonValue& theMesh,
1318 const Handle(Message_ProgressIndicator)& theProgress)
1320 const RWGltf_JsonValue* aName = findObjectMember (theMesh, "name");
1321 const RWGltf_JsonValue* aPrims = findObjectMember (theMesh, "primitives");
1322 if (!aPrims->IsArray())
1324 reportGltfError ("Primitive array attributes within Mesh '" + theMeshId + "' is not an array.");
1328 if (findMeshShape (theMeshShape, theMeshId))
1333 BRep_Builder aBuilder;
1334 TopoDS_Compound aMeshShape;
1336 for (rapidjson::Value::ConstValueIterator aPrimArrIter = aPrims->Begin();
1337 aPrimArrIter != aPrims->End(); ++aPrimArrIter)
1339 TCollection_AsciiString aUserName;
1341 && aName->IsString())
1343 aUserName = aName->GetString();
1346 Handle(RWGltf_GltfLatePrimitiveArray) aMeshData = new RWGltf_GltfLatePrimitiveArray (theMeshId, aUserName);
1347 if (!gltfParsePrimArray (aMeshData, theMeshId, *aPrimArrIter, theProgress))
1352 if (!aMeshData->Data().IsEmpty())
1354 if (aMeshShape.IsNull())
1356 aBuilder.MakeCompound (aMeshShape);
1360 aBuilder.MakeFace (aFace, aMeshData);
1361 aBuilder.Add (aMeshShape, aFace);
1362 if (myAttribMap != NULL
1363 && aMeshData->HasStyle())
1365 RWMesh_NodeAttributes aShapeAttribs;
1366 aShapeAttribs.RawName = aUserName;
1368 // assign material and not color
1369 //aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor());
1371 Handle(XCAFDoc_VisMaterial) aMat;
1372 myMaterials.Find (!aMeshData->MaterialPbr().IsNull() ? aMeshData->MaterialPbr()->Id : aMeshData->MaterialCommon()->Id, aMat);
1373 aShapeAttribs.Style.SetMaterial (aMat);
1375 myAttribMap->Bind (aFace, aShapeAttribs);
1377 myFaceList.Append (aFace);
1384 theMeshShape = TopoDS_Iterator (aMeshShape).Value();
1388 theMeshShape = aMeshShape;
1390 bindMeshShape (theMeshShape, theMeshId, aName);
1394 // =======================================================================
1395 // function : gltfParsePrimArray
1397 // =======================================================================
1398 bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1399 const TCollection_AsciiString& theMeshId,
1400 const RWGltf_JsonValue& thePrimArray,
1401 const Handle(Message_ProgressIndicator)& /*theProgress*/)
1403 const RWGltf_JsonValue* anAttribs = findObjectMember (thePrimArray, "attributes");
1404 const RWGltf_JsonValue* anIndices = findObjectMember (thePrimArray, "indices");
1405 const RWGltf_JsonValue* aMaterial = findObjectMember (thePrimArray, "material");
1406 const RWGltf_JsonValue* aModeVal = findObjectMember (thePrimArray, "mode");
1407 RWGltf_GltfPrimitiveMode aMode = RWGltf_GltfPrimitiveMode_Triangles;
1408 if (anAttribs == NULL
1409 || !anAttribs->IsObject())
1411 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' defines no attributes.");
1414 else if (aModeVal != NULL)
1416 aMode = RWGltf_GltfPrimitiveMode_UNKNOWN;
1417 if (aModeVal->IsInt())
1419 aMode = (RWGltf_GltfPrimitiveMode )aModeVal->GetInt();
1421 if (aMode < RWGltf_GltfPrimitiveMode_Points
1422 || aMode > RWGltf_GltfPrimitiveMode_TriangleFan)
1424 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' has unknown mode.");
1428 if (aMode != RWGltf_GltfPrimitiveMode_Triangles)
1430 Message::SendWarning (TCollection_AsciiString() + "Primitive array within Mesh '" + theMeshId + "' skipped due to unsupported mode");
1433 theMeshData->SetPrimitiveMode (aMode);
1436 if (aMaterial != NULL)
1438 Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
1439 if (myMaterialsPbr.Find (getKeyString (*aMaterial), aMatPbr))
1441 theMeshData->SetMaterialPbr (aMatPbr);
1444 Handle(RWGltf_MaterialCommon) aMatCommon;
1445 if (myMaterialsCommon.Find (getKeyString (*aMaterial), aMatCommon))
1447 theMeshData->SetMaterialCommon (aMatCommon);
1451 bool hasPositions = false;
1452 for (rapidjson::Value::ConstMemberIterator anAttribIter = anAttribs->MemberBegin();
1453 anAttribIter != anAttribs->MemberEnd(); ++anAttribIter)
1455 const TCollection_AsciiString anAttribId = getKeyString (anAttribIter->value);
1456 if (anAttribId.IsEmpty())
1458 reportGltfError ("Primitive array attribute accessor key within Mesh '" + theMeshId + "' is not a string.");
1462 RWGltf_GltfArrayType aType = RWGltf_GltfParseAttribType (anAttribIter->name.GetString());
1463 if (aType == RWGltf_GltfArrayType_UNKNOWN)
1465 // just ignore unknown attributes
1469 const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (anAttribIter->value);
1470 if (anAccessor == NULL
1471 || !anAccessor->IsObject())
1473 reportGltfError ("Primitive array attribute accessor key '" + anAttribId + "' points to non-existing object.");
1476 else if (!gltfParseAccessor (theMeshData, anAttribId, *anAccessor, aType))
1480 else if (aType == RWGltf_GltfArrayType_Position)
1482 hasPositions = true;
1487 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' does not define vertex positions.");
1491 if (anIndices != NULL)
1493 const TCollection_AsciiString anIndicesId = getKeyString (*anIndices);
1494 const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (*anIndices);
1495 if (anAccessor == NULL
1496 || !anAccessor->IsObject())
1498 reportGltfError ("Primitive array indices accessor key '" + anIndicesId + "' points to non-existing object.");
1501 else if (!gltfParseAccessor (theMeshData, anIndicesId, *anAccessor, RWGltf_GltfArrayType_Indices))
1510 // =======================================================================
1511 // function : gltfParseAccessor
1513 // =======================================================================
1514 bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1515 const TCollection_AsciiString& theName,
1516 const RWGltf_JsonValue& theAccessor,
1517 const RWGltf_GltfArrayType theType)
1519 RWGltf_GltfAccessor aStruct;
1520 const RWGltf_JsonValue* aTypeStr = findObjectMember (theAccessor, "type");
1521 const RWGltf_JsonValue* aBufferViewName = findObjectMember (theAccessor, "bufferView");
1522 const RWGltf_JsonValue* aByteOffset = findObjectMember (theAccessor, "byteOffset");
1523 const RWGltf_JsonValue* aByteStride = findObjectMember (theAccessor, "byteStride"); // byteStride was part of bufferView in glTF 1.0
1524 const RWGltf_JsonValue* aCompType = findObjectMember (theAccessor, "componentType");
1525 const RWGltf_JsonValue* aCount = findObjectMember (theAccessor, "count");
1526 if (aTypeStr == NULL
1527 || !aTypeStr->IsString())
1529 reportGltfError ("Accessor '" + theName + "' does not define type.");
1532 aStruct.Type = RWGltf_GltfParseAccessorType (aTypeStr->GetString());
1533 if (aStruct.Type == RWGltf_GltfAccessorLayout_UNKNOWN)
1535 reportGltfError ("Accessor '" + theName + "' has invalid type.");
1539 if (aBufferViewName == NULL)
1541 reportGltfError ("Accessor '" + theName + "' does not define bufferView.");
1544 if (aCompType == NULL
1545 || !aCompType->IsInt())
1547 reportGltfError ("Accessor '" + theName + "' does not define componentType.");
1550 aStruct.ComponentType = (RWGltf_GltfAccessorCompType )aCompType->GetInt();
1551 if (aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int8
1552 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt8
1553 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int16
1554 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt16
1555 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt32
1556 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Float32)
1558 reportGltfError ("Accessor '" + theName + "' defines invalid componentType value.");
1563 || !aCount->IsNumber())
1565 reportGltfError ("Accessor '" + theName + "' does not define count.");
1569 aStruct.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1570 ? (int64_t )aByteOffset->GetDouble()
1572 aStruct.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1573 ? aByteStride->GetInt()
1575 aStruct.Count = (int64_t )aCount->GetDouble();
1577 if (aStruct.ByteOffset < 0)
1579 reportGltfError ("Accessor '" + theName + "' defines invalid byteOffset.");
1582 else if (aStruct.ByteStride < 0
1583 || aStruct.ByteStride > 255)
1585 reportGltfError ("Accessor '" + theName + "' defines invalid byteStride.");
1588 else if (aStruct.Count < 1)
1590 reportGltfError ("Accessor '" + theName + "' defines invalid count.");
1594 // Read Min/Max values for POSITION type. It is used for bounding boxes
1595 if (theType == RWGltf_GltfArrayType_Position)
1597 const RWGltf_JsonValue* aMin = findObjectMember (theAccessor, "min");
1598 const RWGltf_JsonValue* aMax = findObjectMember (theAccessor, "max");
1599 if (aMin != NULL && aMax != NULL)
1601 // Note: Min/Max values can be not defined in glTF file.
1602 // In this case it is not used only.
1603 if (!aMin->IsArray() || !aMax->IsArray() ||
1604 aMin->Size() != 3 || aMax->Size() != 3)
1606 reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max values.");
1610 bool isValidMinMax = true;
1611 gp_Pnt aMinPnt, aMaxPnt;
1612 for (int anIter = 0; anIter < 3; ++anIter)
1614 const RWGltf_JsonValue& aMinVal = (*aMin)[anIter];
1615 const RWGltf_JsonValue& aMaxVal = (*aMax)[anIter];
1616 if (!aMinVal.IsNumber() || !aMaxVal.IsNumber())
1618 reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max value.");
1619 isValidMinMax = false;
1622 aMinPnt.SetCoord (anIter + 1, aMinVal.GetDouble());
1623 aMinPnt.SetCoord (anIter + 1, aMaxVal.GetDouble());
1627 myCSTrsf.TransformPosition (aMinPnt.ChangeCoord());
1628 myCSTrsf.TransformPosition (aMaxPnt.ChangeCoord());
1634 theMeshData->SetBoundingBox (aBox);
1640 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
1641 if (aBufferView == NULL
1642 || !aBufferView->IsObject())
1644 reportGltfError ("Accessor '" + theName + "' refers to non-existing bufferView.");
1648 return gltfParseBufferView (theMeshData, getKeyString (*aBufferViewName), *aBufferView, aStruct, theType);
1651 // =======================================================================
1652 // function : gltfParseBufferView
1654 // =======================================================================
1655 bool RWGltf_GltfJsonParser::gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1656 const TCollection_AsciiString& theName,
1657 const RWGltf_JsonValue& theBufferView,
1658 const RWGltf_GltfAccessor& theAccessor,
1659 const RWGltf_GltfArrayType theType)
1661 RWGltf_GltfBufferView aBuffView;
1662 const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
1663 const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
1664 const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
1665 const RWGltf_JsonValue* aByteStride = findObjectMember (theBufferView, "byteStride"); // byteStride is part of bufferView since glTF 2.0
1666 const RWGltf_JsonValue* aTarget = findObjectMember (theBufferView, "target");
1667 if (aBufferName == NULL)
1669 reportGltfError ("BufferView '" + theName + "' does not define buffer.");
1673 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1674 ? (int64_t )aByteOffset->GetDouble()
1676 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
1677 ? (int64_t )aByteLength->GetDouble()
1679 aBuffView.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1680 ? aByteStride->GetInt()
1682 if (aTarget != NULL && aTarget->IsInt())
1684 aBuffView.Target = (RWGltf_GltfBufferViewTarget )aTarget->GetInt();
1685 if (aBuffView.Target != RWGltf_GltfBufferViewTarget_ARRAY_BUFFER
1686 && aBuffView.Target != RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER)
1688 reportGltfError ("BufferView '" + theName + "' defines invalid target.");
1693 if (aBuffView.ByteLength <= 0)
1695 reportGltfError ("BufferView '" + theName + "' defines invalid byteLength.");
1698 else if (aBuffView.ByteOffset < 0)
1700 reportGltfError ("BufferView '" + theName + "' defines invalid byteOffset.");
1703 else if (aBuffView.ByteStride < 0
1704 || aBuffView.ByteStride > 255)
1706 reportGltfError ("BufferView '" + theName + "' defines invalid byteStride.");
1710 const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
1712 || !aBuffer->IsObject())
1714 reportGltfError ("BufferView '" + theName + "' refers to non-existing buffer.");
1718 return gltfParseBuffer (theMeshData, getKeyString (*aBufferName), *aBuffer, theAccessor, aBuffView, theType);
1721 // =======================================================================
1722 // function : gltfParseBuffer
1724 // =======================================================================
1725 bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1726 const TCollection_AsciiString& theName,
1727 const RWGltf_JsonValue& theBuffer,
1728 const RWGltf_GltfAccessor& theAccessor,
1729 const RWGltf_GltfBufferView& theView,
1730 const RWGltf_GltfArrayType theType)
1732 //const RWGltf_JsonValue* aType = findObjectMember (theBuffer, "type");
1733 //const RWGltf_JsonValue* aByteLength = findObjectMember (theBuffer, "byteLength");
1734 const RWGltf_JsonValue* anUriVal = findObjectMember (theBuffer, "uri");
1736 int64_t anOffset = theView.ByteOffset + theAccessor.ByteOffset;
1737 const int32_t aByteStride = theAccessor.ByteStride != 0 ? theView.ByteStride : theView.ByteStride;
1738 bool isBinary = false;
1741 isBinary = IsEqual ("binary_glTF", theName) // glTF 1.0
1742 || anUriVal == NULL; // glTF 2.0
1746 anOffset += myBinBodyOffset;
1748 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1749 aData.Accessor = theAccessor;
1750 aData.Accessor.ByteStride = aByteStride;
1751 aData.StreamOffset = anOffset;
1752 aData.StreamUri = myFilePath;
1756 if (anUriVal == NULL
1757 || !anUriVal->IsString())
1759 reportGltfError ("Buffer '" + theName + "' does not define uri.");
1763 const char* anUriData = anUriVal->GetString();
1764 if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
1766 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1767 aData.Accessor = theAccessor;
1768 aData.Accessor.ByteStride = aByteStride;
1769 aData.StreamOffset = anOffset;
1770 if (!myDecodedBuffers.Find (theName, aData.StreamData))
1772 // it is better decoding in multiple threads
1773 aData.StreamData = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
1774 myDecodedBuffers.Bind (theName, aData.StreamData);
1780 TCollection_AsciiString anUri = anUriData;
1781 if (anUri.IsEmpty())
1783 reportGltfError ("Buffer '" + theName + "' does not define uri.");
1787 TCollection_AsciiString aPath = myFolder + anUri;
1788 bool isFileExist = false;
1789 if (!myProbedFiles.Find (aPath, isFileExist))
1791 isFileExist = OSD_File (aPath).Exists();
1792 myProbedFiles.Bind (aPath, isFileExist);
1796 reportGltfError ("Buffer '" + theName + "' refers to non-existing file '" + anUri + "'.");
1800 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1801 aData.Accessor = theAccessor;
1802 aData.Accessor.ByteStride = aByteStride;
1803 aData.StreamOffset = anOffset;
1804 aData.StreamUri = myFolder + anUri;
1805 if (myExternalFiles != NULL)
1807 myExternalFiles->Add (aData.StreamUri);
1813 // =======================================================================
1814 // function : bindNamedShape
1816 // =======================================================================
1817 void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape,
1818 ShapeMapGroup theGroup,
1819 const TopLoc_Location& theLoc,
1820 const TCollection_AsciiString& theId,
1821 const RWGltf_JsonValue* theUserName)
1823 if (theShape.IsNull())
1828 if (!theLoc.IsIdentity())
1830 if (!theShape.Location().IsIdentity())
1832 theShape.Location (theLoc * theShape.Location());
1836 theShape.Location (theLoc);
1840 TCollection_AsciiString aUserName;
1841 if (theUserName != NULL
1842 && theUserName->IsString())
1844 aUserName = theUserName->GetString();
1851 myShapeMap[theGroup].Bind (theId, theShape);
1852 if (myAttribMap != NULL)
1854 RWMesh_NodeAttributes aShapeAttribs;
1855 aShapeAttribs.Name = aUserName;
1856 aShapeAttribs.RawName = theId;
1857 if (theShape.ShapeType() == TopAbs_FACE)
1859 TopLoc_Location aDummy;
1860 if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (theShape), aDummy)))
1862 if (aLateData->HasStyle())
1864 // assign material and not color
1865 //aShapeAttribs.Style.SetColorSurf (aLateData->BaseColor());
1867 Handle(XCAFDoc_VisMaterial) aMat;
1868 myMaterials.Find (!aLateData->MaterialPbr().IsNull() ? aLateData->MaterialPbr()->Id : aLateData->MaterialCommon()->Id, aMat);
1869 aShapeAttribs.Style.SetMaterial (aMat);
1871 if (aShapeAttribs.Name.IsEmpty()
1872 && myUseMeshNameAsFallback)
1874 // fallback using Mesh name
1875 aShapeAttribs.Name = aLateData->Name();
1879 else if (aShapeAttribs.Name.IsEmpty()
1880 && myUseMeshNameAsFallback)
1882 // fallback using Mesh name
1883 TopLoc_Location aDummy;
1884 TCollection_AsciiString aMeshName;
1885 for (TopExp_Explorer aFaceIter (theShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
1887 if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (aFaceIter.Value()), aDummy)))
1889 if (aLateData->Name().IsEmpty())
1894 else if (aMeshName.IsEmpty())
1896 aMeshName = aLateData->Name();
1898 else if (!aMeshName.IsEqual (aLateData->Name()))
1905 if (!aMeshName.IsEmpty())
1907 aShapeAttribs.Name = aMeshName;
1910 myAttribMap->Bind (theShape, aShapeAttribs);
1915 // =======================================================================
1918 // =======================================================================
1919 bool RWGltf_GltfJsonParser::Parse (const Handle(Message_ProgressIndicator)& theProgress)
1921 Message_ProgressSentry aPSentry (theProgress, "Reading Gltf", 0, 2, 1);
1922 #ifdef HAVE_RAPIDJSON
1924 if (!gltfParseRoots())
1930 gltfParseMaterials();
1931 if (!gltfParseScene (theProgress))
1937 if (!aPSentry.More())
1943 Message::SendFail ("Error: glTF reader is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined]");