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