0031332: Data Exchange - RWGltf_CafReader ignores bufferView.byteStride within glTF 2.0
[occt.git] / src / RWGltf / RWGltf_GltfJsonParser.cxx
... / ...
CommitLineData
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 //! Data buffer referring to a portion of another buffer.
42 class RWGltf_SubBuffer : public NCollection_Buffer
43 {
44 public:
45 RWGltf_SubBuffer (const Handle(NCollection_Buffer)& theBase,
46 Standard_Size theOffset,
47 Standard_Size theLength)
48 : NCollection_Buffer (Handle(NCollection_BaseAllocator)(), theLength, theBase->ChangeData() + theOffset),
49 myBaseBuffer (theBase) {}
50
51 private:
52 Handle(NCollection_Buffer) myBaseBuffer;
53 };
54}
55
56//! Find member of the object in a safe way.
57inline const RWGltf_JsonValue* findObjectMember (const RWGltf_JsonValue& theObject,
58 const RWGltf_JsonValue& theName)
59{
60 if (!theObject.IsObject()
61 || !theName.IsString())
62 {
63 return NULL;
64 }
65
66 rapidjson::Document::ConstMemberIterator anIter = theObject.FindMember (theName);
67 return anIter != theObject.MemberEnd()
68 ? &anIter->value
69 : NULL;
70}
71
72//! Find member of the object in a safe way.
73inline const RWGltf_JsonValue* findObjectMember (const RWGltf_JsonValue& theObject,
74 const char* theName)
75{
76 if (!theObject.IsObject())
77 {
78 return NULL;
79 }
80
81 rapidjson::Document::ConstMemberIterator anIter = theObject.FindMember (theName);
82 return anIter != theObject.MemberEnd()
83 ? &anIter->value
84 : NULL;
85}
86
87// =======================================================================
88// function : RWGltf_GltfJsonParser::FormatParseError
89// purpose :
90// =======================================================================
91const char* RWGltf_GltfJsonParser::FormatParseError (rapidjson::ParseErrorCode theCode)
92{
93 switch (theCode)
94 {
95 case rapidjson::kParseErrorNone: return "";
96 case rapidjson::kParseErrorDocumentEmpty: return "Empty Document";
97 case rapidjson::kParseErrorDocumentRootNotSingular: return "The document root must not follow by other values";
98 case rapidjson::kParseErrorValueInvalid: return "Invalid value";
99 case rapidjson::kParseErrorObjectMissName: return "Missing a name for object member";
100 case rapidjson::kParseErrorObjectMissColon: return "Missing a colon after a name of object member";
101 case rapidjson::kParseErrorObjectMissCommaOrCurlyBracket: return "Missing a comma or '}' after an object member";
102 case rapidjson::kParseErrorArrayMissCommaOrSquareBracket: return "Missing a comma or ']' after an array element";
103 case rapidjson::kParseErrorStringUnicodeEscapeInvalidHex: return "Incorrect hex digit after \\u escape in string";
104 case rapidjson::kParseErrorStringUnicodeSurrogateInvalid: return "The surrogate pair in string is invalid";
105 case rapidjson::kParseErrorStringEscapeInvalid: return "Invalid escape character in string";
106 case rapidjson::kParseErrorStringMissQuotationMark: return "Missing a closing quotation mark in string";
107 case rapidjson::kParseErrorStringInvalidEncoding: return "Invalid encoding in string";
108 case rapidjson::kParseErrorNumberTooBig: return "Number is too big to be stored in double";
109 case rapidjson::kParseErrorNumberMissFraction: return "Miss fraction part in number";
110 case rapidjson::kParseErrorNumberMissExponent: return "Miss exponent in number";
111 case rapidjson::kParseErrorTermination: return "Parsing was terminated";
112 case rapidjson::kParseErrorUnspecificSyntaxError: return "Unspecific syntax error";
113 }
114 return "UNKOWN syntax error";
115}
116
117// =======================================================================
118// function : GltfElementMap::Init
119// purpose :
120// =======================================================================
121void RWGltf_GltfJsonParser::GltfElementMap::Init (const TCollection_AsciiString& theRootName,
122 const RWGltf_JsonValue* theRoot)
123{
124 myRoot = theRoot;
125 myChildren.Clear();
126 if (theRoot == NULL)
127 {
128 return;
129 }
130
131 if (theRoot->IsObject())
132 {
133 // glTF 1.0
134 for (ConstMemberIterator aChildIter = theRoot->MemberBegin(); aChildIter != theRoot->MemberEnd(); ++aChildIter)
135 {
136 if (!aChildIter->name.IsString())
137 {
138 continue;
139 }
140
141 const TCollection_AsciiString aKey (aChildIter->name.GetString());
142 if (!myChildren.Bind (aKey, &aChildIter->value))
143 {
144 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Invalid glTF syntax - key '")
145 + aKey + "' is already defined in '" + theRootName + "'.", Message_Warning);
146 }
147 }
148 }
149 else if (theRoot->IsArray())
150 {
151 // glTF 2.0
152 int aChildIndex = 0;
153 for (rapidjson::Value::ConstValueIterator aChildIter = theRoot->Begin(); aChildIter != theRoot->End(); ++aChildIter, ++aChildIndex)
154 {
155 myChildren.Bind (TCollection_AsciiString (aChildIndex), aChildIter);
156 }
157 }
158}
159#endif
160
161// Auxiliary macros for formatting message.
162#define reportGltfError(theMsg) reportGltfSyntaxProblem(TCollection_AsciiString() + theMsg, Message_Fail);
163#define reportGltfWarning(theMsg) reportGltfSyntaxProblem(TCollection_AsciiString() + theMsg, Message_Warning);
164
165// =======================================================================
166// function : reportGltfSyntaxProblem
167// purpose :
168// =======================================================================
169void RWGltf_GltfJsonParser::reportGltfSyntaxProblem (const TCollection_AsciiString& theMsg,
170 Message_Gravity theGravity)
171{
172 Message::DefaultMessenger()->Send (myErrorPrefix + theMsg, theGravity);
173}
174
175// =======================================================================
176// function : RWGltf_GltfJsonParser
177// purpose :
178// =======================================================================
179RWGltf_GltfJsonParser::RWGltf_GltfJsonParser (TopTools_SequenceOfShape& theRootShapes)
180: myRootShapes(&theRootShapes),
181 myAttribMap (NULL),
182 myExternalFiles (NULL),
183 myBinBodyOffset (0),
184 myBinBodyLen (0),
185 myIsBinary (false),
186 myIsGltf1 (false),
187 myToSkipEmptyNodes (true),
188 myUseMeshNameAsFallback (true),
189 myToProbeHeader (false)
190{
191 myCSTrsf.SetInputLengthUnit (1.0); // meters
192 myCSTrsf.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
193}
194
195// =======================================================================
196// function : SetFilePath
197// purpose :
198// =======================================================================
199void RWGltf_GltfJsonParser::SetFilePath (const TCollection_AsciiString& theFilePath)
200{
201 myFilePath = theFilePath;
202 // determine file location to load associated files
203 TCollection_AsciiString aFileName;
204 OSD_Path::FolderAndFileFromPath (theFilePath, myFolder, aFileName);
205}
206
207#ifdef HAVE_RAPIDJSON
208// =======================================================================
209// function : gltfParseRoots
210// purpose :
211// =======================================================================
212bool RWGltf_GltfJsonParser::gltfParseRoots()
213{
214 // find glTF root elements for smooth navigation
215 RWGltf_JsonValue aNames[RWGltf_GltfRootElement_NB];
216 for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
217 {
218 aNames[aRootNameIter] = rapidjson::StringRef (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter));
219 }
220
221 for (ConstMemberIterator aRootIter = MemberBegin();
222 aRootIter != MemberEnd(); ++aRootIter)
223 {
224 for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
225 {
226 if (myGltfRoots[aRootNameIter].IsNull()
227 && aNames[aRootNameIter] == aRootIter->name)
228 {
229 // we will not modify JSON document, thus it is OK to keep the pointers
230 myGltfRoots[aRootNameIter].Init (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter), &aRootIter->value);
231 break;
232 }
233 }
234 }
235
236 for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB_MANDATORY; ++aRootNameIter)
237 {
238 if (myGltfRoots[aRootNameIter].IsNull())
239 {
240 reportGltfError ("Member '" + RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter) + "' is not found.");
241 return false;
242 }
243 }
244 return true;
245}
246
247// =======================================================================
248// function : gltfParseAsset
249// purpose :
250// =======================================================================
251void RWGltf_GltfJsonParser::gltfParseAsset()
252{
253 const RWGltf_JsonValue* anAsset = myGltfRoots[RWGltf_GltfRootElement_Asset].Root();
254 if (anAsset == NULL)
255 {
256 return;
257 }
258
259 if (const RWGltf_JsonValue* aVersion = findObjectMember (*anAsset, "version"))
260 {
261 if (aVersion->IsString())
262 {
263 TCollection_AsciiString aVerStr (aVersion->GetString());
264 myIsGltf1 = aVerStr.StartsWith ("1.");
265 }
266 }
267
268 if (myMetadata == NULL)
269 {
270 return;
271 }
272
273 if (const RWGltf_JsonValue* aGenerator = findObjectMember (*anAsset, "generator"))
274 {
275 if (aGenerator->IsString())
276 {
277 myMetadata->Add ("generator", aGenerator->GetString());
278 }
279 }
280 if (const RWGltf_JsonValue* aCopyRight = findObjectMember (*anAsset, "copyright"))
281 {
282 if (aCopyRight->IsString())
283 {
284 myMetadata->Add ("copyright", aCopyRight->GetString());
285 }
286 }
287}
288
289// =======================================================================
290// function : gltfParseMaterials
291// purpose :
292// =======================================================================
293void RWGltf_GltfJsonParser::gltfParseMaterials()
294{
295 const RWGltf_JsonValue* aMatList = myGltfRoots[RWGltf_GltfRootElement_Materials].Root();
296 if (aMatList == NULL)
297 {
298 return;
299 }
300 else if (aMatList->IsObject())
301 {
302 // glTF 1.0
303 for (ConstMemberIterator aMatIter = aMatList->MemberBegin();
304 aMatIter != aMatList->MemberEnd(); ++aMatIter)
305 {
306 Handle(RWGltf_MaterialCommon) aMat;
307 const RWGltf_JsonValue& aMatNode = aMatIter->value;
308 const RWGltf_JsonValue& aMatId = aMatIter->name;
309 const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
310 if (!gltfParseCommonMaterial (aMat, aMatNode))
311 {
312 if (!gltfParseStdMaterial (aMat, aMatNode))
313 {
314 continue;
315 }
316 }
317
318 if (aNameVal != NULL
319 && aNameVal->IsString())
320 {
321 aMat->Name = aNameVal->GetString();
322 }
323 aMat->Id = aMatId.GetString();
324 myMaterialsCommon.Bind (aMat->Id, aMat);
325 gltfBindMaterial (Handle(RWGltf_MaterialMetallicRoughness)(), aMat);
326 }
327 }
328 else if (aMatList->IsArray())
329 {
330 // glTF 2.0
331 int aMatIndex = 0;
332 for (rapidjson::Value::ConstValueIterator aMatIter = aMatList->Begin(); aMatIter != aMatList->End(); ++aMatIter, ++aMatIndex)
333 {
334 Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
335 const RWGltf_JsonValue& aMatNode = *aMatIter;
336 const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
337 if (gltfParsePbrMaterial (aMatPbr, aMatNode))
338 {
339 if (aNameVal != NULL
340 && aNameVal->IsString())
341 {
342 aMatPbr->Name = aNameVal->GetString();
343 }
344 aMatPbr->Id = TCollection_AsciiString ("mat_") + aMatIndex;
345 myMaterialsPbr.Bind (TCollection_AsciiString (aMatIndex), aMatPbr);
346 }
347
348 Handle(RWGltf_MaterialCommon) aMatCommon;
349 if (gltfParseCommonMaterial(aMatCommon, aMatNode)
350 || gltfParseStdMaterial (aMatCommon, aMatNode))
351 {
352 if (aNameVal != NULL
353 && aNameVal->IsString())
354 {
355 aMatCommon->Name = aNameVal->GetString();
356 }
357 aMatCommon->Id = TCollection_AsciiString ("mat_") + aMatIndex;
358 myMaterialsCommon.Bind (TCollection_AsciiString (aMatIndex), aMatCommon);
359 }
360
361 gltfBindMaterial (aMatPbr, aMatCommon);
362 }
363 }
364}
365
366// =======================================================================
367// function : gltfBindMaterial
368// purpose :
369// =======================================================================
370void RWGltf_GltfJsonParser::gltfBindMaterial (const Handle(RWGltf_MaterialMetallicRoughness)& theMatPbr,
371 const Handle(RWGltf_MaterialCommon)& theMatCommon)
372{
373 if (theMatPbr.IsNull()
374 && theMatCommon.IsNull())
375 {
376 return;
377 }
378
379 Handle(XCAFDoc_VisMaterial) aMat = new XCAFDoc_VisMaterial();
380 if (!theMatCommon.IsNull())
381 {
382 XCAFDoc_VisMaterialCommon aMatXde;
383 aMatXde.IsDefined = true;
384 aMatXde.AmbientColor = theMatCommon->AmbientColor;
385 aMatXde.DiffuseColor = theMatCommon->DiffuseColor;
386 aMatXde.SpecularColor = theMatCommon->SpecularColor;
387 aMatXde.EmissiveColor = theMatCommon->EmissiveColor;
388 aMatXde.Shininess = theMatCommon->Shininess;
389 aMatXde.Transparency = theMatCommon->Transparency;
390 aMatXde.DiffuseTexture = theMatCommon->DiffuseTexture;
391 if (aMatXde.DiffuseTexture.IsNull()
392 && !theMatCommon->AmbientTexture.IsNull())
393 {
394 aMatXde.DiffuseTexture = theMatCommon->AmbientTexture;
395 }
396 aMat->SetCommonMaterial (aMatXde);
397 if (!theMatCommon->Name.IsEmpty())
398 {
399 aMat->SetRawName (new TCollection_HAsciiString (theMatCommon->Name));
400 }
401 }
402 if (!theMatPbr.IsNull())
403 {
404 XCAFDoc_VisMaterialPBR aMatXde;
405 aMatXde.IsDefined = true;
406 aMatXde.MetallicRoughnessTexture = theMatPbr->MetallicRoughnessTexture;
407 aMatXde.BaseColorTexture = theMatPbr->BaseColorTexture;
408 aMatXde.EmissiveTexture = theMatPbr->EmissiveTexture;
409 aMatXde.OcclusionTexture = theMatPbr->OcclusionTexture;
410 aMatXde.NormalTexture = theMatPbr->NormalTexture;
411 aMatXde.BaseColor = theMatPbr->BaseColor;
412 aMatXde.EmissiveFactor = theMatPbr->EmissiveFactor;
413 aMatXde.Metallic = theMatPbr->Metallic;
414 aMatXde.Roughness = theMatPbr->Roughness;
415 aMat->SetPbrMaterial (aMatXde);
416
417 Graphic3d_AlphaMode anAlphaMode = Graphic3d_AlphaMode_BlendAuto;
418 switch (theMatPbr->AlphaMode)
419 {
420 case RWGltf_GltfAlphaMode_Opaque:
421 {
422 anAlphaMode = Graphic3d_AlphaMode_Opaque;
423 if (aMatXde.BaseColor.Alpha() < 1.0f)
424 {
425 Message::DefaultMessenger()->Send ("glTF reader - material with non-zero Transparency specifies Opaque AlphaMode", Message_Warning);
426 }
427 break;
428 }
429 case RWGltf_GltfAlphaMode_Mask:
430 {
431 anAlphaMode = Graphic3d_AlphaMode_Mask;
432 break;
433 }
434 case RWGltf_GltfAlphaMode_Blend:
435 {
436 anAlphaMode = Graphic3d_AlphaMode_Blend;
437 break;
438 }
439 }
440 aMat->SetAlphaMode (anAlphaMode, theMatPbr->AlphaCutOff);
441 aMat->SetDoubleSided (theMatPbr->IsDoubleSided);
442
443 if (!theMatPbr->Name.IsEmpty())
444 {
445 aMat->SetRawName (new TCollection_HAsciiString (theMatPbr->Name));
446 }
447 }
448
449 myMaterials.Bind (!theMatPbr.IsNull() ? theMatPbr->Id : theMatCommon->Id, aMat);
450}
451
452// =======================================================================
453// function : gltfParseStdMaterial
454// purpose :
455// =======================================================================
456bool RWGltf_GltfJsonParser::gltfParseStdMaterial (Handle(RWGltf_MaterialCommon)& theMat,
457 const RWGltf_JsonValue& theMatNode)
458{
459 //const RWGltf_JsonValue* aTechVal = findObjectMember (theMatNode, "technique");
460 const RWGltf_JsonValue* aValues = findObjectMember (theMatNode, "values");
461 if (aValues == NULL)
462 {
463 return false;
464 }
465
466 const RWGltf_JsonValue* anAmbVal = findObjectMember (*aValues, "ambient");
467 const RWGltf_JsonValue* aDiffVal = findObjectMember (*aValues, "diffuse");
468 const RWGltf_JsonValue* anEmiVal = findObjectMember (*aValues, "emission");
469 const RWGltf_JsonValue* aSpecVal = findObjectMember (*aValues, "specular");
470 const RWGltf_JsonValue* aShinVal = findObjectMember (*aValues, "shininess");
471 if (anAmbVal == NULL
472 && aDiffVal == NULL
473 && anEmiVal == NULL
474 && aSpecVal == NULL
475 && aShinVal == NULL)
476 {
477 return false;
478 }
479
480 theMat = new RWGltf_MaterialCommon();
481
482 Graphic3d_Vec4d anAmb, aDiff, anEmi, aSpec;
483 if (anAmbVal != NULL
484 && anAmbVal->IsString())
485 {
486 gltfParseTexture (theMat->AmbientTexture, anAmbVal);
487 }
488 else if (gltfReadVec4 (anAmb, anAmbVal)
489 && validateColor4 (anAmb))
490 {
491 theMat->AmbientColor = Quantity_Color (anAmb.r(), anAmb.g(), anAmb.b(), Quantity_TOC_sRGB);
492 }
493
494 if (aDiffVal != NULL
495 && aDiffVal->IsString())
496 {
497 gltfParseTexture (theMat->DiffuseTexture, aDiffVal);
498 }
499 else if (gltfReadVec4 (aDiff, aDiffVal)
500 && validateColor4 (aDiff))
501 {
502 theMat->DiffuseColor = Quantity_Color (aDiff.r(), aDiff.g(), aDiff.b(), Quantity_TOC_sRGB);
503 theMat->Transparency = float(1.0 - aDiff.a());
504 }
505
506 if (gltfReadVec4 (anEmi, anEmiVal)
507 && validateColor4 (anEmi))
508 {
509 theMat->EmissiveColor = Quantity_Color (anEmi.r(), anEmi.g(), anEmi.b(), Quantity_TOC_sRGB);
510 }
511
512 if (aSpecVal != NULL
513 && aSpecVal->IsString())
514 {
515 gltfParseTexture (theMat->SpecularTexture, aSpecVal);
516 }
517 if (gltfReadVec4 (aSpec, aSpecVal)
518 && validateColor4 (aSpec))
519 {
520 theMat->SpecularColor = Quantity_Color (aSpec.r(), aSpec.g(), aSpec.b(), Quantity_TOC_sRGB);
521 }
522
523 if (aShinVal != NULL
524 && aShinVal->IsNumber())
525 {
526 const double aSpecular = aShinVal->GetDouble();
527 if (aSpecular >= 0)
528 {
529 theMat->Shininess = (float )Min (aSpecular / 1000.0, 1.0);
530 }
531 }
532 return true;
533}
534
535// =======================================================================
536// function : gltfParsePbrMaterial
537// purpose :
538// =======================================================================
539bool RWGltf_GltfJsonParser::gltfParsePbrMaterial (Handle(RWGltf_MaterialMetallicRoughness)& theMat,
540 const RWGltf_JsonValue& theMatNode)
541{
542 /*if (const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions"))
543 {
544 if (const RWGltf_JsonValue* anExtDefVal = findObjectMember (*anExtVal, "KHR_materials_pbrSpecularGlossiness"))
545 {
546 const RWGltf_JsonValue* aDiffTexVal = findObjectMember (*anExtDefVal, "diffuseTexture");
547 const RWGltf_JsonValue* aSpecTexVal = findObjectMember (*anExtDefVal, "specularGlossinessTexture");
548 }
549 }*/
550
551 const RWGltf_JsonValue* aMetalRoughVal = findObjectMember (theMatNode, "pbrMetallicRoughness");
552 const RWGltf_JsonValue* aNormTexVal = findObjectMember (theMatNode, "normalTexture");
553 const RWGltf_JsonValue* anEmissFactorVal = findObjectMember (theMatNode, "emissiveFactor");
554 const RWGltf_JsonValue* anEmissTexVal = findObjectMember (theMatNode, "emissiveTexture");
555 const RWGltf_JsonValue* anOcclusionTexVal = findObjectMember (theMatNode, "occlusionTexture");
556 const RWGltf_JsonValue* aDoubleSidedVal = findObjectMember (theMatNode, "doubleSided");
557 const RWGltf_JsonValue* anAlphaModeVal = findObjectMember (theMatNode, "alphaMode");
558 const RWGltf_JsonValue* anAlphaCutoffVal = findObjectMember (theMatNode, "alphaCutoff");
559 // TODO ADOBE_materials_thin_transparency extension can be used to read IOR (Index of Refraction for transparent materials)
560 if (aMetalRoughVal == NULL)
561 {
562 return false;
563 }
564
565 theMat = new RWGltf_MaterialMetallicRoughness();
566 const RWGltf_JsonValue* aBaseColorFactorVal = findObjectMember (*aMetalRoughVal, "baseColorFactor");
567 const RWGltf_JsonValue* aBaseColorTexVal = findObjectMember (*aMetalRoughVal, "baseColorTexture");
568 const RWGltf_JsonValue* aMetallicFactorVal = findObjectMember (*aMetalRoughVal, "metallicFactor");
569 const RWGltf_JsonValue* aRoughnessFactorVal = findObjectMember (*aMetalRoughVal, "roughnessFactor");
570 const RWGltf_JsonValue* aMetalRoughTexVal = findObjectMember (*aMetalRoughVal, "metallicRoughnessTexture");
571
572 if (aDoubleSidedVal != NULL
573 && aDoubleSidedVal->IsBool())
574 {
575 theMat->IsDoubleSided = aDoubleSidedVal->GetBool();
576 }
577 if (anAlphaCutoffVal != NULL
578 && anAlphaCutoffVal->IsNumber())
579 {
580 theMat->AlphaCutOff = (float )anAlphaCutoffVal->GetDouble();
581 }
582 if (anAlphaModeVal != NULL
583 && anAlphaModeVal->IsString())
584 {
585 theMat->AlphaMode = RWGltf_GltfParseAlphaMode (anAlphaModeVal->GetString());
586 }
587
588 if (aBaseColorTexVal != NULL
589 && aBaseColorTexVal->IsObject())
590 {
591 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aBaseColorTexVal, "index"))
592 {
593 gltfParseTexture (theMat->BaseColorTexture, aTexIndexVal);
594 }
595 }
596
597 Graphic3d_Vec4d aBaseColorFactor;
598 if (gltfReadVec4 (aBaseColorFactor, aBaseColorFactorVal)
599 && validateColor4 (aBaseColorFactor))
600 {
601 theMat->BaseColor = Quantity_ColorRGBA (Graphic3d_Vec4 (aBaseColorFactor));
602 }
603
604 Graphic3d_Vec3d anEmissiveFactor;
605 if (gltfReadVec3 (anEmissiveFactor, anEmissFactorVal)
606 && validateColor3 (anEmissiveFactor))
607 {
608 theMat->EmissiveFactor = Graphic3d_Vec3 (anEmissiveFactor);
609 }
610
611 if (aMetalRoughTexVal != NULL
612 && aMetalRoughTexVal->IsObject())
613 {
614 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aMetalRoughTexVal, "index"))
615 {
616 gltfParseTexture (theMat->MetallicRoughnessTexture, aTexIndexVal);
617 }
618 }
619
620 if (aMetallicFactorVal != NULL
621 && aMetallicFactorVal->IsNumber())
622 {
623 theMat->Metallic = (float )aMetallicFactorVal->GetDouble();
624 }
625
626 if (aRoughnessFactorVal != NULL
627 && aRoughnessFactorVal->IsNumber())
628 {
629 theMat->Roughness = (float )aRoughnessFactorVal->GetDouble();
630 }
631
632 if (aNormTexVal != NULL
633 && aNormTexVal->IsObject())
634 {
635 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aNormTexVal, "index"))
636 {
637 gltfParseTexture (theMat->NormalTexture, aTexIndexVal);
638 }
639 }
640
641 if (anEmissTexVal != NULL
642 && anEmissTexVal->IsObject())
643 {
644 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anEmissTexVal, "index"))
645 {
646 gltfParseTexture (theMat->EmissiveTexture, aTexIndexVal);
647 }
648 }
649
650 if (anOcclusionTexVal != NULL
651 && anOcclusionTexVal->IsObject())
652 {
653 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anOcclusionTexVal, "index"))
654 {
655 gltfParseTexture (theMat->OcclusionTexture, aTexIndexVal);
656 }
657 }
658 return true;
659}
660
661// =======================================================================
662// function : gltfParseCommonMaterial
663// purpose :
664// =======================================================================
665bool RWGltf_GltfJsonParser::gltfParseCommonMaterial (Handle(RWGltf_MaterialCommon)& theMat,
666 const RWGltf_JsonValue& theMatNode)
667{
668 const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions");
669 if (anExtVal == NULL)
670 {
671 return false;
672 }
673
674 const RWGltf_JsonValue* aMatCommon = findObjectMember (*anExtVal, THE_KHR_materials_common);
675 if (aMatCommon == NULL)
676 {
677 return false;
678 }
679
680 if (!gltfParseStdMaterial (theMat, *aMatCommon))
681 {
682 return false;
683 }
684 return true;
685}
686
687// =======================================================================
688// function : gltfParseTexture
689// purpose :
690// =======================================================================
691bool RWGltf_GltfJsonParser::gltfParseTexture (Handle(Image_Texture)& theTexture,
692 const RWGltf_JsonValue* theTextureId)
693{
694 if (theTextureId == NULL
695 || myGltfRoots[RWGltf_GltfRootElement_Textures].IsNull()
696 || myGltfRoots[RWGltf_GltfRootElement_Images].IsNull())
697 {
698 return false;
699 }
700
701 const TCollection_AsciiString aTextureId = getKeyString (*theTextureId);
702 const RWGltf_JsonValue* aTexNode = myGltfRoots[RWGltf_GltfRootElement_Textures].FindChild (*theTextureId);
703 if (aTexNode == NULL)
704 {
705 reportGltfWarning ("Texture node '" + aTextureId + "' is not found.");
706 return false;
707 }
708
709 const RWGltf_JsonValue* aSrcVal = findObjectMember (*aTexNode, "source");
710 const RWGltf_JsonValue* aTargVal = findObjectMember (*aTexNode, "target");
711 if (aSrcVal == NULL)
712 {
713 reportGltfWarning ("Invalid texture node '" + aTextureId + "' without a 'source' property.");
714 return false;
715 }
716 if (aTargVal != NULL
717 && aTargVal->IsNumber()
718 && aTargVal->GetInt() != 3553) // GL_TEXTURE_2D
719 {
720 return false;
721 }
722
723 const RWGltf_JsonValue* anImgNode = myGltfRoots[RWGltf_GltfRootElement_Images].FindChild (*aSrcVal);
724 if (anImgNode == NULL)
725 {
726 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to non-existing image '" + getKeyString (*aSrcVal) + "'.");
727 return false;
728 }
729
730 if (myIsBinary)
731 {
732 const RWGltf_JsonValue* aBinVal = NULL;
733 const RWGltf_JsonValue* aBufferViewName = findObjectMember (*anImgNode, "bufferView");
734 if (aBufferViewName != NULL)
735 {
736 aBinVal = anImgNode;
737 }
738 else if (myIsGltf1)
739 {
740 const RWGltf_JsonValue* anExtVal = findObjectMember (*anImgNode, "extensions");
741 if (anExtVal != NULL)
742 {
743 aBinVal = findObjectMember (*anExtVal, THE_KHR_binary_glTF);
744 if (aBinVal != NULL)
745 {
746 aBufferViewName = findObjectMember (*aBinVal, "bufferView");
747 }
748 }
749 }
750
751 if (aBinVal != NULL)
752 {
753 if (aBufferViewName == NULL)
754 {
755 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
756 return false;
757 }
758 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
759 if (aBufferView == NULL
760 || !aBufferView->IsObject())
761 {
762 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid buffer view '" + getKeyString (*aBufferViewName) + "'.");
763 return false;
764 }
765 return gltfParseTexturInGlbBuffer (theTexture, *aBinVal, getKeyString (*aBufferViewName), *aBufferView);
766 }
767 }
768
769 const RWGltf_JsonValue* anUriVal = findObjectMember (*anImgNode, "uri");
770 if (anUriVal == NULL)
771 {
772 const RWGltf_JsonValue* aBufferViewName = findObjectMember (*anImgNode, "bufferView");
773 if (aBufferViewName == NULL)
774 {
775 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
776 return false;
777 }
778
779 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
780 if (aBufferView == NULL
781 || !aBufferView->IsObject())
782 {
783 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid buffer view '" + getKeyString (*aBufferViewName) + "'.");
784 return false;
785 }
786 return gltfParseTextureInBufferView (theTexture, getKeyString (*aSrcVal), getKeyString (*aBufferViewName), *aBufferView);
787 }
788
789 if (!anUriVal->IsString())
790 {
791 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
792 return false;
793 }
794
795 const char* anUriData = anUriVal->GetString();
796 if (::strncmp (anUriData, "data:", 5) == 0) // data:image/png;base64
797 {
798 // uncompressing base64 here is inefficient, because the same image can be shared by several nodes
799 const char* aDataStart = anUriData + 5;
800 for (const char* aDataIter = aDataStart; *aDataIter != '\0'; ++aDataIter)
801 {
802 if (::memcmp (aDataIter, ";base64,", 8) == 0)
803 {
804 const char* aBase64End = anUriData + anUriVal->GetStringLength();
805 const char* aBase64Data = aDataIter + 8;
806 const size_t aBase64Len = size_t(aBase64End - aBase64Data);
807 //const TCollection_AsciiString aMime (aDataStart, aDataIter - aDataStart);
808 Handle(NCollection_Buffer) aData = FSD_Base64Decoder::Decode ((const Standard_Byte* )aBase64Data, aBase64Len);
809 theTexture = new Image_Texture (aData, myFilePath + "@" + getKeyString (*aSrcVal));
810 return true;
811 }
812 }
813 Message::DefaultMessenger()->Send ("glTF reader - embedded image has been skipped", Message_Warning);
814 return false;
815 }
816
817 TCollection_AsciiString anImageFile = myFolder + anUriVal->GetString();
818 theTexture = new Image_Texture (anImageFile);
819 if (myExternalFiles != NULL)
820 {
821 myExternalFiles->Add (anImageFile);
822 }
823 return true;
824}
825
826// =======================================================================
827// function : gltfParseTexturInGlbBuffer
828// purpose :
829// =======================================================================
830bool RWGltf_GltfJsonParser::gltfParseTexturInGlbBuffer (Handle(Image_Texture)& theTexture,
831 const RWGltf_JsonValue& theBinVal,
832 const TCollection_AsciiString& theBufferViewId,
833 const RWGltf_JsonValue& theBufferView)
834{
835 const RWGltf_JsonValue* aMimeTypeVal = findObjectMember (theBinVal, "mimeType");
836 //const RWGltf_JsonValue* aWidthVal = findObjectMember (theBinVal, "width");
837 //const RWGltf_JsonValue* aHeightVal = findObjectMember (theBinVal, "height");
838 (void )aMimeTypeVal;
839
840 const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
841 const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
842 const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
843 if (aBufferName != NULL
844 && aBufferName->IsString()
845 && !IsEqual (aBufferName->GetString(), "binary_glTF"))
846 {
847 reportGltfError ("BufferView '" + theBufferViewId + "' does not define binary_glTF buffer.");
848 return false;
849 }
850
851 RWGltf_GltfBufferView aBuffView;
852 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
853 ? (int64_t )aByteOffset->GetDouble()
854 : 0;
855 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
856 ? (int64_t )aByteLength->GetDouble()
857 : 0;
858 if (aBuffView.ByteLength <= 0)
859 {
860 reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteLength.");
861 return false;
862 }
863 else if (aBuffView.ByteOffset < 0)
864 {
865 reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteOffset.");
866 return false;
867 }
868
869 const int64_t anOffset = myBinBodyOffset + aBuffView.ByteOffset;
870 theTexture = new Image_Texture (myFilePath, anOffset, aBuffView.ByteLength);
871 return true;
872}
873
874// =======================================================================
875// function : gltfParseTextureInBufferView
876// purpose :
877// =======================================================================
878bool RWGltf_GltfJsonParser::gltfParseTextureInBufferView (Handle(Image_Texture)& theTexture,
879 const TCollection_AsciiString& theSourceId,
880 const TCollection_AsciiString& theBufferViewId,
881 const RWGltf_JsonValue& theBufferView)
882{
883 const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
884 const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
885 const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
886 if (aBufferName == NULL)
887 {
888 reportGltfError ("BufferView '" + theBufferViewId + "' does not define buffer.");
889 return false;
890 }
891
892 const TCollection_AsciiString aBufferId = getKeyString (*aBufferName);
893 const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
894 if (aBuffer == NULL
895 || !aBuffer->IsObject())
896 {
897 reportGltfError ("BufferView '" + theBufferViewId + "' refers to non-existing buffer.");
898 return false;
899 }
900
901 RWGltf_GltfBufferView aBuffView;
902 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
903 ? (int64_t )aByteOffset->GetDouble()
904 : 0;
905 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
906 ? (int64_t )aByteLength->GetDouble()
907 : 0;
908 if (aBuffView.ByteLength <= 0)
909 {
910 reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteLength.");
911 return false;
912 }
913 else if (aBuffView.ByteOffset < 0)
914 {
915 reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteOffset.");
916 return false;
917 }
918
919 const RWGltf_JsonValue* anUriVal = findObjectMember (*aBuffer, "uri");
920 if (anUriVal == NULL
921 || !anUriVal->IsString())
922 {
923 reportGltfError ("Buffer '" + aBufferId + "' does not define uri.");
924 return false;
925 }
926
927 const char* anUriData = anUriVal->GetString();
928 if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
929 {
930 Handle(NCollection_Buffer) aBaseBuffer;
931 if (!myDecodedBuffers.Find (aBufferId, aBaseBuffer))
932 {
933 aBaseBuffer = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
934 myDecodedBuffers.Bind (aBufferId, aBaseBuffer);
935 }
936
937 Handle(RWGltf_SubBuffer) aSubBuffer = new RWGltf_SubBuffer (aBaseBuffer, (Standard_Size )aBuffView.ByteOffset, (Standard_Size )aBuffView.ByteLength);
938 theTexture = new Image_Texture (aSubBuffer, myFilePath + "@" + theSourceId);
939 return true;
940 }
941
942 const TCollection_AsciiString anUri (anUriData);
943 if (anUri.IsEmpty())
944 {
945 reportGltfError ("Buffer '" + aBufferId + "' does not define uri.");
946 return false;
947 }
948
949 const TCollection_AsciiString aPath = myFolder + anUri;
950 bool isFileExist = false;
951 if (!myProbedFiles.Find (aPath, isFileExist))
952 {
953 isFileExist = OSD_File (aPath).Exists();
954 myProbedFiles.Bind (aPath, isFileExist);
955 }
956 if (!isFileExist)
957 {
958 reportGltfError ("Buffer '" + aBufferId + "' refers to non-existing file '" + anUri + "'.");
959 return false;
960 }
961
962 theTexture = new Image_Texture (aPath, aBuffView.ByteOffset, aBuffView.ByteLength);
963 if (myExternalFiles != NULL)
964 {
965 myExternalFiles->Add (aPath);
966 }
967 return true;
968}
969
970// =======================================================================
971// function : gltfParseScene
972// purpose :
973// =======================================================================
974bool RWGltf_GltfJsonParser::gltfParseScene (const Handle(Message_ProgressIndicator)& theProgress)
975{
976 // search default scene
977 const RWGltf_JsonValue* aDefScene = myGltfRoots[RWGltf_GltfRootElement_Scenes].FindChild (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root());
978 if (aDefScene == NULL)
979 {
980 reportGltfError ("Default scene is not found.");
981 return false;
982 }
983
984 const RWGltf_JsonValue* aSceneNodes = findObjectMember (*aDefScene, "nodes");
985 if (aSceneNodes == NULL
986 || !aSceneNodes->IsArray())
987 {
988 reportGltfError ("Empty scene '" + getKeyString (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root()) + "'.");
989 return false;
990 }
991
992 return gltfParseSceneNodes (*myRootShapes, *aSceneNodes, theProgress);
993}
994
995// =======================================================================
996// function : gltfParseSceneNodes
997// purpose :
998// =======================================================================
999bool RWGltf_GltfJsonParser::gltfParseSceneNodes (TopTools_SequenceOfShape& theShapeSeq,
1000 const RWGltf_JsonValue& theSceneNodes,
1001 const Handle(Message_ProgressIndicator)& theProgress)
1002{
1003 if (!theSceneNodes.IsArray())
1004 {
1005 reportGltfError ("Scene nodes is not array.");
1006 return false;
1007 }
1008
1009 Message_ProgressSentry aPSentry (theProgress, "Reading scene nodes", 0, theSceneNodes.Size(), 1);
1010 for (rapidjson::Value::ConstValueIterator aSceneNodeIter = theSceneNodes.Begin();
1011 aSceneNodeIter != theSceneNodes.End() && aPSentry.More(); ++aSceneNodeIter, aPSentry.Next())
1012 {
1013 const RWGltf_JsonValue* aSceneNode = myGltfRoots[RWGltf_GltfRootElement_Nodes].FindChild (*aSceneNodeIter);
1014 if (aSceneNode == NULL)
1015 {
1016 reportGltfWarning ("Scene refers to non-existing node '" + getKeyString (*aSceneNodeIter) + "'.");
1017 return true;
1018 }
1019
1020 TopoDS_Shape aNodeShape;
1021 if (!gltfParseSceneNode (aNodeShape, getKeyString (*aSceneNodeIter), *aSceneNode, theProgress))
1022 {
1023 return false;
1024 }
1025
1026 if (aNodeShape.IsNull())
1027 {
1028 continue;
1029 }
1030 else if (myToSkipEmptyNodes
1031 && !TopExp_Explorer (aNodeShape, TopAbs_FACE).More())
1032 {
1033 continue;
1034 }
1035
1036 theShapeSeq.Append (aNodeShape);
1037 }
1038 return true;
1039}
1040
1041// =======================================================================
1042// function : gltfParseSceneNode
1043// purpose :
1044// =======================================================================
1045bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
1046 const TCollection_AsciiString& theSceneNodeId,
1047 const RWGltf_JsonValue& theSceneNode,
1048 const Handle(Message_ProgressIndicator)& theProgress)
1049{
1050 const RWGltf_JsonValue* aName = findObjectMember (theSceneNode, "name");
1051 //const RWGltf_JsonValue* aJointName = findObjectMember (theSceneNode, "jointName");
1052 const RWGltf_JsonValue* aChildren = findObjectMember (theSceneNode, "children");
1053 const RWGltf_JsonValue* aMeshes_1 = findObjectMember (theSceneNode, "meshes");
1054 const RWGltf_JsonValue* aMesh_2 = findObjectMember (theSceneNode, "mesh");
1055 //const RWGltf_JsonValue* aCamera = findObjectMember (theSceneNode, "camera");
1056 const RWGltf_JsonValue* aTrsfMatVal = findObjectMember (theSceneNode, "matrix");
1057 const RWGltf_JsonValue* aTrsfRotVal = findObjectMember (theSceneNode, "rotation");
1058 const RWGltf_JsonValue* aTrsfScaleVal = findObjectMember (theSceneNode, "scale");
1059 const RWGltf_JsonValue* aTrsfTransVal = findObjectMember (theSceneNode, "translation");
1060 if (findNodeShape (theNodeShape, theSceneNodeId))
1061 {
1062 return true;
1063 }
1064
1065 TopLoc_Location aNodeLoc;
1066 const bool hasTrs = aTrsfRotVal != NULL
1067 || aTrsfScaleVal != NULL
1068 || aTrsfTransVal != NULL;
1069 if (aTrsfMatVal != NULL)
1070 {
1071 if (hasTrs)
1072 {
1073 reportGltfError ("Scene node '" + theSceneNodeId + "' defines ambiguous transformation.");
1074 return false;
1075 }
1076 else if (!aTrsfMatVal->IsArray()
1077 || aTrsfMatVal->Size() != 16)
1078 {
1079 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix array.");
1080 return false;
1081 }
1082
1083 Graphic3d_Mat4d aMat4;
1084 for (int aColIter = 0; aColIter < 4; ++aColIter)
1085 {
1086 for (int aRowIter = 0; aRowIter < 4; ++aRowIter)
1087 {
1088 const RWGltf_JsonValue& aGenVal = (*aTrsfMatVal)[aColIter * 4 + aRowIter];
1089 if (!aGenVal.IsNumber())
1090 {
1091 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix.");
1092 return false;
1093 }
1094 aMat4.SetValue (aRowIter, aColIter, aGenVal.GetDouble());
1095 }
1096 }
1097
1098 if (!aMat4.IsIdentity())
1099 {
1100 gp_Trsf aTrsf;
1101 aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
1102 aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
1103 aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
1104 myCSTrsf.TransformTransformation (aTrsf);
1105 if (aTrsf.Form() != gp_Identity)
1106 {
1107 aNodeLoc = TopLoc_Location (aTrsf);
1108 }
1109 }
1110 }
1111 else if (hasTrs)
1112 {
1113 gp_Trsf aTrsf;
1114 if (aTrsfRotVal != NULL)
1115 {
1116 if (!aTrsfRotVal->IsArray()
1117 || aTrsfRotVal->Size() != 4)
1118 {
1119 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation quaternion.");
1120 return false;
1121 }
1122
1123 Graphic3d_Vec4d aRotVec4;
1124 for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
1125 {
1126 const RWGltf_JsonValue& aGenVal = (*aTrsfRotVal)[aCompIter];
1127 if (!aGenVal.IsNumber())
1128 {
1129 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation.");
1130 return false;
1131 }
1132 aRotVec4[aCompIter] = aGenVal.GetDouble();
1133 }
1134 const gp_Quaternion aQuaternion (aRotVec4.x(), aRotVec4.y(), aRotVec4.z(), aRotVec4.w());
1135 if (Abs (aQuaternion.X()) > gp::Resolution()
1136 || Abs (aQuaternion.Y()) > gp::Resolution()
1137 || Abs (aQuaternion.Z()) > gp::Resolution()
1138 || Abs (aQuaternion.W() - 1.0) > gp::Resolution())
1139 {
1140 aTrsf.SetRotation (aQuaternion);
1141 }
1142 }
1143
1144 if (aTrsfTransVal != NULL)
1145 {
1146 if (!aTrsfTransVal->IsArray()
1147 || aTrsfTransVal->Size() != 3)
1148 {
1149 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation vector.");
1150 return false;
1151 }
1152
1153 gp_XYZ aTransVec;
1154 for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
1155 {
1156 const RWGltf_JsonValue& aGenVal = (*aTrsfTransVal)[aCompIter];
1157 if (!aGenVal.IsNumber())
1158 {
1159 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation.");
1160 return false;
1161 }
1162 aTransVec.SetCoord (aCompIter + 1, aGenVal.GetDouble());
1163 }
1164 aTrsf.SetTranslationPart (aTransVec);
1165 }
1166
1167 if (aTrsfScaleVal != NULL)
1168 {
1169 Graphic3d_Vec3d aScaleVec;
1170 if (!aTrsfScaleVal->IsArray()
1171 || aTrsfScaleVal->Size() != 3)
1172 {
1173 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale vector.");
1174 return false;
1175 }
1176 for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
1177 {
1178 const RWGltf_JsonValue& aGenVal = (*aTrsfScaleVal)[aCompIter];
1179 if (!aGenVal.IsNumber())
1180 {
1181 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
1182 return false;
1183 }
1184 aScaleVec[aCompIter] = aGenVal.GetDouble();
1185 if (Abs (aScaleVec[aCompIter]) <= gp::Resolution())
1186 {
1187 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
1188 return false;
1189 }
1190 }
1191
1192 if (Abs (aScaleVec.x() - aScaleVec.y()) > Precision::Confusion()
1193 || Abs (aScaleVec.y() - aScaleVec.z()) > Precision::Confusion()
1194 || Abs (aScaleVec.x() - aScaleVec.z()) > Precision::Confusion())
1195 {
1196 Graphic3d_Mat4d aScaleMat;
1197 aScaleMat.SetDiagonal (aScaleVec);
1198
1199 Graphic3d_Mat4d aMat4;
1200 aTrsf.GetMat4 (aMat4);
1201
1202 aMat4 = aMat4 * aScaleMat;
1203 aTrsf = gp_Trsf();
1204 aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
1205 aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
1206 aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
1207
1208 Message::DefaultMessenger()->Send (TCollection_AsciiString ("glTF reader, scene node '")
1209 + theSceneNodeId + "' defines unsupported scaling "
1210 + aScaleVec.x() + " " + aScaleVec.y() + " " + aScaleVec.z(), Message_Warning);
1211 }
1212 else if (Abs (aScaleVec.x() - 1.0) > Precision::Confusion())
1213 {
1214 aTrsf.SetScaleFactor (aScaleVec.x());
1215 }
1216 }
1217
1218 myCSTrsf.TransformTransformation (aTrsf);
1219 if (aTrsf.Form() != gp_Identity)
1220 {
1221 aNodeLoc = TopLoc_Location (aTrsf);
1222 }
1223 }
1224
1225 BRep_Builder aBuilder;
1226 TopoDS_Compound aNodeShape;
1227 aBuilder.MakeCompound (aNodeShape);
1228 TopTools_SequenceOfShape aChildShapes;
1229 int aNbSubShapes = 0;
1230 if (aChildren != NULL
1231 && !gltfParseSceneNodes (aChildShapes, *aChildren, theProgress))
1232 {
1233 theNodeShape = aNodeShape;
1234 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1235 return false;
1236 }
1237 for (TopTools_SequenceOfShape::Iterator aChildShapeIter (aChildShapes); aChildShapeIter.More(); aChildShapeIter.Next())
1238 {
1239 aBuilder.Add (aNodeShape, aChildShapeIter.Value());
1240 ++aNbSubShapes;
1241 }
1242
1243 if (aMeshes_1 != NULL
1244 && aMeshes_1->IsArray())
1245 {
1246 // glTF 1.0
1247 Message_ProgressSentry aPSentry (theProgress, "Reading scene meshes", 0, aMeshes_1->Size(), 1);
1248 for (rapidjson::Value::ConstValueIterator aMeshIter = aMeshes_1->Begin();
1249 aMeshIter != aMeshes_1->End() && aPSentry.More(); ++aMeshIter, aPSentry.Next())
1250 {
1251 const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMeshIter);
1252 if (aMesh == NULL)
1253 {
1254 theNodeShape = aNodeShape;
1255 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1256 reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1257 return false;
1258 }
1259
1260 TopoDS_Shape aMeshShape;
1261 if (!gltfParseMesh (aMeshShape, getKeyString (*aMeshIter), *aMesh, theProgress))
1262 {
1263 theNodeShape = aNodeShape;
1264 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1265 return false;
1266 }
1267 if (!aMeshShape.IsNull())
1268 {
1269 aBuilder.Add (aNodeShape, aMeshShape);
1270 ++aNbSubShapes;
1271 }
1272 }
1273 }
1274 if (aMesh_2 != NULL)
1275 {
1276 // glTF 2.0
1277 const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMesh_2);
1278 if (aMesh == NULL)
1279 {
1280 theNodeShape = aNodeShape;
1281 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1282 reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1283 return false;
1284 }
1285
1286 TopoDS_Shape aMeshShape;
1287 if (!gltfParseMesh (aMeshShape, getKeyString (*aMesh_2), *aMesh, theProgress))
1288 {
1289 theNodeShape = aNodeShape;
1290 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1291 return false;
1292 }
1293 if (!aMeshShape.IsNull())
1294 {
1295 aBuilder.Add (aNodeShape, aMeshShape);
1296 ++aNbSubShapes;
1297 }
1298 }
1299
1300 if (aNbSubShapes == 1)
1301 {
1302 theNodeShape = TopoDS_Iterator (aNodeShape).Value();
1303 }
1304 else
1305 {
1306 theNodeShape = aNodeShape;
1307 }
1308 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1309 return true;
1310}
1311
1312// =======================================================================
1313// function : gltfParseMesh
1314// purpose :
1315// =======================================================================
1316bool RWGltf_GltfJsonParser::gltfParseMesh (TopoDS_Shape& theMeshShape,
1317 const TCollection_AsciiString& theMeshId,
1318 const RWGltf_JsonValue& theMesh,
1319 const Handle(Message_ProgressIndicator)& theProgress)
1320{
1321 const RWGltf_JsonValue* aName = findObjectMember (theMesh, "name");
1322 const RWGltf_JsonValue* aPrims = findObjectMember (theMesh, "primitives");
1323 if (!aPrims->IsArray())
1324 {
1325 reportGltfError ("Primitive array attributes within Mesh '" + theMeshId + "' is not an array.");
1326 return false;
1327 }
1328
1329 if (findMeshShape (theMeshShape, theMeshId))
1330 {
1331 return true;
1332 }
1333
1334 BRep_Builder aBuilder;
1335 TopoDS_Compound aMeshShape;
1336 int aNbFaces = 0;
1337 for (rapidjson::Value::ConstValueIterator aPrimArrIter = aPrims->Begin();
1338 aPrimArrIter != aPrims->End(); ++aPrimArrIter)
1339 {
1340 TCollection_AsciiString aUserName;
1341 if (aName != NULL
1342 && aName->IsString())
1343 {
1344 aUserName = aName->GetString();
1345 }
1346
1347 Handle(RWGltf_GltfLatePrimitiveArray) aMeshData = new RWGltf_GltfLatePrimitiveArray (theMeshId, aUserName);
1348 if (!gltfParsePrimArray (aMeshData, theMeshId, *aPrimArrIter, theProgress))
1349 {
1350 return false;
1351 }
1352
1353 if (!aMeshData->Data().IsEmpty())
1354 {
1355 if (aMeshShape.IsNull())
1356 {
1357 aBuilder.MakeCompound (aMeshShape);
1358 }
1359
1360 TopoDS_Face aFace;
1361 aBuilder.MakeFace (aFace, aMeshData);
1362 aBuilder.Add (aMeshShape, aFace);
1363 if (myAttribMap != NULL
1364 && aMeshData->HasStyle())
1365 {
1366 RWMesh_NodeAttributes aShapeAttribs;
1367 aShapeAttribs.RawName = aUserName;
1368
1369 // assign material and not color
1370 //aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor());
1371
1372 Handle(XCAFDoc_VisMaterial) aMat;
1373 myMaterials.Find (!aMeshData->MaterialPbr().IsNull() ? aMeshData->MaterialPbr()->Id : aMeshData->MaterialCommon()->Id, aMat);
1374 aShapeAttribs.Style.SetMaterial (aMat);
1375
1376 myAttribMap->Bind (aFace, aShapeAttribs);
1377 }
1378 myFaceList.Append (aFace);
1379 ++aNbFaces;
1380 }
1381 }
1382
1383 if (aNbFaces == 1)
1384 {
1385 theMeshShape = TopoDS_Iterator (aMeshShape).Value();
1386 }
1387 else
1388 {
1389 theMeshShape = aMeshShape;
1390 }
1391 bindMeshShape (theMeshShape, theMeshId, aName);
1392 return true;
1393}
1394
1395// =======================================================================
1396// function : gltfParsePrimArray
1397// purpose :
1398// =======================================================================
1399bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1400 const TCollection_AsciiString& theMeshId,
1401 const RWGltf_JsonValue& thePrimArray,
1402 const Handle(Message_ProgressIndicator)& /*theProgress*/)
1403{
1404 const RWGltf_JsonValue* anAttribs = findObjectMember (thePrimArray, "attributes");
1405 const RWGltf_JsonValue* anIndices = findObjectMember (thePrimArray, "indices");
1406 const RWGltf_JsonValue* aMaterial = findObjectMember (thePrimArray, "material");
1407 const RWGltf_JsonValue* aModeVal = findObjectMember (thePrimArray, "mode");
1408 RWGltf_GltfPrimitiveMode aMode = RWGltf_GltfPrimitiveMode_Triangles;
1409 if (anAttribs == NULL
1410 || !anAttribs->IsObject())
1411 {
1412 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' defines no attributes.");
1413 return false;
1414 }
1415 else if (aModeVal != NULL)
1416 {
1417 aMode = RWGltf_GltfPrimitiveMode_UNKNOWN;
1418 if (aModeVal->IsInt())
1419 {
1420 aMode = (RWGltf_GltfPrimitiveMode )aModeVal->GetInt();
1421 }
1422 if (aMode < RWGltf_GltfPrimitiveMode_Points
1423 || aMode > RWGltf_GltfPrimitiveMode_TriangleFan)
1424 {
1425 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' has unknown mode.");
1426 return false;
1427 }
1428 }
1429 if (aMode != RWGltf_GltfPrimitiveMode_Triangles)
1430 {
1431 Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Primitive array within Mesh '"
1432 + theMeshId + "' skipped due to unsupported mode.", Message_Warning);
1433 return true;
1434 }
1435 theMeshData->SetPrimitiveMode (aMode);
1436
1437 // assign material
1438 if (aMaterial != NULL)
1439 {
1440 Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
1441 if (myMaterialsPbr.Find (getKeyString (*aMaterial), aMatPbr))
1442 {
1443 theMeshData->SetMaterialPbr (aMatPbr);
1444 }
1445
1446 Handle(RWGltf_MaterialCommon) aMatCommon;
1447 if (myMaterialsCommon.Find (getKeyString (*aMaterial), aMatCommon))
1448 {
1449 theMeshData->SetMaterialCommon (aMatCommon);
1450 }
1451 }
1452
1453 bool hasPositions = false;
1454 for (rapidjson::Value::ConstMemberIterator anAttribIter = anAttribs->MemberBegin();
1455 anAttribIter != anAttribs->MemberEnd(); ++anAttribIter)
1456 {
1457 const TCollection_AsciiString anAttribId = getKeyString (anAttribIter->value);
1458 if (anAttribId.IsEmpty())
1459 {
1460 reportGltfError ("Primitive array attribute accessor key within Mesh '" + theMeshId + "' is not a string.");
1461 return false;
1462 }
1463
1464 RWGltf_GltfArrayType aType = RWGltf_GltfParseAttribType (anAttribIter->name.GetString());
1465 if (aType == RWGltf_GltfArrayType_UNKNOWN)
1466 {
1467 // just ignore unknown attributes
1468 continue;
1469 }
1470
1471 const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (anAttribIter->value);
1472 if (anAccessor == NULL
1473 || !anAccessor->IsObject())
1474 {
1475 reportGltfError ("Primitive array attribute accessor key '" + anAttribId + "' points to non-existing object.");
1476 return false;
1477 }
1478 else if (!gltfParseAccessor (theMeshData, anAttribId, *anAccessor, aType))
1479 {
1480 return false;
1481 }
1482 else if (aType == RWGltf_GltfArrayType_Position)
1483 {
1484 hasPositions = true;
1485 }
1486 }
1487 if (!hasPositions)
1488 {
1489 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' does not define vertex positions.");
1490 return false;
1491 }
1492
1493 if (anIndices != NULL)
1494 {
1495 const TCollection_AsciiString anIndicesId = getKeyString (*anIndices);
1496 const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (*anIndices);
1497 if (anAccessor == NULL
1498 || !anAccessor->IsObject())
1499 {
1500 reportGltfError ("Primitive array indices accessor key '" + anIndicesId + "' points to non-existing object.");
1501 return false;
1502 }
1503 else if (!gltfParseAccessor (theMeshData, anIndicesId, *anAccessor, RWGltf_GltfArrayType_Indices))
1504 {
1505 return false;
1506 }
1507 }
1508
1509 return true;
1510}
1511
1512// =======================================================================
1513// function : gltfParseAccessor
1514// purpose :
1515// =======================================================================
1516bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1517 const TCollection_AsciiString& theName,
1518 const RWGltf_JsonValue& theAccessor,
1519 const RWGltf_GltfArrayType theType)
1520{
1521 RWGltf_GltfAccessor aStruct;
1522 const RWGltf_JsonValue* aTypeStr = findObjectMember (theAccessor, "type");
1523 const RWGltf_JsonValue* aBufferViewName = findObjectMember (theAccessor, "bufferView");
1524 const RWGltf_JsonValue* aByteOffset = findObjectMember (theAccessor, "byteOffset");
1525 const RWGltf_JsonValue* aByteStride = findObjectMember (theAccessor, "byteStride"); // byteStride was part of bufferView in glTF 1.0
1526 const RWGltf_JsonValue* aCompType = findObjectMember (theAccessor, "componentType");
1527 const RWGltf_JsonValue* aCount = findObjectMember (theAccessor, "count");
1528 if (aTypeStr == NULL
1529 || !aTypeStr->IsString())
1530 {
1531 reportGltfError ("Accessor '" + theName + "' does not define type.");
1532 return false;
1533 }
1534 aStruct.Type = RWGltf_GltfParseAccessorType (aTypeStr->GetString());
1535 if (aStruct.Type == RWGltf_GltfAccessorLayout_UNKNOWN)
1536 {
1537 reportGltfError ("Accessor '" + theName + "' has invalid type.");
1538 return false;
1539 }
1540
1541 if (aBufferViewName == NULL)
1542 {
1543 reportGltfError ("Accessor '" + theName + "' does not define bufferView.");
1544 return false;
1545 }
1546 if (aCompType == NULL
1547 || !aCompType->IsInt())
1548 {
1549 reportGltfError ("Accessor '" + theName + "' does not define componentType.");
1550 return false;
1551 }
1552 aStruct.ComponentType = (RWGltf_GltfAccessorCompType )aCompType->GetInt();
1553 if (aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int8
1554 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt8
1555 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int16
1556 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt16
1557 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt32
1558 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Float32)
1559 {
1560 reportGltfError ("Accessor '" + theName + "' defines invalid componentType value.");
1561 return false;
1562 }
1563
1564 if (aCount == NULL
1565 || !aCount->IsNumber())
1566 {
1567 reportGltfError ("Accessor '" + theName + "' does not define count.");
1568 return false;
1569 }
1570
1571 aStruct.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1572 ? (int64_t )aByteOffset->GetDouble()
1573 : 0;
1574 aStruct.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1575 ? aByteStride->GetInt()
1576 : 0;
1577 aStruct.Count = (int64_t )aCount->GetDouble();
1578
1579 if (aStruct.ByteOffset < 0)
1580 {
1581 reportGltfError ("Accessor '" + theName + "' defines invalid byteOffset.");
1582 return false;
1583 }
1584 else if (aStruct.ByteStride < 0
1585 || aStruct.ByteStride > 255)
1586 {
1587 reportGltfError ("Accessor '" + theName + "' defines invalid byteStride.");
1588 return false;
1589 }
1590 else if (aStruct.Count < 1)
1591 {
1592 reportGltfError ("Accessor '" + theName + "' defines invalid count.");
1593 return false;
1594 }
1595
1596 // Read Min/Max values for POSITION type. It is used for bounding boxes
1597 if (theType == RWGltf_GltfArrayType_Position)
1598 {
1599 const RWGltf_JsonValue* aMin = findObjectMember (theAccessor, "min");
1600 const RWGltf_JsonValue* aMax = findObjectMember (theAccessor, "max");
1601 if (aMin != NULL && aMax != NULL)
1602 {
1603 // Note: Min/Max values can be not defined in glTF file.
1604 // In this case it is not used only.
1605 if (!aMin->IsArray() || !aMax->IsArray() ||
1606 aMin->Size() != 3 || aMax->Size() != 3)
1607 {
1608 reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max values.");
1609 }
1610 else
1611 {
1612 bool isValidMinMax = true;
1613 gp_Pnt aMinPnt, aMaxPnt;
1614 for (int anIter = 0; anIter < 3; ++anIter)
1615 {
1616 const RWGltf_JsonValue& aMinVal = (*aMin)[anIter];
1617 const RWGltf_JsonValue& aMaxVal = (*aMax)[anIter];
1618 if (!aMinVal.IsNumber() || !aMaxVal.IsNumber())
1619 {
1620 reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max value.");
1621 isValidMinMax = false;
1622 break;
1623 }
1624 aMinPnt.SetCoord (anIter + 1, aMinVal.GetDouble());
1625 aMinPnt.SetCoord (anIter + 1, aMaxVal.GetDouble());
1626 }
1627 if (isValidMinMax)
1628 {
1629 myCSTrsf.TransformPosition (aMinPnt.ChangeCoord());
1630 myCSTrsf.TransformPosition (aMaxPnt.ChangeCoord());
1631
1632 Bnd_Box aBox;
1633 aBox.Add (aMinPnt);
1634 aBox.Add (aMaxPnt);
1635
1636 theMeshData->SetBoundingBox (aBox);
1637 }
1638 }
1639 }
1640 }
1641
1642 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
1643 if (aBufferView == NULL
1644 || !aBufferView->IsObject())
1645 {
1646 reportGltfError ("Accessor '" + theName + "' refers to non-existing bufferView.");
1647 return false;
1648 }
1649
1650 return gltfParseBufferView (theMeshData, getKeyString (*aBufferViewName), *aBufferView, aStruct, theType);
1651}
1652
1653// =======================================================================
1654// function : gltfParseBufferView
1655// purpose :
1656// =======================================================================
1657bool RWGltf_GltfJsonParser::gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1658 const TCollection_AsciiString& theName,
1659 const RWGltf_JsonValue& theBufferView,
1660 const RWGltf_GltfAccessor& theAccessor,
1661 const RWGltf_GltfArrayType theType)
1662{
1663 RWGltf_GltfBufferView aBuffView;
1664 const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
1665 const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
1666 const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
1667 const RWGltf_JsonValue* aByteStride = findObjectMember (theBufferView, "byteStride"); // byteStride is part of bufferView since glTF 2.0
1668 const RWGltf_JsonValue* aTarget = findObjectMember (theBufferView, "target");
1669 if (aBufferName == NULL)
1670 {
1671 reportGltfError ("BufferView '" + theName + "' does not define buffer.");
1672 return false;
1673 }
1674
1675 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1676 ? (int64_t )aByteOffset->GetDouble()
1677 : 0;
1678 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
1679 ? (int64_t )aByteLength->GetDouble()
1680 : 0;
1681 aBuffView.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1682 ? aByteStride->GetInt()
1683 : 0;
1684 if (aTarget != NULL && aTarget->IsInt())
1685 {
1686 aBuffView.Target = (RWGltf_GltfBufferViewTarget )aTarget->GetInt();
1687 if (aBuffView.Target != RWGltf_GltfBufferViewTarget_ARRAY_BUFFER
1688 && aBuffView.Target != RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER)
1689 {
1690 reportGltfError ("BufferView '" + theName + "' defines invalid target.");
1691 return false;
1692 }
1693 }
1694
1695 if (aBuffView.ByteLength <= 0)
1696 {
1697 reportGltfError ("BufferView '" + theName + "' defines invalid byteLength.");
1698 return false;
1699 }
1700 else if (aBuffView.ByteOffset < 0)
1701 {
1702 reportGltfError ("BufferView '" + theName + "' defines invalid byteOffset.");
1703 return false;
1704 }
1705 else if (aBuffView.ByteStride < 0
1706 || aBuffView.ByteStride > 255)
1707 {
1708 reportGltfError ("BufferView '" + theName + "' defines invalid byteStride.");
1709 return false;
1710 }
1711
1712 const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
1713 if (aBuffer == NULL
1714 || !aBuffer->IsObject())
1715 {
1716 reportGltfError ("BufferView '" + theName + "' refers to non-existing buffer.");
1717 return false;
1718 }
1719
1720 return gltfParseBuffer (theMeshData, getKeyString (*aBufferName), *aBuffer, theAccessor, aBuffView, theType);
1721}
1722
1723// =======================================================================
1724// function : gltfParseBuffer
1725// purpose :
1726// =======================================================================
1727bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1728 const TCollection_AsciiString& theName,
1729 const RWGltf_JsonValue& theBuffer,
1730 const RWGltf_GltfAccessor& theAccessor,
1731 const RWGltf_GltfBufferView& theView,
1732 const RWGltf_GltfArrayType theType)
1733{
1734 //const RWGltf_JsonValue* aType = findObjectMember (theBuffer, "type");
1735 //const RWGltf_JsonValue* aByteLength = findObjectMember (theBuffer, "byteLength");
1736 const RWGltf_JsonValue* anUriVal = findObjectMember (theBuffer, "uri");
1737
1738 int64_t anOffset = theView.ByteOffset + theAccessor.ByteOffset;
1739 const int32_t aByteStride = theAccessor.ByteStride != 0 ? theView.ByteStride : theView.ByteStride;
1740 bool isBinary = false;
1741 if (myIsBinary)
1742 {
1743 isBinary = IsEqual ("binary_glTF", theName) // glTF 1.0
1744 || anUriVal == NULL; // glTF 2.0
1745 }
1746 if (isBinary)
1747 {
1748 anOffset += myBinBodyOffset;
1749
1750 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1751 aData.Accessor = theAccessor;
1752 aData.Accessor.ByteStride = aByteStride;
1753 aData.StreamOffset = anOffset;
1754 aData.StreamUri = myFilePath;
1755 return true;
1756 }
1757
1758 if (anUriVal == NULL
1759 || !anUriVal->IsString())
1760 {
1761 reportGltfError ("Buffer '" + theName + "' does not define uri.");
1762 return false;
1763 }
1764
1765 const char* anUriData = anUriVal->GetString();
1766 if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
1767 {
1768 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1769 aData.Accessor = theAccessor;
1770 aData.Accessor.ByteStride = aByteStride;
1771 aData.StreamOffset = anOffset;
1772 if (!myDecodedBuffers.Find (theName, aData.StreamData))
1773 {
1774 // it is better decoding in multiple threads
1775 aData.StreamData = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
1776 myDecodedBuffers.Bind (theName, aData.StreamData);
1777 }
1778 return true;
1779 }
1780 else
1781 {
1782 TCollection_AsciiString anUri = anUriData;
1783 if (anUri.IsEmpty())
1784 {
1785 reportGltfError ("Buffer '" + theName + "' does not define uri.");
1786 return false;
1787 }
1788
1789 TCollection_AsciiString aPath = myFolder + anUri;
1790 bool isFileExist = false;
1791 if (!myProbedFiles.Find (aPath, isFileExist))
1792 {
1793 isFileExist = OSD_File (aPath).Exists();
1794 myProbedFiles.Bind (aPath, isFileExist);
1795 }
1796 if (!isFileExist)
1797 {
1798 reportGltfError ("Buffer '" + theName + "' refers to non-existing file '" + anUri + "'.");
1799 return false;
1800 }
1801
1802 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1803 aData.Accessor = theAccessor;
1804 aData.Accessor.ByteStride = aByteStride;
1805 aData.StreamOffset = anOffset;
1806 aData.StreamUri = myFolder + anUri;
1807 if (myExternalFiles != NULL)
1808 {
1809 myExternalFiles->Add (aData.StreamUri);
1810 }
1811 return true;
1812 }
1813}
1814
1815// =======================================================================
1816// function : bindNamedShape
1817// purpose :
1818// =======================================================================
1819void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape,
1820 ShapeMapGroup theGroup,
1821 const TopLoc_Location& theLoc,
1822 const TCollection_AsciiString& theId,
1823 const RWGltf_JsonValue* theUserName)
1824{
1825 if (theShape.IsNull())
1826 {
1827 return;
1828 }
1829
1830 if (!theLoc.IsIdentity())
1831 {
1832 theShape.Location (theLoc);
1833 }
1834
1835 TCollection_AsciiString aUserName;
1836 if (theUserName != NULL
1837 && theUserName->IsString())
1838 {
1839 aUserName = theUserName->GetString();
1840 }
1841 else if (myIsGltf1)
1842 {
1843 aUserName = theId;
1844 }
1845
1846 myShapeMap[theGroup].Bind (theId, theShape);
1847 if (myAttribMap != NULL)
1848 {
1849 RWMesh_NodeAttributes aShapeAttribs;
1850 aShapeAttribs.Name = aUserName;
1851 aShapeAttribs.RawName = theId;
1852 if (theShape.ShapeType() == TopAbs_FACE)
1853 {
1854 TopLoc_Location aDummy;
1855 if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (theShape), aDummy)))
1856 {
1857 if (aLateData->HasStyle())
1858 {
1859 // assign material and not color
1860 //aShapeAttribs.Style.SetColorSurf (aLateData->BaseColor());
1861
1862 Handle(XCAFDoc_VisMaterial) aMat;
1863 myMaterials.Find (!aLateData->MaterialPbr().IsNull() ? aLateData->MaterialPbr()->Id : aLateData->MaterialCommon()->Id, aMat);
1864 aShapeAttribs.Style.SetMaterial (aMat);
1865 }
1866 if (aShapeAttribs.Name.IsEmpty()
1867 && myUseMeshNameAsFallback)
1868 {
1869 // fallback using Mesh name
1870 aShapeAttribs.Name = aLateData->Name();
1871 }
1872 }
1873 }
1874 else if (aShapeAttribs.Name.IsEmpty()
1875 && myUseMeshNameAsFallback)
1876 {
1877 // fallback using Mesh name
1878 TopLoc_Location aDummy;
1879 TCollection_AsciiString aMeshName;
1880 for (TopExp_Explorer aFaceIter (theShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
1881 {
1882 if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (aFaceIter.Value()), aDummy)))
1883 {
1884 if (aLateData->Name().IsEmpty())
1885 {
1886 aMeshName.Clear();
1887 break;
1888 }
1889 else if (aMeshName.IsEmpty())
1890 {
1891 aMeshName = aLateData->Name();
1892 }
1893 else if (!aMeshName.IsEqual (aLateData->Name()))
1894 {
1895 aMeshName.Clear();
1896 break;
1897 }
1898 }
1899 }
1900 if (!aMeshName.IsEmpty())
1901 {
1902 aShapeAttribs.Name = aMeshName;
1903 }
1904 }
1905 myAttribMap->Bind (theShape, aShapeAttribs);
1906 }
1907}
1908#endif
1909
1910// =======================================================================
1911// function : Parse
1912// purpose :
1913// =======================================================================
1914bool RWGltf_GltfJsonParser::Parse (const Handle(Message_ProgressIndicator)& theProgress)
1915{
1916 Message_ProgressSentry aPSentry (theProgress, "Reading Gltf", 0, 2, 1);
1917#ifdef HAVE_RAPIDJSON
1918 {
1919 if (!gltfParseRoots())
1920 {
1921 return false;
1922 }
1923
1924 gltfParseAsset();
1925 gltfParseMaterials();
1926 if (!gltfParseScene (theProgress))
1927 {
1928 return false;
1929 }
1930 }
1931 aPSentry.Next();
1932 if (!aPSentry.More())
1933 {
1934 return false;
1935 }
1936 return true;
1937#else
1938 Message::DefaultMessenger()->Send ("Error: glTF reader is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined].", Message_Fail);
1939 return false;
1940#endif
1941}