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