0030691: Data Exchange - implement import of mesh data from files in glTF 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   myToProbeHeader (false)
175 {
176   myCSTrsf.SetInputLengthUnit (1.0); // meters
177   myCSTrsf.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
178 }
179
180 // =======================================================================
181 // function : SetFilePath
182 // purpose  :
183 // =======================================================================
184 void RWGltf_GltfJsonParser::SetFilePath (const TCollection_AsciiString& theFilePath)
185 {
186   myFilePath = theFilePath;
187   // determine file location to load associated files
188   TCollection_AsciiString aFileName;
189   OSD_Path::FolderAndFileFromPath (theFilePath, myFolder, aFileName);
190 }
191
192 #ifdef HAVE_RAPIDJSON
193 // =======================================================================
194 // function : gltfParseRoots
195 // purpose  :
196 // =======================================================================
197 bool RWGltf_GltfJsonParser::gltfParseRoots()
198 {
199   // find glTF root elements for smooth navigation
200   RWGltf_JsonValue aNames[RWGltf_GltfRootElement_NB];
201   for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
202   {
203     aNames[aRootNameIter] = rapidjson::StringRef (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter));
204   }
205
206   for (ConstMemberIterator aRootIter = MemberBegin();
207        aRootIter != MemberEnd(); ++aRootIter)
208   {
209     for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
210     {
211       if (myGltfRoots[aRootNameIter].IsNull()
212             && aNames[aRootNameIter] == aRootIter->name)
213       {
214         // we will not modify JSON document, thus it is OK to keep the pointers
215         myGltfRoots[aRootNameIter].Init (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter), &aRootIter->value);
216         break;
217       }
218     }
219   }
220
221   for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB_MANDATORY; ++aRootNameIter)
222   {
223     if (myGltfRoots[aRootNameIter].IsNull())
224     {
225       reportGltfError ("Member '" + RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter) + "' is not found.");
226       return false;
227     }
228   }
229   return true;
230 }
231
232 // =======================================================================
233 // function : gltfParseAsset
234 // purpose  :
235 // =======================================================================
236 void RWGltf_GltfJsonParser::gltfParseAsset()
237 {
238   const RWGltf_JsonValue* anAsset = myGltfRoots[RWGltf_GltfRootElement_Asset].Root();
239   if (anAsset == NULL)
240   {
241     return;
242   }
243
244   if (const RWGltf_JsonValue* aVersion = findObjectMember (*anAsset, "version"))
245   {
246     if (aVersion->IsString())
247     {
248       TCollection_AsciiString aVerStr (aVersion->GetString());
249       myIsGltf1 = aVerStr.StartsWith ("1.");
250     }
251   }
252
253   if (const RWGltf_JsonValue* aGenerator = findObjectMember (*anAsset, "generator"))
254   {
255     if (aGenerator->IsString())
256     {
257       myMetadata.Add ("generator", aGenerator->GetString());
258     }
259   }
260   if (const RWGltf_JsonValue* aCopyRight = findObjectMember (*anAsset, "copyright"))
261   {
262     if (aCopyRight->IsString())
263     {
264       myMetadata.Add ("copyright", aCopyRight->GetString());
265     }
266   }
267 }
268
269 // =======================================================================
270 // function : gltfParseMaterials
271 // purpose  :
272 // =======================================================================
273 void RWGltf_GltfJsonParser::gltfParseMaterials()
274 {
275   const RWGltf_JsonValue* aMatList = myGltfRoots[RWGltf_GltfRootElement_Materials].Root();
276   if (aMatList == NULL)
277   {
278     return;
279   }
280   else if (aMatList->IsObject())
281   {
282     // glTF 1.0
283     for (ConstMemberIterator aMatIter = aMatList->MemberBegin();
284          aMatIter != aMatList->MemberEnd(); ++aMatIter)
285     {
286       Handle(RWGltf_MaterialCommon) aMat;
287       const RWGltf_JsonValue& aMatNode = aMatIter->value;
288       const RWGltf_JsonValue& aMatId   = aMatIter->name;
289       const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
290       if (!gltfParseCommonMaterial (aMat, aMatNode))
291       {
292         if (!gltfParseStdMaterial (aMat, aMatNode))
293         {
294           continue;
295         }
296       }
297
298       if (aNameVal != NULL
299        && aNameVal->IsString())
300       {
301         aMat->Name = aNameVal->GetString();
302       }
303       aMat->Id = aMatId.GetString();
304       myMaterialsCommon.Bind (aMat->Id, aMat);
305     }
306   }
307   else if (aMatList->IsArray())
308   {
309     // glTF 2.0
310     int aMatIndex = 0;
311     for (rapidjson::Value::ConstValueIterator aMatIter = aMatList->Begin(); aMatIter != aMatList->End(); ++aMatIter, ++aMatIndex)
312     {
313       Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
314       const RWGltf_JsonValue& aMatNode = *aMatIter;
315       const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
316       if (gltfParsePbrMaterial (aMatPbr,  aMatNode))
317       {
318         if (aNameVal != NULL
319          && aNameVal->IsString())
320         {
321           aMatPbr->Name = aNameVal->GetString();
322         }
323         aMatPbr->Id = TCollection_AsciiString ("mat_") + aMatIndex;
324         myMaterialsPbr.Bind (TCollection_AsciiString (aMatIndex), aMatPbr);
325       }
326
327       Handle(RWGltf_MaterialCommon) aMatCommon;
328       if (gltfParseCommonMaterial(aMatCommon, aMatNode)
329        || gltfParseStdMaterial   (aMatCommon, aMatNode))
330       {
331         if (aNameVal != NULL
332          && aNameVal->IsString())
333         {
334           aMatCommon->Name = aNameVal->GetString();
335         }
336         aMatCommon->Id = TCollection_AsciiString ("mat_") + aMatIndex;
337         myMaterialsCommon.Bind (TCollection_AsciiString (aMatIndex), aMatCommon);
338       }
339     }
340   }
341 }
342
343 // =======================================================================
344 // function : gltfParseStdMaterial
345 // purpose  :
346 // =======================================================================
347 bool RWGltf_GltfJsonParser::gltfParseStdMaterial (Handle(RWGltf_MaterialCommon)& theMat,
348                                                   const RWGltf_JsonValue& theMatNode)
349 {
350   //const RWGltf_JsonValue* aTechVal = findObjectMember (theMatNode, "technique");
351   const RWGltf_JsonValue* aValues  = findObjectMember (theMatNode, "values");
352   if (aValues == NULL)
353   {
354     return false;
355   }
356
357   const RWGltf_JsonValue* anAmbVal = findObjectMember (*aValues, "ambient");
358   const RWGltf_JsonValue* aDiffVal = findObjectMember (*aValues, "diffuse");
359   const RWGltf_JsonValue* anEmiVal = findObjectMember (*aValues, "emission");
360   const RWGltf_JsonValue* aSpecVal = findObjectMember (*aValues, "specular");
361   const RWGltf_JsonValue* aShinVal = findObjectMember (*aValues, "shininess");
362   if (anAmbVal == NULL
363    && aDiffVal == NULL
364    && anEmiVal == NULL
365    && aSpecVal == NULL
366    && aShinVal == NULL)
367   {
368     return false;
369   }
370
371   theMat = new RWGltf_MaterialCommon();
372
373   Graphic3d_Vec4d anAmb, aDiff, anEmi, aSpec;
374   if (anAmbVal != NULL
375    && anAmbVal->IsString())
376   {
377     gltfParseTexture (theMat->AmbientTexture, anAmbVal);
378   }
379   else if (gltfReadVec4   (anAmb, anAmbVal)
380         && validateColor4 (anAmb))
381   {
382     theMat->AmbientColor = Quantity_Color (anAmb.r(), anAmb.g(), anAmb.b(), Quantity_TOC_RGB);
383   }
384
385   if (aDiffVal != NULL
386    && aDiffVal->IsString())
387   {
388     gltfParseTexture (theMat->DiffuseTexture, aDiffVal);
389   }
390   else if (gltfReadVec4   (aDiff, aDiffVal)
391         && validateColor4 (aDiff))
392   {
393     theMat->DiffuseColor = Quantity_Color (aDiff.r(), aDiff.g(), aDiff.b(), Quantity_TOC_RGB);
394     theMat->Transparency = float(1.0 - aDiff.a());
395   }
396
397   if (gltfReadVec4   (anEmi, anEmiVal)
398    && validateColor4 (anEmi))
399   {
400     theMat->EmissiveColor = Quantity_Color (anEmi.r(), anEmi.g(), anEmi.b(), Quantity_TOC_RGB);
401   }
402
403   if (aSpecVal != NULL
404    && aSpecVal->IsString())
405   {
406     gltfParseTexture (theMat->SpecularTexture, aSpecVal);
407   }
408   if (gltfReadVec4   (aSpec, aSpecVal)
409    && validateColor4 (aSpec))
410   {
411     theMat->SpecularColor = Quantity_Color (aSpec.r(), aSpec.g(), aSpec.b(), Quantity_TOC_RGB);
412   }
413
414   if (aShinVal != NULL
415    && aShinVal->IsNumber())
416   {
417     const double aSpecular = aShinVal->GetDouble();
418     if (aSpecular >= 0)
419     {
420       theMat->Shininess = (float )Min (aSpecular / 1000.0, 1.0);
421     }
422   }
423   return true;
424 }
425
426 // =======================================================================
427 // function : gltfParsePbrMaterial
428 // purpose  :
429 // =======================================================================
430 bool RWGltf_GltfJsonParser::gltfParsePbrMaterial (Handle(RWGltf_MaterialMetallicRoughness)& theMat,
431                                                   const RWGltf_JsonValue& theMatNode)
432 {
433   /*if (const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions"))
434   {
435     if (const RWGltf_JsonValue* anExtDefVal = findObjectMember (*anExtVal, "KHR_materials_pbrSpecularGlossiness"))
436     {
437       const RWGltf_JsonValue* aDiffTexVal = findObjectMember (*anExtDefVal, "diffuseTexture");
438       const RWGltf_JsonValue* aSpecTexVal = findObjectMember (*anExtDefVal, "specularGlossinessTexture");
439     }
440   }*/
441
442   const RWGltf_JsonValue* aMetalRoughVal    = findObjectMember (theMatNode, "pbrMetallicRoughness");
443   const RWGltf_JsonValue* aNormTexVal       = findObjectMember (theMatNode, "normalTexture");
444   const RWGltf_JsonValue* anEmissFactorVal  = findObjectMember (theMatNode, "emissiveFactor");
445   const RWGltf_JsonValue* anEmissTexVal     = findObjectMember (theMatNode, "emissiveTexture");
446   const RWGltf_JsonValue* anOcclusionTexVal = findObjectMember (theMatNode, "occlusionTexture");
447   if (aMetalRoughVal == NULL)
448   {
449     return false;
450   }
451
452   theMat = new RWGltf_MaterialMetallicRoughness();
453   const RWGltf_JsonValue* aBaseColorFactorVal = findObjectMember (*aMetalRoughVal, "baseColorFactor");
454   const RWGltf_JsonValue* aBaseColorTexVal    = findObjectMember (*aMetalRoughVal, "baseColorTexture");
455   const RWGltf_JsonValue* aMetallicFactorVal  = findObjectMember (*aMetalRoughVal, "metallicFactor");
456   const RWGltf_JsonValue* aRoughnessFactorVal = findObjectMember (*aMetalRoughVal, "roughnessFactor");
457   const RWGltf_JsonValue* aMetalRoughTexVal   = findObjectMember (*aMetalRoughVal, "metallicRoughnessTexture");
458
459   if (aBaseColorTexVal != NULL
460    && aBaseColorTexVal->IsObject())
461   {
462     if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aBaseColorTexVal, "index"))
463     {
464       gltfParseTexture (theMat->BaseColorTexture, aTexIndexVal);
465     }
466   }
467
468   Graphic3d_Vec4d aBaseColorFactor;
469   if (gltfReadVec4   (aBaseColorFactor, aBaseColorFactorVal)
470    && validateColor4 (aBaseColorFactor))
471   {
472     theMat->BaseColor = Quantity_ColorRGBA (Graphic3d_Vec4 (aBaseColorFactor));
473   }
474
475   Graphic3d_Vec3d anEmissiveFactor;
476   if (gltfReadVec3   (anEmissiveFactor, anEmissFactorVal)
477    && validateColor3 (anEmissiveFactor))
478   {
479     theMat->EmissiveFactor = Graphic3d_Vec3 (anEmissiveFactor);
480   }
481
482   if (aMetalRoughTexVal != NULL
483    && aMetalRoughTexVal->IsObject())
484   {
485     if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aMetalRoughTexVal, "index"))
486     {
487       gltfParseTexture (theMat->MetallicRoughnessTexture, aTexIndexVal);
488     }
489   }
490
491   if (aMetallicFactorVal != NULL
492    && aMetallicFactorVal->IsNumber())
493   {
494     theMat->Metallic = (float )aMetallicFactorVal->GetDouble();
495   }
496
497   if (aRoughnessFactorVal != NULL
498    && aRoughnessFactorVal->IsNumber())
499   {
500     theMat->Roughness = (float )aRoughnessFactorVal->GetDouble();
501   }
502
503   if (aNormTexVal != NULL
504    && aNormTexVal->IsObject())
505   {
506     if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aNormTexVal, "index"))
507     {
508       gltfParseTexture (theMat->NormalTexture, aTexIndexVal);
509     }
510   }
511
512   if (anEmissTexVal != NULL
513    && anEmissTexVal->IsObject())
514   {
515     if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anEmissTexVal, "index"))
516     {
517       gltfParseTexture (theMat->EmissiveTexture, aTexIndexVal);
518     }
519   }
520
521   if (anOcclusionTexVal != NULL
522    && anOcclusionTexVal->IsObject())
523   {
524     if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anOcclusionTexVal, "index"))
525     {
526       gltfParseTexture (theMat->OcclusionTexture, aTexIndexVal);
527     }
528   }
529   return true;
530 }
531
532 // =======================================================================
533 // function : gltfParseCommonMaterial
534 // purpose  :
535 // =======================================================================
536 bool RWGltf_GltfJsonParser::gltfParseCommonMaterial (Handle(RWGltf_MaterialCommon)& theMat,
537                                                      const RWGltf_JsonValue& theMatNode)
538 {
539   const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions");
540   if (anExtVal == NULL)
541   {
542     return false;
543   }
544
545   const RWGltf_JsonValue* aMatCommon = findObjectMember (*anExtVal, THE_KHR_materials_common);
546   if (aMatCommon == NULL)
547   {
548     return false;
549   }
550
551   if (!gltfParseStdMaterial (theMat, *aMatCommon))
552   {
553     return false;
554   }
555   return true;
556 }
557
558 // =======================================================================
559 // function : gltfParseTexture
560 // purpose  :
561 // =======================================================================
562 bool RWGltf_GltfJsonParser::gltfParseTexture (Handle(Image_Texture)& theTexture,
563                                               const RWGltf_JsonValue* theTextureId)
564 {
565   if (theTextureId == NULL
566   ||  myGltfRoots[RWGltf_GltfRootElement_Textures].IsNull()
567   ||  myGltfRoots[RWGltf_GltfRootElement_Images].IsNull())
568   {
569     return false;
570   }
571
572   const TCollection_AsciiString aTextureId = getKeyString (*theTextureId);
573   const RWGltf_JsonValue* aTexNode = myGltfRoots[RWGltf_GltfRootElement_Textures].FindChild (*theTextureId);
574   if (aTexNode == NULL)
575   {
576     reportGltfWarning ("Texture node '" + aTextureId + "' is not found.");
577     return false;
578   }
579
580   const RWGltf_JsonValue* aSrcVal  = findObjectMember (*aTexNode, "source");
581   const RWGltf_JsonValue* aTargVal = findObjectMember (*aTexNode, "target");
582   if (aSrcVal == NULL)
583   {
584     reportGltfWarning ("Invalid texture node '" + aTextureId + "' without a 'source' property.");
585     return false;
586   }
587   if (aTargVal != NULL
588    && aTargVal->IsNumber()
589    && aTargVal->GetInt() != 3553) // GL_TEXTURE_2D
590   {
591     return false;
592   }
593
594   const RWGltf_JsonValue* anImgNode = myGltfRoots[RWGltf_GltfRootElement_Images].FindChild (*aSrcVal);
595   if (anImgNode == NULL)
596   {
597     reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to non-existing image '" + getKeyString (*aSrcVal) + "'.");
598     return false;
599   }
600
601   if (myIsBinary)
602   {
603     const RWGltf_JsonValue* aBinVal = NULL;
604     const RWGltf_JsonValue* aBufferViewName = findObjectMember (*anImgNode, "bufferView");
605     if (aBufferViewName != NULL)
606     {
607       aBinVal = anImgNode;
608     }
609     else if (myIsGltf1)
610     {
611       const RWGltf_JsonValue* anExtVal = findObjectMember (*anImgNode, "extensions");
612       if (anExtVal != NULL)
613       {
614         aBinVal = findObjectMember (*anExtVal, THE_KHR_binary_glTF);
615         if (aBinVal != NULL)
616         {
617           aBufferViewName = findObjectMember (*aBinVal, "bufferView");
618         }
619       }
620     }
621
622     if (aBinVal != NULL)
623     {
624       //const RWGltf_JsonValue* aMimeTypeVal = findObjectMember (*aBinVal, "mimeType");
625       //const RWGltf_JsonValue* aWidthVal    = findObjectMember (*aBinVal, "width");
626       //const RWGltf_JsonValue* aHeightVal   = findObjectMember (*aBinVal, "height");
627       if (aBufferViewName == NULL)
628       {
629         reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
630         return false;
631       }
632
633       const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
634       if (aBufferView == NULL
635       || !aBufferView->IsObject())
636       {
637         reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid buffer view '" + getKeyString (*aBufferViewName) + "'.");
638         return false;
639       }
640
641       const RWGltf_JsonValue* aBufferName = findObjectMember (*aBufferView, "buffer");
642       const RWGltf_JsonValue* aByteLength = findObjectMember (*aBufferView, "byteLength");
643       const RWGltf_JsonValue* aByteOffset = findObjectMember (*aBufferView, "byteOffset");
644       if (aBufferName != NULL
645       &&  aBufferName->IsString()
646       && !IsEqual (aBufferName->GetString(), "binary_glTF"))
647       {
648         reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' does not define binary_glTF buffer.");
649         return false;
650       }
651
652       RWGltf_GltfBufferView aBuffView;
653       aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
654                            ? (int64_t )aByteOffset->GetDouble()
655                            : 0;
656       aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
657                            ? (int64_t )aByteLength->GetDouble()
658                            : 0;
659       if (aBuffView.ByteLength < 0)
660       {
661         reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' defines invalid byteLength.");
662         return false;
663       }
664       else if (aBuffView.ByteOffset < 0)
665       {
666         reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' defines invalid byteOffset.");
667         return false;
668       }
669
670
671       const int64_t anOffset = myBinBodyOffset + aBuffView.ByteOffset;
672       theTexture = new Image_Texture (myFilePath, anOffset, aBuffView.ByteLength);
673       return true;
674     }
675   }
676
677   const RWGltf_JsonValue* anUriVal = findObjectMember (*anImgNode, "uri");
678   if (anUriVal == NULL
679   || !anUriVal->IsString())
680   {
681     return false;
682   }
683
684   const char* anUriData = anUriVal->GetString();
685   if (::strncmp (anUriData, "data:", 5) == 0) // data:image/png;base64
686   {
687     // uncompressing base64 here is inefficient, because the same image can be shared by several nodes
688     const char* aDataStart = anUriData + 5;
689     for (const char* aDataIter = aDataStart; *aDataIter != '\0'; ++aDataIter)
690     {
691       if (::memcmp (aDataIter, ";base64,", 8) == 0)
692       {
693         const char* aBase64End  = anUriData + anUriVal->GetStringLength();
694         const char* aBase64Data = aDataIter + 8;
695         const size_t aBase64Len = size_t(aBase64End - aBase64Data);
696         //const TCollection_AsciiString aMime (aDataStart, aDataIter - aDataStart);
697         Handle(NCollection_Buffer) aData = FSD_Base64Decoder::Decode ((const Standard_Byte* )aBase64Data, aBase64Len);
698         theTexture = new Image_Texture (aData, myFilePath + "@" + getKeyString (*aSrcVal));
699         return true;
700       }
701     }
702     Message::DefaultMessenger()->Send ("glTF reader - embedded image has been skipped", Message_Warning);
703     return false;
704   }
705
706   TCollection_AsciiString anImageFile = myFolder + anUriVal->GetString();
707   theTexture = new Image_Texture (anImageFile);
708   if (myExternalFiles != NULL)
709   {
710     myExternalFiles->Add (anImageFile);
711   }
712   return true;
713 }
714
715 // =======================================================================
716 // function : gltfParseScene
717 // purpose  :
718 // =======================================================================
719 bool RWGltf_GltfJsonParser::gltfParseScene (const Handle(Message_ProgressIndicator)& theProgress)
720 {
721   // search default scene
722   const RWGltf_JsonValue* aDefScene = myGltfRoots[RWGltf_GltfRootElement_Scenes].FindChild (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root());
723   if (aDefScene == NULL)
724   {
725     reportGltfError ("Default scene is not found.");
726     return false;
727   }
728
729   const RWGltf_JsonValue* aSceneNodes = findObjectMember (*aDefScene, "nodes");
730   if (aSceneNodes == NULL
731   || !aSceneNodes->IsArray())
732   {
733     reportGltfError ("Empty scene '" + getKeyString (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root()) + "'.");
734     return false;
735   }
736
737   return gltfParseSceneNodes (*myRootShapes, *aSceneNodes, theProgress);
738 }
739
740 // =======================================================================
741 // function : gltfParseSceneNodes
742 // purpose  :
743 // =======================================================================
744 bool RWGltf_GltfJsonParser::gltfParseSceneNodes (TopTools_SequenceOfShape& theShapeSeq,
745                                                  const RWGltf_JsonValue& theSceneNodes,
746                                                  const Handle(Message_ProgressIndicator)& theProgress)
747 {
748   if (!theSceneNodes.IsArray())
749   {
750     reportGltfError ("Scene nodes is not array.");
751     return false;
752   }
753
754   Message_ProgressSentry aPSentry (theProgress, "Reading scene nodes", 0, theSceneNodes.Size(), 1);
755   for (rapidjson::Value::ConstValueIterator aSceneNodeIter = theSceneNodes.Begin();
756        aSceneNodeIter != theSceneNodes.End() && aPSentry.More(); ++aSceneNodeIter, aPSentry.Next())
757   {
758     const RWGltf_JsonValue* aSceneNode = myGltfRoots[RWGltf_GltfRootElement_Nodes].FindChild (*aSceneNodeIter);
759     if (aSceneNode == NULL)
760     {
761       reportGltfWarning ("Scene refers to non-existing node '" + getKeyString (*aSceneNodeIter) + "'.");
762       return true;
763     }
764
765     TopoDS_Shape aNodeShape;
766     if (!gltfParseSceneNode (aNodeShape, getKeyString (*aSceneNodeIter), *aSceneNode, theProgress))
767     {
768       return false;
769     }
770
771     if (aNodeShape.IsNull())
772     {
773       continue;
774     }
775     else if (myToSkipEmptyNodes
776          && !TopExp_Explorer (aNodeShape, TopAbs_FACE).More())
777     {
778       continue;
779     }
780
781     theShapeSeq.Append (aNodeShape);
782   }
783   return true;
784 }
785
786 // =======================================================================
787 // function : gltfParseSceneNode
788 // purpose  :
789 // =======================================================================
790 bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
791                                                 const TCollection_AsciiString& theSceneNodeId,
792                                                 const RWGltf_JsonValue& theSceneNode,
793                                                 const Handle(Message_ProgressIndicator)& theProgress)
794 {
795   const RWGltf_JsonValue* aName         = findObjectMember (theSceneNode, "name");
796   //const RWGltf_JsonValue* aJointName    = findObjectMember (theSceneNode, "jointName");
797   const RWGltf_JsonValue* aChildren     = findObjectMember (theSceneNode, "children");
798   const RWGltf_JsonValue* aMeshes_1     = findObjectMember (theSceneNode, "meshes");
799   const RWGltf_JsonValue* aMesh_2       = findObjectMember (theSceneNode, "mesh");
800   //const RWGltf_JsonValue* aCamera       = findObjectMember (theSceneNode, "camera");
801   const RWGltf_JsonValue* aTrsfMatVal   = findObjectMember (theSceneNode, "matrix");
802   const RWGltf_JsonValue* aTrsfRotVal   = findObjectMember (theSceneNode, "rotation");
803   const RWGltf_JsonValue* aTrsfScaleVal = findObjectMember (theSceneNode, "scale");
804   const RWGltf_JsonValue* aTrsfTransVal = findObjectMember (theSceneNode, "translation");
805   if (findNodeShape (theNodeShape, theSceneNodeId))
806   {
807     return true;
808   }
809
810   TopLoc_Location aNodeLoc;
811   const bool hasTrs = aTrsfRotVal   != NULL
812                    || aTrsfScaleVal != NULL
813                    || aTrsfTransVal != NULL;
814   if (aTrsfMatVal != NULL)
815   {
816     if (hasTrs)
817     {
818       reportGltfError ("Scene node '" + theSceneNodeId + "' defines ambiguous transformation.");
819       return false;
820     }
821     else if (!aTrsfMatVal->IsArray()
822            || aTrsfMatVal->Size() != 16)
823     {
824       reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix array.");
825       return false;
826     }
827
828     Graphic3d_Mat4d aMat4;
829     for (int aColIter = 0; aColIter < 4; ++aColIter)
830     {
831       for (int aRowIter = 0; aRowIter < 4; ++aRowIter)
832       {
833         const RWGltf_JsonValue& aGenVal = (*aTrsfMatVal)[aColIter * 4 + aRowIter];
834         if (!aGenVal.IsNumber())
835         {
836           reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix.");
837           return false;
838         }
839         aMat4.SetValue (aRowIter, aColIter, aGenVal.GetDouble());
840       }
841     }
842
843     if (!aMat4.IsIdentity())
844     {
845       gp_Trsf aTrsf;
846       aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
847                        aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
848                        aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
849       myCSTrsf.TransformTransformation (aTrsf);
850       if (aTrsf.Form() != gp_Identity)
851       {
852         aNodeLoc = TopLoc_Location (aTrsf);
853       }
854     }
855   }
856   else if (hasTrs)
857   {
858     gp_Trsf aTrsf;
859     if (aTrsfRotVal != NULL)
860     {
861       if (!aTrsfRotVal->IsArray()
862         || aTrsfRotVal->Size() != 4)
863       {
864         reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation quaternion.");
865         return false;
866       }
867
868       Graphic3d_Vec4d aRotVec4;
869       for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
870       {
871         const RWGltf_JsonValue& aGenVal = (*aTrsfRotVal)[aCompIter];
872         if (!aGenVal.IsNumber())
873         {
874           reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation.");
875           return false;
876         }
877         aRotVec4[aCompIter] = aGenVal.GetDouble();
878       }
879       const gp_Quaternion aQuaternion (aRotVec4.x(), aRotVec4.y(), aRotVec4.z(), aRotVec4.w());
880       if (Abs (aQuaternion.X())       > gp::Resolution()
881        || Abs (aQuaternion.Y())       > gp::Resolution()
882        || Abs (aQuaternion.Z())       > gp::Resolution()
883        || Abs (aQuaternion.W() - 1.0) > gp::Resolution())
884       {
885         aTrsf.SetRotation (aQuaternion);
886       }
887     }
888
889     if (aTrsfTransVal != NULL)
890     {
891       if (!aTrsfTransVal->IsArray()
892         || aTrsfTransVal->Size() != 3)
893       {
894         reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation vector.");
895         return false;
896       }
897
898       gp_XYZ aTransVec;
899       for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
900       {
901         const RWGltf_JsonValue& aGenVal = (*aTrsfTransVal)[aCompIter];
902         if (!aGenVal.IsNumber())
903         {
904           reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation.");
905           return false;
906         }
907         aTransVec.SetCoord (aCompIter + 1, aGenVal.GetDouble());
908       }
909       aTrsf.SetTranslationPart (aTransVec);
910     }
911
912     if (aTrsfScaleVal != NULL)
913     {
914       Graphic3d_Vec3d aScaleVec;
915       if (!aTrsfScaleVal->IsArray()
916         || aTrsfScaleVal->Size() != 3)
917       {
918         reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale vector.");
919         return false;
920       }
921       for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
922       {
923         const RWGltf_JsonValue& aGenVal = (*aTrsfScaleVal)[aCompIter];
924         if (!aGenVal.IsNumber())
925         {
926           reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
927           return false;
928         }
929         aScaleVec[aCompIter] = aGenVal.GetDouble();
930         if (Abs (aScaleVec[aCompIter]) <= gp::Resolution())
931         {
932           reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
933           return false;
934         }
935       }
936
937       if (Abs (aScaleVec.x() - aScaleVec.y()) > Precision::Confusion()
938        || Abs (aScaleVec.y() - aScaleVec.z()) > Precision::Confusion()
939        || Abs (aScaleVec.x() - aScaleVec.z()) > Precision::Confusion())
940       {
941         Graphic3d_Mat4d aScaleMat;
942         aScaleMat.SetDiagonal (aScaleVec);
943
944         Graphic3d_Mat4d aMat4;
945         aTrsf.GetMat4 (aMat4);
946
947         aMat4 = aMat4 * aScaleMat;
948         aTrsf = gp_Trsf();
949         aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
950                          aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
951                          aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
952
953         Message::DefaultMessenger()->Send (TCollection_AsciiString ("glTF reader, scene node '")
954                                          + theSceneNodeId + "' defines unsupported scaling "
955                                          + aScaleVec.x() + " " + aScaleVec.y() + " " + aScaleVec.z(), Message_Warning);
956       }
957       else if (Abs (aScaleVec.x() - 1.0) > Precision::Confusion())
958       {
959         aTrsf.SetScaleFactor (aScaleVec.x());
960       }
961     }
962
963     myCSTrsf.TransformTransformation (aTrsf);
964     if (aTrsf.Form() != gp_Identity)
965     {
966       aNodeLoc = TopLoc_Location (aTrsf);
967     }
968   }
969
970   BRep_Builder aBuilder;
971   TopoDS_Compound aNodeShape;
972   aBuilder.MakeCompound (aNodeShape);
973   TopTools_SequenceOfShape aChildShapes;
974   int aNbSubShapes = 0;
975   if (aChildren != NULL
976   && !gltfParseSceneNodes (aChildShapes, *aChildren, theProgress))
977   {
978     theNodeShape = aNodeShape;
979     bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
980     return false;
981   }
982   for (TopTools_SequenceOfShape::Iterator aChildShapeIter (aChildShapes); aChildShapeIter.More(); aChildShapeIter.Next())
983   {
984     aBuilder.Add (aNodeShape, aChildShapeIter.Value());
985     ++aNbSubShapes;
986   }
987
988   if (aMeshes_1 != NULL
989    && aMeshes_1->IsArray())
990   {
991     // glTF 1.0
992     Message_ProgressSentry aPSentry (theProgress, "Reading scene meshes", 0, aMeshes_1->Size(), 1);
993     for (rapidjson::Value::ConstValueIterator aMeshIter = aMeshes_1->Begin();
994          aMeshIter != aMeshes_1->End() && aPSentry.More(); ++aMeshIter, aPSentry.Next())
995     {
996       const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMeshIter);
997       if (aMesh == NULL)
998       {
999         theNodeShape = aNodeShape;
1000         bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1001         reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1002         return false;
1003       }
1004
1005       TopoDS_Shape aMeshShape;
1006       if (!gltfParseMesh (aMeshShape, getKeyString (*aMeshIter), *aMesh, theProgress))
1007       {
1008         theNodeShape = aNodeShape;
1009         bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1010         return false;
1011       }
1012       if (!aMeshShape.IsNull())
1013       {
1014         aBuilder.Add (aNodeShape, aMeshShape);
1015         ++aNbSubShapes;
1016       }
1017     }
1018   }
1019   if (aMesh_2 != NULL)
1020   {
1021     // glTF 2.0
1022     const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMesh_2);
1023     if (aMesh == NULL)
1024     {
1025       theNodeShape = aNodeShape;
1026       bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1027       reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1028       return false;
1029     }
1030
1031     TopoDS_Shape aMeshShape;
1032     if (!gltfParseMesh (aMeshShape, getKeyString (*aMesh_2), *aMesh, theProgress))
1033     {
1034       theNodeShape = aNodeShape;
1035       bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1036       return false;
1037     }
1038     if (!aMeshShape.IsNull())
1039     {
1040       aBuilder.Add (aNodeShape, aMeshShape);
1041       ++aNbSubShapes;
1042     }
1043   }
1044
1045   if (aNbSubShapes == 1)
1046   {
1047     theNodeShape = TopoDS_Iterator (aNodeShape).Value();
1048   }
1049   else
1050   {
1051     theNodeShape = aNodeShape;
1052   }
1053   bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1054   return true;
1055 }
1056
1057 // =======================================================================
1058 // function : gltfParseMesh
1059 // purpose  :
1060 // =======================================================================
1061 bool RWGltf_GltfJsonParser::gltfParseMesh (TopoDS_Shape& theMeshShape,
1062                                            const TCollection_AsciiString& theMeshId,
1063                                            const RWGltf_JsonValue& theMesh,
1064                                            const Handle(Message_ProgressIndicator)& theProgress)
1065 {
1066   const RWGltf_JsonValue* aName  = findObjectMember (theMesh, "name");
1067   const RWGltf_JsonValue* aPrims = findObjectMember (theMesh, "primitives");
1068   if (!aPrims->IsArray())
1069   {
1070     reportGltfError ("Primitive array attributes within Mesh '" + theMeshId + "' is not an array.");
1071     return false;
1072   }
1073
1074   if (findMeshShape (theMeshShape, theMeshId))
1075   {
1076     return true;
1077   }
1078
1079   BRep_Builder aBuilder;
1080   TopoDS_Compound aMeshShape;
1081   int aNbFaces = 0;
1082   for (rapidjson::Value::ConstValueIterator aPrimArrIter = aPrims->Begin();
1083        aPrimArrIter != aPrims->End(); ++aPrimArrIter)
1084   {
1085     TCollection_AsciiString aUserName;
1086     if (aName != NULL
1087      && aName->IsString())
1088     {
1089       aUserName = aName->GetString();
1090     }
1091
1092     Handle(RWGltf_GltfLatePrimitiveArray) aMeshData = new RWGltf_GltfLatePrimitiveArray (theMeshId, aUserName);
1093     if (!gltfParsePrimArray (aMeshData, theMeshId, *aPrimArrIter, theProgress))
1094     {
1095       return false;
1096     }
1097
1098     if (!aMeshData->Data().IsEmpty())
1099     {
1100       if (aMeshShape.IsNull())
1101       {
1102         aBuilder.MakeCompound (aMeshShape);
1103       }
1104
1105       TopoDS_Face aFace;
1106       aBuilder.MakeFace (aFace, aMeshData);
1107       aBuilder.Add (aMeshShape, aFace);
1108       if (myAttribMap != NULL
1109        && aMeshData->HasStyle())
1110       {
1111         RWMesh_NodeAttributes aShapeAttribs;
1112         aShapeAttribs.RawName = aUserName;
1113         aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor());
1114         myAttribMap->Bind (aFace, aShapeAttribs);
1115       }
1116       myFaceList.Append (aFace);
1117       ++aNbFaces;
1118     }
1119   }
1120
1121   if (aNbFaces == 1)
1122   {
1123     theMeshShape = TopoDS_Iterator (aMeshShape).Value();
1124   }
1125   else
1126   {
1127     theMeshShape = aMeshShape;
1128   }
1129   bindMeshShape (theMeshShape, theMeshId, aName);
1130   return true;
1131 }
1132
1133 // =======================================================================
1134 // function : gltfParsePrimArray
1135 // purpose  :
1136 // =======================================================================
1137 bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1138                                                 const TCollection_AsciiString& theMeshId,
1139                                                 const RWGltf_JsonValue& thePrimArray,
1140                                                 const Handle(Message_ProgressIndicator)& /*theProgress*/)
1141 {
1142   const RWGltf_JsonValue* anAttribs = findObjectMember (thePrimArray, "attributes");
1143   const RWGltf_JsonValue* anIndices = findObjectMember (thePrimArray, "indices");
1144   const RWGltf_JsonValue* aMaterial = findObjectMember (thePrimArray, "material");
1145   const RWGltf_JsonValue* aModeVal  = findObjectMember (thePrimArray, "mode");
1146   RWGltf_GltfPrimitiveMode aMode = RWGltf_GltfPrimitiveMode_Triangles;
1147   if (anAttribs == NULL
1148   || !anAttribs->IsObject())
1149   {
1150     reportGltfError ("Primitive array within Mesh '" + theMeshId + "' defines no attributes.");
1151     return false;
1152   }
1153   else if (aModeVal != NULL)
1154   {
1155     aMode = RWGltf_GltfPrimitiveMode_UNKNOWN;
1156     if (aModeVal->IsInt())
1157     {
1158       aMode = (RWGltf_GltfPrimitiveMode )aModeVal->GetInt();
1159     }
1160     if (aMode < RWGltf_GltfPrimitiveMode_Points
1161      || aMode > RWGltf_GltfPrimitiveMode_TriangleFan)
1162     {
1163       reportGltfError ("Primitive array within Mesh '" + theMeshId + "' has unknown mode.");
1164       return false;
1165     }
1166   }
1167   if (aMode != RWGltf_GltfPrimitiveMode_Triangles)
1168   {
1169     Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Primitive array within Mesh '"
1170                                                    + theMeshId + "' skipped due to unsupported mode.", Message_Warning);
1171     return true;
1172   }
1173   theMeshData->SetPrimitiveMode (aMode);
1174
1175   // assign material
1176   if (aMaterial != NULL)
1177   {
1178     Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
1179     if (myMaterialsPbr.Find (getKeyString (*aMaterial), aMatPbr))
1180     {
1181       theMeshData->SetMaterialPbr (aMatPbr);
1182     }
1183
1184     Handle(RWGltf_MaterialCommon) aMatCommon;
1185     if (myMaterialsCommon.Find (getKeyString (*aMaterial), aMatCommon))
1186     {
1187       theMeshData->SetMaterialCommon (aMatCommon);
1188     }
1189   }
1190
1191   bool hasPositions = false;
1192   for (rapidjson::Value::ConstMemberIterator anAttribIter = anAttribs->MemberBegin();
1193        anAttribIter != anAttribs->MemberEnd(); ++anAttribIter)
1194   {
1195     const TCollection_AsciiString anAttribId = getKeyString (anAttribIter->value);
1196     if (anAttribId.IsEmpty())
1197     {
1198       reportGltfError ("Primitive array attribute accessor key within Mesh '" + theMeshId + "' is not a string.");
1199       return false;
1200     }
1201
1202     RWGltf_GltfArrayType aType = RWGltf_GltfParseAttribType (anAttribIter->name.GetString());
1203     if (aType == RWGltf_GltfArrayType_UNKNOWN)
1204     {
1205       // just ignore unknown attributes
1206       continue;
1207     }
1208
1209     const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (anAttribIter->value);
1210     if (anAccessor == NULL
1211     || !anAccessor->IsObject())
1212     {
1213       reportGltfError ("Primitive array attribute accessor key '" + anAttribId + "' points to non-existing object.");
1214       return false;
1215     }
1216     else if (!gltfParseAccessor (theMeshData, anAttribId, *anAccessor, aType))
1217     {
1218       return false;
1219     }
1220     else if (aType == RWGltf_GltfArrayType_Position)
1221     {
1222       hasPositions = true;
1223     }
1224   }
1225   if (!hasPositions)
1226   {
1227     reportGltfError ("Primitive array within Mesh '" + theMeshId + "' does not define vertex positions.");
1228     return false;
1229   }
1230
1231   if (anIndices != NULL)
1232   {
1233     const TCollection_AsciiString anIndicesId = getKeyString (*anIndices);
1234     const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (*anIndices);
1235     if (anAccessor == NULL
1236     || !anAccessor->IsObject())
1237     {
1238       reportGltfError ("Primitive array indices accessor key '" + anIndicesId + "' points to non-existing object.");
1239       return false;
1240     }
1241     else if (!gltfParseAccessor (theMeshData, anIndicesId, *anAccessor, RWGltf_GltfArrayType_Indices))
1242     {
1243       return false;
1244     }
1245   }
1246
1247   return true;
1248 }
1249
1250 // =======================================================================
1251 // function : gltfParseAccessor
1252 // purpose  :
1253 // =======================================================================
1254 bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1255                                                const TCollection_AsciiString& theName,
1256                                                const RWGltf_JsonValue& theAccessor,
1257                                                const RWGltf_GltfArrayType theType)
1258 {
1259   RWGltf_GltfAccessor aStruct;
1260   const RWGltf_JsonValue* aTypeStr        = findObjectMember (theAccessor, "type");
1261   const RWGltf_JsonValue* aBufferViewName = findObjectMember (theAccessor, "bufferView");
1262   const RWGltf_JsonValue* aByteOffset     = findObjectMember (theAccessor, "byteOffset");
1263   const RWGltf_JsonValue* aByteStride     = findObjectMember (theAccessor, "byteStride");
1264   const RWGltf_JsonValue* aCompType       = findObjectMember (theAccessor, "componentType");
1265   const RWGltf_JsonValue* aCount          = findObjectMember (theAccessor, "count");
1266   if (aTypeStr == NULL
1267   || !aTypeStr->IsString())
1268   {
1269     reportGltfError ("Accessor '" + theName + "' does not define type.");
1270     return false;
1271   }
1272   aStruct.Type = RWGltf_GltfParseAccessorType (aTypeStr->GetString());
1273   if (aStruct.Type == RWGltf_GltfAccessorLayout_UNKNOWN)
1274   {
1275     reportGltfError ("Accessor '" + theName + "' has invalid type.");
1276     return false;
1277   }
1278
1279   if (aBufferViewName == NULL)
1280   {
1281     reportGltfError ("Accessor '" + theName + "' does not define bufferView.");
1282     return false;
1283   }
1284   if (aCompType == NULL
1285   || !aCompType->IsInt())
1286   {
1287     reportGltfError ("Accessor '" + theName + "' does not define componentType.");
1288     return false;
1289   }
1290   aStruct.ComponentType = (RWGltf_GltfAccessorCompType )aCompType->GetInt();
1291   if (aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int8
1292    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt8
1293    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int16
1294    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt16
1295    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt32
1296    && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Float32)
1297   {
1298     reportGltfError ("Accessor '" + theName + "' defines invalid componentType value.");
1299     return false;
1300   }
1301
1302   if (aCount == NULL
1303   || !aCount->IsNumber())
1304   {
1305     reportGltfError ("Accessor '" + theName + "' does not define count.");
1306     return false;
1307   }
1308
1309   aStruct.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1310                      ? (int64_t )aByteOffset->GetDouble()
1311                      : 0;
1312   aStruct.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1313                      ? aByteStride->GetInt()
1314                      : 0;
1315   aStruct.Count = (int64_t )aCount->GetDouble();
1316
1317   if (aStruct.ByteOffset < 0)
1318   {
1319     reportGltfError ("Accessor '" + theName + "' defines invalid byteOffset.");
1320     return false;
1321   }
1322   else if (aStruct.ByteStride < 0
1323         || aStruct.ByteStride > 255)
1324   {
1325     reportGltfError ("Accessor '" + theName + "' defines invalid byteStride.");
1326     return false;
1327   }
1328   else if (aStruct.Count < 1)
1329   {
1330     reportGltfError ("Accessor '" + theName + "' defines invalid count.");
1331     return false;
1332   }
1333
1334   // Read Min/Max values for POSITION type. It is used for bounding boxes
1335   if (theType == RWGltf_GltfArrayType_Position)
1336   {
1337     const RWGltf_JsonValue* aMin = findObjectMember (theAccessor, "min");
1338     const RWGltf_JsonValue* aMax = findObjectMember (theAccessor, "max");
1339     if (aMin != NULL && aMax != NULL)
1340     {
1341       // Note: Min/Max values can be not defined in glTF file.
1342       // In this case it is not used only.
1343       if (!aMin->IsArray()   || !aMax->IsArray() ||
1344            aMin->Size() != 3 ||  aMax->Size() != 3)
1345       {
1346         reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max values.");
1347       }
1348       else
1349       {
1350         bool isValidMinMax = true;
1351         gp_Pnt aMinPnt, aMaxPnt;
1352         for (int anIter = 0; anIter < 3; ++anIter)
1353         {
1354           const RWGltf_JsonValue& aMinVal = (*aMin)[anIter];
1355           const RWGltf_JsonValue& aMaxVal = (*aMax)[anIter];
1356           if (!aMinVal.IsNumber() || !aMaxVal.IsNumber())
1357           {
1358             reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max value.");
1359             isValidMinMax = false;
1360             break;
1361           }
1362           aMinPnt.SetCoord (anIter + 1, aMinVal.GetDouble());
1363           aMinPnt.SetCoord (anIter + 1, aMaxVal.GetDouble());
1364         }
1365         if (isValidMinMax)
1366         {
1367           myCSTrsf.TransformPosition (aMinPnt.ChangeCoord());
1368           myCSTrsf.TransformPosition (aMaxPnt.ChangeCoord());
1369
1370           Bnd_Box aBox;
1371           aBox.Add (aMinPnt);
1372           aBox.Add (aMaxPnt);
1373
1374           theMeshData->SetBoundingBox (aBox);
1375         }
1376       }
1377     }
1378   }
1379
1380   const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
1381   if (aBufferView == NULL
1382   || !aBufferView->IsObject())
1383   {
1384     reportGltfError ("Accessor '" + theName + "' refers to non-existing bufferView.");
1385     return false;
1386   }
1387
1388   return gltfParseBufferView (theMeshData, getKeyString (*aBufferViewName), *aBufferView, aStruct, theType);
1389 }
1390
1391 // =======================================================================
1392 // function : gltfParseBufferView
1393 // purpose  :
1394 // =======================================================================
1395 bool RWGltf_GltfJsonParser::gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1396                                                  const TCollection_AsciiString& theName,
1397                                                  const RWGltf_JsonValue& theBufferView,
1398                                                  const RWGltf_GltfAccessor& theAccessor,
1399                                                  const RWGltf_GltfArrayType theType)
1400 {
1401   RWGltf_GltfBufferView aBuffView;
1402   const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
1403   const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
1404   const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
1405   const RWGltf_JsonValue* aTarget     = findObjectMember (theBufferView, "target");
1406   if (aBufferName == NULL)
1407   {
1408     reportGltfError ("BufferView '" + theName + "' does not define buffer.");
1409     return false;
1410   }
1411
1412   aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1413                        ? (int64_t )aByteOffset->GetDouble()
1414                        : 0;
1415   aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
1416                        ? (int64_t )aByteLength->GetDouble()
1417                        : 0;
1418   if (aTarget != NULL && aTarget->IsInt())
1419   {
1420     aBuffView.Target = (RWGltf_GltfBufferViewTarget )aTarget->GetInt();
1421     if (aBuffView.Target != RWGltf_GltfBufferViewTarget_ARRAY_BUFFER
1422      && aBuffView.Target != RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER)
1423     {
1424       reportGltfError ("BufferView '" + theName + "' defines invalid target.");
1425       return false;
1426     }
1427   }
1428
1429   if (aBuffView.ByteLength < 0)
1430   {
1431     reportGltfError ("BufferView '" + theName + "' defines invalid byteLength.");
1432     return false;
1433   }
1434   else if (aBuffView.ByteOffset < 0)
1435   {
1436     reportGltfError ("BufferView '" + theName + "' defines invalid byteOffset.");
1437     return false;
1438   }
1439
1440   const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
1441   if (aBuffer == NULL
1442   || !aBuffer->IsObject())
1443   {
1444     reportGltfError ("BufferView '" + theName + "' refers to non-existing buffer.");
1445     return false;
1446   }
1447
1448   return gltfParseBuffer (theMeshData, getKeyString (*aBufferName), *aBuffer, theAccessor, aBuffView, theType);
1449 }
1450
1451 // =======================================================================
1452 // function : gltfParseBuffer
1453 // purpose  :
1454 // =======================================================================
1455 bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1456                                              const TCollection_AsciiString& theName,
1457                                              const RWGltf_JsonValue& theBuffer,
1458                                              const RWGltf_GltfAccessor&     theAccessor,
1459                                              const RWGltf_GltfBufferView&   theView,
1460                                              const RWGltf_GltfArrayType     theType)
1461 {
1462   //const RWGltf_JsonValue* aType       = findObjectMember (theBuffer, "type");
1463   //const RWGltf_JsonValue* aByteLength = findObjectMember (theBuffer, "byteLength");
1464   const RWGltf_JsonValue* anUriVal      = findObjectMember (theBuffer, "uri");
1465
1466   int64_t anOffset = theView.ByteOffset + theAccessor.ByteOffset;
1467   bool isBinary = false;
1468   if (myIsBinary)
1469   {
1470     isBinary = IsEqual ("binary_glTF", theName) // glTF 1.0
1471             || anUriVal == NULL;                // glTF 2.0
1472   }
1473   if (isBinary)
1474   {
1475     anOffset += myBinBodyOffset;
1476
1477     RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1478     aData.Accessor = theAccessor;
1479     aData.StreamOffset = anOffset;
1480     aData.StreamUri = myFilePath;
1481     return true;
1482   }
1483
1484   if (anUriVal == NULL
1485   || !anUriVal->IsString())
1486   {
1487     reportGltfError ("Buffer '" + theName + "' does not define uri.");
1488     return false;
1489   }
1490
1491   const char* anUriData = anUriVal->GetString();
1492   if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
1493   {
1494     RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1495     aData.Accessor = theAccessor;
1496     aData.StreamOffset = anOffset;
1497     if (!myDecodedBuffers.Find (theName, aData.StreamData))
1498     {
1499       // it is better decoding in multiple threads
1500       aData.StreamData = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
1501       myDecodedBuffers.Bind (theName, aData.StreamData);
1502     }
1503     return true;
1504   }
1505   else
1506   {
1507     TCollection_AsciiString anUri = anUriData;
1508     if (anUri.IsEmpty())
1509     {
1510       reportGltfError ("Buffer '" + theName + "' does not define uri.");
1511       return false;
1512     }
1513
1514     TCollection_AsciiString aPath = myFolder + anUri;
1515     bool isFileExist = false;
1516     if (!myProbedFiles.Find (aPath, isFileExist))
1517     {
1518       isFileExist = OSD_File (aPath).Exists();
1519       myProbedFiles.Bind (aPath, isFileExist);
1520     }
1521     if (!isFileExist)
1522     {
1523       reportGltfError ("Buffer '" + theName + "' refers to non-existing file '" + anUri + "'.");
1524       return false;
1525     }
1526
1527     RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1528     aData.Accessor = theAccessor;
1529     aData.StreamOffset = anOffset;
1530     aData.StreamUri = myFolder + anUri;
1531     if (myExternalFiles != NULL)
1532     {
1533       myExternalFiles->Add (aData.StreamUri);
1534     }
1535     return true;
1536   }
1537 }
1538
1539 // =======================================================================
1540 // function : bindNamedShape
1541 // purpose  :
1542 // =======================================================================
1543 void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape,
1544                                             ShapeMapGroup theGroup,
1545                                             const TopLoc_Location& theLoc,
1546                                             const TCollection_AsciiString& theId,
1547                                             const RWGltf_JsonValue* theUserName)
1548 {
1549   if (theShape.IsNull())
1550   {
1551     return;
1552   }
1553
1554   if (!theLoc.IsIdentity())
1555   {
1556     theShape.Location (theLoc);
1557   }
1558
1559   TCollection_AsciiString aUserName;
1560   if (theUserName != NULL
1561    && theUserName->IsString())
1562   {
1563     aUserName = theUserName->GetString();
1564   }
1565   else if (myIsGltf1)
1566   {
1567     aUserName = theId;
1568   }
1569
1570   myShapeMap[theGroup].Bind (theId, theShape);
1571   if (myAttribMap != NULL)
1572   {
1573     RWMesh_NodeAttributes aShapeAttribs;
1574     aShapeAttribs.Name    = aUserName;
1575     aShapeAttribs.RawName = theId;
1576     if (theShape.ShapeType() == TopAbs_FACE)
1577     {
1578       TopLoc_Location aDummy;
1579       if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (theShape), aDummy)))
1580       {
1581         if (aLateData->HasStyle())
1582         {
1583           aShapeAttribs.Style.SetColorSurf (aLateData->BaseColor());
1584         }
1585       }
1586     }
1587     myAttribMap->Bind (theShape, aShapeAttribs);
1588   }
1589 }
1590 #endif
1591
1592 // =======================================================================
1593 // function : Parse
1594 // purpose  :
1595 // =======================================================================
1596 bool RWGltf_GltfJsonParser::Parse (const Handle(Message_ProgressIndicator)& theProgress)
1597 {
1598   Message_ProgressSentry aPSentry (theProgress, "Reading Gltf", 0, 2, 1);
1599 #ifdef HAVE_RAPIDJSON
1600   {
1601     if (!gltfParseRoots())
1602     {
1603       return false;
1604     }
1605
1606     gltfParseAsset();
1607     gltfParseMaterials();
1608     if (!gltfParseScene (theProgress))
1609     {
1610       return false;
1611     }
1612   }
1613   aPSentry.Next();
1614   if (!aPSentry.More())
1615   {
1616     return false;
1617   }
1618   return true;
1619 #else
1620   Message::DefaultMessenger()->Send ("Error: glTF reader is unavailable - OCCT has been built without RapidJSON support.", Message_Fail);
1621   return false;
1622 #endif
1623 }