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