0031035: Coding - uninitialized class fields reported by Visual Studio Code Analysis
[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   myMetadata (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::SendWarning ("glTF reader - material with non-zero Transparency specifies Opaque AlphaMode");
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::SendWarning ("glTF reader - embedded image has been skipped");
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::SendWarning (TCollection_AsciiString ("glTF reader, scene node '")
1209                             + theSceneNodeId + "' defines unsupported scaling " + aScaleVec.x() + " " + aScaleVec.y() + " " + aScaleVec.z());
1210       }
1211       else if (Abs (aScaleVec.x() - 1.0) > Precision::Confusion())
1212       {
1213         aTrsf.SetScaleFactor (aScaleVec.x());
1214       }
1215     }
1216
1217     myCSTrsf.TransformTransformation (aTrsf);
1218     if (aTrsf.Form() != gp_Identity)
1219     {
1220       aNodeLoc = TopLoc_Location (aTrsf);
1221     }
1222   }
1223
1224   BRep_Builder aBuilder;
1225   TopoDS_Compound aNodeShape;
1226   aBuilder.MakeCompound (aNodeShape);
1227   TopTools_SequenceOfShape aChildShapes;
1228   int aNbSubShapes = 0;
1229   if (aChildren != NULL
1230   && !gltfParseSceneNodes (aChildShapes, *aChildren, theProgress))
1231   {
1232     theNodeShape = aNodeShape;
1233     bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1234     return false;
1235   }
1236   for (TopTools_SequenceOfShape::Iterator aChildShapeIter (aChildShapes); aChildShapeIter.More(); aChildShapeIter.Next())
1237   {
1238     aBuilder.Add (aNodeShape, aChildShapeIter.Value());
1239     ++aNbSubShapes;
1240   }
1241
1242   if (aMeshes_1 != NULL
1243    && aMeshes_1->IsArray())
1244   {
1245     // glTF 1.0
1246     Message_ProgressSentry aPSentry (theProgress, "Reading scene meshes", 0, aMeshes_1->Size(), 1);
1247     for (rapidjson::Value::ConstValueIterator aMeshIter = aMeshes_1->Begin();
1248          aMeshIter != aMeshes_1->End() && aPSentry.More(); ++aMeshIter, aPSentry.Next())
1249     {
1250       const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMeshIter);
1251       if (aMesh == NULL)
1252       {
1253         theNodeShape = aNodeShape;
1254         bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1255         reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1256         return false;
1257       }
1258
1259       TopoDS_Shape aMeshShape;
1260       if (!gltfParseMesh (aMeshShape, getKeyString (*aMeshIter), *aMesh, theProgress))
1261       {
1262         theNodeShape = aNodeShape;
1263         bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1264         return false;
1265       }
1266       if (!aMeshShape.IsNull())
1267       {
1268         aBuilder.Add (aNodeShape, aMeshShape);
1269         ++aNbSubShapes;
1270       }
1271     }
1272   }
1273   if (aMesh_2 != NULL)
1274   {
1275     // glTF 2.0
1276     const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMesh_2);
1277     if (aMesh == NULL)
1278     {
1279       theNodeShape = aNodeShape;
1280       bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1281       reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1282       return false;
1283     }
1284
1285     TopoDS_Shape aMeshShape;
1286     if (!gltfParseMesh (aMeshShape, getKeyString (*aMesh_2), *aMesh, theProgress))
1287     {
1288       theNodeShape = aNodeShape;
1289       bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1290       return false;
1291     }
1292     if (!aMeshShape.IsNull())
1293     {
1294       aBuilder.Add (aNodeShape, aMeshShape);
1295       ++aNbSubShapes;
1296     }
1297   }
1298
1299   if (aNbSubShapes == 1)
1300   {
1301     theNodeShape = TopoDS_Iterator (aNodeShape).Value();
1302   }
1303   else
1304   {
1305     theNodeShape = aNodeShape;
1306   }
1307   bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1308   return true;
1309 }
1310
1311 // =======================================================================
1312 // function : gltfParseMesh
1313 // purpose  :
1314 // =======================================================================
1315 bool RWGltf_GltfJsonParser::gltfParseMesh (TopoDS_Shape& theMeshShape,
1316                                            const TCollection_AsciiString& theMeshId,
1317                                            const RWGltf_JsonValue& theMesh,
1318                                            const Handle(Message_ProgressIndicator)& theProgress)
1319 {
1320   const RWGltf_JsonValue* aName  = findObjectMember (theMesh, "name");
1321   const RWGltf_JsonValue* aPrims = findObjectMember (theMesh, "primitives");
1322   if (!aPrims->IsArray())
1323   {
1324     reportGltfError ("Primitive array attributes within Mesh '" + theMeshId + "' is not an array.");
1325     return false;
1326   }
1327
1328   if (findMeshShape (theMeshShape, theMeshId))
1329   {
1330     return true;
1331   }
1332
1333   BRep_Builder aBuilder;
1334   TopoDS_Compound aMeshShape;
1335   int aNbFaces = 0;
1336   for (rapidjson::Value::ConstValueIterator aPrimArrIter = aPrims->Begin();
1337        aPrimArrIter != aPrims->End(); ++aPrimArrIter)
1338   {
1339     TCollection_AsciiString aUserName;
1340     if (aName != NULL
1341      && aName->IsString())
1342     {
1343       aUserName = aName->GetString();
1344     }
1345
1346     Handle(RWGltf_GltfLatePrimitiveArray) aMeshData = new RWGltf_GltfLatePrimitiveArray (theMeshId, aUserName);
1347     if (!gltfParsePrimArray (aMeshData, theMeshId, *aPrimArrIter, theProgress))
1348     {
1349       return false;
1350     }
1351
1352     if (!aMeshData->Data().IsEmpty())
1353     {
1354       if (aMeshShape.IsNull())
1355       {
1356         aBuilder.MakeCompound (aMeshShape);
1357       }
1358
1359       TopoDS_Face aFace;
1360       aBuilder.MakeFace (aFace, aMeshData);
1361       aBuilder.Add (aMeshShape, aFace);
1362       if (myAttribMap != NULL
1363        && aMeshData->HasStyle())
1364       {
1365         RWMesh_NodeAttributes aShapeAttribs;
1366         aShapeAttribs.RawName = aUserName;
1367
1368         // assign material and not color
1369         //aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor());
1370
1371         Handle(XCAFDoc_VisMaterial) aMat;
1372         myMaterials.Find (!aMeshData->MaterialPbr().IsNull() ? aMeshData->MaterialPbr()->Id : aMeshData->MaterialCommon()->Id, aMat);
1373         aShapeAttribs.Style.SetMaterial (aMat);
1374
1375         myAttribMap->Bind (aFace, aShapeAttribs);
1376       }
1377       myFaceList.Append (aFace);
1378       ++aNbFaces;
1379     }
1380   }
1381
1382   if (aNbFaces == 1)
1383   {
1384     theMeshShape = TopoDS_Iterator (aMeshShape).Value();
1385   }
1386   else
1387   {
1388     theMeshShape = aMeshShape;
1389   }
1390   bindMeshShape (theMeshShape, theMeshId, aName);
1391   return true;
1392 }
1393
1394 // =======================================================================
1395 // function : gltfParsePrimArray
1396 // purpose  :
1397 // =======================================================================
1398 bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1399                                                 const TCollection_AsciiString& theMeshId,
1400                                                 const RWGltf_JsonValue& thePrimArray,
1401                                                 const Handle(Message_ProgressIndicator)& /*theProgress*/)
1402 {
1403   const RWGltf_JsonValue* anAttribs = findObjectMember (thePrimArray, "attributes");
1404   const RWGltf_JsonValue* anIndices = findObjectMember (thePrimArray, "indices");
1405   const RWGltf_JsonValue* aMaterial = findObjectMember (thePrimArray, "material");
1406   const RWGltf_JsonValue* aModeVal  = findObjectMember (thePrimArray, "mode");
1407   RWGltf_GltfPrimitiveMode aMode = RWGltf_GltfPrimitiveMode_Triangles;
1408   if (anAttribs == NULL
1409   || !anAttribs->IsObject())
1410   {
1411     reportGltfError ("Primitive array within Mesh '" + theMeshId + "' defines no attributes.");
1412     return false;
1413   }
1414   else if (aModeVal != NULL)
1415   {
1416     aMode = RWGltf_GltfPrimitiveMode_UNKNOWN;
1417     if (aModeVal->IsInt())
1418     {
1419       aMode = (RWGltf_GltfPrimitiveMode )aModeVal->GetInt();
1420     }
1421     if (aMode < RWGltf_GltfPrimitiveMode_Points
1422      || aMode > RWGltf_GltfPrimitiveMode_TriangleFan)
1423     {
1424       reportGltfError ("Primitive array within Mesh '" + theMeshId + "' has unknown mode.");
1425       return false;
1426     }
1427   }
1428   if (aMode != RWGltf_GltfPrimitiveMode_Triangles)
1429   {
1430     Message::SendWarning (TCollection_AsciiString() + "Primitive array within Mesh '" + theMeshId + "' skipped due to unsupported mode");
1431     return true;
1432   }
1433   theMeshData->SetPrimitiveMode (aMode);
1434
1435   // assign material
1436   if (aMaterial != NULL)
1437   {
1438     Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
1439     if (myMaterialsPbr.Find (getKeyString (*aMaterial), aMatPbr))
1440     {
1441       theMeshData->SetMaterialPbr (aMatPbr);
1442     }
1443
1444     Handle(RWGltf_MaterialCommon) aMatCommon;
1445     if (myMaterialsCommon.Find (getKeyString (*aMaterial), aMatCommon))
1446     {
1447       theMeshData->SetMaterialCommon (aMatCommon);
1448     }
1449   }
1450
1451   bool hasPositions = false;
1452   for (rapidjson::Value::ConstMemberIterator anAttribIter = anAttribs->MemberBegin();
1453        anAttribIter != anAttribs->MemberEnd(); ++anAttribIter)
1454   {
1455     const TCollection_AsciiString anAttribId = getKeyString (anAttribIter->value);
1456     if (anAttribId.IsEmpty())
1457     {
1458       reportGltfError ("Primitive array attribute accessor key within Mesh '" + theMeshId + "' is not a string.");
1459       return false;
1460     }
1461
1462     RWGltf_GltfArrayType aType = RWGltf_GltfParseAttribType (anAttribIter->name.GetString());
1463     if (aType == RWGltf_GltfArrayType_UNKNOWN)
1464     {
1465       // just ignore unknown attributes
1466       continue;
1467     }
1468
1469     const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (anAttribIter->value);
1470     if (anAccessor == NULL
1471     || !anAccessor->IsObject())
1472     {
1473       reportGltfError ("Primitive array attribute accessor key '" + anAttribId + "' points to non-existing object.");
1474       return false;
1475     }
1476     else if (!gltfParseAccessor (theMeshData, anAttribId, *anAccessor, aType))
1477     {
1478       return false;
1479     }
1480     else if (aType == RWGltf_GltfArrayType_Position)
1481     {
1482       hasPositions = true;
1483     }
1484   }
1485   if (!hasPositions)
1486   {
1487     reportGltfError ("Primitive array within Mesh '" + theMeshId + "' does not define vertex positions.");
1488     return false;
1489   }
1490
1491   if (anIndices != NULL)
1492   {
1493     const TCollection_AsciiString anIndicesId = getKeyString (*anIndices);
1494     const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (*anIndices);
1495     if (anAccessor == NULL
1496     || !anAccessor->IsObject())
1497     {
1498       reportGltfError ("Primitive array indices accessor key '" + anIndicesId + "' points to non-existing object.");
1499       return false;
1500     }
1501     else if (!gltfParseAccessor (theMeshData, anIndicesId, *anAccessor, RWGltf_GltfArrayType_Indices))
1502     {
1503       return false;
1504     }
1505   }
1506
1507   return true;
1508 }
1509
1510 // =======================================================================
1511 // function : gltfParseAccessor
1512 // purpose  :
1513 // =======================================================================
1514 bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1515                                                const TCollection_AsciiString& theName,
1516                                                const RWGltf_JsonValue& theAccessor,
1517                                                const RWGltf_GltfArrayType theType)
1518 {
1519   RWGltf_GltfAccessor aStruct;
1520   const RWGltf_JsonValue* aTypeStr        = findObjectMember (theAccessor, "type");
1521   const RWGltf_JsonValue* aBufferViewName = findObjectMember (theAccessor, "bufferView");
1522   const RWGltf_JsonValue* aByteOffset     = findObjectMember (theAccessor, "byteOffset");
1523   const RWGltf_JsonValue* aByteStride     = findObjectMember (theAccessor, "byteStride"); // byteStride was part of bufferView in glTF 1.0
1524   const RWGltf_JsonValue* aCompType       = findObjectMember (theAccessor, "componentType");
1525   const RWGltf_JsonValue* aCount          = findObjectMember (theAccessor, "count");
1526   if (aTypeStr == NULL
1527   || !aTypeStr->IsString())
1528   {
1529     reportGltfError ("Accessor '" + theName + "' does not define type.");
1530     return false;
1531   }
1532   aStruct.Type = RWGltf_GltfParseAccessorType (aTypeStr->GetString());
1533   if (aStruct.Type == RWGltf_GltfAccessorLayout_UNKNOWN)
1534   {
1535     reportGltfError ("Accessor '" + theName + "' has invalid type.");
1536     return false;
1537   }
1538
1539   if (aBufferViewName == NULL)
1540   {
1541     reportGltfError ("Accessor '" + theName + "' does not define bufferView.");
1542     return false;
1543   }
1544   if (aCompType == NULL
1545   || !aCompType->IsInt())
1546   {
1547     reportGltfError ("Accessor '" + theName + "' does not define componentType.");
1548     return false;
1549   }
1550   aStruct.ComponentType = (RWGltf_GltfAccessorCompType )aCompType->GetInt();
1551   if (aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int8
1552    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt8
1553    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int16
1554    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt16
1555    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt32
1556    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Float32)
1557   {
1558     reportGltfError ("Accessor '" + theName + "' defines invalid componentType value.");
1559     return false;
1560   }
1561
1562   if (aCount == NULL
1563   || !aCount->IsNumber())
1564   {
1565     reportGltfError ("Accessor '" + theName + "' does not define count.");
1566     return false;
1567   }
1568
1569   aStruct.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1570                      ? (int64_t )aByteOffset->GetDouble()
1571                      : 0;
1572   aStruct.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1573                      ? aByteStride->GetInt()
1574                      : 0;
1575   aStruct.Count = (int64_t )aCount->GetDouble();
1576
1577   if (aStruct.ByteOffset < 0)
1578   {
1579     reportGltfError ("Accessor '" + theName + "' defines invalid byteOffset.");
1580     return false;
1581   }
1582   else if (aStruct.ByteStride < 0
1583         || aStruct.ByteStride > 255)
1584   {
1585     reportGltfError ("Accessor '" + theName + "' defines invalid byteStride.");
1586     return false;
1587   }
1588   else if (aStruct.Count < 1)
1589   {
1590     reportGltfError ("Accessor '" + theName + "' defines invalid count.");
1591     return false;
1592   }
1593
1594   // Read Min/Max values for POSITION type. It is used for bounding boxes
1595   if (theType == RWGltf_GltfArrayType_Position)
1596   {
1597     const RWGltf_JsonValue* aMin = findObjectMember (theAccessor, "min");
1598     const RWGltf_JsonValue* aMax = findObjectMember (theAccessor, "max");
1599     if (aMin != NULL && aMax != NULL)
1600     {
1601       // Note: Min/Max values can be not defined in glTF file.
1602       // In this case it is not used only.
1603       if (!aMin->IsArray()   || !aMax->IsArray() ||
1604            aMin->Size() != 3 ||  aMax->Size() != 3)
1605       {
1606         reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max values.");
1607       }
1608       else
1609       {
1610         bool isValidMinMax = true;
1611         gp_Pnt aMinPnt, aMaxPnt;
1612         for (int anIter = 0; anIter < 3; ++anIter)
1613         {
1614           const RWGltf_JsonValue& aMinVal = (*aMin)[anIter];
1615           const RWGltf_JsonValue& aMaxVal = (*aMax)[anIter];
1616           if (!aMinVal.IsNumber() || !aMaxVal.IsNumber())
1617           {
1618             reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max value.");
1619             isValidMinMax = false;
1620             break;
1621           }
1622           aMinPnt.SetCoord (anIter + 1, aMinVal.GetDouble());
1623           aMinPnt.SetCoord (anIter + 1, aMaxVal.GetDouble());
1624         }
1625         if (isValidMinMax)
1626         {
1627           myCSTrsf.TransformPosition (aMinPnt.ChangeCoord());
1628           myCSTrsf.TransformPosition (aMaxPnt.ChangeCoord());
1629
1630           Bnd_Box aBox;
1631           aBox.Add (aMinPnt);
1632           aBox.Add (aMaxPnt);
1633
1634           theMeshData->SetBoundingBox (aBox);
1635         }
1636       }
1637     }
1638   }
1639
1640   const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
1641   if (aBufferView == NULL
1642   || !aBufferView->IsObject())
1643   {
1644     reportGltfError ("Accessor '" + theName + "' refers to non-existing bufferView.");
1645     return false;
1646   }
1647
1648   return gltfParseBufferView (theMeshData, getKeyString (*aBufferViewName), *aBufferView, aStruct, theType);
1649 }
1650
1651 // =======================================================================
1652 // function : gltfParseBufferView
1653 // purpose  :
1654 // =======================================================================
1655 bool RWGltf_GltfJsonParser::gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1656                                                  const TCollection_AsciiString& theName,
1657                                                  const RWGltf_JsonValue& theBufferView,
1658                                                  const RWGltf_GltfAccessor& theAccessor,
1659                                                  const RWGltf_GltfArrayType theType)
1660 {
1661   RWGltf_GltfBufferView aBuffView;
1662   const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
1663   const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
1664   const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
1665   const RWGltf_JsonValue* aByteStride = findObjectMember (theBufferView, "byteStride"); // byteStride is part of bufferView since glTF 2.0
1666   const RWGltf_JsonValue* aTarget     = findObjectMember (theBufferView, "target");
1667   if (aBufferName == NULL)
1668   {
1669     reportGltfError ("BufferView '" + theName + "' does not define buffer.");
1670     return false;
1671   }
1672
1673   aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1674                        ? (int64_t )aByteOffset->GetDouble()
1675                        : 0;
1676   aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
1677                        ? (int64_t )aByteLength->GetDouble()
1678                        : 0;
1679   aBuffView.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1680                        ? aByteStride->GetInt()
1681                        : 0;
1682   if (aTarget != NULL && aTarget->IsInt())
1683   {
1684     aBuffView.Target = (RWGltf_GltfBufferViewTarget )aTarget->GetInt();
1685     if (aBuffView.Target != RWGltf_GltfBufferViewTarget_ARRAY_BUFFER
1686      && aBuffView.Target != RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER)
1687     {
1688       reportGltfError ("BufferView '" + theName + "' defines invalid target.");
1689       return false;
1690     }
1691   }
1692
1693   if (aBuffView.ByteLength <= 0)
1694   {
1695     reportGltfError ("BufferView '" + theName + "' defines invalid byteLength.");
1696     return false;
1697   }
1698   else if (aBuffView.ByteOffset < 0)
1699   {
1700     reportGltfError ("BufferView '" + theName + "' defines invalid byteOffset.");
1701     return false;
1702   }
1703   else if (aBuffView.ByteStride < 0
1704         || aBuffView.ByteStride > 255)
1705   {
1706     reportGltfError ("BufferView '" + theName + "' defines invalid byteStride.");
1707     return false;
1708   }
1709
1710   const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
1711   if (aBuffer == NULL
1712   || !aBuffer->IsObject())
1713   {
1714     reportGltfError ("BufferView '" + theName + "' refers to non-existing buffer.");
1715     return false;
1716   }
1717
1718   return gltfParseBuffer (theMeshData, getKeyString (*aBufferName), *aBuffer, theAccessor, aBuffView, theType);
1719 }
1720
1721 // =======================================================================
1722 // function : gltfParseBuffer
1723 // purpose  :
1724 // =======================================================================
1725 bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1726                                              const TCollection_AsciiString& theName,
1727                                              const RWGltf_JsonValue& theBuffer,
1728                                              const RWGltf_GltfAccessor&     theAccessor,
1729                                              const RWGltf_GltfBufferView&   theView,
1730                                              const RWGltf_GltfArrayType     theType)
1731 {
1732   //const RWGltf_JsonValue* aType       = findObjectMember (theBuffer, "type");
1733   //const RWGltf_JsonValue* aByteLength = findObjectMember (theBuffer, "byteLength");
1734   const RWGltf_JsonValue* anUriVal      = findObjectMember (theBuffer, "uri");
1735
1736   int64_t anOffset = theView.ByteOffset + theAccessor.ByteOffset;
1737   const int32_t aByteStride = theAccessor.ByteStride != 0 ? theView.ByteStride : theView.ByteStride;
1738   bool isBinary = false;
1739   if (myIsBinary)
1740   {
1741     isBinary = IsEqual ("binary_glTF", theName) // glTF 1.0
1742             || anUriVal == NULL;                // glTF 2.0
1743   }
1744   if (isBinary)
1745   {
1746     anOffset += myBinBodyOffset;
1747
1748     RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1749     aData.Accessor = theAccessor;
1750     aData.Accessor.ByteStride = aByteStride;
1751     aData.StreamOffset = anOffset;
1752     aData.StreamUri = myFilePath;
1753     return true;
1754   }
1755
1756   if (anUriVal == NULL
1757   || !anUriVal->IsString())
1758   {
1759     reportGltfError ("Buffer '" + theName + "' does not define uri.");
1760     return false;
1761   }
1762
1763   const char* anUriData = anUriVal->GetString();
1764   if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
1765   {
1766     RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1767     aData.Accessor = theAccessor;
1768     aData.Accessor.ByteStride = aByteStride;
1769     aData.StreamOffset = anOffset;
1770     if (!myDecodedBuffers.Find (theName, aData.StreamData))
1771     {
1772       // it is better decoding in multiple threads
1773       aData.StreamData = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
1774       myDecodedBuffers.Bind (theName, aData.StreamData);
1775     }
1776     return true;
1777   }
1778   else
1779   {
1780     TCollection_AsciiString anUri = anUriData;
1781     if (anUri.IsEmpty())
1782     {
1783       reportGltfError ("Buffer '" + theName + "' does not define uri.");
1784       return false;
1785     }
1786
1787     TCollection_AsciiString aPath = myFolder + anUri;
1788     bool isFileExist = false;
1789     if (!myProbedFiles.Find (aPath, isFileExist))
1790     {
1791       isFileExist = OSD_File (aPath).Exists();
1792       myProbedFiles.Bind (aPath, isFileExist);
1793     }
1794     if (!isFileExist)
1795     {
1796       reportGltfError ("Buffer '" + theName + "' refers to non-existing file '" + anUri + "'.");
1797       return false;
1798     }
1799
1800     RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1801     aData.Accessor = theAccessor;
1802     aData.Accessor.ByteStride = aByteStride;
1803     aData.StreamOffset = anOffset;
1804     aData.StreamUri = myFolder + anUri;
1805     if (myExternalFiles != NULL)
1806     {
1807       myExternalFiles->Add (aData.StreamUri);
1808     }
1809     return true;
1810   }
1811 }
1812
1813 // =======================================================================
1814 // function : bindNamedShape
1815 // purpose  :
1816 // =======================================================================
1817 void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape,
1818                                             ShapeMapGroup theGroup,
1819                                             const TopLoc_Location& theLoc,
1820                                             const TCollection_AsciiString& theId,
1821                                             const RWGltf_JsonValue* theUserName)
1822 {
1823   if (theShape.IsNull())
1824   {
1825     return;
1826   }
1827
1828   if (!theLoc.IsIdentity())
1829   {
1830     theShape.Location (theLoc);
1831   }
1832
1833   TCollection_AsciiString aUserName;
1834   if (theUserName != NULL
1835    && theUserName->IsString())
1836   {
1837     aUserName = theUserName->GetString();
1838   }
1839   else if (myIsGltf1)
1840   {
1841     aUserName = theId;
1842   }
1843
1844   myShapeMap[theGroup].Bind (theId, theShape);
1845   if (myAttribMap != NULL)
1846   {
1847     RWMesh_NodeAttributes aShapeAttribs;
1848     aShapeAttribs.Name    = aUserName;
1849     aShapeAttribs.RawName = theId;
1850     if (theShape.ShapeType() == TopAbs_FACE)
1851     {
1852       TopLoc_Location aDummy;
1853       if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (theShape), aDummy)))
1854       {
1855         if (aLateData->HasStyle())
1856         {
1857           // assign material and not color
1858           //aShapeAttribs.Style.SetColorSurf (aLateData->BaseColor());
1859
1860           Handle(XCAFDoc_VisMaterial) aMat;
1861           myMaterials.Find (!aLateData->MaterialPbr().IsNull() ? aLateData->MaterialPbr()->Id : aLateData->MaterialCommon()->Id, aMat);
1862           aShapeAttribs.Style.SetMaterial (aMat);
1863         }
1864         if (aShapeAttribs.Name.IsEmpty()
1865          && myUseMeshNameAsFallback)
1866         {
1867           // fallback using Mesh name
1868           aShapeAttribs.Name = aLateData->Name();
1869         }
1870       }
1871     }
1872     else if (aShapeAttribs.Name.IsEmpty()
1873           && myUseMeshNameAsFallback)
1874     {
1875       // fallback using Mesh name
1876       TopLoc_Location aDummy;
1877       TCollection_AsciiString aMeshName;
1878       for (TopExp_Explorer aFaceIter (theShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
1879       {
1880         if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (aFaceIter.Value()), aDummy)))
1881         {
1882           if (aLateData->Name().IsEmpty())
1883           {
1884             aMeshName.Clear();
1885             break;
1886           }
1887           else if (aMeshName.IsEmpty())
1888           {
1889             aMeshName = aLateData->Name();
1890           }
1891           else if (!aMeshName.IsEqual (aLateData->Name()))
1892           {
1893             aMeshName.Clear();
1894             break;
1895           }
1896         }
1897       }
1898       if (!aMeshName.IsEmpty())
1899       {
1900         aShapeAttribs.Name = aMeshName;
1901       }
1902     }
1903     myAttribMap->Bind (theShape, aShapeAttribs);
1904   }
1905 }
1906 #endif
1907
1908 // =======================================================================
1909 // function : Parse
1910 // purpose  :
1911 // =======================================================================
1912 bool RWGltf_GltfJsonParser::Parse (const Handle(Message_ProgressIndicator)& theProgress)
1913 {
1914   Message_ProgressSentry aPSentry (theProgress, "Reading Gltf", 0, 2, 1);
1915 #ifdef HAVE_RAPIDJSON
1916   {
1917     if (!gltfParseRoots())
1918     {
1919       return false;
1920     }
1921
1922     gltfParseAsset();
1923     gltfParseMaterials();
1924     if (!gltfParseScene (theProgress))
1925     {
1926       return false;
1927     }
1928   }
1929   aPSentry.Next();
1930   if (!aPSentry.More())
1931   {
1932     return false;
1933   }
1934   return true;
1935 #else
1936   Message::SendFail ("Error: glTF reader is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined]");
1937   return false;
1938 #endif
1939 }