0030811: Data Exchange, RWGltf_CafReader - fix inaccessibility of properties
[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
82c59511 253 if (myMetadata == NULL)
254 {
255 return;
256 }
257
0a419c51 258 if (const RWGltf_JsonValue* aGenerator = findObjectMember (*anAsset, "generator"))
259 {
260 if (aGenerator->IsString())
261 {
82c59511 262 myMetadata->Add ("generator", aGenerator->GetString());
0a419c51 263 }
264 }
265 if (const RWGltf_JsonValue* aCopyRight = findObjectMember (*anAsset, "copyright"))
266 {
267 if (aCopyRight->IsString())
268 {
82c59511 269 myMetadata->Add ("copyright", aCopyRight->GetString());
0a419c51 270 }
271 }
272}
273
274// =======================================================================
275// function : gltfParseMaterials
276// purpose :
277// =======================================================================
278void 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// =======================================================================
352bool 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// =======================================================================
435bool 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// =======================================================================
541bool 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// =======================================================================
567bool 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// =======================================================================
724bool 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// =======================================================================
749bool 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// =======================================================================
795bool 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// =======================================================================
1066bool 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// =======================================================================
1142bool 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// =======================================================================
1259bool 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// =======================================================================
1400bool 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// =======================================================================
1460bool 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// =======================================================================
1548void 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// =======================================================================
1601bool 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}