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