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