0031284: Visualization - XCAFDoc_VisMaterialPBR lacks Index of Refraction
[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   // TODO ADOBE_materials_thin_transparency extension can be used to read IOR (Index of Refraction for transparent materials)
546   if (aMetalRoughVal == NULL)
547   {
548     return false;
549   }
550
551   theMat = new RWGltf_MaterialMetallicRoughness();
552   const RWGltf_JsonValue* aBaseColorFactorVal = findObjectMember (*aMetalRoughVal, "baseColorFactor");
553   const RWGltf_JsonValue* aBaseColorTexVal    = findObjectMember (*aMetalRoughVal, "baseColorTexture");
554   const RWGltf_JsonValue* aMetallicFactorVal  = findObjectMember (*aMetalRoughVal, "metallicFactor");
555   const RWGltf_JsonValue* aRoughnessFactorVal = findObjectMember (*aMetalRoughVal, "roughnessFactor");
556   const RWGltf_JsonValue* aMetalRoughTexVal   = findObjectMember (*aMetalRoughVal, "metallicRoughnessTexture");
557
558   if (aDoubleSidedVal != NULL
559    && aDoubleSidedVal->IsBool())
560   {
561     theMat->IsDoubleSided = aDoubleSidedVal->GetBool();
562   }
563   if (anAlphaCutoffVal != NULL
564    && anAlphaCutoffVal->IsNumber())
565   {
566     theMat->AlphaCutOff = (float )anAlphaCutoffVal->GetDouble();
567   }
568   if (anAlphaModeVal != NULL
569    && anAlphaModeVal->IsString())
570   {
571     theMat->AlphaMode = RWGltf_GltfParseAlphaMode (anAlphaModeVal->GetString());
572   }
573
574   if (aBaseColorTexVal != NULL
575    && aBaseColorTexVal->IsObject())
576   {
577     if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aBaseColorTexVal, "index"))
578     {
579       gltfParseTexture (theMat->BaseColorTexture, aTexIndexVal);
580     }
581   }
582
583   Graphic3d_Vec4d aBaseColorFactor;
584   if (gltfReadVec4   (aBaseColorFactor, aBaseColorFactorVal)
585    && validateColor4 (aBaseColorFactor))
586   {
587     theMat->BaseColor = Quantity_ColorRGBA (Graphic3d_Vec4 (aBaseColorFactor));
588   }
589
590   Graphic3d_Vec3d anEmissiveFactor;
591   if (gltfReadVec3   (anEmissiveFactor, anEmissFactorVal)
592    && validateColor3 (anEmissiveFactor))
593   {
594     theMat->EmissiveFactor = Graphic3d_Vec3 (anEmissiveFactor);
595   }
596
597   if (aMetalRoughTexVal != NULL
598    && aMetalRoughTexVal->IsObject())
599   {
600     if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aMetalRoughTexVal, "index"))
601     {
602       gltfParseTexture (theMat->MetallicRoughnessTexture, aTexIndexVal);
603     }
604   }
605
606   if (aMetallicFactorVal != NULL
607    && aMetallicFactorVal->IsNumber())
608   {
609     theMat->Metallic = (float )aMetallicFactorVal->GetDouble();
610   }
611
612   if (aRoughnessFactorVal != NULL
613    && aRoughnessFactorVal->IsNumber())
614   {
615     theMat->Roughness = (float )aRoughnessFactorVal->GetDouble();
616   }
617
618   if (aNormTexVal != NULL
619    && aNormTexVal->IsObject())
620   {
621     if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aNormTexVal, "index"))
622     {
623       gltfParseTexture (theMat->NormalTexture, aTexIndexVal);
624     }
625   }
626
627   if (anEmissTexVal != NULL
628    && anEmissTexVal->IsObject())
629   {
630     if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anEmissTexVal, "index"))
631     {
632       gltfParseTexture (theMat->EmissiveTexture, aTexIndexVal);
633     }
634   }
635
636   if (anOcclusionTexVal != NULL
637    && anOcclusionTexVal->IsObject())
638   {
639     if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anOcclusionTexVal, "index"))
640     {
641       gltfParseTexture (theMat->OcclusionTexture, aTexIndexVal);
642     }
643   }
644   return true;
645 }
646
647 // =======================================================================
648 // function : gltfParseCommonMaterial
649 // purpose  :
650 // =======================================================================
651 bool RWGltf_GltfJsonParser::gltfParseCommonMaterial (Handle(RWGltf_MaterialCommon)& theMat,
652                                                      const RWGltf_JsonValue& theMatNode)
653 {
654   const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions");
655   if (anExtVal == NULL)
656   {
657     return false;
658   }
659
660   const RWGltf_JsonValue* aMatCommon = findObjectMember (*anExtVal, THE_KHR_materials_common);
661   if (aMatCommon == NULL)
662   {
663     return false;
664   }
665
666   if (!gltfParseStdMaterial (theMat, *aMatCommon))
667   {
668     return false;
669   }
670   return true;
671 }
672
673 // =======================================================================
674 // function : gltfParseTexture
675 // purpose  :
676 // =======================================================================
677 bool RWGltf_GltfJsonParser::gltfParseTexture (Handle(Image_Texture)& theTexture,
678                                               const RWGltf_JsonValue* theTextureId)
679 {
680   if (theTextureId == NULL
681   ||  myGltfRoots[RWGltf_GltfRootElement_Textures].IsNull()
682   ||  myGltfRoots[RWGltf_GltfRootElement_Images].IsNull())
683   {
684     return false;
685   }
686
687   const TCollection_AsciiString aTextureId = getKeyString (*theTextureId);
688   const RWGltf_JsonValue* aTexNode = myGltfRoots[RWGltf_GltfRootElement_Textures].FindChild (*theTextureId);
689   if (aTexNode == NULL)
690   {
691     reportGltfWarning ("Texture node '" + aTextureId + "' is not found.");
692     return false;
693   }
694
695   const RWGltf_JsonValue* aSrcVal  = findObjectMember (*aTexNode, "source");
696   const RWGltf_JsonValue* aTargVal = findObjectMember (*aTexNode, "target");
697   if (aSrcVal == NULL)
698   {
699     reportGltfWarning ("Invalid texture node '" + aTextureId + "' without a 'source' property.");
700     return false;
701   }
702   if (aTargVal != NULL
703    && aTargVal->IsNumber()
704    && aTargVal->GetInt() != 3553) // GL_TEXTURE_2D
705   {
706     return false;
707   }
708
709   const RWGltf_JsonValue* anImgNode = myGltfRoots[RWGltf_GltfRootElement_Images].FindChild (*aSrcVal);
710   if (anImgNode == NULL)
711   {
712     reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to non-existing image '" + getKeyString (*aSrcVal) + "'.");
713     return false;
714   }
715
716   if (myIsBinary)
717   {
718     const RWGltf_JsonValue* aBinVal = NULL;
719     const RWGltf_JsonValue* aBufferViewName = findObjectMember (*anImgNode, "bufferView");
720     if (aBufferViewName != NULL)
721     {
722       aBinVal = anImgNode;
723     }
724     else if (myIsGltf1)
725     {
726       const RWGltf_JsonValue* anExtVal = findObjectMember (*anImgNode, "extensions");
727       if (anExtVal != NULL)
728       {
729         aBinVal = findObjectMember (*anExtVal, THE_KHR_binary_glTF);
730         if (aBinVal != NULL)
731         {
732           aBufferViewName = findObjectMember (*aBinVal, "bufferView");
733         }
734       }
735     }
736
737     if (aBinVal != NULL)
738     {
739       //const RWGltf_JsonValue* aMimeTypeVal = findObjectMember (*aBinVal, "mimeType");
740       //const RWGltf_JsonValue* aWidthVal    = findObjectMember (*aBinVal, "width");
741       //const RWGltf_JsonValue* aHeightVal   = findObjectMember (*aBinVal, "height");
742       if (aBufferViewName == NULL)
743       {
744         reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
745         return false;
746       }
747
748       const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
749       if (aBufferView == NULL
750       || !aBufferView->IsObject())
751       {
752         reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid buffer view '" + getKeyString (*aBufferViewName) + "'.");
753         return false;
754       }
755
756       const RWGltf_JsonValue* aBufferName = findObjectMember (*aBufferView, "buffer");
757       const RWGltf_JsonValue* aByteLength = findObjectMember (*aBufferView, "byteLength");
758       const RWGltf_JsonValue* aByteOffset = findObjectMember (*aBufferView, "byteOffset");
759       if (aBufferName != NULL
760       &&  aBufferName->IsString()
761       && !IsEqual (aBufferName->GetString(), "binary_glTF"))
762       {
763         reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' does not define binary_glTF buffer.");
764         return false;
765       }
766
767       RWGltf_GltfBufferView aBuffView;
768       aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
769                            ? (int64_t )aByteOffset->GetDouble()
770                            : 0;
771       aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
772                            ? (int64_t )aByteLength->GetDouble()
773                            : 0;
774       if (aBuffView.ByteLength < 0)
775       {
776         reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' defines invalid byteLength.");
777         return false;
778       }
779       else if (aBuffView.ByteOffset < 0)
780       {
781         reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' defines invalid byteOffset.");
782         return false;
783       }
784
785
786       const int64_t anOffset = myBinBodyOffset + aBuffView.ByteOffset;
787       theTexture = new Image_Texture (myFilePath, anOffset, aBuffView.ByteLength);
788       return true;
789     }
790   }
791
792   const RWGltf_JsonValue* anUriVal = findObjectMember (*anImgNode, "uri");
793   if (anUriVal == NULL
794   || !anUriVal->IsString())
795   {
796     return false;
797   }
798
799   const char* anUriData = anUriVal->GetString();
800   if (::strncmp (anUriData, "data:", 5) == 0) // data:image/png;base64
801   {
802     // uncompressing base64 here is inefficient, because the same image can be shared by several nodes
803     const char* aDataStart = anUriData + 5;
804     for (const char* aDataIter = aDataStart; *aDataIter != '\0'; ++aDataIter)
805     {
806       if (::memcmp (aDataIter, ";base64,", 8) == 0)
807       {
808         const char* aBase64End  = anUriData + anUriVal->GetStringLength();
809         const char* aBase64Data = aDataIter + 8;
810         const size_t aBase64Len = size_t(aBase64End - aBase64Data);
811         //const TCollection_AsciiString aMime (aDataStart, aDataIter - aDataStart);
812         Handle(NCollection_Buffer) aData = FSD_Base64Decoder::Decode ((const Standard_Byte* )aBase64Data, aBase64Len);
813         theTexture = new Image_Texture (aData, myFilePath + "@" + getKeyString (*aSrcVal));
814         return true;
815       }
816     }
817     Message::DefaultMessenger()->Send ("glTF reader - embedded image has been skipped", Message_Warning);
818     return false;
819   }
820
821   TCollection_AsciiString anImageFile = myFolder + anUriVal->GetString();
822   theTexture = new Image_Texture (anImageFile);
823   if (myExternalFiles != NULL)
824   {
825     myExternalFiles->Add (anImageFile);
826   }
827   return true;
828 }
829
830 // =======================================================================
831 // function : gltfParseScene
832 // purpose  :
833 // =======================================================================
834 bool RWGltf_GltfJsonParser::gltfParseScene (const Handle(Message_ProgressIndicator)& theProgress)
835 {
836   // search default scene
837   const RWGltf_JsonValue* aDefScene = myGltfRoots[RWGltf_GltfRootElement_Scenes].FindChild (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root());
838   if (aDefScene == NULL)
839   {
840     reportGltfError ("Default scene is not found.");
841     return false;
842   }
843
844   const RWGltf_JsonValue* aSceneNodes = findObjectMember (*aDefScene, "nodes");
845   if (aSceneNodes == NULL
846   || !aSceneNodes->IsArray())
847   {
848     reportGltfError ("Empty scene '" + getKeyString (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root()) + "'.");
849     return false;
850   }
851
852   return gltfParseSceneNodes (*myRootShapes, *aSceneNodes, theProgress);
853 }
854
855 // =======================================================================
856 // function : gltfParseSceneNodes
857 // purpose  :
858 // =======================================================================
859 bool RWGltf_GltfJsonParser::gltfParseSceneNodes (TopTools_SequenceOfShape& theShapeSeq,
860                                                  const RWGltf_JsonValue& theSceneNodes,
861                                                  const Handle(Message_ProgressIndicator)& theProgress)
862 {
863   if (!theSceneNodes.IsArray())
864   {
865     reportGltfError ("Scene nodes is not array.");
866     return false;
867   }
868
869   Message_ProgressSentry aPSentry (theProgress, "Reading scene nodes", 0, theSceneNodes.Size(), 1);
870   for (rapidjson::Value::ConstValueIterator aSceneNodeIter = theSceneNodes.Begin();
871        aSceneNodeIter != theSceneNodes.End() && aPSentry.More(); ++aSceneNodeIter, aPSentry.Next())
872   {
873     const RWGltf_JsonValue* aSceneNode = myGltfRoots[RWGltf_GltfRootElement_Nodes].FindChild (*aSceneNodeIter);
874     if (aSceneNode == NULL)
875     {
876       reportGltfWarning ("Scene refers to non-existing node '" + getKeyString (*aSceneNodeIter) + "'.");
877       return true;
878     }
879
880     TopoDS_Shape aNodeShape;
881     if (!gltfParseSceneNode (aNodeShape, getKeyString (*aSceneNodeIter), *aSceneNode, theProgress))
882     {
883       return false;
884     }
885
886     if (aNodeShape.IsNull())
887     {
888       continue;
889     }
890     else if (myToSkipEmptyNodes
891          && !TopExp_Explorer (aNodeShape, TopAbs_FACE).More())
892     {
893       continue;
894     }
895
896     theShapeSeq.Append (aNodeShape);
897   }
898   return true;
899 }
900
901 // =======================================================================
902 // function : gltfParseSceneNode
903 // purpose  :
904 // =======================================================================
905 bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
906                                                 const TCollection_AsciiString& theSceneNodeId,
907                                                 const RWGltf_JsonValue& theSceneNode,
908                                                 const Handle(Message_ProgressIndicator)& theProgress)
909 {
910   const RWGltf_JsonValue* aName         = findObjectMember (theSceneNode, "name");
911   //const RWGltf_JsonValue* aJointName    = findObjectMember (theSceneNode, "jointName");
912   const RWGltf_JsonValue* aChildren     = findObjectMember (theSceneNode, "children");
913   const RWGltf_JsonValue* aMeshes_1     = findObjectMember (theSceneNode, "meshes");
914   const RWGltf_JsonValue* aMesh_2       = findObjectMember (theSceneNode, "mesh");
915   //const RWGltf_JsonValue* aCamera       = findObjectMember (theSceneNode, "camera");
916   const RWGltf_JsonValue* aTrsfMatVal   = findObjectMember (theSceneNode, "matrix");
917   const RWGltf_JsonValue* aTrsfRotVal   = findObjectMember (theSceneNode, "rotation");
918   const RWGltf_JsonValue* aTrsfScaleVal = findObjectMember (theSceneNode, "scale");
919   const RWGltf_JsonValue* aTrsfTransVal = findObjectMember (theSceneNode, "translation");
920   if (findNodeShape (theNodeShape, theSceneNodeId))
921   {
922     return true;
923   }
924
925   TopLoc_Location aNodeLoc;
926   const bool hasTrs = aTrsfRotVal   != NULL
927                    || aTrsfScaleVal != NULL
928                    || aTrsfTransVal != NULL;
929   if (aTrsfMatVal != NULL)
930   {
931     if (hasTrs)
932     {
933       reportGltfError ("Scene node '" + theSceneNodeId + "' defines ambiguous transformation.");
934       return false;
935     }
936     else if (!aTrsfMatVal->IsArray()
937            || aTrsfMatVal->Size() != 16)
938     {
939       reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix array.");
940       return false;
941     }
942
943     Graphic3d_Mat4d aMat4;
944     for (int aColIter = 0; aColIter < 4; ++aColIter)
945     {
946       for (int aRowIter = 0; aRowIter < 4; ++aRowIter)
947       {
948         const RWGltf_JsonValue& aGenVal = (*aTrsfMatVal)[aColIter * 4 + aRowIter];
949         if (!aGenVal.IsNumber())
950         {
951           reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix.");
952           return false;
953         }
954         aMat4.SetValue (aRowIter, aColIter, aGenVal.GetDouble());
955       }
956     }
957
958     if (!aMat4.IsIdentity())
959     {
960       gp_Trsf aTrsf;
961       aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
962                        aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
963                        aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
964       myCSTrsf.TransformTransformation (aTrsf);
965       if (aTrsf.Form() != gp_Identity)
966       {
967         aNodeLoc = TopLoc_Location (aTrsf);
968       }
969     }
970   }
971   else if (hasTrs)
972   {
973     gp_Trsf aTrsf;
974     if (aTrsfRotVal != NULL)
975     {
976       if (!aTrsfRotVal->IsArray()
977         || aTrsfRotVal->Size() != 4)
978       {
979         reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation quaternion.");
980         return false;
981       }
982
983       Graphic3d_Vec4d aRotVec4;
984       for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
985       {
986         const RWGltf_JsonValue& aGenVal = (*aTrsfRotVal)[aCompIter];
987         if (!aGenVal.IsNumber())
988         {
989           reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation.");
990           return false;
991         }
992         aRotVec4[aCompIter] = aGenVal.GetDouble();
993       }
994       const gp_Quaternion aQuaternion (aRotVec4.x(), aRotVec4.y(), aRotVec4.z(), aRotVec4.w());
995       if (Abs (aQuaternion.X())       > gp::Resolution()
996        || Abs (aQuaternion.Y())       > gp::Resolution()
997        || Abs (aQuaternion.Z())       > gp::Resolution()
998        || Abs (aQuaternion.W() - 1.0) > gp::Resolution())
999       {
1000         aTrsf.SetRotation (aQuaternion);
1001       }
1002     }
1003
1004     if (aTrsfTransVal != NULL)
1005     {
1006       if (!aTrsfTransVal->IsArray()
1007         || aTrsfTransVal->Size() != 3)
1008       {
1009         reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation vector.");
1010         return false;
1011       }
1012
1013       gp_XYZ aTransVec;
1014       for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
1015       {
1016         const RWGltf_JsonValue& aGenVal = (*aTrsfTransVal)[aCompIter];
1017         if (!aGenVal.IsNumber())
1018         {
1019           reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation.");
1020           return false;
1021         }
1022         aTransVec.SetCoord (aCompIter + 1, aGenVal.GetDouble());
1023       }
1024       aTrsf.SetTranslationPart (aTransVec);
1025     }
1026
1027     if (aTrsfScaleVal != NULL)
1028     {
1029       Graphic3d_Vec3d aScaleVec;
1030       if (!aTrsfScaleVal->IsArray()
1031         || aTrsfScaleVal->Size() != 3)
1032       {
1033         reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale vector.");
1034         return false;
1035       }
1036       for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
1037       {
1038         const RWGltf_JsonValue& aGenVal = (*aTrsfScaleVal)[aCompIter];
1039         if (!aGenVal.IsNumber())
1040         {
1041           reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
1042           return false;
1043         }
1044         aScaleVec[aCompIter] = aGenVal.GetDouble();
1045         if (Abs (aScaleVec[aCompIter]) <= gp::Resolution())
1046         {
1047           reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
1048           return false;
1049         }
1050       }
1051
1052       if (Abs (aScaleVec.x() - aScaleVec.y()) > Precision::Confusion()
1053        || Abs (aScaleVec.y() - aScaleVec.z()) > Precision::Confusion()
1054        || Abs (aScaleVec.x() - aScaleVec.z()) > Precision::Confusion())
1055       {
1056         Graphic3d_Mat4d aScaleMat;
1057         aScaleMat.SetDiagonal (aScaleVec);
1058
1059         Graphic3d_Mat4d aMat4;
1060         aTrsf.GetMat4 (aMat4);
1061
1062         aMat4 = aMat4 * aScaleMat;
1063         aTrsf = gp_Trsf();
1064         aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
1065                          aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
1066                          aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
1067
1068         Message::DefaultMessenger()->Send (TCollection_AsciiString ("glTF reader, scene node '")
1069                                          + theSceneNodeId + "' defines unsupported scaling "
1070                                          + aScaleVec.x() + " " + aScaleVec.y() + " " + aScaleVec.z(), Message_Warning);
1071       }
1072       else if (Abs (aScaleVec.x() - 1.0) > Precision::Confusion())
1073       {
1074         aTrsf.SetScaleFactor (aScaleVec.x());
1075       }
1076     }
1077
1078     myCSTrsf.TransformTransformation (aTrsf);
1079     if (aTrsf.Form() != gp_Identity)
1080     {
1081       aNodeLoc = TopLoc_Location (aTrsf);
1082     }
1083   }
1084
1085   BRep_Builder aBuilder;
1086   TopoDS_Compound aNodeShape;
1087   aBuilder.MakeCompound (aNodeShape);
1088   TopTools_SequenceOfShape aChildShapes;
1089   int aNbSubShapes = 0;
1090   if (aChildren != NULL
1091   && !gltfParseSceneNodes (aChildShapes, *aChildren, theProgress))
1092   {
1093     theNodeShape = aNodeShape;
1094     bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1095     return false;
1096   }
1097   for (TopTools_SequenceOfShape::Iterator aChildShapeIter (aChildShapes); aChildShapeIter.More(); aChildShapeIter.Next())
1098   {
1099     aBuilder.Add (aNodeShape, aChildShapeIter.Value());
1100     ++aNbSubShapes;
1101   }
1102
1103   if (aMeshes_1 != NULL
1104    && aMeshes_1->IsArray())
1105   {
1106     // glTF 1.0
1107     Message_ProgressSentry aPSentry (theProgress, "Reading scene meshes", 0, aMeshes_1->Size(), 1);
1108     for (rapidjson::Value::ConstValueIterator aMeshIter = aMeshes_1->Begin();
1109          aMeshIter != aMeshes_1->End() && aPSentry.More(); ++aMeshIter, aPSentry.Next())
1110     {
1111       const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMeshIter);
1112       if (aMesh == NULL)
1113       {
1114         theNodeShape = aNodeShape;
1115         bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1116         reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1117         return false;
1118       }
1119
1120       TopoDS_Shape aMeshShape;
1121       if (!gltfParseMesh (aMeshShape, getKeyString (*aMeshIter), *aMesh, theProgress))
1122       {
1123         theNodeShape = aNodeShape;
1124         bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1125         return false;
1126       }
1127       if (!aMeshShape.IsNull())
1128       {
1129         aBuilder.Add (aNodeShape, aMeshShape);
1130         ++aNbSubShapes;
1131       }
1132     }
1133   }
1134   if (aMesh_2 != NULL)
1135   {
1136     // glTF 2.0
1137     const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMesh_2);
1138     if (aMesh == NULL)
1139     {
1140       theNodeShape = aNodeShape;
1141       bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1142       reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1143       return false;
1144     }
1145
1146     TopoDS_Shape aMeshShape;
1147     if (!gltfParseMesh (aMeshShape, getKeyString (*aMesh_2), *aMesh, theProgress))
1148     {
1149       theNodeShape = aNodeShape;
1150       bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1151       return false;
1152     }
1153     if (!aMeshShape.IsNull())
1154     {
1155       aBuilder.Add (aNodeShape, aMeshShape);
1156       ++aNbSubShapes;
1157     }
1158   }
1159
1160   if (aNbSubShapes == 1)
1161   {
1162     theNodeShape = TopoDS_Iterator (aNodeShape).Value();
1163   }
1164   else
1165   {
1166     theNodeShape = aNodeShape;
1167   }
1168   bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1169   return true;
1170 }
1171
1172 // =======================================================================
1173 // function : gltfParseMesh
1174 // purpose  :
1175 // =======================================================================
1176 bool RWGltf_GltfJsonParser::gltfParseMesh (TopoDS_Shape& theMeshShape,
1177                                            const TCollection_AsciiString& theMeshId,
1178                                            const RWGltf_JsonValue& theMesh,
1179                                            const Handle(Message_ProgressIndicator)& theProgress)
1180 {
1181   const RWGltf_JsonValue* aName  = findObjectMember (theMesh, "name");
1182   const RWGltf_JsonValue* aPrims = findObjectMember (theMesh, "primitives");
1183   if (!aPrims->IsArray())
1184   {
1185     reportGltfError ("Primitive array attributes within Mesh '" + theMeshId + "' is not an array.");
1186     return false;
1187   }
1188
1189   if (findMeshShape (theMeshShape, theMeshId))
1190   {
1191     return true;
1192   }
1193
1194   BRep_Builder aBuilder;
1195   TopoDS_Compound aMeshShape;
1196   int aNbFaces = 0;
1197   for (rapidjson::Value::ConstValueIterator aPrimArrIter = aPrims->Begin();
1198        aPrimArrIter != aPrims->End(); ++aPrimArrIter)
1199   {
1200     TCollection_AsciiString aUserName;
1201     if (aName != NULL
1202      && aName->IsString())
1203     {
1204       aUserName = aName->GetString();
1205     }
1206
1207     Handle(RWGltf_GltfLatePrimitiveArray) aMeshData = new RWGltf_GltfLatePrimitiveArray (theMeshId, aUserName);
1208     if (!gltfParsePrimArray (aMeshData, theMeshId, *aPrimArrIter, theProgress))
1209     {
1210       return false;
1211     }
1212
1213     if (!aMeshData->Data().IsEmpty())
1214     {
1215       if (aMeshShape.IsNull())
1216       {
1217         aBuilder.MakeCompound (aMeshShape);
1218       }
1219
1220       TopoDS_Face aFace;
1221       aBuilder.MakeFace (aFace, aMeshData);
1222       aBuilder.Add (aMeshShape, aFace);
1223       if (myAttribMap != NULL
1224        && aMeshData->HasStyle())
1225       {
1226         RWMesh_NodeAttributes aShapeAttribs;
1227         aShapeAttribs.RawName = aUserName;
1228
1229         // assign material and not color
1230         //aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor());
1231
1232         Handle(XCAFDoc_VisMaterial) aMat;
1233         myMaterials.Find (!aMeshData->MaterialPbr().IsNull() ? aMeshData->MaterialPbr()->Id : aMeshData->MaterialCommon()->Id, aMat);
1234         aShapeAttribs.Style.SetMaterial (aMat);
1235
1236         myAttribMap->Bind (aFace, aShapeAttribs);
1237       }
1238       myFaceList.Append (aFace);
1239       ++aNbFaces;
1240     }
1241   }
1242
1243   if (aNbFaces == 1)
1244   {
1245     theMeshShape = TopoDS_Iterator (aMeshShape).Value();
1246   }
1247   else
1248   {
1249     theMeshShape = aMeshShape;
1250   }
1251   bindMeshShape (theMeshShape, theMeshId, aName);
1252   return true;
1253 }
1254
1255 // =======================================================================
1256 // function : gltfParsePrimArray
1257 // purpose  :
1258 // =======================================================================
1259 bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1260                                                 const TCollection_AsciiString& theMeshId,
1261                                                 const RWGltf_JsonValue& thePrimArray,
1262                                                 const Handle(Message_ProgressIndicator)& /*theProgress*/)
1263 {
1264   const RWGltf_JsonValue* anAttribs = findObjectMember (thePrimArray, "attributes");
1265   const RWGltf_JsonValue* anIndices = findObjectMember (thePrimArray, "indices");
1266   const RWGltf_JsonValue* aMaterial = findObjectMember (thePrimArray, "material");
1267   const RWGltf_JsonValue* aModeVal  = findObjectMember (thePrimArray, "mode");
1268   RWGltf_GltfPrimitiveMode aMode = RWGltf_GltfPrimitiveMode_Triangles;
1269   if (anAttribs == NULL
1270   || !anAttribs->IsObject())
1271   {
1272     reportGltfError ("Primitive array within Mesh '" + theMeshId + "' defines no attributes.");
1273     return false;
1274   }
1275   else if (aModeVal != NULL)
1276   {
1277     aMode = RWGltf_GltfPrimitiveMode_UNKNOWN;
1278     if (aModeVal->IsInt())
1279     {
1280       aMode = (RWGltf_GltfPrimitiveMode )aModeVal->GetInt();
1281     }
1282     if (aMode < RWGltf_GltfPrimitiveMode_Points
1283      || aMode > RWGltf_GltfPrimitiveMode_TriangleFan)
1284     {
1285       reportGltfError ("Primitive array within Mesh '" + theMeshId + "' has unknown mode.");
1286       return false;
1287     }
1288   }
1289   if (aMode != RWGltf_GltfPrimitiveMode_Triangles)
1290   {
1291     Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Primitive array within Mesh '"
1292                                                    + theMeshId + "' skipped due to unsupported mode.", Message_Warning);
1293     return true;
1294   }
1295   theMeshData->SetPrimitiveMode (aMode);
1296
1297   // assign material
1298   if (aMaterial != NULL)
1299   {
1300     Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
1301     if (myMaterialsPbr.Find (getKeyString (*aMaterial), aMatPbr))
1302     {
1303       theMeshData->SetMaterialPbr (aMatPbr);
1304     }
1305
1306     Handle(RWGltf_MaterialCommon) aMatCommon;
1307     if (myMaterialsCommon.Find (getKeyString (*aMaterial), aMatCommon))
1308     {
1309       theMeshData->SetMaterialCommon (aMatCommon);
1310     }
1311   }
1312
1313   bool hasPositions = false;
1314   for (rapidjson::Value::ConstMemberIterator anAttribIter = anAttribs->MemberBegin();
1315        anAttribIter != anAttribs->MemberEnd(); ++anAttribIter)
1316   {
1317     const TCollection_AsciiString anAttribId = getKeyString (anAttribIter->value);
1318     if (anAttribId.IsEmpty())
1319     {
1320       reportGltfError ("Primitive array attribute accessor key within Mesh '" + theMeshId + "' is not a string.");
1321       return false;
1322     }
1323
1324     RWGltf_GltfArrayType aType = RWGltf_GltfParseAttribType (anAttribIter->name.GetString());
1325     if (aType == RWGltf_GltfArrayType_UNKNOWN)
1326     {
1327       // just ignore unknown attributes
1328       continue;
1329     }
1330
1331     const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (anAttribIter->value);
1332     if (anAccessor == NULL
1333     || !anAccessor->IsObject())
1334     {
1335       reportGltfError ("Primitive array attribute accessor key '" + anAttribId + "' points to non-existing object.");
1336       return false;
1337     }
1338     else if (!gltfParseAccessor (theMeshData, anAttribId, *anAccessor, aType))
1339     {
1340       return false;
1341     }
1342     else if (aType == RWGltf_GltfArrayType_Position)
1343     {
1344       hasPositions = true;
1345     }
1346   }
1347   if (!hasPositions)
1348   {
1349     reportGltfError ("Primitive array within Mesh '" + theMeshId + "' does not define vertex positions.");
1350     return false;
1351   }
1352
1353   if (anIndices != NULL)
1354   {
1355     const TCollection_AsciiString anIndicesId = getKeyString (*anIndices);
1356     const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (*anIndices);
1357     if (anAccessor == NULL
1358     || !anAccessor->IsObject())
1359     {
1360       reportGltfError ("Primitive array indices accessor key '" + anIndicesId + "' points to non-existing object.");
1361       return false;
1362     }
1363     else if (!gltfParseAccessor (theMeshData, anIndicesId, *anAccessor, RWGltf_GltfArrayType_Indices))
1364     {
1365       return false;
1366     }
1367   }
1368
1369   return true;
1370 }
1371
1372 // =======================================================================
1373 // function : gltfParseAccessor
1374 // purpose  :
1375 // =======================================================================
1376 bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1377                                                const TCollection_AsciiString& theName,
1378                                                const RWGltf_JsonValue& theAccessor,
1379                                                const RWGltf_GltfArrayType theType)
1380 {
1381   RWGltf_GltfAccessor aStruct;
1382   const RWGltf_JsonValue* aTypeStr        = findObjectMember (theAccessor, "type");
1383   const RWGltf_JsonValue* aBufferViewName = findObjectMember (theAccessor, "bufferView");
1384   const RWGltf_JsonValue* aByteOffset     = findObjectMember (theAccessor, "byteOffset");
1385   const RWGltf_JsonValue* aByteStride     = findObjectMember (theAccessor, "byteStride");
1386   const RWGltf_JsonValue* aCompType       = findObjectMember (theAccessor, "componentType");
1387   const RWGltf_JsonValue* aCount          = findObjectMember (theAccessor, "count");
1388   if (aTypeStr == NULL
1389   || !aTypeStr->IsString())
1390   {
1391     reportGltfError ("Accessor '" + theName + "' does not define type.");
1392     return false;
1393   }
1394   aStruct.Type = RWGltf_GltfParseAccessorType (aTypeStr->GetString());
1395   if (aStruct.Type == RWGltf_GltfAccessorLayout_UNKNOWN)
1396   {
1397     reportGltfError ("Accessor '" + theName + "' has invalid type.");
1398     return false;
1399   }
1400
1401   if (aBufferViewName == NULL)
1402   {
1403     reportGltfError ("Accessor '" + theName + "' does not define bufferView.");
1404     return false;
1405   }
1406   if (aCompType == NULL
1407   || !aCompType->IsInt())
1408   {
1409     reportGltfError ("Accessor '" + theName + "' does not define componentType.");
1410     return false;
1411   }
1412   aStruct.ComponentType = (RWGltf_GltfAccessorCompType )aCompType->GetInt();
1413   if (aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int8
1414    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt8
1415    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int16
1416    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt16
1417    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt32
1418    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Float32)
1419   {
1420     reportGltfError ("Accessor '" + theName + "' defines invalid componentType value.");
1421     return false;
1422   }
1423
1424   if (aCount == NULL
1425   || !aCount->IsNumber())
1426   {
1427     reportGltfError ("Accessor '" + theName + "' does not define count.");
1428     return false;
1429   }
1430
1431   aStruct.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1432                      ? (int64_t )aByteOffset->GetDouble()
1433                      : 0;
1434   aStruct.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1435                      ? aByteStride->GetInt()
1436                      : 0;
1437   aStruct.Count = (int64_t )aCount->GetDouble();
1438
1439   if (aStruct.ByteOffset < 0)
1440   {
1441     reportGltfError ("Accessor '" + theName + "' defines invalid byteOffset.");
1442     return false;
1443   }
1444   else if (aStruct.ByteStride < 0
1445         || aStruct.ByteStride > 255)
1446   {
1447     reportGltfError ("Accessor '" + theName + "' defines invalid byteStride.");
1448     return false;
1449   }
1450   else if (aStruct.Count < 1)
1451   {
1452     reportGltfError ("Accessor '" + theName + "' defines invalid count.");
1453     return false;
1454   }
1455
1456   // Read Min/Max values for POSITION type. It is used for bounding boxes
1457   if (theType == RWGltf_GltfArrayType_Position)
1458   {
1459     const RWGltf_JsonValue* aMin = findObjectMember (theAccessor, "min");
1460     const RWGltf_JsonValue* aMax = findObjectMember (theAccessor, "max");
1461     if (aMin != NULL && aMax != NULL)
1462     {
1463       // Note: Min/Max values can be not defined in glTF file.
1464       // In this case it is not used only.
1465       if (!aMin->IsArray()   || !aMax->IsArray() ||
1466            aMin->Size() != 3 ||  aMax->Size() != 3)
1467       {
1468         reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max values.");
1469       }
1470       else
1471       {
1472         bool isValidMinMax = true;
1473         gp_Pnt aMinPnt, aMaxPnt;
1474         for (int anIter = 0; anIter < 3; ++anIter)
1475         {
1476           const RWGltf_JsonValue& aMinVal = (*aMin)[anIter];
1477           const RWGltf_JsonValue& aMaxVal = (*aMax)[anIter];
1478           if (!aMinVal.IsNumber() || !aMaxVal.IsNumber())
1479           {
1480             reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max value.");
1481             isValidMinMax = false;
1482             break;
1483           }
1484           aMinPnt.SetCoord (anIter + 1, aMinVal.GetDouble());
1485           aMinPnt.SetCoord (anIter + 1, aMaxVal.GetDouble());
1486         }
1487         if (isValidMinMax)
1488         {
1489           myCSTrsf.TransformPosition (aMinPnt.ChangeCoord());
1490           myCSTrsf.TransformPosition (aMaxPnt.ChangeCoord());
1491
1492           Bnd_Box aBox;
1493           aBox.Add (aMinPnt);
1494           aBox.Add (aMaxPnt);
1495
1496           theMeshData->SetBoundingBox (aBox);
1497         }
1498       }
1499     }
1500   }
1501
1502   const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
1503   if (aBufferView == NULL
1504   || !aBufferView->IsObject())
1505   {
1506     reportGltfError ("Accessor '" + theName + "' refers to non-existing bufferView.");
1507     return false;
1508   }
1509
1510   return gltfParseBufferView (theMeshData, getKeyString (*aBufferViewName), *aBufferView, aStruct, theType);
1511 }
1512
1513 // =======================================================================
1514 // function : gltfParseBufferView
1515 // purpose  :
1516 // =======================================================================
1517 bool RWGltf_GltfJsonParser::gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1518                                                  const TCollection_AsciiString& theName,
1519                                                  const RWGltf_JsonValue& theBufferView,
1520                                                  const RWGltf_GltfAccessor& theAccessor,
1521                                                  const RWGltf_GltfArrayType theType)
1522 {
1523   RWGltf_GltfBufferView aBuffView;
1524   const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
1525   const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
1526   const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
1527   const RWGltf_JsonValue* aTarget     = findObjectMember (theBufferView, "target");
1528   if (aBufferName == NULL)
1529   {
1530     reportGltfError ("BufferView '" + theName + "' does not define buffer.");
1531     return false;
1532   }
1533
1534   aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1535                        ? (int64_t )aByteOffset->GetDouble()
1536                        : 0;
1537   aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
1538                        ? (int64_t )aByteLength->GetDouble()
1539                        : 0;
1540   if (aTarget != NULL && aTarget->IsInt())
1541   {
1542     aBuffView.Target = (RWGltf_GltfBufferViewTarget )aTarget->GetInt();
1543     if (aBuffView.Target != RWGltf_GltfBufferViewTarget_ARRAY_BUFFER
1544      && aBuffView.Target != RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER)
1545     {
1546       reportGltfError ("BufferView '" + theName + "' defines invalid target.");
1547       return false;
1548     }
1549   }
1550
1551   if (aBuffView.ByteLength < 0)
1552   {
1553     reportGltfError ("BufferView '" + theName + "' defines invalid byteLength.");
1554     return false;
1555   }
1556   else if (aBuffView.ByteOffset < 0)
1557   {
1558     reportGltfError ("BufferView '" + theName + "' defines invalid byteOffset.");
1559     return false;
1560   }
1561
1562   const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
1563   if (aBuffer == NULL
1564   || !aBuffer->IsObject())
1565   {
1566     reportGltfError ("BufferView '" + theName + "' refers to non-existing buffer.");
1567     return false;
1568   }
1569
1570   return gltfParseBuffer (theMeshData, getKeyString (*aBufferName), *aBuffer, theAccessor, aBuffView, theType);
1571 }
1572
1573 // =======================================================================
1574 // function : gltfParseBuffer
1575 // purpose  :
1576 // =======================================================================
1577 bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1578                                              const TCollection_AsciiString& theName,
1579                                              const RWGltf_JsonValue& theBuffer,
1580                                              const RWGltf_GltfAccessor&     theAccessor,
1581                                              const RWGltf_GltfBufferView&   theView,
1582                                              const RWGltf_GltfArrayType     theType)
1583 {
1584   //const RWGltf_JsonValue* aType       = findObjectMember (theBuffer, "type");
1585   //const RWGltf_JsonValue* aByteLength = findObjectMember (theBuffer, "byteLength");
1586   const RWGltf_JsonValue* anUriVal      = findObjectMember (theBuffer, "uri");
1587
1588   int64_t anOffset = theView.ByteOffset + theAccessor.ByteOffset;
1589   bool isBinary = false;
1590   if (myIsBinary)
1591   {
1592     isBinary = IsEqual ("binary_glTF", theName) // glTF 1.0
1593             || anUriVal == NULL;                // glTF 2.0
1594   }
1595   if (isBinary)
1596   {
1597     anOffset += myBinBodyOffset;
1598
1599     RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1600     aData.Accessor = theAccessor;
1601     aData.StreamOffset = anOffset;
1602     aData.StreamUri = myFilePath;
1603     return true;
1604   }
1605
1606   if (anUriVal == NULL
1607   || !anUriVal->IsString())
1608   {
1609     reportGltfError ("Buffer '" + theName + "' does not define uri.");
1610     return false;
1611   }
1612
1613   const char* anUriData = anUriVal->GetString();
1614   if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
1615   {
1616     RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1617     aData.Accessor = theAccessor;
1618     aData.StreamOffset = anOffset;
1619     if (!myDecodedBuffers.Find (theName, aData.StreamData))
1620     {
1621       // it is better decoding in multiple threads
1622       aData.StreamData = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
1623       myDecodedBuffers.Bind (theName, aData.StreamData);
1624     }
1625     return true;
1626   }
1627   else
1628   {
1629     TCollection_AsciiString anUri = anUriData;
1630     if (anUri.IsEmpty())
1631     {
1632       reportGltfError ("Buffer '" + theName + "' does not define uri.");
1633       return false;
1634     }
1635
1636     TCollection_AsciiString aPath = myFolder + anUri;
1637     bool isFileExist = false;
1638     if (!myProbedFiles.Find (aPath, isFileExist))
1639     {
1640       isFileExist = OSD_File (aPath).Exists();
1641       myProbedFiles.Bind (aPath, isFileExist);
1642     }
1643     if (!isFileExist)
1644     {
1645       reportGltfError ("Buffer '" + theName + "' refers to non-existing file '" + anUri + "'.");
1646       return false;
1647     }
1648
1649     RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1650     aData.Accessor = theAccessor;
1651     aData.StreamOffset = anOffset;
1652     aData.StreamUri = myFolder + anUri;
1653     if (myExternalFiles != NULL)
1654     {
1655       myExternalFiles->Add (aData.StreamUri);
1656     }
1657     return true;
1658   }
1659 }
1660
1661 // =======================================================================
1662 // function : bindNamedShape
1663 // purpose  :
1664 // =======================================================================
1665 void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape,
1666                                             ShapeMapGroup theGroup,
1667                                             const TopLoc_Location& theLoc,
1668                                             const TCollection_AsciiString& theId,
1669                                             const RWGltf_JsonValue* theUserName)
1670 {
1671   if (theShape.IsNull())
1672   {
1673     return;
1674   }
1675
1676   if (!theLoc.IsIdentity())
1677   {
1678     theShape.Location (theLoc);
1679   }
1680
1681   TCollection_AsciiString aUserName;
1682   if (theUserName != NULL
1683    && theUserName->IsString())
1684   {
1685     aUserName = theUserName->GetString();
1686   }
1687   else if (myIsGltf1)
1688   {
1689     aUserName = theId;
1690   }
1691
1692   myShapeMap[theGroup].Bind (theId, theShape);
1693   if (myAttribMap != NULL)
1694   {
1695     RWMesh_NodeAttributes aShapeAttribs;
1696     aShapeAttribs.Name    = aUserName;
1697     aShapeAttribs.RawName = theId;
1698     if (theShape.ShapeType() == TopAbs_FACE)
1699     {
1700       TopLoc_Location aDummy;
1701       if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (theShape), aDummy)))
1702       {
1703         if (aLateData->HasStyle())
1704         {
1705           // assign material and not color
1706           //aShapeAttribs.Style.SetColorSurf (aLateData->BaseColor());
1707
1708           Handle(XCAFDoc_VisMaterial) aMat;
1709           myMaterials.Find (!aLateData->MaterialPbr().IsNull() ? aLateData->MaterialPbr()->Id : aLateData->MaterialCommon()->Id, aMat);
1710           aShapeAttribs.Style.SetMaterial (aMat);
1711         }
1712         if (aShapeAttribs.Name.IsEmpty()
1713          && myUseMeshNameAsFallback)
1714         {
1715           // fallback using Mesh name
1716           aShapeAttribs.Name = aLateData->Name();
1717         }
1718       }
1719     }
1720     else if (aShapeAttribs.Name.IsEmpty()
1721           && myUseMeshNameAsFallback)
1722     {
1723       // fallback using Mesh name
1724       TopLoc_Location aDummy;
1725       TCollection_AsciiString aMeshName;
1726       for (TopExp_Explorer aFaceIter (theShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
1727       {
1728         if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (aFaceIter.Value()), aDummy)))
1729         {
1730           if (aLateData->Name().IsEmpty())
1731           {
1732             aMeshName.Clear();
1733             break;
1734           }
1735           else if (aMeshName.IsEmpty())
1736           {
1737             aMeshName = aLateData->Name();
1738           }
1739           else if (!aMeshName.IsEqual (aLateData->Name()))
1740           {
1741             aMeshName.Clear();
1742             break;
1743           }
1744         }
1745       }
1746       if (!aMeshName.IsEmpty())
1747       {
1748         aShapeAttribs.Name = aMeshName;
1749       }
1750     }
1751     myAttribMap->Bind (theShape, aShapeAttribs);
1752   }
1753 }
1754 #endif
1755
1756 // =======================================================================
1757 // function : Parse
1758 // purpose  :
1759 // =======================================================================
1760 bool RWGltf_GltfJsonParser::Parse (const Handle(Message_ProgressIndicator)& theProgress)
1761 {
1762   Message_ProgressSentry aPSentry (theProgress, "Reading Gltf", 0, 2, 1);
1763 #ifdef HAVE_RAPIDJSON
1764   {
1765     if (!gltfParseRoots())
1766     {
1767       return false;
1768     }
1769
1770     gltfParseAsset();
1771     gltfParseMaterials();
1772     if (!gltfParseScene (theProgress))
1773     {
1774       return false;
1775     }
1776   }
1777   aPSentry.Next();
1778   if (!aPSentry.More())
1779   {
1780     return false;
1781   }
1782   return true;
1783 #else
1784   Message::DefaultMessenger()->Send ("Error: glTF reader is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined].", Message_Fail);
1785   return false;
1786 #endif
1787 }