1 // Author: Kirill Gavrilov
2 // Copyright (c) 2016-2019 OPEN CASCADE SAS
4 // This file is part of Open CASCADE Technology software library.
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.
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
15 #include "RWGltf_GltfJsonParser.hxx"
17 #include <BRep_Builder.hxx>
18 #include <gp_Quaternion.hxx>
19 #include <Message.hxx>
20 #include <Message_Messenger.hxx>
21 #include <Message_ProgressScope.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>
30 #include <TopoDS_Iterator.hxx>
37 //! Material extension.
38 static const char THE_KHR_materials_common[] = "KHR_materials_common";
39 static const char THE_KHR_binary_glTF[] = "KHR_binary_glTF";
40 static const char THE_KHR_draco_mesh_compression[] = "KHR_draco_mesh_compression";
42 //! Data buffer referring to a portion of another buffer.
43 class RWGltf_SubBuffer : public NCollection_Buffer
46 RWGltf_SubBuffer (const Handle(NCollection_Buffer)& theBase,
47 Standard_Size theOffset,
48 Standard_Size theLength)
49 : NCollection_Buffer (Handle(NCollection_BaseAllocator)(), theLength, theBase->ChangeData() + theOffset),
50 myBaseBuffer (theBase) {}
53 Handle(NCollection_Buffer) myBaseBuffer;
57 //! Find member of the object in a safe way.
58 inline const RWGltf_JsonValue* findObjectMember (const RWGltf_JsonValue& theObject,
59 const RWGltf_JsonValue& theName)
61 if (!theObject.IsObject()
62 || !theName.IsString())
67 rapidjson::Document::ConstMemberIterator anIter = theObject.FindMember (theName);
68 return anIter != theObject.MemberEnd()
73 //! Find member of the object in a safe way.
74 inline const RWGltf_JsonValue* findObjectMember (const RWGltf_JsonValue& theObject,
77 if (!theObject.IsObject())
82 rapidjson::Document::ConstMemberIterator anIter = theObject.FindMember (theName);
83 return anIter != theObject.MemberEnd()
88 // =======================================================================
89 // function : RWGltf_GltfJsonParser::FormatParseError
91 // =======================================================================
92 const char* RWGltf_GltfJsonParser::FormatParseError (rapidjson::ParseErrorCode theCode)
96 case rapidjson::kParseErrorNone: return "";
97 case rapidjson::kParseErrorDocumentEmpty: return "Empty Document";
98 case rapidjson::kParseErrorDocumentRootNotSingular: return "The document root must not follow by other values";
99 case rapidjson::kParseErrorValueInvalid: return "Invalid value";
100 case rapidjson::kParseErrorObjectMissName: return "Missing a name for object member";
101 case rapidjson::kParseErrorObjectMissColon: return "Missing a colon after a name of object member";
102 case rapidjson::kParseErrorObjectMissCommaOrCurlyBracket: return "Missing a comma or '}' after an object member";
103 case rapidjson::kParseErrorArrayMissCommaOrSquareBracket: return "Missing a comma or ']' after an array element";
104 case rapidjson::kParseErrorStringUnicodeEscapeInvalidHex: return "Incorrect hex digit after \\u escape in string";
105 case rapidjson::kParseErrorStringUnicodeSurrogateInvalid: return "The surrogate pair in string is invalid";
106 case rapidjson::kParseErrorStringEscapeInvalid: return "Invalid escape character in string";
107 case rapidjson::kParseErrorStringMissQuotationMark: return "Missing a closing quotation mark in string";
108 case rapidjson::kParseErrorStringInvalidEncoding: return "Invalid encoding in string";
109 case rapidjson::kParseErrorNumberTooBig: return "Number is too big to be stored in double";
110 case rapidjson::kParseErrorNumberMissFraction: return "Miss fraction part in number";
111 case rapidjson::kParseErrorNumberMissExponent: return "Miss exponent in number";
112 case rapidjson::kParseErrorTermination: return "Parsing was terminated";
113 case rapidjson::kParseErrorUnspecificSyntaxError: return "Unspecific syntax error";
115 return "UNKOWN syntax error";
118 // =======================================================================
119 // function : GltfElementMap::Init
121 // =======================================================================
122 void RWGltf_GltfJsonParser::GltfElementMap::Init (const TCollection_AsciiString& theRootName,
123 const RWGltf_JsonValue* theRoot)
132 if (theRoot->IsObject())
135 for (ConstMemberIterator aChildIter = theRoot->MemberBegin(); aChildIter != theRoot->MemberEnd(); ++aChildIter)
137 if (!aChildIter->name.IsString())
142 const TCollection_AsciiString aKey (aChildIter->name.GetString());
143 if (!myChildren.Bind (aKey, &aChildIter->value))
145 Message::SendWarning (TCollection_AsciiString ("Invalid glTF syntax - key '") + aKey + "' is already defined in '" + theRootName + "'.");
149 else if (theRoot->IsArray())
153 for (rapidjson::Value::ConstValueIterator aChildIter = theRoot->Begin(); aChildIter != theRoot->End(); ++aChildIter, ++aChildIndex)
155 myChildren.Bind (TCollection_AsciiString (aChildIndex), aChildIter);
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);
165 // =======================================================================
166 // function : reportGltfSyntaxProblem
168 // =======================================================================
169 void RWGltf_GltfJsonParser::reportGltfSyntaxProblem (const TCollection_AsciiString& theMsg,
170 Message_Gravity theGravity)
172 Message::Send (myErrorPrefix + theMsg, theGravity);
175 // =======================================================================
176 // function : RWGltf_GltfJsonParser
178 // =======================================================================
179 RWGltf_GltfJsonParser::RWGltf_GltfJsonParser (TopTools_SequenceOfShape& theRootShapes)
180 : myRootShapes(&theRootShapes),
182 myExternalFiles (NULL),
188 myToSkipEmptyNodes (true),
189 myUseMeshNameAsFallback (true),
190 myToProbeHeader (false)
192 myCSTrsf.SetInputLengthUnit (1.0); // meters
193 myCSTrsf.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
196 // =======================================================================
197 // function : SetFilePath
199 // =======================================================================
200 void RWGltf_GltfJsonParser::SetFilePath (const TCollection_AsciiString& theFilePath)
202 myFilePath = theFilePath;
203 // determine file location to load associated files
204 TCollection_AsciiString aFileName;
205 OSD_Path::FolderAndFileFromPath (theFilePath, myFolder, aFileName);
208 #ifdef HAVE_RAPIDJSON
209 // =======================================================================
210 // function : gltfParseRoots
212 // =======================================================================
213 bool RWGltf_GltfJsonParser::gltfParseRoots()
215 // find glTF root elements for smooth navigation
216 RWGltf_JsonValue aNames[RWGltf_GltfRootElement_NB];
217 for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
219 aNames[aRootNameIter] = rapidjson::StringRef (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter));
222 for (ConstMemberIterator aRootIter = MemberBegin();
223 aRootIter != MemberEnd(); ++aRootIter)
225 for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
227 if (myGltfRoots[aRootNameIter].IsNull()
228 && aNames[aRootNameIter] == aRootIter->name)
230 // we will not modify JSON document, thus it is OK to keep the pointers
231 myGltfRoots[aRootNameIter].Init (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter), &aRootIter->value);
237 for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB_MANDATORY; ++aRootNameIter)
239 if (myGltfRoots[aRootNameIter].IsNull())
241 reportGltfError ("Member '" + RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter) + "' is not found.");
248 // =======================================================================
249 // function : gltfParseAsset
251 // =======================================================================
252 void RWGltf_GltfJsonParser::gltfParseAsset()
254 const RWGltf_JsonValue* anAsset = myGltfRoots[RWGltf_GltfRootElement_Asset].Root();
260 if (const RWGltf_JsonValue* aVersion = findObjectMember (*anAsset, "version"))
262 if (aVersion->IsString())
264 TCollection_AsciiString aVerStr (aVersion->GetString());
265 myIsGltf1 = aVerStr.StartsWith ("1.");
269 if (myMetadata == NULL)
274 if (const RWGltf_JsonValue* aGenerator = findObjectMember (*anAsset, "generator"))
276 if (aGenerator->IsString())
278 myMetadata->Add ("generator", aGenerator->GetString());
281 if (const RWGltf_JsonValue* aCopyRight = findObjectMember (*anAsset, "copyright"))
283 if (aCopyRight->IsString())
285 myMetadata->Add ("copyright", aCopyRight->GetString());
290 // =======================================================================
291 // function : gltfParseMaterials
293 // =======================================================================
294 void RWGltf_GltfJsonParser::gltfParseMaterials()
296 const RWGltf_JsonValue* aMatList = myGltfRoots[RWGltf_GltfRootElement_Materials].Root();
297 if (aMatList == NULL)
301 else if (aMatList->IsObject())
304 for (ConstMemberIterator aMatIter = aMatList->MemberBegin();
305 aMatIter != aMatList->MemberEnd(); ++aMatIter)
307 Handle(RWGltf_MaterialCommon) aMat;
308 const RWGltf_JsonValue& aMatNode = aMatIter->value;
309 const RWGltf_JsonValue& aMatId = aMatIter->name;
310 const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
311 if (!gltfParseCommonMaterial (aMat, aMatNode))
313 if (!gltfParseStdMaterial (aMat, aMatNode))
320 && aNameVal->IsString())
322 aMat->Name = aNameVal->GetString();
324 aMat->Id = aMatId.GetString();
325 myMaterialsCommon.Bind (aMat->Id, aMat);
326 gltfBindMaterial (Handle(RWGltf_MaterialMetallicRoughness)(), aMat);
329 else if (aMatList->IsArray())
333 for (rapidjson::Value::ConstValueIterator aMatIter = aMatList->Begin(); aMatIter != aMatList->End(); ++aMatIter, ++aMatIndex)
335 Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
336 const RWGltf_JsonValue& aMatNode = *aMatIter;
337 const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
338 if (gltfParsePbrMaterial (aMatPbr, aMatNode))
341 && aNameVal->IsString())
343 aMatPbr->Name = aNameVal->GetString();
345 aMatPbr->Id = TCollection_AsciiString ("mat_") + aMatIndex;
346 myMaterialsPbr.Bind (TCollection_AsciiString (aMatIndex), aMatPbr);
349 Handle(RWGltf_MaterialCommon) aMatCommon;
350 if (gltfParseCommonMaterial(aMatCommon, aMatNode)
351 || gltfParseStdMaterial (aMatCommon, aMatNode))
354 && aNameVal->IsString())
356 aMatCommon->Name = aNameVal->GetString();
358 aMatCommon->Id = TCollection_AsciiString ("mat_") + aMatIndex;
359 myMaterialsCommon.Bind (TCollection_AsciiString (aMatIndex), aMatCommon);
362 gltfBindMaterial (aMatPbr, aMatCommon);
367 // =======================================================================
368 // function : gltfBindMaterial
370 // =======================================================================
371 void RWGltf_GltfJsonParser::gltfBindMaterial (const Handle(RWGltf_MaterialMetallicRoughness)& theMatPbr,
372 const Handle(RWGltf_MaterialCommon)& theMatCommon)
374 if (theMatPbr.IsNull()
375 && theMatCommon.IsNull())
380 Handle(XCAFDoc_VisMaterial) aMat = new XCAFDoc_VisMaterial();
381 if (!theMatCommon.IsNull())
383 XCAFDoc_VisMaterialCommon aMatXde;
384 aMatXde.IsDefined = true;
385 aMatXde.AmbientColor = theMatCommon->AmbientColor;
386 aMatXde.DiffuseColor = theMatCommon->DiffuseColor;
387 aMatXde.SpecularColor = theMatCommon->SpecularColor;
388 aMatXde.EmissiveColor = theMatCommon->EmissiveColor;
389 aMatXde.Shininess = theMatCommon->Shininess;
390 aMatXde.Transparency = theMatCommon->Transparency;
391 aMatXde.DiffuseTexture = theMatCommon->DiffuseTexture;
392 if (aMatXde.DiffuseTexture.IsNull()
393 && !theMatCommon->AmbientTexture.IsNull())
395 aMatXde.DiffuseTexture = theMatCommon->AmbientTexture;
397 aMat->SetCommonMaterial (aMatXde);
398 if (!theMatCommon->Name.IsEmpty())
400 aMat->SetRawName (new TCollection_HAsciiString (theMatCommon->Name));
403 if (!theMatPbr.IsNull())
405 XCAFDoc_VisMaterialPBR aMatXde;
406 aMatXde.IsDefined = true;
407 aMatXde.MetallicRoughnessTexture = theMatPbr->MetallicRoughnessTexture;
408 aMatXde.BaseColorTexture = theMatPbr->BaseColorTexture;
409 aMatXde.EmissiveTexture = theMatPbr->EmissiveTexture;
410 aMatXde.OcclusionTexture = theMatPbr->OcclusionTexture;
411 aMatXde.NormalTexture = theMatPbr->NormalTexture;
412 aMatXde.BaseColor = theMatPbr->BaseColor;
413 aMatXde.EmissiveFactor = theMatPbr->EmissiveFactor;
414 aMatXde.Metallic = theMatPbr->Metallic;
415 aMatXde.Roughness = theMatPbr->Roughness;
416 aMat->SetPbrMaterial (aMatXde);
418 Graphic3d_AlphaMode anAlphaMode = Graphic3d_AlphaMode_BlendAuto;
419 switch (theMatPbr->AlphaMode)
421 case RWGltf_GltfAlphaMode_Opaque:
423 anAlphaMode = Graphic3d_AlphaMode_Opaque;
424 if (aMatXde.BaseColor.Alpha() < 1.0f)
426 Message::SendWarning ("glTF reader - material with non-zero Transparency specifies Opaque AlphaMode");
430 case RWGltf_GltfAlphaMode_Mask:
432 anAlphaMode = Graphic3d_AlphaMode_Mask;
435 case RWGltf_GltfAlphaMode_Blend:
437 anAlphaMode = Graphic3d_AlphaMode_Blend;
441 aMat->SetAlphaMode (anAlphaMode, theMatPbr->AlphaCutOff);
442 // consider "doubleSided" material flag as indication of automatically culled material
443 // as glTF doesn't define closed/open flag and culling will be practically disabled
444 aMat->SetFaceCulling (theMatPbr->IsDoubleSided ? Graphic3d_TypeOfBackfacingModel_Auto : Graphic3d_TypeOfBackfacingModel_BackCulled);
446 if (!theMatPbr->Name.IsEmpty())
448 aMat->SetRawName (new TCollection_HAsciiString (theMatPbr->Name));
452 myMaterials.Bind (!theMatPbr.IsNull() ? theMatPbr->Id : theMatCommon->Id, aMat);
455 // =======================================================================
456 // function : gltfParseStdMaterial
458 // =======================================================================
459 bool RWGltf_GltfJsonParser::gltfParseStdMaterial (Handle(RWGltf_MaterialCommon)& theMat,
460 const RWGltf_JsonValue& theMatNode)
462 //const RWGltf_JsonValue* aTechVal = findObjectMember (theMatNode, "technique");
463 const RWGltf_JsonValue* aValues = findObjectMember (theMatNode, "values");
469 const RWGltf_JsonValue* anAmbVal = findObjectMember (*aValues, "ambient");
470 const RWGltf_JsonValue* aDiffVal = findObjectMember (*aValues, "diffuse");
471 const RWGltf_JsonValue* anEmiVal = findObjectMember (*aValues, "emission");
472 const RWGltf_JsonValue* aSpecVal = findObjectMember (*aValues, "specular");
473 const RWGltf_JsonValue* aShinVal = findObjectMember (*aValues, "shininess");
483 theMat = new RWGltf_MaterialCommon();
485 Graphic3d_Vec4d anAmb, aDiff, anEmi, aSpec;
487 && anAmbVal->IsString())
489 gltfParseTexture (theMat->AmbientTexture, anAmbVal);
491 else if (gltfReadVec4 (anAmb, anAmbVal)
492 && validateColor4 (anAmb))
494 theMat->AmbientColor = Quantity_Color (anAmb.r(), anAmb.g(), anAmb.b(), Quantity_TOC_sRGB);
498 && aDiffVal->IsString())
500 gltfParseTexture (theMat->DiffuseTexture, aDiffVal);
502 else if (gltfReadVec4 (aDiff, aDiffVal)
503 && validateColor4 (aDiff))
505 theMat->DiffuseColor = Quantity_Color (aDiff.r(), aDiff.g(), aDiff.b(), Quantity_TOC_sRGB);
506 theMat->Transparency = float(1.0 - aDiff.a());
509 if (gltfReadVec4 (anEmi, anEmiVal)
510 && validateColor4 (anEmi))
512 theMat->EmissiveColor = Quantity_Color (anEmi.r(), anEmi.g(), anEmi.b(), Quantity_TOC_sRGB);
516 && aSpecVal->IsString())
518 gltfParseTexture (theMat->SpecularTexture, aSpecVal);
520 if (gltfReadVec4 (aSpec, aSpecVal)
521 && validateColor4 (aSpec))
523 theMat->SpecularColor = Quantity_Color (aSpec.r(), aSpec.g(), aSpec.b(), Quantity_TOC_sRGB);
527 && aShinVal->IsNumber())
529 const double aSpecular = aShinVal->GetDouble();
532 theMat->Shininess = (float )Min (aSpecular / 1000.0, 1.0);
538 // =======================================================================
539 // function : gltfParsePbrMaterial
541 // =======================================================================
542 bool RWGltf_GltfJsonParser::gltfParsePbrMaterial (Handle(RWGltf_MaterialMetallicRoughness)& theMat,
543 const RWGltf_JsonValue& theMatNode)
545 /*if (const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions"))
547 if (const RWGltf_JsonValue* anExtDefVal = findObjectMember (*anExtVal, "KHR_materials_pbrSpecularGlossiness"))
549 const RWGltf_JsonValue* aDiffTexVal = findObjectMember (*anExtDefVal, "diffuseTexture");
550 const RWGltf_JsonValue* aSpecTexVal = findObjectMember (*anExtDefVal, "specularGlossinessTexture");
554 const RWGltf_JsonValue* aMetalRoughVal = findObjectMember (theMatNode, "pbrMetallicRoughness");
555 const RWGltf_JsonValue* aNormTexVal = findObjectMember (theMatNode, "normalTexture");
556 const RWGltf_JsonValue* anEmissFactorVal = findObjectMember (theMatNode, "emissiveFactor");
557 const RWGltf_JsonValue* anEmissTexVal = findObjectMember (theMatNode, "emissiveTexture");
558 const RWGltf_JsonValue* anOcclusionTexVal = findObjectMember (theMatNode, "occlusionTexture");
559 const RWGltf_JsonValue* aDoubleSidedVal = findObjectMember (theMatNode, "doubleSided");
560 const RWGltf_JsonValue* anAlphaModeVal = findObjectMember (theMatNode, "alphaMode");
561 const RWGltf_JsonValue* anAlphaCutoffVal = findObjectMember (theMatNode, "alphaCutoff");
562 // TODO ADOBE_materials_thin_transparency extension can be used to read IOR (Index of Refraction for transparent materials)
563 if (aMetalRoughVal == NULL)
568 theMat = new RWGltf_MaterialMetallicRoughness();
569 const RWGltf_JsonValue* aBaseColorFactorVal = findObjectMember (*aMetalRoughVal, "baseColorFactor");
570 const RWGltf_JsonValue* aBaseColorTexVal = findObjectMember (*aMetalRoughVal, "baseColorTexture");
571 const RWGltf_JsonValue* aMetallicFactorVal = findObjectMember (*aMetalRoughVal, "metallicFactor");
572 const RWGltf_JsonValue* aRoughnessFactorVal = findObjectMember (*aMetalRoughVal, "roughnessFactor");
573 const RWGltf_JsonValue* aMetalRoughTexVal = findObjectMember (*aMetalRoughVal, "metallicRoughnessTexture");
575 if (aDoubleSidedVal != NULL
576 && aDoubleSidedVal->IsBool())
578 theMat->IsDoubleSided = aDoubleSidedVal->GetBool();
580 if (anAlphaCutoffVal != NULL
581 && anAlphaCutoffVal->IsNumber())
583 theMat->AlphaCutOff = (float )anAlphaCutoffVal->GetDouble();
585 if (anAlphaModeVal != NULL
586 && anAlphaModeVal->IsString())
588 theMat->AlphaMode = RWGltf_GltfParseAlphaMode (anAlphaModeVal->GetString());
591 if (aBaseColorTexVal != NULL
592 && aBaseColorTexVal->IsObject())
594 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aBaseColorTexVal, "index"))
596 gltfParseTexture (theMat->BaseColorTexture, aTexIndexVal);
600 Graphic3d_Vec4d aBaseColorFactor;
601 if (gltfReadVec4 (aBaseColorFactor, aBaseColorFactorVal)
602 && validateColor4 (aBaseColorFactor))
604 theMat->BaseColor = Quantity_ColorRGBA (Graphic3d_Vec4 (aBaseColorFactor));
607 Graphic3d_Vec3d anEmissiveFactor;
608 if (gltfReadVec3 (anEmissiveFactor, anEmissFactorVal)
609 && validateColor3 (anEmissiveFactor))
611 theMat->EmissiveFactor = Graphic3d_Vec3 (anEmissiveFactor);
614 if (aMetalRoughTexVal != NULL
615 && aMetalRoughTexVal->IsObject())
617 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aMetalRoughTexVal, "index"))
619 gltfParseTexture (theMat->MetallicRoughnessTexture, aTexIndexVal);
623 if (aMetallicFactorVal != NULL
624 && aMetallicFactorVal->IsNumber())
626 theMat->Metallic = (float )aMetallicFactorVal->GetDouble();
629 if (aRoughnessFactorVal != NULL
630 && aRoughnessFactorVal->IsNumber())
632 theMat->Roughness = (float )aRoughnessFactorVal->GetDouble();
635 if (aNormTexVal != NULL
636 && aNormTexVal->IsObject())
638 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aNormTexVal, "index"))
640 gltfParseTexture (theMat->NormalTexture, aTexIndexVal);
644 if (anEmissTexVal != NULL
645 && anEmissTexVal->IsObject())
647 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anEmissTexVal, "index"))
649 gltfParseTexture (theMat->EmissiveTexture, aTexIndexVal);
653 if (anOcclusionTexVal != NULL
654 && anOcclusionTexVal->IsObject())
656 if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anOcclusionTexVal, "index"))
658 gltfParseTexture (theMat->OcclusionTexture, aTexIndexVal);
664 // =======================================================================
665 // function : gltfParseCommonMaterial
667 // =======================================================================
668 bool RWGltf_GltfJsonParser::gltfParseCommonMaterial (Handle(RWGltf_MaterialCommon)& theMat,
669 const RWGltf_JsonValue& theMatNode)
671 const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions");
672 if (anExtVal == NULL)
677 const RWGltf_JsonValue* aMatCommon = findObjectMember (*anExtVal, THE_KHR_materials_common);
678 if (aMatCommon == NULL)
683 if (!gltfParseStdMaterial (theMat, *aMatCommon))
690 // =======================================================================
691 // function : gltfParseTexture
693 // =======================================================================
694 bool RWGltf_GltfJsonParser::gltfParseTexture (Handle(Image_Texture)& theTexture,
695 const RWGltf_JsonValue* theTextureId)
697 if (theTextureId == NULL
698 || myGltfRoots[RWGltf_GltfRootElement_Textures].IsNull()
699 || myGltfRoots[RWGltf_GltfRootElement_Images].IsNull())
704 const TCollection_AsciiString aTextureId = getKeyString (*theTextureId);
705 const RWGltf_JsonValue* aTexNode = myGltfRoots[RWGltf_GltfRootElement_Textures].FindChild (*theTextureId);
706 if (aTexNode == NULL)
708 reportGltfWarning ("Texture node '" + aTextureId + "' is not found.");
712 const RWGltf_JsonValue* aSrcVal = findObjectMember (*aTexNode, "source");
713 const RWGltf_JsonValue* aTargVal = findObjectMember (*aTexNode, "target");
716 reportGltfWarning ("Invalid texture node '" + aTextureId + "' without a 'source' property.");
720 && aTargVal->IsNumber()
721 && aTargVal->GetInt() != 3553) // GL_TEXTURE_2D
726 const RWGltf_JsonValue* anImgNode = myGltfRoots[RWGltf_GltfRootElement_Images].FindChild (*aSrcVal);
727 if (anImgNode == NULL)
729 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to non-existing image '" + getKeyString (*aSrcVal) + "'.");
735 const RWGltf_JsonValue* aBinVal = NULL;
736 const RWGltf_JsonValue* aBufferViewName = findObjectMember (*anImgNode, "bufferView");
737 if (aBufferViewName != NULL)
743 const RWGltf_JsonValue* anExtVal = findObjectMember (*anImgNode, "extensions");
744 if (anExtVal != NULL)
746 aBinVal = findObjectMember (*anExtVal, THE_KHR_binary_glTF);
749 aBufferViewName = findObjectMember (*aBinVal, "bufferView");
756 if (aBufferViewName == NULL)
758 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
761 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
762 if (aBufferView == NULL
763 || !aBufferView->IsObject())
765 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid buffer view '" + getKeyString (*aBufferViewName) + "'.");
768 return gltfParseTexturInGlbBuffer (theTexture, *aBinVal, getKeyString (*aBufferViewName), *aBufferView);
772 const RWGltf_JsonValue* anUriVal = findObjectMember (*anImgNode, "uri");
773 if (anUriVal == NULL)
775 const RWGltf_JsonValue* aBufferViewName = findObjectMember (*anImgNode, "bufferView");
776 if (aBufferViewName == NULL)
778 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
782 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
783 if (aBufferView == NULL
784 || !aBufferView->IsObject())
786 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid buffer view '" + getKeyString (*aBufferViewName) + "'.");
789 return gltfParseTextureInBufferView (theTexture, getKeyString (*aSrcVal), getKeyString (*aBufferViewName), *aBufferView);
792 if (!anUriVal->IsString())
794 reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
798 const char* anUriData = anUriVal->GetString();
799 if (::strncmp (anUriData, "data:", 5) == 0) // data:image/png;base64
801 // uncompressing base64 here is inefficient, because the same image can be shared by several nodes
802 const char* aDataStart = anUriData + 5;
803 for (const char* aDataIter = aDataStart; *aDataIter != '\0'; ++aDataIter)
805 if (::memcmp (aDataIter, ";base64,", 8) == 0)
807 const char* aBase64End = anUriData + anUriVal->GetStringLength();
808 const char* aBase64Data = aDataIter + 8;
809 const size_t aBase64Len = size_t(aBase64End - aBase64Data);
810 //const TCollection_AsciiString aMime (aDataStart, aDataIter - aDataStart);
811 Handle(NCollection_Buffer) aData = FSD_Base64Decoder::Decode ((const Standard_Byte* )aBase64Data, aBase64Len);
812 theTexture = new Image_Texture (aData, myFilePath + "@" + getKeyString (*aSrcVal));
816 Message::SendWarning ("glTF reader - embedded image has been skipped");
820 TCollection_AsciiString anImageFile = myFolder + anUriVal->GetString();
821 theTexture = new Image_Texture (anImageFile);
822 if (myExternalFiles != NULL)
824 myExternalFiles->Add (anImageFile);
829 // =======================================================================
830 // function : gltfParseTexturInGlbBuffer
832 // =======================================================================
833 bool RWGltf_GltfJsonParser::gltfParseTexturInGlbBuffer (Handle(Image_Texture)& theTexture,
834 const RWGltf_JsonValue& theBinVal,
835 const TCollection_AsciiString& theBufferViewId,
836 const RWGltf_JsonValue& theBufferView)
838 const RWGltf_JsonValue* aMimeTypeVal = findObjectMember (theBinVal, "mimeType");
839 //const RWGltf_JsonValue* aWidthVal = findObjectMember (theBinVal, "width");
840 //const RWGltf_JsonValue* aHeightVal = findObjectMember (theBinVal, "height");
843 const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
844 const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
845 const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
846 if (aBufferName != NULL
847 && aBufferName->IsString()
848 && !IsEqual (aBufferName->GetString(), "binary_glTF"))
850 reportGltfError ("BufferView '" + theBufferViewId + "' does not define binary_glTF buffer.");
854 RWGltf_GltfBufferView aBuffView;
855 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
856 ? (int64_t )aByteOffset->GetDouble()
858 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
859 ? (int64_t )aByteLength->GetDouble()
861 if (aBuffView.ByteLength <= 0)
863 reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteLength.");
866 else if (aBuffView.ByteOffset < 0)
868 reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteOffset.");
872 const int64_t anOffset = myBinBodyOffset + aBuffView.ByteOffset;
873 theTexture = new Image_Texture (myFilePath, anOffset, aBuffView.ByteLength);
877 // =======================================================================
878 // function : gltfParseTextureInBufferView
880 // =======================================================================
881 bool RWGltf_GltfJsonParser::gltfParseTextureInBufferView (Handle(Image_Texture)& theTexture,
882 const TCollection_AsciiString& theSourceId,
883 const TCollection_AsciiString& theBufferViewId,
884 const RWGltf_JsonValue& theBufferView)
886 const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
887 const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
888 const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
889 if (aBufferName == NULL)
891 reportGltfError ("BufferView '" + theBufferViewId + "' does not define buffer.");
895 const TCollection_AsciiString aBufferId = getKeyString (*aBufferName);
896 const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
898 || !aBuffer->IsObject())
900 reportGltfError ("BufferView '" + theBufferViewId + "' refers to non-existing buffer.");
904 RWGltf_GltfBufferView aBuffView;
905 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
906 ? (int64_t )aByteOffset->GetDouble()
908 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
909 ? (int64_t )aByteLength->GetDouble()
911 if (aBuffView.ByteLength <= 0)
913 reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteLength.");
916 else if (aBuffView.ByteOffset < 0)
918 reportGltfError ("BufferView '" + theBufferViewId + "' defines invalid byteOffset.");
922 const RWGltf_JsonValue* anUriVal = findObjectMember (*aBuffer, "uri");
924 || !anUriVal->IsString())
926 reportGltfError ("Buffer '" + aBufferId + "' does not define uri.");
930 const char* anUriData = anUriVal->GetString();
931 if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
933 Handle(NCollection_Buffer) aBaseBuffer;
934 if (!myDecodedBuffers.Find (aBufferId, aBaseBuffer))
936 aBaseBuffer = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
937 myDecodedBuffers.Bind (aBufferId, aBaseBuffer);
940 Handle(RWGltf_SubBuffer) aSubBuffer = new RWGltf_SubBuffer (aBaseBuffer, (Standard_Size )aBuffView.ByteOffset, (Standard_Size )aBuffView.ByteLength);
941 theTexture = new Image_Texture (aSubBuffer, myFilePath + "@" + theSourceId);
945 const TCollection_AsciiString anUri (anUriData);
948 reportGltfError ("Buffer '" + aBufferId + "' does not define uri.");
952 const TCollection_AsciiString aPath = myFolder + anUri;
953 bool isFileExist = false;
954 if (!myProbedFiles.Find (aPath, isFileExist))
956 isFileExist = OSD_File (aPath).Exists();
957 myProbedFiles.Bind (aPath, isFileExist);
961 reportGltfError ("Buffer '" + aBufferId + "' refers to non-existing file '" + anUri + "'.");
965 theTexture = new Image_Texture (aPath, aBuffView.ByteOffset, aBuffView.ByteLength);
966 if (myExternalFiles != NULL)
968 myExternalFiles->Add (aPath);
973 // =======================================================================
974 // function : gltfParseScene
976 // =======================================================================
977 bool RWGltf_GltfJsonParser::gltfParseScene (const Message_ProgressRange& theProgress)
979 // search default scene
980 const RWGltf_JsonValue* aDefScene = myGltfRoots[RWGltf_GltfRootElement_Scenes].FindChild (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root());
981 if (aDefScene == NULL)
983 reportGltfError ("Default scene is not found.");
987 const RWGltf_JsonValue* aSceneNodes = findObjectMember (*aDefScene, "nodes");
988 if (aSceneNodes == NULL
989 || !aSceneNodes->IsArray())
991 reportGltfError ("Empty scene '" + getKeyString (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root()) + "'.");
995 return gltfParseSceneNodes (*myRootShapes, *aSceneNodes, theProgress);
998 // =======================================================================
999 // function : gltfParseSceneNodes
1001 // =======================================================================
1002 bool RWGltf_GltfJsonParser::gltfParseSceneNodes (TopTools_SequenceOfShape& theShapeSeq,
1003 const RWGltf_JsonValue& theSceneNodes,
1004 const Message_ProgressRange& theProgress)
1006 if (!theSceneNodes.IsArray())
1008 reportGltfError ("Scene nodes is not array.");
1012 Message_ProgressScope aPS (theProgress, "Reading scene nodes", theSceneNodes.Size());
1013 for (rapidjson::Value::ConstValueIterator aSceneNodeIter = theSceneNodes.Begin();
1014 aSceneNodeIter != theSceneNodes.End() && aPS.More(); ++aSceneNodeIter)
1016 const RWGltf_JsonValue* aSceneNode = myGltfRoots[RWGltf_GltfRootElement_Nodes].FindChild (*aSceneNodeIter);
1017 if (aSceneNode == NULL)
1019 reportGltfWarning ("Scene refers to non-existing node '" + getKeyString (*aSceneNodeIter) + "'.");
1023 TopoDS_Shape aNodeShape;
1024 if (!gltfParseSceneNode (aNodeShape, getKeyString (*aSceneNodeIter), *aSceneNode, aPS.Next()))
1029 if (aNodeShape.IsNull())
1033 else if (myToSkipEmptyNodes
1034 && !TopExp_Explorer (aNodeShape, TopAbs_FACE).More())
1039 theShapeSeq.Append (aNodeShape);
1044 // =======================================================================
1045 // function : gltfParseSceneNode
1047 // =======================================================================
1048 bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
1049 const TCollection_AsciiString& theSceneNodeId,
1050 const RWGltf_JsonValue& theSceneNode,
1051 const Message_ProgressRange& theProgress)
1053 const RWGltf_JsonValue* aName = findObjectMember (theSceneNode, "name");
1054 //const RWGltf_JsonValue* aJointName = findObjectMember (theSceneNode, "jointName");
1055 const RWGltf_JsonValue* aChildren = findObjectMember (theSceneNode, "children");
1056 const RWGltf_JsonValue* aMeshes_1 = findObjectMember (theSceneNode, "meshes");
1057 const RWGltf_JsonValue* aMesh_2 = findObjectMember (theSceneNode, "mesh");
1058 //const RWGltf_JsonValue* aCamera = findObjectMember (theSceneNode, "camera");
1059 const RWGltf_JsonValue* aTrsfMatVal = findObjectMember (theSceneNode, "matrix");
1060 const RWGltf_JsonValue* aTrsfRotVal = findObjectMember (theSceneNode, "rotation");
1061 const RWGltf_JsonValue* aTrsfScaleVal = findObjectMember (theSceneNode, "scale");
1062 const RWGltf_JsonValue* aTrsfTransVal = findObjectMember (theSceneNode, "translation");
1063 if (findNodeShape (theNodeShape, theSceneNodeId))
1068 TopLoc_Location aNodeLoc;
1069 const bool hasTrs = aTrsfRotVal != NULL
1070 || aTrsfScaleVal != NULL
1071 || aTrsfTransVal != NULL;
1072 if (aTrsfMatVal != NULL)
1076 reportGltfError ("Scene node '" + theSceneNodeId + "' defines ambiguous transformation.");
1079 else if (!aTrsfMatVal->IsArray()
1080 || aTrsfMatVal->Size() != 16)
1082 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix array.");
1086 Graphic3d_Mat4d aMat4;
1087 for (int aColIter = 0; aColIter < 4; ++aColIter)
1089 for (int aRowIter = 0; aRowIter < 4; ++aRowIter)
1091 const RWGltf_JsonValue& aGenVal = (*aTrsfMatVal)[aColIter * 4 + aRowIter];
1092 if (!aGenVal.IsNumber())
1094 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix.");
1097 aMat4.SetValue (aRowIter, aColIter, aGenVal.GetDouble());
1101 if (!aMat4.IsIdentity())
1104 aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
1105 aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
1106 aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
1107 myCSTrsf.TransformTransformation (aTrsf);
1108 if (aTrsf.Form() != gp_Identity)
1110 aNodeLoc = TopLoc_Location (aTrsf);
1117 if (aTrsfRotVal != NULL)
1119 if (!aTrsfRotVal->IsArray()
1120 || aTrsfRotVal->Size() != 4)
1122 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation quaternion.");
1126 Graphic3d_Vec4d aRotVec4;
1127 for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
1129 const RWGltf_JsonValue& aGenVal = (*aTrsfRotVal)[aCompIter];
1130 if (!aGenVal.IsNumber())
1132 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation.");
1135 aRotVec4[aCompIter] = aGenVal.GetDouble();
1137 const gp_Quaternion aQuaternion (aRotVec4.x(), aRotVec4.y(), aRotVec4.z(), aRotVec4.w());
1138 if (Abs (aQuaternion.X()) > gp::Resolution()
1139 || Abs (aQuaternion.Y()) > gp::Resolution()
1140 || Abs (aQuaternion.Z()) > gp::Resolution()
1141 || Abs (aQuaternion.W() - 1.0) > gp::Resolution())
1143 aTrsf.SetRotation (aQuaternion);
1147 if (aTrsfTransVal != NULL)
1149 if (!aTrsfTransVal->IsArray()
1150 || aTrsfTransVal->Size() != 3)
1152 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation vector.");
1157 for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
1159 const RWGltf_JsonValue& aGenVal = (*aTrsfTransVal)[aCompIter];
1160 if (!aGenVal.IsNumber())
1162 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation.");
1165 aTransVec.SetCoord (aCompIter + 1, aGenVal.GetDouble());
1167 aTrsf.SetTranslationPart (aTransVec);
1170 if (aTrsfScaleVal != NULL)
1172 Graphic3d_Vec3d aScaleVec;
1173 if (!aTrsfScaleVal->IsArray()
1174 || aTrsfScaleVal->Size() != 3)
1176 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale vector.");
1179 for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
1181 const RWGltf_JsonValue& aGenVal = (*aTrsfScaleVal)[aCompIter];
1182 if (!aGenVal.IsNumber())
1184 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
1187 aScaleVec[aCompIter] = aGenVal.GetDouble();
1188 if (Abs (aScaleVec[aCompIter]) <= gp::Resolution())
1190 reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
1195 if (Abs (aScaleVec.x() - aScaleVec.y()) > Precision::Confusion()
1196 || Abs (aScaleVec.y() - aScaleVec.z()) > Precision::Confusion()
1197 || Abs (aScaleVec.x() - aScaleVec.z()) > Precision::Confusion())
1199 Graphic3d_Mat4d aScaleMat;
1200 aScaleMat.SetDiagonal (aScaleVec);
1202 Graphic3d_Mat4d aMat4;
1203 aTrsf.GetMat4 (aMat4);
1205 aMat4 = aMat4 * aScaleMat;
1207 aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
1208 aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
1209 aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
1211 Message::SendWarning (TCollection_AsciiString ("glTF reader, scene node '")
1212 + theSceneNodeId + "' defines unsupported scaling " + aScaleVec.x() + " " + aScaleVec.y() + " " + aScaleVec.z());
1214 else if (Abs (aScaleVec.x() - 1.0) > Precision::Confusion())
1216 aTrsf.SetScaleFactor (aScaleVec.x());
1220 myCSTrsf.TransformTransformation (aTrsf);
1221 if (aTrsf.Form() != gp_Identity)
1223 aNodeLoc = TopLoc_Location (aTrsf);
1227 BRep_Builder aBuilder;
1228 TopoDS_Compound aNodeShape;
1229 aBuilder.MakeCompound (aNodeShape);
1230 TopTools_SequenceOfShape aChildShapes;
1231 int aNbSubShapes = 0;
1232 if (aChildren != NULL
1233 && !gltfParseSceneNodes (aChildShapes, *aChildren, theProgress))
1235 theNodeShape = aNodeShape;
1236 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1239 for (TopTools_SequenceOfShape::Iterator aChildShapeIter (aChildShapes); aChildShapeIter.More(); aChildShapeIter.Next())
1241 aBuilder.Add (aNodeShape, aChildShapeIter.Value());
1245 if (aMeshes_1 != NULL
1246 && aMeshes_1->IsArray())
1249 for (rapidjson::Value::ConstValueIterator aMeshIter = aMeshes_1->Begin();
1250 aMeshIter != aMeshes_1->End(); ++aMeshIter)
1252 const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMeshIter);
1255 theNodeShape = aNodeShape;
1256 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1257 reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1261 TopoDS_Shape aMeshShape;
1262 if (!gltfParseMesh (aMeshShape, getKeyString (*aMeshIter), *aMesh))
1264 theNodeShape = aNodeShape;
1265 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1268 if (!aMeshShape.IsNull())
1270 aBuilder.Add (aNodeShape, aMeshShape);
1275 if (aMesh_2 != NULL)
1278 const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMesh_2);
1281 theNodeShape = aNodeShape;
1282 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1283 reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
1287 TopoDS_Shape aMeshShape;
1288 if (!gltfParseMesh (aMeshShape, getKeyString (*aMesh_2), *aMesh))
1290 theNodeShape = aNodeShape;
1291 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1294 if (!aMeshShape.IsNull())
1296 aBuilder.Add (aNodeShape, aMeshShape);
1301 if (aNbSubShapes == 1)
1303 theNodeShape = TopoDS_Iterator (aNodeShape).Value();
1307 theNodeShape = aNodeShape;
1309 bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
1313 // =======================================================================
1314 // function : gltfParseMesh
1316 // =======================================================================
1317 bool RWGltf_GltfJsonParser::gltfParseMesh (TopoDS_Shape& theMeshShape,
1318 const TCollection_AsciiString& theMeshId,
1319 const RWGltf_JsonValue& theMesh)
1321 const RWGltf_JsonValue* aName = findObjectMember (theMesh, "name");
1322 const RWGltf_JsonValue* aPrims = findObjectMember (theMesh, "primitives");
1324 || !aPrims->IsArray())
1326 reportGltfError ("Primitive array attributes within Mesh '" + theMeshId + "' is not an array.");
1330 if (findMeshShape (theMeshShape, theMeshId))
1335 BRep_Builder aBuilder;
1336 TopoDS_Compound aMeshShape;
1338 for (rapidjson::Value::ConstValueIterator aPrimArrIter = aPrims->Begin();
1339 aPrimArrIter != aPrims->End(); ++aPrimArrIter)
1341 TCollection_AsciiString aUserName;
1343 && aName->IsString())
1345 aUserName = aName->GetString();
1348 Handle(RWGltf_GltfLatePrimitiveArray) aMeshData = new RWGltf_GltfLatePrimitiveArray (theMeshId, aUserName);
1349 if (!gltfParsePrimArray (aMeshData, theMeshId, *aPrimArrIter))
1354 if (!aMeshData->Data().IsEmpty())
1356 if (aMeshShape.IsNull())
1358 aBuilder.MakeCompound (aMeshShape);
1362 aBuilder.MakeFace (aFace, aMeshData);
1363 aBuilder.Add (aMeshShape, aFace);
1364 if (myAttribMap != NULL
1365 && aMeshData->HasStyle())
1367 RWMesh_NodeAttributes aShapeAttribs;
1368 aShapeAttribs.RawName = aUserName;
1370 // assign material and not color
1371 //aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor());
1373 Handle(XCAFDoc_VisMaterial) aMat;
1374 myMaterials.Find (!aMeshData->MaterialPbr().IsNull() ? aMeshData->MaterialPbr()->Id : aMeshData->MaterialCommon()->Id, aMat);
1375 aShapeAttribs.Style.SetMaterial (aMat);
1377 myAttribMap->Bind (aFace, aShapeAttribs);
1379 myFaceList.Append (aFace);
1386 theMeshShape = TopoDS_Iterator (aMeshShape).Value();
1390 theMeshShape = aMeshShape;
1392 bindMeshShape (theMeshShape, theMeshId, aName);
1396 // =======================================================================
1397 // function : gltfParsePrimArray
1399 // =======================================================================
1400 bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1401 const TCollection_AsciiString& theMeshId,
1402 const RWGltf_JsonValue& thePrimArray)
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 const RWGltf_JsonValue* anExtVal = findObjectMember (thePrimArray, "extensions");
1409 const RWGltf_JsonValue* aDracoVal = anExtVal != NULL
1410 ? findObjectMember (*anExtVal, THE_KHR_draco_mesh_compression)
1412 const RWGltf_JsonValue* aDracoBuf = aDracoVal != NULL
1413 ? findObjectMember (*aDracoVal, "bufferView")
1416 RWGltf_GltfPrimitiveMode aMode = RWGltf_GltfPrimitiveMode_Triangles;
1417 if (anAttribs == NULL
1418 || !anAttribs->IsObject())
1420 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' defines no attributes.");
1423 else if (aModeVal != NULL)
1425 aMode = RWGltf_GltfPrimitiveMode_UNKNOWN;
1426 if (aModeVal->IsInt())
1428 aMode = (RWGltf_GltfPrimitiveMode )aModeVal->GetInt();
1430 if (aMode < RWGltf_GltfPrimitiveMode_Points
1431 || aMode > RWGltf_GltfPrimitiveMode_TriangleFan)
1433 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' has unknown mode.");
1437 if (aMode != RWGltf_GltfPrimitiveMode_Triangles)
1439 Message::SendWarning (TCollection_AsciiString() + "Primitive array within Mesh '" + theMeshId + "' skipped due to unsupported mode");
1442 theMeshData->SetPrimitiveMode (aMode);
1445 if (aMaterial != NULL)
1447 Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
1448 if (myMaterialsPbr.Find (getKeyString (*aMaterial), aMatPbr))
1450 theMeshData->SetMaterialPbr (aMatPbr);
1453 Handle(RWGltf_MaterialCommon) aMatCommon;
1454 if (myMaterialsCommon.Find (getKeyString (*aMaterial), aMatCommon))
1456 theMeshData->SetMaterialCommon (aMatCommon);
1460 bool hasPositions = false;
1461 for (rapidjson::Value::ConstMemberIterator anAttribIter = anAttribs->MemberBegin();
1462 anAttribIter != anAttribs->MemberEnd(); ++anAttribIter)
1464 const TCollection_AsciiString anAttribId = getKeyString (anAttribIter->value);
1465 if (anAttribId.IsEmpty())
1467 reportGltfError ("Primitive array attribute accessor key within Mesh '" + theMeshId + "' is not a string.");
1471 RWGltf_GltfArrayType aType = RWGltf_GltfParseAttribType (anAttribIter->name.GetString());
1472 if (aType == RWGltf_GltfArrayType_UNKNOWN)
1474 // just ignore unknown attributes
1478 const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (anAttribIter->value);
1479 if (anAccessor == NULL
1480 || !anAccessor->IsObject())
1482 reportGltfError ("Primitive array attribute accessor key '" + anAttribId + "' points to non-existing object.");
1485 else if (!gltfParseAccessor (theMeshData, anAttribId, *anAccessor, aType, aDracoBuf))
1489 else if (aType == RWGltf_GltfArrayType_Position)
1491 hasPositions = true;
1496 reportGltfError ("Primitive array within Mesh '" + theMeshId + "' does not define vertex positions.");
1500 if (anIndices != NULL)
1502 const TCollection_AsciiString anIndicesId = getKeyString (*anIndices);
1503 const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (*anIndices);
1504 if (anAccessor == NULL
1505 || !anAccessor->IsObject())
1507 reportGltfError ("Primitive array indices accessor key '" + anIndicesId + "' points to non-existing object.");
1510 else if (!gltfParseAccessor (theMeshData, anIndicesId, *anAccessor, RWGltf_GltfArrayType_Indices, aDracoBuf))
1517 theMeshData->SetNbDeferredTriangles (theMeshData->NbDeferredNodes() / 3);
1523 // =======================================================================
1524 // function : gltfParseAccessor
1526 // =======================================================================
1527 bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1528 const TCollection_AsciiString& theName,
1529 const RWGltf_JsonValue& theAccessor,
1530 const RWGltf_GltfArrayType theType,
1531 const RWGltf_JsonValue* theCompBuffView)
1533 RWGltf_GltfAccessor aStruct;
1534 const RWGltf_JsonValue* aTypeStr = findObjectMember (theAccessor, "type");
1535 const RWGltf_JsonValue* aBufferViewName = theCompBuffView == NULL
1536 ? findObjectMember (theAccessor, "bufferView")
1538 const RWGltf_JsonValue* aByteOffset = theCompBuffView == NULL
1539 ? findObjectMember (theAccessor, "byteOffset")
1541 const RWGltf_JsonValue* aByteStride = findObjectMember (theAccessor, "byteStride"); // byteStride was part of bufferView in glTF 1.0
1542 const RWGltf_JsonValue* aCompType = findObjectMember (theAccessor, "componentType");
1543 const RWGltf_JsonValue* aCount = findObjectMember (theAccessor, "count");
1544 if (aTypeStr == NULL
1545 || !aTypeStr->IsString())
1547 reportGltfError ("Accessor '" + theName + "' does not define type.");
1550 aStruct.Type = RWGltf_GltfParseAccessorType (aTypeStr->GetString());
1551 aStruct.IsCompressed = theCompBuffView != NULL;
1552 if (aStruct.Type == RWGltf_GltfAccessorLayout_UNKNOWN)
1554 reportGltfError ("Accessor '" + theName + "' has invalid type.");
1558 if (aBufferViewName == NULL)
1560 reportGltfError ("Accessor '" + theName + "' does not define bufferView.");
1563 if (aCompType == NULL
1564 || !aCompType->IsInt())
1566 reportGltfError ("Accessor '" + theName + "' does not define componentType.");
1569 aStruct.ComponentType = (RWGltf_GltfAccessorCompType )aCompType->GetInt();
1570 if (aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int8
1571 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt8
1572 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int16
1573 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt16
1574 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt32
1575 && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Float32)
1577 reportGltfError ("Accessor '" + theName + "' defines invalid componentType value.");
1582 || !aCount->IsNumber())
1584 reportGltfError ("Accessor '" + theName + "' does not define count.");
1588 aStruct.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1589 ? (int64_t )aByteOffset->GetDouble()
1591 aStruct.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1592 ? aByteStride->GetInt()
1594 aStruct.Count = (int64_t )aCount->GetDouble();
1596 if (aStruct.ByteOffset < 0)
1598 reportGltfError ("Accessor '" + theName + "' defines invalid byteOffset.");
1601 else if (aStruct.ByteStride < 0
1602 || aStruct.ByteStride > 255)
1604 reportGltfError ("Accessor '" + theName + "' defines invalid byteStride.");
1607 else if (aStruct.Count < 1)
1609 reportGltfError ("Accessor '" + theName + "' defines invalid count.");
1613 // Read Min/Max values for POSITION type. It is used for bounding boxes
1614 if (theType == RWGltf_GltfArrayType_Position)
1616 theMeshData->SetNbDeferredNodes ((Standard_Integer )aStruct.Count);
1618 const RWGltf_JsonValue* aMin = findObjectMember (theAccessor, "min");
1619 const RWGltf_JsonValue* aMax = findObjectMember (theAccessor, "max");
1620 if (aMin != NULL && aMax != NULL)
1622 // Note: Min/Max values can be not defined in glTF file.
1623 // In this case it is not used only.
1624 if (!aMin->IsArray() || !aMax->IsArray() ||
1625 aMin->Size() != 3 || aMax->Size() != 3)
1627 reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max values.");
1631 bool isValidMinMax = true;
1632 gp_Pnt aMinPnt, aMaxPnt;
1633 for (int anIter = 0; anIter < 3; ++anIter)
1635 const RWGltf_JsonValue& aMinVal = (*aMin)[anIter];
1636 const RWGltf_JsonValue& aMaxVal = (*aMax)[anIter];
1637 if (!aMinVal.IsNumber() || !aMaxVal.IsNumber())
1639 reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max value.");
1640 isValidMinMax = false;
1643 double aMinDVal = aMinVal.GetDouble();
1644 double aMaxDVal = aMaxVal.GetDouble();
1645 if (aMinDVal > aMaxDVal)
1647 reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max value.");
1648 isValidMinMax = false;
1651 aMinPnt.SetCoord (anIter + 1, aMinDVal);
1652 aMaxPnt.SetCoord (anIter + 1, aMaxDVal);
1656 myCSTrsf.TransformPosition (aMinPnt.ChangeCoord());
1657 myCSTrsf.TransformPosition (aMaxPnt.ChangeCoord());
1663 theMeshData->SetCachedMinMax (aBox);
1668 else if (theType == RWGltf_GltfArrayType_Indices)
1670 theMeshData->SetNbDeferredTriangles ((Standard_Integer )(aStruct.Count / 3));
1673 const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
1674 if (aBufferView == NULL
1675 || !aBufferView->IsObject())
1677 reportGltfError ("Accessor '" + theName + "' refers to non-existing bufferView.");
1681 return gltfParseBufferView (theMeshData, getKeyString (*aBufferViewName), *aBufferView, aStruct, theType);
1684 // =======================================================================
1685 // function : gltfParseBufferView
1687 // =======================================================================
1688 bool RWGltf_GltfJsonParser::gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1689 const TCollection_AsciiString& theName,
1690 const RWGltf_JsonValue& theBufferView,
1691 const RWGltf_GltfAccessor& theAccessor,
1692 const RWGltf_GltfArrayType theType)
1694 RWGltf_GltfBufferView aBuffView;
1695 const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
1696 const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
1697 const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
1698 const RWGltf_JsonValue* aByteStride = findObjectMember (theBufferView, "byteStride"); // byteStride is part of bufferView since glTF 2.0
1699 const RWGltf_JsonValue* aTarget = findObjectMember (theBufferView, "target");
1700 if (aBufferName == NULL)
1702 reportGltfError ("BufferView '" + theName + "' does not define buffer.");
1706 aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
1707 ? (int64_t )aByteOffset->GetDouble()
1709 aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
1710 ? (int64_t )aByteLength->GetDouble()
1712 aBuffView.ByteStride = aByteStride != NULL && aByteStride->IsInt()
1713 ? aByteStride->GetInt()
1715 if (aTarget != NULL && aTarget->IsInt())
1717 aBuffView.Target = (RWGltf_GltfBufferViewTarget )aTarget->GetInt();
1718 if (aBuffView.Target != RWGltf_GltfBufferViewTarget_ARRAY_BUFFER
1719 && aBuffView.Target != RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER)
1721 reportGltfError ("BufferView '" + theName + "' defines invalid target.");
1726 if (aBuffView.ByteLength <= 0)
1728 reportGltfError ("BufferView '" + theName + "' defines invalid byteLength.");
1731 else if (aBuffView.ByteOffset < 0)
1733 reportGltfError ("BufferView '" + theName + "' defines invalid byteOffset.");
1736 else if (aBuffView.ByteStride < 0
1737 || aBuffView.ByteStride > 255)
1739 reportGltfError ("BufferView '" + theName + "' defines invalid byteStride.");
1743 const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
1745 || !aBuffer->IsObject())
1747 reportGltfError ("BufferView '" + theName + "' refers to non-existing buffer.");
1751 return gltfParseBuffer (theMeshData, getKeyString (*aBufferName), *aBuffer, theAccessor, aBuffView, theType);
1754 // =======================================================================
1755 // function : gltfParseBuffer
1757 // =======================================================================
1758 bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
1759 const TCollection_AsciiString& theName,
1760 const RWGltf_JsonValue& theBuffer,
1761 const RWGltf_GltfAccessor& theAccessor,
1762 const RWGltf_GltfBufferView& theView,
1763 const RWGltf_GltfArrayType theType)
1765 //const RWGltf_JsonValue* aType = findObjectMember (theBuffer, "type");
1766 //const RWGltf_JsonValue* aByteLength = findObjectMember (theBuffer, "byteLength");
1767 const RWGltf_JsonValue* anUriVal = findObjectMember (theBuffer, "uri");
1769 int64_t anOffset = theView.ByteOffset + theAccessor.ByteOffset;
1770 const int32_t aByteStride = theAccessor.ByteStride != 0 ? theView.ByteStride : theView.ByteStride;
1771 bool isBinary = false;
1774 isBinary = IsEqual ("binary_glTF", theName) // glTF 1.0
1775 || anUriVal == NULL; // glTF 2.0
1779 anOffset += myBinBodyOffset;
1781 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1782 aData.Accessor = theAccessor;
1783 aData.Accessor.ByteStride = aByteStride;
1784 aData.StreamOffset = anOffset;
1785 aData.StreamLength = theView.ByteLength;
1786 aData.StreamUri = myFilePath;
1790 if (anUriVal == NULL
1791 || !anUriVal->IsString())
1793 reportGltfError ("Buffer '" + theName + "' does not define uri.");
1797 const char* anUriData = anUriVal->GetString();
1798 if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
1800 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1801 aData.Accessor = theAccessor;
1802 aData.Accessor.ByteStride = aByteStride;
1803 aData.StreamOffset = anOffset;
1804 aData.StreamLength = 0;
1805 if (!myDecodedBuffers.Find (theName, aData.StreamData))
1807 // it is better decoding in multiple threads
1808 aData.StreamData = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
1809 myDecodedBuffers.Bind (theName, aData.StreamData);
1815 TCollection_AsciiString anUri = anUriData;
1816 if (anUri.IsEmpty())
1818 reportGltfError ("Buffer '" + theName + "' does not define uri.");
1822 TCollection_AsciiString aPath = myFolder + anUri;
1823 bool isFileExist = false;
1824 if (!myProbedFiles.Find (aPath, isFileExist))
1826 isFileExist = OSD_File (aPath).Exists();
1827 myProbedFiles.Bind (aPath, isFileExist);
1831 reportGltfError ("Buffer '" + theName + "' refers to non-existing file '" + anUri + "'.");
1835 RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
1836 aData.Accessor = theAccessor;
1837 aData.Accessor.ByteStride = aByteStride;
1838 aData.StreamOffset = anOffset;
1839 aData.StreamLength = theView.ByteLength;
1840 aData.StreamUri = myFolder + anUri;
1841 if (myExternalFiles != NULL)
1843 myExternalFiles->Add (aData.StreamUri);
1849 // =======================================================================
1850 // function : bindNamedShape
1852 // =======================================================================
1853 void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape,
1854 ShapeMapGroup theGroup,
1855 const TopLoc_Location& theLoc,
1856 const TCollection_AsciiString& theId,
1857 const RWGltf_JsonValue* theUserName)
1859 if (theShape.IsNull())
1864 if (!theLoc.IsIdentity())
1866 if (!theShape.Location().IsIdentity())
1868 theShape.Location (theLoc * theShape.Location());
1872 theShape.Location (theLoc);
1876 TCollection_AsciiString aUserName;
1877 if (theUserName != NULL
1878 && theUserName->IsString())
1880 aUserName = theUserName->GetString();
1887 myShapeMap[theGroup].Bind (theId, theShape);
1888 if (myAttribMap != NULL)
1890 RWMesh_NodeAttributes aShapeAttribs;
1891 aShapeAttribs.Name = aUserName;
1892 aShapeAttribs.RawName = theId;
1893 if (theShape.ShapeType() == TopAbs_FACE)
1895 TopLoc_Location aDummy;
1896 if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (theShape), aDummy)))
1898 if (aLateData->HasStyle())
1900 // assign material and not color
1901 //aShapeAttribs.Style.SetColorSurf (aLateData->BaseColor());
1903 Handle(XCAFDoc_VisMaterial) aMat;
1904 myMaterials.Find (!aLateData->MaterialPbr().IsNull() ? aLateData->MaterialPbr()->Id : aLateData->MaterialCommon()->Id, aMat);
1905 aShapeAttribs.Style.SetMaterial (aMat);
1907 if (aShapeAttribs.Name.IsEmpty()
1908 && myUseMeshNameAsFallback)
1910 // fallback using Mesh name
1911 aShapeAttribs.Name = aLateData->Name();
1915 else if (aShapeAttribs.Name.IsEmpty()
1916 && myUseMeshNameAsFallback)
1918 // fallback using Mesh name
1919 TopLoc_Location aDummy;
1920 TCollection_AsciiString aMeshName;
1921 for (TopExp_Explorer aFaceIter (theShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
1923 if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (aFaceIter.Value()), aDummy)))
1925 if (aLateData->Name().IsEmpty())
1930 else if (aMeshName.IsEmpty())
1932 aMeshName = aLateData->Name();
1934 else if (!aMeshName.IsEqual (aLateData->Name()))
1941 if (!aMeshName.IsEmpty())
1943 aShapeAttribs.Name = aMeshName;
1946 myAttribMap->Bind (theShape, aShapeAttribs);
1951 // =======================================================================
1954 // =======================================================================
1955 bool RWGltf_GltfJsonParser::Parse (const Message_ProgressRange& theProgress)
1957 Message_ProgressScope aPS (theProgress, "Parsing glTF", 1);
1958 #ifdef HAVE_RAPIDJSON
1960 if (!gltfParseRoots())
1966 gltfParseMaterials();
1967 if (!gltfParseScene (aPS.Next()))
1978 Message::SendFail ("Error: glTF reader is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined]");