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