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