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