0031501: Foundation Classes, Message_Printer - remove theToPutEndl argument -- use...
[occt.git] / src / RWGltf / RWGltf_GltfJsonParser.cxx
1 // Author: Kirill Gavrilov
2 // Copyright (c) 2016-2019 OPEN CASCADE SAS
3 //
4 // This file is part of Open CASCADE Technology software library.
5 //
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.
11 //
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
14
15 #include "RWGltf_GltfJsonParser.pxx"
16
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>
29 #include <TopoDS.hxx>
30 #include <TopoDS_Iterator.hxx>
31
32 #include <fstream>
33
34 #ifdef HAVE_RAPIDJSON
35 namespace
36 {
37   //! Material extension.
38   const char THE_KHR_materials_common[] = "KHR_materials_common";
39   const char THE_KHR_binary_glTF[]      = "KHR_binary_glTF";
40
41   //! Data buffer referring to a portion of another buffer.
42   class RWGltf_SubBuffer : public NCollection_Buffer
43   {
44   public:
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) {}
50
51   private:
52     Handle(NCollection_Buffer) myBaseBuffer;
53   };
54 }
55
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)
59 {
60   if (!theObject.IsObject()
61    || !theName.IsString())
62   {
63     return NULL;
64   }
65
66   rapidjson::Document::ConstMemberIterator anIter = theObject.FindMember (theName);
67   return anIter != theObject.MemberEnd()
68        ? &anIter->value
69        : NULL;
70 }
71
72 //! Find member of the object in a safe way.
73 inline const RWGltf_JsonValue* findObjectMember (const RWGltf_JsonValue& theObject,
74                                                  const char*  theName)
75 {
76   if (!theObject.IsObject())
77   {
78     return NULL;
79   }
80
81   rapidjson::Document::ConstMemberIterator anIter = theObject.FindMember (theName);
82   return anIter != theObject.MemberEnd()
83        ? &anIter->value
84        : NULL;
85 }
86
87 // =======================================================================
88 // function : RWGltf_GltfJsonParser::FormatParseError
89 // purpose  :
90 // =======================================================================
91 const char* RWGltf_GltfJsonParser::FormatParseError (rapidjson::ParseErrorCode theCode)
92 {
93   switch (theCode)
94   {
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";
113   }
114   return "UNKOWN syntax error";
115 }
116
117 // =======================================================================
118 // function : GltfElementMap::Init
119 // purpose  :
120 // =======================================================================
121 void RWGltf_GltfJsonParser::GltfElementMap::Init (const TCollection_AsciiString& theRootName,
122                                                   const RWGltf_JsonValue* theRoot)
123 {
124   myRoot = theRoot;
125   myChildren.Clear();
126   if (theRoot == NULL)
127   {
128     return;
129   }
130
131   if (theRoot->IsObject())
132   {
133     // glTF 1.0
134     for (ConstMemberIterator aChildIter = theRoot->MemberBegin(); aChildIter != theRoot->MemberEnd(); ++aChildIter)
135     {
136       if (!aChildIter->name.IsString())
137       {
138         continue;
139       }
140
141       const TCollection_AsciiString aKey (aChildIter->name.GetString());
142       if (!myChildren.Bind (aKey, &aChildIter->value))
143       {
144         Message::SendWarning (TCollection_AsciiString ("Invalid glTF syntax - key '") + aKey + "' is already defined in '" + theRootName + "'.");
145       }
146     }
147   }
148   else if (theRoot->IsArray())
149   {
150     // glTF 2.0
151     int aChildIndex = 0;
152     for (rapidjson::Value::ConstValueIterator aChildIter = theRoot->Begin(); aChildIter != theRoot->End(); ++aChildIter, ++aChildIndex)
153     {
154       myChildren.Bind (TCollection_AsciiString (aChildIndex), aChildIter);
155     }
156   }
157 }
158 #endif
159
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);
163
164 // =======================================================================
165 // function : reportGltfSyntaxProblem
166 // purpose  :
167 // =======================================================================
168 void RWGltf_GltfJsonParser::reportGltfSyntaxProblem (const TCollection_AsciiString& theMsg,
169                                                     Message_Gravity theGravity)
170 {
171   Message::Send (myErrorPrefix + theMsg, theGravity);
172 }
173
174 // =======================================================================
175 // function : RWGltf_GltfJsonParser
176 // purpose  :
177 // =======================================================================
178 RWGltf_GltfJsonParser::RWGltf_GltfJsonParser (TopTools_SequenceOfShape& theRootShapes)
179 : myRootShapes(&theRootShapes),
180   myAttribMap (NULL),
181   myExternalFiles (NULL),
182   myBinBodyOffset (0),
183   myBinBodyLen (0),
184   myIsBinary (false),
185   myIsGltf1 (false),
186   myToSkipEmptyNodes (true),
187   myUseMeshNameAsFallback (true),
188   myToProbeHeader (false)
189 {
190   myCSTrsf.SetInputLengthUnit (1.0); // meters
191   myCSTrsf.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
192 }
193
194 // =======================================================================
195 // function : SetFilePath
196 // purpose  :
197 // =======================================================================
198 void RWGltf_GltfJsonParser::SetFilePath (const TCollection_AsciiString& theFilePath)
199 {
200   myFilePath = theFilePath;
201   // determine file location to load associated files
202   TCollection_AsciiString aFileName;
203   OSD_Path::FolderAndFileFromPath (theFilePath, myFolder, aFileName);
204 }
205
206 #ifdef HAVE_RAPIDJSON
207 // =======================================================================
208 // function : gltfParseRoots
209 // purpose  :
210 // =======================================================================
211 bool RWGltf_GltfJsonParser::gltfParseRoots()
212 {
213   // find glTF root elements for smooth navigation
214   RWGltf_JsonValue aNames[RWGltf_GltfRootElement_NB];
215   for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
216   {
217     aNames[aRootNameIter] = rapidjson::StringRef (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter));
218   }
219
220   for (ConstMemberIterator aRootIter = MemberBegin();
221        aRootIter != MemberEnd(); ++aRootIter)
222   {
223     for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
224     {
225       if (myGltfRoots[aRootNameIter].IsNull()
226             && aNames[aRootNameIter] == aRootIter->name)
227       {
228         // we will not modify JSON document, thus it is OK to keep the pointers
229         myGltfRoots[aRootNameIter].Init (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter), &aRootIter->value);
230         break;
231       }
232     }
233   }
234
235   for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB_MANDATORY; ++aRootNameIter)
236   {
237     if (myGltfRoots[aRootNameIter].IsNull())
238     {
239       reportGltfError ("Member '" + RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter) + "' is not found.");
240       return false;
241     }
242   }
243   return true;
244 }
245
246 // =======================================================================
247 // function : gltfParseAsset
248 // purpose  :
249 // =======================================================================
250 void RWGltf_GltfJsonParser::gltfParseAsset()
251 {
252   const RWGltf_JsonValue* anAsset = myGltfRoots[RWGltf_GltfRootElement_Asset].Root();
253   if (anAsset == NULL)
254   {
255     return;
256   }
257
258   if (const RWGltf_JsonValue* aVersion = findObjectMember (*anAsset, "version"))
259   {
260     if (aVersion->IsString())
261     {
262       TCollection_AsciiString aVerStr (aVersion->GetString());
263       myIsGltf1 = aVerStr.StartsWith ("1.");
264     }
265   }
266
267   if (myMetadata == NULL)
268   {
269     return;
270   }
271
272   if (const RWGltf_JsonValue* aGenerator = findObjectMember (*anAsset, "generator"))
273   {
274     if (aGenerator->IsString())
275     {
276       myMetadata->Add ("generator", aGenerator->GetString());
277     }
278   }
279   if (const RWGltf_JsonValue* aCopyRight = findObjectMember (*anAsset, "copyright"))
280   {
281     if (aCopyRight->IsString())
282     {
283       myMetadata->Add ("copyright", aCopyRight->GetString());
284     }
285   }
286 }
287
288 // =======================================================================
289 // function : gltfParseMaterials
290 // purpose  :
291 // =======================================================================
292 void RWGltf_GltfJsonParser::gltfParseMaterials()
293 {
294   const RWGltf_JsonValue* aMatList = myGltfRoots[RWGltf_GltfRootElement_Materials].Root();
295   if (aMatList == NULL)
296   {
297     return;
298   }
299   else if (aMatList->IsObject())
300   {
301     // glTF 1.0
302     for (ConstMemberIterator aMatIter = aMatList->MemberBegin();
303          aMatIter != aMatList->MemberEnd(); ++aMatIter)
304     {
305       Handle(RWGltf_MaterialCommon) aMat;
306       const RWGltf_JsonValue& aMatNode = aMatIter->value;
307       const RWGltf_JsonValue& aMatId   = aMatIter->name;
308       const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
309       if (!gltfParseCommonMaterial (aMat, aMatNode))
310       {
311         if (!gltfParseStdMaterial (aMat, aMatNode))
312         {
313           continue;
314         }
315       }
316
317       if (aNameVal != NULL
318        && aNameVal->IsString())
319       {
320         aMat->Name = aNameVal->GetString();
321       }
322       aMat->Id = aMatId.GetString();
323       myMaterialsCommon.Bind (aMat->Id, aMat);
324       gltfBindMaterial (Handle(RWGltf_MaterialMetallicRoughness)(), aMat);
325     }
326   }
327   else if (aMatList->IsArray())
328   {
329     // glTF 2.0
330     int aMatIndex = 0;
331     for (rapidjson::Value::ConstValueIterator aMatIter = aMatList->Begin(); aMatIter != aMatList->End(); ++aMatIter, ++aMatIndex)
332     {
333       Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
334       const RWGltf_JsonValue& aMatNode = *aMatIter;
335       const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
336       if (gltfParsePbrMaterial (aMatPbr,  aMatNode))
337       {
338         if (aNameVal != NULL
339          && aNameVal->IsString())
340         {
341           aMatPbr->Name = aNameVal->GetString();
342         }
343         aMatPbr->Id = TCollection_AsciiString ("mat_") + aMatIndex;
344         myMaterialsPbr.Bind (TCollection_AsciiString (aMatIndex), aMatPbr);
345       }
346
347       Handle(RWGltf_MaterialCommon) aMatCommon;
348       if (gltfParseCommonMaterial(aMatCommon, aMatNode)
349        || gltfParseStdMaterial   (aMatCommon, aMatNode))
350       {
351         if (aNameVal != NULL
352          && aNameVal->IsString())
353         {
354           aMatCommon->Name = aNameVal->GetString();
355         }
356         aMatCommon->Id = TCollection_AsciiString ("mat_") + aMatIndex;
357         myMaterialsCommon.Bind (TCollection_AsciiString (aMatIndex), aMatCommon);
358       }
359
360       gltfBindMaterial (aMatPbr, aMatCommon);
361     }
362   }
363 }
364
365 // =======================================================================
366 // function : gltfBindMaterial
367 // purpose  :
368 // =======================================================================
369 void RWGltf_GltfJsonParser::gltfBindMaterial (const Handle(RWGltf_MaterialMetallicRoughness)& theMatPbr,
370                                               const Handle(RWGltf_MaterialCommon)& theMatCommon)
371 {
372   if (theMatPbr.IsNull()
373    && theMatCommon.IsNull())
374   {
375     return;
376   }
377
378   Handle(XCAFDoc_VisMaterial) aMat = new XCAFDoc_VisMaterial();
379   if (!theMatCommon.IsNull())
380   {
381     XCAFDoc_VisMaterialCommon aMatXde;
382     aMatXde.IsDefined = true;
383     aMatXde.AmbientColor    = theMatCommon->AmbientColor;
384     aMatXde.DiffuseColor    = theMatCommon->DiffuseColor;
385     aMatXde.SpecularColor   = theMatCommon->SpecularColor;
386     aMatXde.EmissiveColor   = theMatCommon->EmissiveColor;
387     aMatXde.Shininess       = theMatCommon->Shininess;
388     aMatXde.Transparency    = theMatCommon->Transparency;
389     aMatXde.DiffuseTexture  = theMatCommon->DiffuseTexture;
390     if (aMatXde.DiffuseTexture.IsNull()
391     && !theMatCommon->AmbientTexture.IsNull())
392     {
393       aMatXde.DiffuseTexture = theMatCommon->AmbientTexture;
394     }
395     aMat->SetCommonMaterial (aMatXde);
396     if (!theMatCommon->Name.IsEmpty())
397     {
398       aMat->SetRawName (new TCollection_HAsciiString (theMatCommon->Name));
399     }
400   }
401   if (!theMatPbr.IsNull())
402   {
403     XCAFDoc_VisMaterialPBR aMatXde;
404     aMatXde.IsDefined = true;
405     aMatXde.MetallicRoughnessTexture = theMatPbr->MetallicRoughnessTexture;
406     aMatXde.BaseColorTexture = theMatPbr->BaseColorTexture;
407     aMatXde.EmissiveTexture  = theMatPbr->EmissiveTexture;
408     aMatXde.OcclusionTexture = theMatPbr->OcclusionTexture;
409     aMatXde.NormalTexture    = theMatPbr->NormalTexture;
410     aMatXde.BaseColor        = theMatPbr->BaseColor;
411     aMatXde.EmissiveFactor   = theMatPbr->EmissiveFactor;
412     aMatXde.Metallic         = theMatPbr->Metallic;
413     aMatXde.Roughness        = theMatPbr->Roughness;
414     aMat->SetPbrMaterial (aMatXde);
415
416     Graphic3d_AlphaMode anAlphaMode = Graphic3d_AlphaMode_BlendAuto;
417     switch (theMatPbr->AlphaMode)
418     {
419       case RWGltf_GltfAlphaMode_Opaque:
420       {
421         anAlphaMode = Graphic3d_AlphaMode_Opaque;
422         if (aMatXde.BaseColor.Alpha() < 1.0f)
423         {
424           Message::SendWarning ("glTF reader - material with non-zero Transparency specifies Opaque AlphaMode");
425         }
426         break;
427       }
428       case RWGltf_GltfAlphaMode_Mask:
429       {
430         anAlphaMode = Graphic3d_AlphaMode_Mask;
431         break;
432       }
433       case RWGltf_GltfAlphaMode_Blend:
434       {
435         anAlphaMode = Graphic3d_AlphaMode_Blend;
436         break;
437       }
438     }
439     aMat->SetAlphaMode (anAlphaMode, theMatPbr->AlphaCutOff);
440     aMat->SetDoubleSided (theMatPbr->IsDoubleSided);
441
442     if (!theMatPbr->Name.IsEmpty())
443     {
444       aMat->SetRawName (new TCollection_HAsciiString (theMatPbr->Name));
445     }
446   }
447
448   myMaterials.Bind (!theMatPbr.IsNull() ? theMatPbr->Id : theMatCommon->Id, aMat);
449 }
450
451 // =======================================================================
452 // function : gltfParseStdMaterial
453 // purpose  :
454 // =======================================================================
455 bool RWGltf_GltfJsonParser::gltfParseStdMaterial (Handle(RWGltf_MaterialCommon)& theMat,
456                                                   const RWGltf_JsonValue& theMatNode)
457 {
458   //const RWGltf_JsonValue* aTechVal = findObjectMember (theMatNode, "technique");
459   const RWGltf_JsonValue* aValues  = findObjectMember (theMatNode, "values");
460   if (aValues == NULL)
461   {
462     return false;
463   }
464
465   const RWGltf_JsonValue* anAmbVal = findObjectMember (*aValues, "ambient");
466   const RWGltf_JsonValue* aDiffVal = findObjectMember (*aValues, "diffuse");
467   const RWGltf_JsonValue* anEmiVal = findObjectMember (*aValues, "emission");
468   const RWGltf_JsonValue* aSpecVal = findObjectMember (*aValues, "specular");
469   const RWGltf_JsonValue* aShinVal = findObjectMember (*aValues, "shininess");
470   if (anAmbVal == NULL
471    && aDiffVal == NULL
472    && anEmiVal == NULL
473    && aSpecVal == NULL
474    && aShinVal == NULL)
475   {
476     return false;
477   }
478
479   theMat = new RWGltf_MaterialCommon();
480
481   Graphic3d_Vec4d anAmb, aDiff, anEmi, aSpec;
482   if (anAmbVal != NULL
483    && anAmbVal->IsString())
484   {
485     gltfParseTexture (theMat->AmbientTexture, anAmbVal);
486   }
487   else if (gltfReadVec4   (anAmb, anAmbVal)
488         && validateColor4 (anAmb))
489   {
490     theMat->AmbientColor = Quantity_Color (anAmb.r(), anAmb.g(), anAmb.b(), Quantity_TOC_sRGB);
491   }
492
493   if (aDiffVal != NULL
494    && aDiffVal->IsString())
495   {
496     gltfParseTexture (theMat->DiffuseTexture, aDiffVal);
497   }
498   else if (gltfReadVec4   (aDiff, aDiffVal)
499         && validateColor4 (aDiff))
500   {
501     theMat->DiffuseColor = Quantity_Color (aDiff.r(), aDiff.g(), aDiff.b(), Quantity_TOC_sRGB);
502     theMat->Transparency = float(1.0 - aDiff.a());
503   }
504
505   if (gltfReadVec4   (anEmi, anEmiVal)
506    && validateColor4 (anEmi))
507   {
508     theMat->EmissiveColor = Quantity_Color (anEmi.r(), anEmi.g(), anEmi.b(), Quantity_TOC_sRGB);
509   }
510
511   if (aSpecVal != NULL
512    && aSpecVal->IsString())
513   {
514     gltfParseTexture (theMat->SpecularTexture, aSpecVal);
515   }
516   if (gltfReadVec4   (aSpec, aSpecVal)
517    && validateColor4 (aSpec))
518   {
519     theMat->SpecularColor = Quantity_Color (aSpec.r(), aSpec.g(), aSpec.b(), Quantity_TOC_sRGB);
520   }
521
522   if (aShinVal != NULL
523    && aShinVal->IsNumber())
524   {
525     const double aSpecular = aShinVal->GetDouble();
526     if (aSpecular >= 0)
527     {
528       theMat->Shininess = (float )Min (aSpecular / 1000.0, 1.0);
529     }
530   }
531   return true;
532 }
533
534 // =======================================================================
535 // function : gltfParsePbrMaterial
536 // purpose  :
537 // =======================================================================
538 bool RWGltf_GltfJsonParser::gltfParsePbrMaterial (Handle(RWGltf_MaterialMetallicRoughness)& theMat,
539                                                   const RWGltf_JsonValue& theMatNode)
540 {
541   /*if (const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions"))
542   {
543     if (const RWGltf_JsonValue* anExtDefVal = findObjectMember (*anExtVal, "KHR_materials_pbrSpecularGlossiness"))
544     {
545       const RWGltf_JsonValue* aDiffTexVal = findObjectMember (*anExtDefVal, "diffuseTexture");
546       const RWGltf_JsonValue* aSpecTexVal = findObjectMember (*anExtDefVal, "specularGlossinessTexture");
547     }
548   }*/
549
550   const RWGltf_JsonValue* aMetalRoughVal    = findObjectMember (theMatNode, "pbrMetallicRoughness");
551   const RWGltf_JsonValue* aNormTexVal       = findObjectMember (theMatNode, "normalTexture");
552   const RWGltf_JsonValue* anEmissFactorVal  = findObjectMember (theMatNode, "emissiveFactor");
553   const RWGltf_JsonValue* anEmissTexVal     = findObjectMember (theMatNode, "emissiveTexture");
554   const RWGltf_JsonValue* anOcclusionTexVal = findObjectMember (theMatNode, "occlusionTexture");
555   const RWGltf_JsonValue* aDoubleSidedVal   = findObjectMember (theMatNode, "doubleSided");
556   const RWGltf_JsonValue* anAlphaModeVal    = findObjectMember (theMatNode, "alphaMode");
557   const RWGltf_JsonValue* anAlphaCutoffVal  = findObjectMember (theMatNode, "alphaCutoff");
558   // TODO ADOBE_materials_thin_transparency extension can be used to read IOR (Index of Refraction for transparent materials)
559   if (aMetalRoughVal == NULL)
560   {
561     return false;
562   }
563
564   theMat = new RWGltf_MaterialMetallicRoughness();
565   const RWGltf_JsonValue* aBaseColorFactorVal = findObjectMember (*aMetalRoughVal, "baseColorFactor");
566   const RWGltf_JsonValue* aBaseColorTexVal    = findObjectMember (*aMetalRoughVal, "baseColorTexture");
567   const RWGltf_JsonValue* aMetallicFactorVal  = findObjectMember (*aMetalRoughVal, "metallicFactor");
568   const RWGltf_JsonValue* aRoughnessFactorVal = findObjectMember (*aMetalRoughVal, "roughnessFactor");
569   const RWGltf_JsonValue* aMetalRoughTexVal   = findObjectMember (*aMetalRoughVal, "metallicRoughnessTexture");
570
571   if (aDoubleSidedVal != NULL
572    && aDoubleSidedVal->IsBool())
573   {
574     theMat->IsDoubleSided = aDoubleSidedVal->GetBool();
575   }
576   if (anAlphaCutoffVal != NULL
577    && anAlphaCutoffVal->IsNumber())
578   {
579     theMat->AlphaCutOff = (float )anAlphaCutoffVal->GetDouble();
580   }
581   if (anAlphaModeVal != NULL
582    && anAlphaModeVal->IsString())
583   {
584     theMat->AlphaMode = RWGltf_GltfParseAlphaMode (anAlphaModeVal->GetString());
585   }
586
587   if (aBaseColorTexVal != NULL
588    && aBaseColorTexVal->IsObject())
589   {
590     if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aBaseColorTexVal, "index"))
591     {
592       gltfParseTexture (theMat->BaseColorTexture, aTexIndexVal);
593     }
594   }
595
596   Graphic3d_Vec4d aBaseColorFactor;
597   if (gltfReadVec4   (aBaseColorFactor, aBaseColorFactorVal)
598    && validateColor4 (aBaseColorFactor))
599   {
600     theMat->BaseColor = Quantity_ColorRGBA (Graphic3d_Vec4 (aBaseColorFactor));
601   }
602
603   Graphic3d_Vec3d anEmissiveFactor;
604   if (gltfReadVec3   (anEmissiveFactor, anEmissFactorVal)
605    && validateColor3 (anEmissiveFactor))
606   {
607     theMat->EmissiveFactor = Graphic3d_Vec3 (anEmissiveFactor);
608   }
609
610   if (aMetalRoughTexVal != NULL
611    && aMetalRoughTexVal->IsObject())
612   {
613     if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aMetalRoughTexVal, "index"))
614     {
615       gltfParseTexture (theMat->MetallicRoughnessTexture, aTexIndexVal);
616     }
617   }
618
619   if (aMetallicFactorVal != NULL
620    && aMetallicFactorVal->IsNumber())
621   {
622     theMat->Metallic = (float )aMetallicFactorVal->GetDouble();
623   }
624
625   if (aRoughnessFactorVal != NULL
626    && aRoughnessFactorVal->IsNumber())
627   {
628     theMat->Roughness = (float )aRoughnessFactorVal->GetDouble();
629   }
630
631   if (aNormTexVal != NULL
632    && aNormTexVal->IsObject())
633   {
634     if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aNormTexVal, "index"))
635     {
636       gltfParseTexture (theMat->NormalTexture, aTexIndexVal);
637     }
638   }
639
640   if (anEmissTexVal != NULL
641    && anEmissTexVal->IsObject())
642   {
643     if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anEmissTexVal, "index"))
644     {
645       gltfParseTexture (theMat->EmissiveTexture, aTexIndexVal);
646     }
647   }
648
649   if (anOcclusionTexVal != NULL
650    && anOcclusionTexVal->IsObject())
651   {
652     if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anOcclusionTexVal, "index"))
653     {
654       gltfParseTexture (theMat->OcclusionTexture, aTexIndexVal);
655     }
656   }
657   return true;
658 }
659
660 // =======================================================================
661 // function : gltfParseCommonMaterial
662 // purpose  :
663 // =======================================================================
664 bool RWGltf_GltfJsonParser::gltfParseCommonMaterial (Handle(RWGltf_MaterialCommon)& theMat,
665                                                      const RWGltf_JsonValue& theMatNode)
666 {
667   const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions");
668   if (anExtVal == NULL)
669   {
670     return false;
671   }
672
673   const RWGltf_JsonValue* aMatCommon = findObjectMember (*anExtVal, THE_KHR_materials_common);
674   if (aMatCommon == NULL)
675   {
676     return false;
677   }
678
679   if (!gltfParseStdMaterial (theMat, *aMatCommon))
680   {
681     return false;
682   }
683   return true;
684 }
685
686 // =======================================================================
687 // function : gltfParseTexture
688 // purpose  :
689 // =======================================================================
690 bool RWGltf_GltfJsonParser::gltfParseTexture (Handle(Image_Texture)& theTexture,
691                                               const RWGltf_JsonValue* theTextureId)
692 {
693   if (theTextureId == NULL
694   ||  myGltfRoots[RWGltf_GltfRootElement_Textures].IsNull()
695   ||  myGltfRoots[RWGltf_GltfRootElement_Images].IsNull())
696   {
697     return false;
698   }
699
700   const TCollection_AsciiString aTextureId = getKeyString (*theTextureId);
701   const RWGltf_JsonValue* aTexNode = myGltfRoots[RWGltf_GltfRootElement_Textures].FindChild (*theTextureId);
702   if (aTexNode == NULL)
703   {
704     reportGltfWarning ("Texture node '" + aTextureId + "' is not found.");
705     return false;
706   }
707
708   const RWGltf_JsonValue* aSrcVal  = findObjectMember (*aTexNode, "source");
709   const RWGltf_JsonValue* aTargVal = findObjectMember (*aTexNode, "target");
710   if (aSrcVal == NULL)
711   {
712     reportGltfWarning ("Invalid texture node '" + aTextureId + "' without a 'source' property.");
713     return false;
714   }
715   if (aTargVal != NULL
716    && aTargVal->IsNumber()
717    && aTargVal->GetInt() != 3553) // GL_TEXTURE_2D
718   {
719     return false;
720   }
721
722   const RWGltf_JsonValue* anImgNode = myGltfRoots[RWGltf_GltfRootElement_Images].FindChild (*aSrcVal);
723   if (anImgNode == NULL)
724   {
725     reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to non-existing image '" + getKeyString (*aSrcVal) + "'.");
726     return false;
727   }
728
729   if (myIsBinary)
730   {
731     const RWGltf_JsonValue* aBinVal = NULL;
732     const RWGltf_JsonValue* aBufferViewName = findObjectMember (*anImgNode, "bufferView");
733     if (aBufferViewName != NULL)
734     {
735       aBinVal = anImgNode;
736     }
737     else if (myIsGltf1)
738     {
739       const RWGltf_JsonValue* anExtVal = findObjectMember (*anImgNode, "extensions");
740       if (anExtVal != NULL)
741       {
742         aBinVal = findObjectMember (*anExtVal, THE_KHR_binary_glTF);
743         if (aBinVal != NULL)
744         {
745           aBufferViewName = findObjectMember (*aBinVal, "bufferView");
746         }
747       }
748     }
749
750     if (aBinVal != NULL)
751     {
752       if (aBufferViewName == NULL)
753       {
754         reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
755         return false;
756       }
757       const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
758       if (aBufferView == NULL
759       || !aBufferView->IsObject())
760       {
761         reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid buffer view '" + getKeyString (*aBufferViewName) + "'.");
762         return false;
763       }
764       return gltfParseTexturInGlbBuffer (theTexture, *aBinVal, getKeyString (*aBufferViewName), *aBufferView);
765     }
766   }
767
768   const RWGltf_JsonValue* anUriVal = findObjectMember (*anImgNode, "uri");
769   if (anUriVal == NULL)
770   {
771     const RWGltf_JsonValue* aBufferViewName = findObjectMember (*anImgNode, "bufferView");
772     if (aBufferViewName == NULL)
773     {
774       reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
775       return false;
776     }
777
778     const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
779     if (aBufferView == NULL
780     || !aBufferView->IsObject())
781     {
782       reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid buffer view '" + getKeyString (*aBufferViewName) + "'.");
783       return false;
784     }
785     return gltfParseTextureInBufferView (theTexture, getKeyString (*aSrcVal), getKeyString (*aBufferViewName), *aBufferView);
786   }
787
788   if (!anUriVal->IsString())
789   {
790     reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
791     return false;
792   }
793
794   const char* anUriData = anUriVal->GetString();
795   if (::strncmp (anUriData, "data:", 5) == 0) // data:image/png;base64
796   {
797     // uncompressing base64 here is inefficient, because the same image can be shared by several nodes
798     const char* aDataStart = anUriData + 5;
799     for (const char* aDataIter = aDataStart; *aDataIter != '\0'; ++aDataIter)
800     {
801       if (::memcmp (aDataIter, ";base64,", 8) == 0)
802       {
803         const char* aBase64End  = anUriData + anUriVal->GetStringLength();
804         const char* aBase64Data = aDataIter + 8;
805         const size_t aBase64Len = size_t(aBase64End - aBase64Data);
806         //const TCollection_AsciiString aMime (aDataStart, aDataIter - aDataStart);
807         Handle(NCollection_Buffer) aData = FSD_Base64Decoder::Decode ((const Standard_Byte* )aBase64Data, aBase64Len);
808         theTexture = new Image_Texture (aData, myFilePath + "@" + getKeyString (*aSrcVal));
809         return true;
810       }
811     }
812     Message::SendWarning ("glTF reader - embedded image has been skipped");
813     return false;
814   }
815
816   TCollection_AsciiString anImageFile = myFolder + anUriVal->GetString();
817   theTexture = new Image_Texture (anImageFile);
818   if (myExternalFiles != NULL)
819   {
820     myExternalFiles->Add (anImageFile);
821   }
822   return true;
823 }
824
825 // =======================================================================
826 // function : gltfParseTexturInGlbBuffer
827 // purpose  :
828 // =======================================================================
829 bool RWGltf_GltfJsonParser::gltfParseTexturInGlbBuffer (Handle(Image_Texture)& theTexture,
830                                                         const RWGltf_JsonValue& theBinVal,
831                                                         const TCollection_AsciiString& theBufferViewId,
832                                                         const RWGltf_JsonValue& theBufferView)
833 {
834   const RWGltf_JsonValue* aMimeTypeVal = findObjectMember (theBinVal, "mimeType");
835   //const RWGltf_JsonValue* aWidthVal    = findObjectMember (theBinVal, "width");
836   //const RWGltf_JsonValue* aHeightVal   = findObjectMember (theBinVal, "height");
837   (void )aMimeTypeVal;
838
839   const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
840   const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
841   const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
842   if (aBufferName != NULL
843   &&  aBufferName->IsString()
844   && !IsEqual (aBufferName->GetString(), "binary_glTF"))
845   {
846     reportGltfError ("BufferView '" + theBufferViewId + "' does not define binary_glTF buffer.");
847     return false;
848   }
849
850   RWGltf_GltfBufferView aBuffView;
851   aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
852                        ? (int64_t )aByteOffset->GetDouble()
853                        : 0;
854   aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
855                        ? (int64_t )aByteLength->GetDouble()
856                        : 0;
857   if (aBuffView.ByteLength <= 0)
858   {
859     reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteLength.");
860     return false;
861   }
862   else if (aBuffView.ByteOffset < 0)
863   {
864     reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteOffset.");
865     return false;
866   }
867
868   const int64_t anOffset = myBinBodyOffset + aBuffView.ByteOffset;
869   theTexture = new Image_Texture (myFilePath, anOffset, aBuffView.ByteLength);
870   return true;
871 }
872
873 // =======================================================================
874 // function : gltfParseTextureInBufferView
875 // purpose  :
876 // =======================================================================
877 bool RWGltf_GltfJsonParser::gltfParseTextureInBufferView (Handle(Image_Texture)& theTexture,
878                                                           const TCollection_AsciiString& theSourceId,
879                                                           const TCollection_AsciiString& theBufferViewId,
880                                                           const RWGltf_JsonValue& theBufferView)
881 {
882   const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
883   const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
884   const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
885   if (aBufferName == NULL)
886   {
887     reportGltfError ("BufferView '" + theBufferViewId + "' does not define buffer.");
888     return false;
889   }
890
891   const TCollection_AsciiString aBufferId = getKeyString (*aBufferName);
892   const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
893   if (aBuffer == NULL
894   || !aBuffer->IsObject())
895   {
896     reportGltfError ("BufferView '" + theBufferViewId + "' refers to non-existing buffer.");
897     return false;
898   }
899
900   RWGltf_GltfBufferView aBuffView;
901   aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
902                        ? (int64_t )aByteOffset->GetDouble()
903                        : 0;
904   aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
905                        ? (int64_t )aByteLength->GetDouble()
906                        : 0;
907   if (aBuffView.ByteLength <= 0)
908   {
909     reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteLength.");
910     return false;
911   }
912   else if (aBuffView.ByteOffset < 0)
913   {
914     reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteOffset.");
915     return false;
916   }
917
918   const RWGltf_JsonValue* anUriVal = findObjectMember (*aBuffer, "uri");
919   if (anUriVal == NULL
920   || !anUriVal->IsString())
921   {
922     reportGltfError ("Buffer '" + aBufferId + "' does not define uri.");
923     return false;
924   }
925
926   const char* anUriData = anUriVal->GetString();
927   if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
928   {
929     Handle(NCollection_Buffer) aBaseBuffer;
930     if (!myDecodedBuffers.Find (aBufferId, aBaseBuffer))
931     {
932       aBaseBuffer = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
933       myDecodedBuffers.Bind (aBufferId, aBaseBuffer);
934     }
935
936     Handle(RWGltf_SubBuffer) aSubBuffer = new RWGltf_SubBuffer (aBaseBuffer, (Standard_Size )aBuffView.ByteOffset, (Standard_Size )aBuffView.ByteLength);
937     theTexture = new Image_Texture (aSubBuffer, myFilePath + "@" + theSourceId);
938     return true;
939   }
940
941   const TCollection_AsciiString anUri (anUriData);
942   if (anUri.IsEmpty())
943   {
944     reportGltfError ("Buffer '" + aBufferId + "' does not define uri.");
945     return false;
946   }
947
948   const TCollection_AsciiString aPath = myFolder + anUri;
949   bool isFileExist = false;
950   if (!myProbedFiles.Find (aPath, isFileExist))
951   {
952     isFileExist = OSD_File (aPath).Exists();
953     myProbedFiles.Bind (aPath, isFileExist);
954   }
955   if (!isFileExist)
956   {
957     reportGltfError ("Buffer '" + aBufferId + "' refers to non-existing file '" + anUri + "'.");
958     return false;
959   }
960
961   theTexture = new Image_Texture (aPath, aBuffView.ByteOffset, aBuffView.ByteLength);
962   if (myExternalFiles != NULL)
963   {
964     myExternalFiles->Add (aPath);
965   }
966   return true;
967 }
968
969 // =======================================================================
970 // function : gltfParseScene
971 // purpose  :
972 // =======================================================================
973 bool RWGltf_GltfJsonParser::gltfParseScene (const Handle(Message_ProgressIndicator)& theProgress)
974 {
975   // search default scene
976   const RWGltf_JsonValue* aDefScene = myGltfRoots[RWGltf_GltfRootElement_Scenes].FindChild (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root());
977   if (aDefScene == NULL)
978   {
979     reportGltfError ("Default scene is not found.");
980     return false;
981   }
982
983   const RWGltf_JsonValue* aSceneNodes = findObjectMember (*aDefScene, "nodes");
984   if (aSceneNodes == NULL
985   || !aSceneNodes->IsArray())
986   {
987     reportGltfError ("Empty scene '" + getKeyString (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root()) + "'.");
988     return false;
989   }
990
991   return gltfParseSceneNodes (*myRootShapes, *aSceneNodes, theProgress);
992 }
993
994 // =======================================================================
995 // function : gltfParseSceneNodes
996 // purpose  :
997 // =======================================================================
998 bool RWGltf_GltfJsonParser::gltfParseSceneNodes (TopTools_SequenceOfShape& theShapeSeq,
999                                                  const RWGltf_JsonValue& theSceneNodes,
1000                                                  const Handle(Message_ProgressIndicator)& theProgress)
1001 {
1002   if (!theSceneNodes.IsArray())
1003   {
1004     reportGltfError ("Scene nodes is not array.");
1005     return false;
1006   }
1007
1008   Message_ProgressSentry aPSentry (theProgress, "Reading scene nodes", 0, theSceneNodes.Size(), 1);
1009   for (rapidjson::Value::ConstValueIterator aSceneNodeIter = theSceneNodes.Begin();
1010        aSceneNodeIter != theSceneNodes.End() && aPSentry.More(); ++aSceneNodeIter, aPSentry.Next())
1011   {
1012     const RWGltf_JsonValue* aSceneNode = myGltfRoots[RWGltf_GltfRootElement_Nodes].FindChild (*aSceneNodeIter);
1013     if (aSceneNode == NULL)
1014     {
1015       reportGltfWarning ("Scene refers to non-existing node '" + getKeyString (*aSceneNodeIter) + "'.");
1016       return true;
1017     }
1018
1019     TopoDS_Shape aNodeShape;
1020     if (!gltfParseSceneNode (aNodeShape, getKeyString (*aSceneNodeIter), *aSceneNode, theProgress))
1021     {
1022       return false;
1023     }
1024
1025     if (aNodeShape.IsNull())
1026     {
1027       continue;
1028     }
1029     else if (myToSkipEmptyNodes
1030          && !TopExp_Explorer (aNodeShape, TopAbs_FACE).More())
1031     {
1032       continue;
1033     }
1034
1035     theShapeSeq.Append (aNodeShape);
1036   }
1037   return true;
1038 }
1039
1040 // =======================================================================
1041 // function : gltfParseSceneNode
1042 // purpose  :
1043 // =======================================================================
1044 bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
1045                                                 const TCollection_AsciiString& theSceneNodeId,
1046                                                 const RWGltf_JsonValue& theSceneNode,
1047                                                 const Handle(Message_ProgressIndicator)& theProgress)
1048 {
1049   const RWGltf_JsonValue* aName         = findObjectMember (theSceneNode, "name");
1050   //const RWGltf_JsonValue* aJointName    = findObjectMember (theSceneNode, "jointName");
1051   const RWGltf_JsonValue* aChildren     = findObjectMember (theSceneNode, "children");
1052   const RWGltf_JsonValue* aMeshes_1     = findObjectMember (theSceneNode, "meshes");
1053   const RWGltf_JsonValue* aMesh_2       = findObjectMember (theSceneNode, "mesh");
1054   //const RWGltf_JsonValue* aCamera       = findObjectMember (theSceneNode, "camera");
1055   const RWGltf_JsonValue* aTrsfMatVal   = findObjectMember (theSceneNode, "matrix");
1056   const RWGltf_JsonValue* aTrsfRotVal   = findObjectMember (theSceneNode, "rotation");
1057   const RWGltf_JsonValue* aTrsfScaleVal = findObjectMember (theSceneNode, "scale");
1058   const RWGltf_JsonValue* aTrsfTransVal = findObjectMember (theSceneNode, "translation");
1059   if (findNodeShape (theNodeShape, theSceneNodeId))
1060   {
1061     return true;
1062   }
1063
1064   TopLoc_Location aNodeLoc;
1065   const bool hasTrs = aTrsfRotVal   != NULL
1066                    || aTrsfScaleVal != NULL
1067                    || aTrsfTransVal != NULL;
1068   if (aTrsfMatVal != NULL)
1069   {
1070     if (hasTrs)
1071     {
1072       reportGltfError ("Scene node '" + theSceneNodeId + "' defines ambiguous transformation.");
1073       return false;
1074     }
1075     else if (!aTrsfMatVal->IsArray()
1076            || aTrsfMatVal->Size() != 16)
1077     {
1078       reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix array.");
1079       return false;
1080     }
1081
1082     Graphic3d_Mat4d aMat4;
1083     for (int aColIter = 0; aColIter < 4; ++aColIter)
1084     {
1085       for (int aRowIter = 0; aRowIter < 4; ++aRowIter)
1086       {
1087         const RWGltf_JsonValue& aGenVal = (*aTrsfMatVal)[aColIter * 4 + aRowIter];
1088         if (!aGenVal.IsNumber())
1089         {
1090           reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix.");
1091           return false;
1092         }
1093         aMat4.SetValue (aRowIter, aColIter, aGenVal.GetDouble());
1094       }
1095     }
1096
1097     if (!aMat4.IsIdentity())
1098     {
1099       gp_Trsf aTrsf;
1100       aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
1101                        aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
1102                        aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
1103       myCSTrsf.TransformTransformation (aTrsf);
1104       if (aTrsf.Form() != gp_Identity)
1105       {
1106         aNodeLoc = TopLoc_Location (aTrsf);
1107       }
1108     }
1109   }
1110   else if (hasTrs)
1111   {
1112     gp_Trsf aTrsf;
1113     if (aTrsfRotVal != NULL)
1114     {
1115       if (!aTrsfRotVal->IsArray()
1116         || aTrsfRotVal->Size() != 4)
1117       {
1118         reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation quaternion.");
1119         return false;
1120       }
1121
1122       Graphic3d_Vec4d aRotVec4;
1123       for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
1124       {
1125         const RWGltf_JsonValue& aGenVal = (*aTrsfRotVal)[aCompIter];
1126         if (!aGenVal.IsNumber())
1127         {
1128           reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation.");
1129           return false;
1130         }
1131         aRotVec4[aCompIter] = aGenVal.GetDouble();
1132       }
1133       const gp_Quaternion aQuaternion (aRotVec4.x(), aRotVec4.y(), aRotVec4.z(), aRotVec4.w());
1134       if (Abs (aQuaternion.X())       > gp::Resolution()
1135        || Abs (aQuaternion.Y())       > gp::Resolution()
1136        || Abs (aQuaternion.Z())       > gp::Resolution()
1137        || Abs (aQuaternion.W() - 1.0) > gp::Resolution())
1138       {
1139         aTrsf.SetRotation (aQuaternion);
1140       }
1141     }
1142
1143     if (aTrsfTransVal != NULL)
1144     {
1145       if (!aTrsfTransVal->IsArray()
1146         || aTrsfTransVal->Size() != 3)
1147       {
1148         reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation vector.");
1149         return false;
1150       }
1151
1152       gp_XYZ aTransVec;
1153       for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
1154       {
1155         const RWGltf_JsonValue& aGenVal = (*aTrsfTransVal)[aCompIter];
1156         if (!aGenVal.IsNumber())
1157         {
1158           reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation.");
1159           return false;
1160         }
1161         aTransVec.SetCoord (aCompIter + 1, aGenVal.GetDouble());
1162       }
1163       aTrsf.SetTranslationPart (aTransVec);
1164     }
1165
1166     if (aTrsfScaleVal != NULL)
1167     {
1168       Graphic3d_Vec3d aScaleVec;
1169       if (!aTrsfScaleVal->IsArray()
1170         || aTrsfScaleVal->Size() != 3)
1171       {
1172         reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale vector.");
1173         return false;
1174       }
1175       for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
1176       {
1177         const RWGltf_JsonValue& aGenVal = (*aTrsfScaleVal)[aCompIter];
1178         if (!aGenVal.IsNumber())
1179         {
1180           reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
1181           return false;
1182         }
1183         aScaleVec[aCompIter] = aGenVal.GetDouble();
1184         if (Abs (aScaleVec[aCompIter]) <= gp::Resolution())
1185         {
1186           reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
1187           return false;
1188         }
1189       }
1190
1191       if (Abs (aScaleVec.x() - aScaleVec.y()) > Precision::Confusion()
1192        || Abs (aScaleVec.y() - aScaleVec.z()) > Precision::Confusion()
1193        || Abs (aScaleVec.x() - aScaleVec.z()) > Precision::Confusion())
1194       {
1195         Graphic3d_Mat4d aScaleMat;
1196         aScaleMat.SetDiagonal (aScaleVec);
1197
1198         Graphic3d_Mat4d aMat4;
1199         aTrsf.GetMat4 (aMat4);
1200
1201         aMat4 = aMat4 * aScaleMat;
1202         aTrsf = gp_Trsf();
1203         aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
1204                          aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
1205                          aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
1206
1207         Message::SendWarning (TCollection_AsciiString ("glTF reader, scene node '")
1208                             + theSceneNodeId + "' defines unsupported scaling " + aScaleVec.x() + " " + aScaleVec.y() + " " + aScaleVec.z());
1209       }
1210       else if (Abs (aScaleVec.x() - 1.0) > Precision::Confusion())
1211       {
1212         aTrsf.SetScaleFactor (aScaleVec.x());
1213       }
1214     }
1215
1216     myCSTrsf.TransformTransformation (aTrsf);
1217     if (aTrsf.Form() != gp_Identity)
1218     {
1219       aNodeLoc = TopLoc_Location (aTrsf);
1220     }
1221   }
1222
1223   BRep_Builder aBuilder;
1224   TopoDS_Compound aNodeShape;
1225   aBuilder.MakeCompound (aNodeShape);
1226   TopTools_SequenceOfShape aChildShapes;
1227   int aNbSubShapes = 0;
1228   if (aChildren != NULL
1229   && !gltfParseSceneNodes (aChildShapes, *aChildren, theProgress))
1230   {
1231     theNodeShape = aNodeShape;
1232     bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1233     return false;
1234   }
1235   for (TopTools_SequenceOfShape::Iterator aChildShapeIter (aChildShapes); aChildShapeIter.More(); aChildShapeIter.Next())
1236   {
1237     aBuilder.Add (aNodeShape, aChildShapeIter.Value());
1238     ++aNbSubShapes;
1239   }
1240
1241   if (aMeshes_1 != NULL
1242    && aMeshes_1->IsArray())
1243   {
1244     // glTF 1.0
1245     Message_ProgressSentry aPSentry (theProgress, "Reading scene meshes", 0, aMeshes_1->Size(), 1);
1246     for (rapidjson::Value::ConstValueIterator aMeshIter = aMeshes_1->Begin();
1247          aMeshIter != aMeshes_1->End() && aPSentry.More(); ++aMeshIter, aPSentry.Next())
1248     {
1249       const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMeshIter);
1250       if (aMesh == NULL)
1251       {
1252         theNodeShape = aNodeShape;
1253         bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1254         reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1255         return false;
1256       }
1257
1258       TopoDS_Shape aMeshShape;
1259       if (!gltfParseMesh (aMeshShape, getKeyString (*aMeshIter), *aMesh, theProgress))
1260       {
1261         theNodeShape = aNodeShape;
1262         bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1263         return false;
1264       }
1265       if (!aMeshShape.IsNull())
1266       {
1267         aBuilder.Add (aNodeShape, aMeshShape);
1268         ++aNbSubShapes;
1269       }
1270     }
1271   }
1272   if (aMesh_2 != NULL)
1273   {
1274     // glTF 2.0
1275     const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMesh_2);
1276     if (aMesh == NULL)
1277     {
1278       theNodeShape = aNodeShape;
1279       bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1280       reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1281       return false;
1282     }
1283
1284     TopoDS_Shape aMeshShape;
1285     if (!gltfParseMesh (aMeshShape, getKeyString (*aMesh_2), *aMesh, theProgress))
1286     {
1287       theNodeShape = aNodeShape;
1288       bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1289       return false;
1290     }
1291     if (!aMeshShape.IsNull())
1292     {
1293       aBuilder.Add (aNodeShape, aMeshShape);
1294       ++aNbSubShapes;
1295     }
1296   }
1297
1298   if (aNbSubShapes == 1)
1299   {
1300     theNodeShape = TopoDS_Iterator (aNodeShape).Value();
1301   }
1302   else
1303   {
1304     theNodeShape = aNodeShape;
1305   }
1306   bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1307   return true;
1308 }
1309
1310 // =======================================================================
1311 // function : gltfParseMesh
1312 // purpose  :
1313 // =======================================================================
1314 bool RWGltf_GltfJsonParser::gltfParseMesh (TopoDS_Shape& theMeshShape,
1315                                            const TCollection_AsciiString& theMeshId,
1316                                            const RWGltf_JsonValue& theMesh,
1317                                            const Handle(Message_ProgressIndicator)& theProgress)
1318 {
1319   const RWGltf_JsonValue* aName  = findObjectMember (theMesh, "name");
1320   const RWGltf_JsonValue* aPrims = findObjectMember (theMesh, "primitives");
1321   if (!aPrims->IsArray())
1322   {
1323     reportGltfError ("Primitive array attributes within Mesh '" + theMeshId + "' is not an array.");
1324     return false;
1325   }
1326
1327   if (findMeshShape (theMeshShape, theMeshId))
1328   {
1329     return true;
1330   }
1331
1332   BRep_Builder aBuilder;
1333   TopoDS_Compound aMeshShape;
1334   int aNbFaces = 0;
1335   for (rapidjson::Value::ConstValueIterator aPrimArrIter = aPrims->Begin();
1336        aPrimArrIter != aPrims->End(); ++aPrimArrIter)
1337   {
1338     TCollection_AsciiString aUserName;
1339     if (aName != NULL
1340      && aName->IsString())
1341     {
1342       aUserName = aName->GetString();
1343     }
1344
1345     Handle(RWGltf_GltfLatePrimitiveArray) aMeshData = new RWGltf_GltfLatePrimitiveArray (theMeshId, aUserName);
1346     if (!gltfParsePrimArray (aMeshData, theMeshId, *aPrimArrIter, theProgress))
1347     {
1348       return false;
1349     }
1350
1351     if (!aMeshData->Data().IsEmpty())
1352     {
1353       if (aMeshShape.IsNull())
1354       {
1355         aBuilder.MakeCompound (aMeshShape);
1356       }
1357
1358       TopoDS_Face aFace;
1359       aBuilder.MakeFace (aFace, aMeshData);
1360       aBuilder.Add (aMeshShape, aFace);
1361       if (myAttribMap != NULL
1362        && aMeshData->HasStyle())
1363       {
1364         RWMesh_NodeAttributes aShapeAttribs;
1365         aShapeAttribs.RawName = aUserName;
1366
1367         // assign material and not color
1368         //aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor());
1369
1370         Handle(XCAFDoc_VisMaterial) aMat;
1371         myMaterials.Find (!aMeshData->MaterialPbr().IsNull() ? aMeshData->MaterialPbr()->Id : aMeshData->MaterialCommon()->Id, aMat);
1372         aShapeAttribs.Style.SetMaterial (aMat);
1373
1374         myAttribMap->Bind (aFace, aShapeAttribs);
1375       }
1376       myFaceList.Append (aFace);
1377       ++aNbFaces;
1378     }
1379   }
1380
1381   if (aNbFaces == 1)
1382   {
1383     theMeshShape = TopoDS_Iterator (aMeshShape).Value();
1384   }
1385   else
1386   {
1387     theMeshShape = aMeshShape;
1388   }
1389   bindMeshShape (theMeshShape, theMeshId, aName);
1390   return true;
1391 }
1392
1393 // =======================================================================
1394 // function : gltfParsePrimArray
1395 // purpose  :
1396 // =======================================================================
1397 bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1398                                                 const TCollection_AsciiString& theMeshId,
1399                                                 const RWGltf_JsonValue& thePrimArray,
1400                                                 const Handle(Message_ProgressIndicator)& /*theProgress*/)
1401 {
1402   const RWGltf_JsonValue* anAttribs = findObjectMember (thePrimArray, "attributes");
1403   const RWGltf_JsonValue* anIndices = findObjectMember (thePrimArray, "indices");
1404   const RWGltf_JsonValue* aMaterial = findObjectMember (thePrimArray, "material");
1405   const RWGltf_JsonValue* aModeVal  = findObjectMember (thePrimArray, "mode");
1406   RWGltf_GltfPrimitiveMode aMode = RWGltf_GltfPrimitiveMode_Triangles;
1407   if (anAttribs == NULL
1408   || !anAttribs->IsObject())
1409   {
1410     reportGltfError ("Primitive array within Mesh '" + theMeshId + "' defines no attributes.");
1411     return false;
1412   }
1413   else if (aModeVal != NULL)
1414   {
1415     aMode = RWGltf_GltfPrimitiveMode_UNKNOWN;
1416     if (aModeVal->IsInt())
1417     {
1418       aMode = (RWGltf_GltfPrimitiveMode )aModeVal->GetInt();
1419     }
1420     if (aMode < RWGltf_GltfPrimitiveMode_Points
1421      || aMode > RWGltf_GltfPrimitiveMode_TriangleFan)
1422     {
1423       reportGltfError ("Primitive array within Mesh '" + theMeshId + "' has unknown mode.");
1424       return false;
1425     }
1426   }
1427   if (aMode != RWGltf_GltfPrimitiveMode_Triangles)
1428   {
1429     Message::SendWarning (TCollection_AsciiString() + "Primitive array within Mesh '" + theMeshId + "' skipped due to unsupported mode");
1430     return true;
1431   }
1432   theMeshData->SetPrimitiveMode (aMode);
1433
1434   // assign material
1435   if (aMaterial != NULL)
1436   {
1437     Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
1438     if (myMaterialsPbr.Find (getKeyString (*aMaterial), aMatPbr))
1439     {
1440       theMeshData->SetMaterialPbr (aMatPbr);
1441     }
1442
1443     Handle(RWGltf_MaterialCommon) aMatCommon;
1444     if (myMaterialsCommon.Find (getKeyString (*aMaterial), aMatCommon))
1445     {
1446       theMeshData->SetMaterialCommon (aMatCommon);
1447     }
1448   }
1449
1450   bool hasPositions = false;
1451   for (rapidjson::Value::ConstMemberIterator anAttribIter = anAttribs->MemberBegin();
1452        anAttribIter != anAttribs->MemberEnd(); ++anAttribIter)
1453   {
1454     const TCollection_AsciiString anAttribId = getKeyString (anAttribIter->value);
1455     if (anAttribId.IsEmpty())
1456     {
1457       reportGltfError ("Primitive array attribute accessor key within Mesh '" + theMeshId + "' is not a string.");
1458       return false;
1459     }
1460
1461     RWGltf_GltfArrayType aType = RWGltf_GltfParseAttribType (anAttribIter->name.GetString());
1462     if (aType == RWGltf_GltfArrayType_UNKNOWN)
1463     {
1464       // just ignore unknown attributes
1465       continue;
1466     }
1467
1468     const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (anAttribIter->value);
1469     if (anAccessor == NULL
1470     || !anAccessor->IsObject())
1471     {
1472       reportGltfError ("Primitive array attribute accessor key '" + anAttribId + "' points to non-existing object.");
1473       return false;
1474     }
1475     else if (!gltfParseAccessor (theMeshData, anAttribId, *anAccessor, aType))
1476     {
1477       return false;
1478     }
1479     else if (aType == RWGltf_GltfArrayType_Position)
1480     {
1481       hasPositions = true;
1482     }
1483   }
1484   if (!hasPositions)
1485   {
1486     reportGltfError ("Primitive array within Mesh '" + theMeshId + "' does not define vertex positions.");
1487     return false;
1488   }
1489
1490   if (anIndices != NULL)
1491   {
1492     const TCollection_AsciiString anIndicesId = getKeyString (*anIndices);
1493     const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (*anIndices);
1494     if (anAccessor == NULL
1495     || !anAccessor->IsObject())
1496     {
1497       reportGltfError ("Primitive array indices accessor key '" + anIndicesId + "' points to non-existing object.");
1498       return false;
1499     }
1500     else if (!gltfParseAccessor (theMeshData, anIndicesId, *anAccessor, RWGltf_GltfArrayType_Indices))
1501     {
1502       return false;
1503     }
1504   }
1505
1506   return true;
1507 }
1508
1509 // =======================================================================
1510 // function : gltfParseAccessor
1511 // purpose  :
1512 // =======================================================================
1513 bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1514                                                const TCollection_AsciiString& theName,
1515                                                const RWGltf_JsonValue& theAccessor,
1516                                                const RWGltf_GltfArrayType theType)
1517 {
1518   RWGltf_GltfAccessor aStruct;
1519   const RWGltf_JsonValue* aTypeStr        = findObjectMember (theAccessor, "type");
1520   const RWGltf_JsonValue* aBufferViewName = findObjectMember (theAccessor, "bufferView");
1521   const RWGltf_JsonValue* aByteOffset     = findObjectMember (theAccessor, "byteOffset");
1522   const RWGltf_JsonValue* aByteStride     = findObjectMember (theAccessor, "byteStride"); // byteStride was part of bufferView in glTF 1.0
1523   const RWGltf_JsonValue* aCompType       = findObjectMember (theAccessor, "componentType");
1524   const RWGltf_JsonValue* aCount          = findObjectMember (theAccessor, "count");
1525   if (aTypeStr == NULL
1526   || !aTypeStr->IsString())
1527   {
1528     reportGltfError ("Accessor '" + theName + "' does not define type.");
1529     return false;
1530   }
1531   aStruct.Type = RWGltf_GltfParseAccessorType (aTypeStr->GetString());
1532   if (aStruct.Type == RWGltf_GltfAccessorLayout_UNKNOWN)
1533   {
1534     reportGltfError ("Accessor '" + theName + "' has invalid type.");
1535     return false;
1536   }
1537
1538   if (aBufferViewName == NULL)
1539   {
1540     reportGltfError ("Accessor '" + theName + "' does not define bufferView.");
1541     return false;
1542   }
1543   if (aCompType == NULL
1544   || !aCompType->IsInt())
1545   {
1546     reportGltfError ("Accessor '" + theName + "' does not define componentType.");
1547     return false;
1548   }
1549   aStruct.ComponentType = (RWGltf_GltfAccessorCompType )aCompType->GetInt();
1550   if (aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int8
1551    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt8
1552    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int16
1553    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt16
1554    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt32
1555    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Float32)
1556   {
1557     reportGltfError ("Accessor '" + theName + "' defines invalid componentType value.");
1558     return false;
1559   }
1560
1561   if (aCount == NULL
1562   || !aCount->IsNumber())
1563   {
1564     reportGltfError ("Accessor '" + theName + "' does not define count.");
1565     return false;
1566   }
1567
1568   aStruct.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1569                      ? (int64_t )aByteOffset->GetDouble()
1570                      : 0;
1571   aStruct.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1572                      ? aByteStride->GetInt()
1573                      : 0;
1574   aStruct.Count = (int64_t )aCount->GetDouble();
1575
1576   if (aStruct.ByteOffset < 0)
1577   {
1578     reportGltfError ("Accessor '" + theName + "' defines invalid byteOffset.");
1579     return false;
1580   }
1581   else if (aStruct.ByteStride < 0
1582         || aStruct.ByteStride > 255)
1583   {
1584     reportGltfError ("Accessor '" + theName + "' defines invalid byteStride.");
1585     return false;
1586   }
1587   else if (aStruct.Count < 1)
1588   {
1589     reportGltfError ("Accessor '" + theName + "' defines invalid count.");
1590     return false;
1591   }
1592
1593   // Read Min/Max values for POSITION type. It is used for bounding boxes
1594   if (theType == RWGltf_GltfArrayType_Position)
1595   {
1596     const RWGltf_JsonValue* aMin = findObjectMember (theAccessor, "min");
1597     const RWGltf_JsonValue* aMax = findObjectMember (theAccessor, "max");
1598     if (aMin != NULL && aMax != NULL)
1599     {
1600       // Note: Min/Max values can be not defined in glTF file.
1601       // In this case it is not used only.
1602       if (!aMin->IsArray()   || !aMax->IsArray() ||
1603            aMin->Size() != 3 ||  aMax->Size() != 3)
1604       {
1605         reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max values.");
1606       }
1607       else
1608       {
1609         bool isValidMinMax = true;
1610         gp_Pnt aMinPnt, aMaxPnt;
1611         for (int anIter = 0; anIter < 3; ++anIter)
1612         {
1613           const RWGltf_JsonValue& aMinVal = (*aMin)[anIter];
1614           const RWGltf_JsonValue& aMaxVal = (*aMax)[anIter];
1615           if (!aMinVal.IsNumber() || !aMaxVal.IsNumber())
1616           {
1617             reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max value.");
1618             isValidMinMax = false;
1619             break;
1620           }
1621           aMinPnt.SetCoord (anIter + 1, aMinVal.GetDouble());
1622           aMinPnt.SetCoord (anIter + 1, aMaxVal.GetDouble());
1623         }
1624         if (isValidMinMax)
1625         {
1626           myCSTrsf.TransformPosition (aMinPnt.ChangeCoord());
1627           myCSTrsf.TransformPosition (aMaxPnt.ChangeCoord());
1628
1629           Bnd_Box aBox;
1630           aBox.Add (aMinPnt);
1631           aBox.Add (aMaxPnt);
1632
1633           theMeshData->SetBoundingBox (aBox);
1634         }
1635       }
1636     }
1637   }
1638
1639   const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
1640   if (aBufferView == NULL
1641   || !aBufferView->IsObject())
1642   {
1643     reportGltfError ("Accessor '" + theName + "' refers to non-existing bufferView.");
1644     return false;
1645   }
1646
1647   return gltfParseBufferView (theMeshData, getKeyString (*aBufferViewName), *aBufferView, aStruct, theType);
1648 }
1649
1650 // =======================================================================
1651 // function : gltfParseBufferView
1652 // purpose  :
1653 // =======================================================================
1654 bool RWGltf_GltfJsonParser::gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1655                                                  const TCollection_AsciiString& theName,
1656                                                  const RWGltf_JsonValue& theBufferView,
1657                                                  const RWGltf_GltfAccessor& theAccessor,
1658                                                  const RWGltf_GltfArrayType theType)
1659 {
1660   RWGltf_GltfBufferView aBuffView;
1661   const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
1662   const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
1663   const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
1664   const RWGltf_JsonValue* aByteStride = findObjectMember (theBufferView, "byteStride"); // byteStride is part of bufferView since glTF 2.0
1665   const RWGltf_JsonValue* aTarget     = findObjectMember (theBufferView, "target");
1666   if (aBufferName == NULL)
1667   {
1668     reportGltfError ("BufferView '" + theName + "' does not define buffer.");
1669     return false;
1670   }
1671
1672   aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1673                        ? (int64_t )aByteOffset->GetDouble()
1674                        : 0;
1675   aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
1676                        ? (int64_t )aByteLength->GetDouble()
1677                        : 0;
1678   aBuffView.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1679                        ? aByteStride->GetInt()
1680                        : 0;
1681   if (aTarget != NULL && aTarget->IsInt())
1682   {
1683     aBuffView.Target = (RWGltf_GltfBufferViewTarget )aTarget->GetInt();
1684     if (aBuffView.Target != RWGltf_GltfBufferViewTarget_ARRAY_BUFFER
1685      && aBuffView.Target != RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER)
1686     {
1687       reportGltfError ("BufferView '" + theName + "' defines invalid target.");
1688       return false;
1689     }
1690   }
1691
1692   if (aBuffView.ByteLength <= 0)
1693   {
1694     reportGltfError ("BufferView '" + theName + "' defines invalid byteLength.");
1695     return false;
1696   }
1697   else if (aBuffView.ByteOffset < 0)
1698   {
1699     reportGltfError ("BufferView '" + theName + "' defines invalid byteOffset.");
1700     return false;
1701   }
1702   else if (aBuffView.ByteStride < 0
1703         || aBuffView.ByteStride > 255)
1704   {
1705     reportGltfError ("BufferView '" + theName + "' defines invalid byteStride.");
1706     return false;
1707   }
1708
1709   const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
1710   if (aBuffer == NULL
1711   || !aBuffer->IsObject())
1712   {
1713     reportGltfError ("BufferView '" + theName + "' refers to non-existing buffer.");
1714     return false;
1715   }
1716
1717   return gltfParseBuffer (theMeshData, getKeyString (*aBufferName), *aBuffer, theAccessor, aBuffView, theType);
1718 }
1719
1720 // =======================================================================
1721 // function : gltfParseBuffer
1722 // purpose  :
1723 // =======================================================================
1724 bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1725                                              const TCollection_AsciiString& theName,
1726                                              const RWGltf_JsonValue& theBuffer,
1727                                              const RWGltf_GltfAccessor&     theAccessor,
1728                                              const RWGltf_GltfBufferView&   theView,
1729                                              const RWGltf_GltfArrayType     theType)
1730 {
1731   //const RWGltf_JsonValue* aType       = findObjectMember (theBuffer, "type");
1732   //const RWGltf_JsonValue* aByteLength = findObjectMember (theBuffer, "byteLength");
1733   const RWGltf_JsonValue* anUriVal      = findObjectMember (theBuffer, "uri");
1734
1735   int64_t anOffset = theView.ByteOffset + theAccessor.ByteOffset;
1736   const int32_t aByteStride = theAccessor.ByteStride != 0 ? theView.ByteStride : theView.ByteStride;
1737   bool isBinary = false;
1738   if (myIsBinary)
1739   {
1740     isBinary = IsEqual ("binary_glTF", theName) // glTF 1.0
1741             || anUriVal == NULL;                // glTF 2.0
1742   }
1743   if (isBinary)
1744   {
1745     anOffset += myBinBodyOffset;
1746
1747     RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1748     aData.Accessor = theAccessor;
1749     aData.Accessor.ByteStride = aByteStride;
1750     aData.StreamOffset = anOffset;
1751     aData.StreamUri = myFilePath;
1752     return true;
1753   }
1754
1755   if (anUriVal == NULL
1756   || !anUriVal->IsString())
1757   {
1758     reportGltfError ("Buffer '" + theName + "' does not define uri.");
1759     return false;
1760   }
1761
1762   const char* anUriData = anUriVal->GetString();
1763   if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
1764   {
1765     RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1766     aData.Accessor = theAccessor;
1767     aData.Accessor.ByteStride = aByteStride;
1768     aData.StreamOffset = anOffset;
1769     if (!myDecodedBuffers.Find (theName, aData.StreamData))
1770     {
1771       // it is better decoding in multiple threads
1772       aData.StreamData = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
1773       myDecodedBuffers.Bind (theName, aData.StreamData);
1774     }
1775     return true;
1776   }
1777   else
1778   {
1779     TCollection_AsciiString anUri = anUriData;
1780     if (anUri.IsEmpty())
1781     {
1782       reportGltfError ("Buffer '" + theName + "' does not define uri.");
1783       return false;
1784     }
1785
1786     TCollection_AsciiString aPath = myFolder + anUri;
1787     bool isFileExist = false;
1788     if (!myProbedFiles.Find (aPath, isFileExist))
1789     {
1790       isFileExist = OSD_File (aPath).Exists();
1791       myProbedFiles.Bind (aPath, isFileExist);
1792     }
1793     if (!isFileExist)
1794     {
1795       reportGltfError ("Buffer '" + theName + "' refers to non-existing file '" + anUri + "'.");
1796       return false;
1797     }
1798
1799     RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1800     aData.Accessor = theAccessor;
1801     aData.Accessor.ByteStride = aByteStride;
1802     aData.StreamOffset = anOffset;
1803     aData.StreamUri = myFolder + anUri;
1804     if (myExternalFiles != NULL)
1805     {
1806       myExternalFiles->Add (aData.StreamUri);
1807     }
1808     return true;
1809   }
1810 }
1811
1812 // =======================================================================
1813 // function : bindNamedShape
1814 // purpose  :
1815 // =======================================================================
1816 void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape,
1817                                             ShapeMapGroup theGroup,
1818                                             const TopLoc_Location& theLoc,
1819                                             const TCollection_AsciiString& theId,
1820                                             const RWGltf_JsonValue* theUserName)
1821 {
1822   if (theShape.IsNull())
1823   {
1824     return;
1825   }
1826
1827   if (!theLoc.IsIdentity())
1828   {
1829     theShape.Location (theLoc);
1830   }
1831
1832   TCollection_AsciiString aUserName;
1833   if (theUserName != NULL
1834    && theUserName->IsString())
1835   {
1836     aUserName = theUserName->GetString();
1837   }
1838   else if (myIsGltf1)
1839   {
1840     aUserName = theId;
1841   }
1842
1843   myShapeMap[theGroup].Bind (theId, theShape);
1844   if (myAttribMap != NULL)
1845   {
1846     RWMesh_NodeAttributes aShapeAttribs;
1847     aShapeAttribs.Name    = aUserName;
1848     aShapeAttribs.RawName = theId;
1849     if (theShape.ShapeType() == TopAbs_FACE)
1850     {
1851       TopLoc_Location aDummy;
1852       if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (theShape), aDummy)))
1853       {
1854         if (aLateData->HasStyle())
1855         {
1856           // assign material and not color
1857           //aShapeAttribs.Style.SetColorSurf (aLateData->BaseColor());
1858
1859           Handle(XCAFDoc_VisMaterial) aMat;
1860           myMaterials.Find (!aLateData->MaterialPbr().IsNull() ? aLateData->MaterialPbr()->Id : aLateData->MaterialCommon()->Id, aMat);
1861           aShapeAttribs.Style.SetMaterial (aMat);
1862         }
1863         if (aShapeAttribs.Name.IsEmpty()
1864          && myUseMeshNameAsFallback)
1865         {
1866           // fallback using Mesh name
1867           aShapeAttribs.Name = aLateData->Name();
1868         }
1869       }
1870     }
1871     else if (aShapeAttribs.Name.IsEmpty()
1872           && myUseMeshNameAsFallback)
1873     {
1874       // fallback using Mesh name
1875       TopLoc_Location aDummy;
1876       TCollection_AsciiString aMeshName;
1877       for (TopExp_Explorer aFaceIter (theShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
1878       {
1879         if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (aFaceIter.Value()), aDummy)))
1880         {
1881           if (aLateData->Name().IsEmpty())
1882           {
1883             aMeshName.Clear();
1884             break;
1885           }
1886           else if (aMeshName.IsEmpty())
1887           {
1888             aMeshName = aLateData->Name();
1889           }
1890           else if (!aMeshName.IsEqual (aLateData->Name()))
1891           {
1892             aMeshName.Clear();
1893             break;
1894           }
1895         }
1896       }
1897       if (!aMeshName.IsEmpty())
1898       {
1899         aShapeAttribs.Name = aMeshName;
1900       }
1901     }
1902     myAttribMap->Bind (theShape, aShapeAttribs);
1903   }
1904 }
1905 #endif
1906
1907 // =======================================================================
1908 // function : Parse
1909 // purpose  :
1910 // =======================================================================
1911 bool RWGltf_GltfJsonParser::Parse (const Handle(Message_ProgressIndicator)& theProgress)
1912 {
1913   Message_ProgressSentry aPSentry (theProgress, "Reading Gltf", 0, 2, 1);
1914 #ifdef HAVE_RAPIDJSON
1915   {
1916     if (!gltfParseRoots())
1917     {
1918       return false;
1919     }
1920
1921     gltfParseAsset();
1922     gltfParseMaterials();
1923     if (!gltfParseScene (theProgress))
1924     {
1925       return false;
1926     }
1927   }
1928   aPSentry.Next();
1929   if (!aPSentry.More())
1930   {
1931     return false;
1932   }
1933   return true;
1934 #else
1935   Message::SendFail ("Error: glTF reader is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined]");
1936   return false;
1937 #endif
1938 }