0a419c51 |
1 | // Author: Kirill Gavrilov |
2 | // Copyright (c) 2016-2019 OPEN CASCADE SAS |
3 | // |
4 | // This file is part of Open CASCADE Technology software library. |
5 | // |
6 | // This library is free software; you can redistribute it and/or modify it under |
7 | // the terms of the GNU Lesser General Public License version 2.1 as published |
8 | // by the Free Software Foundation, with special exception defined in the file |
9 | // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT |
10 | // distribution for complete text of the license and disclaimer of any warranty. |
11 | // |
12 | // Alternatively, this file may be used under the terms of Open CASCADE |
13 | // commercial license or contractual agreement. |
14 | |
5be130de |
15 | #include "RWGltf_GltfJsonParser.hxx" |
0a419c51 |
16 | |
17 | #include <BRep_Builder.hxx> |
18 | #include <gp_Quaternion.hxx> |
19 | #include <Message.hxx> |
20 | #include <Message_Messenger.hxx> |
7e785937 |
21 | #include <Message_ProgressScope.hxx> |
0a419c51 |
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 |
35 | namespace |
36 | { |
37 | //! Material extension. |
38 | const char THE_KHR_materials_common[] = "KHR_materials_common"; |
39 | const char THE_KHR_binary_glTF[] = "KHR_binary_glTF"; |
6216ed57 |
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 | }; |
0a419c51 |
54 | } |
55 | |
56 | //! Find member of the object in a safe way. |
57 | inline 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. |
73 | inline 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 | // ======================================================================= |
91 | const 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 | // ======================================================================= |
121 | void 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 | { |
a87b1b37 |
144 | Message::SendWarning (TCollection_AsciiString ("Invalid glTF syntax - key '") + aKey + "' is already defined in '" + theRootName + "'."); |
0a419c51 |
145 | } |
146 | } |
147 | } |
148 | else if (theRoot->IsArray()) |
149 | { |
150 | // glTF 2.0 |
151 | int aChildIndex = 0; |
152 | for (rapidjson::Value::ConstValueIterator aChildIter = theRoot->Begin(); aChildIter != theRoot->End(); ++aChildIter, ++aChildIndex) |
153 | { |
154 | myChildren.Bind (TCollection_AsciiString (aChildIndex), aChildIter); |
155 | } |
156 | } |
157 | } |
158 | #endif |
159 | |
160 | // Auxiliary macros for formatting message. |
161 | #define reportGltfError(theMsg) reportGltfSyntaxProblem(TCollection_AsciiString() + theMsg, Message_Fail); |
162 | #define reportGltfWarning(theMsg) reportGltfSyntaxProblem(TCollection_AsciiString() + theMsg, Message_Warning); |
163 | |
164 | // ======================================================================= |
165 | // function : reportGltfSyntaxProblem |
166 | // purpose : |
167 | // ======================================================================= |
168 | void RWGltf_GltfJsonParser::reportGltfSyntaxProblem (const TCollection_AsciiString& theMsg, |
169 | Message_Gravity theGravity) |
170 | { |
a87b1b37 |
171 | Message::Send (myErrorPrefix + theMsg, theGravity); |
0a419c51 |
172 | } |
173 | |
174 | // ======================================================================= |
175 | // function : RWGltf_GltfJsonParser |
176 | // purpose : |
177 | // ======================================================================= |
178 | RWGltf_GltfJsonParser::RWGltf_GltfJsonParser (TopTools_SequenceOfShape& theRootShapes) |
179 | : myRootShapes(&theRootShapes), |
180 | myAttribMap (NULL), |
181 | myExternalFiles (NULL), |
d533dafb |
182 | myMetadata (NULL), |
0a419c51 |
183 | myBinBodyOffset (0), |
184 | myBinBodyLen (0), |
185 | myIsBinary (false), |
186 | myIsGltf1 (false), |
187 | myToSkipEmptyNodes (true), |
803bdcdf |
188 | myUseMeshNameAsFallback (true), |
0a419c51 |
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 | // ======================================================================= |
199 | void 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 | // ======================================================================= |
212 | bool 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 | // ======================================================================= |
251 | void 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 | |
82c59511 |
268 | if (myMetadata == NULL) |
269 | { |
270 | return; |
271 | } |
272 | |
0a419c51 |
273 | if (const RWGltf_JsonValue* aGenerator = findObjectMember (*anAsset, "generator")) |
274 | { |
275 | if (aGenerator->IsString()) |
276 | { |
82c59511 |
277 | myMetadata->Add ("generator", aGenerator->GetString()); |
0a419c51 |
278 | } |
279 | } |
280 | if (const RWGltf_JsonValue* aCopyRight = findObjectMember (*anAsset, "copyright")) |
281 | { |
282 | if (aCopyRight->IsString()) |
283 | { |
82c59511 |
284 | myMetadata->Add ("copyright", aCopyRight->GetString()); |
0a419c51 |
285 | } |
286 | } |
287 | } |
288 | |
289 | // ======================================================================= |
290 | // function : gltfParseMaterials |
291 | // purpose : |
292 | // ======================================================================= |
293 | void 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); |
a4815d55 |
325 | gltfBindMaterial (Handle(RWGltf_MaterialMetallicRoughness)(), aMat); |
0a419c51 |
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 | } |
a4815d55 |
360 | |
361 | gltfBindMaterial (aMatPbr, aMatCommon); |
0a419c51 |
362 | } |
363 | } |
364 | } |
365 | |
a4815d55 |
366 | // ======================================================================= |
367 | // function : gltfBindMaterial |
368 | // purpose : |
369 | // ======================================================================= |
370 | void 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 | { |
a87b1b37 |
425 | Message::SendWarning ("glTF reader - material with non-zero Transparency specifies Opaque AlphaMode"); |
a4815d55 |
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 | |
0a419c51 |
452 | // ======================================================================= |
453 | // function : gltfParseStdMaterial |
454 | // purpose : |
455 | // ======================================================================= |
456 | bool 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 | { |
ba00aab7 |
491 | theMat->AmbientColor = Quantity_Color (anAmb.r(), anAmb.g(), anAmb.b(), Quantity_TOC_sRGB); |
0a419c51 |
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 | { |
ba00aab7 |
502 | theMat->DiffuseColor = Quantity_Color (aDiff.r(), aDiff.g(), aDiff.b(), Quantity_TOC_sRGB); |
0a419c51 |
503 | theMat->Transparency = float(1.0 - aDiff.a()); |
504 | } |
505 | |
506 | if (gltfReadVec4 (anEmi, anEmiVal) |
507 | && validateColor4 (anEmi)) |
508 | { |
ba00aab7 |
509 | theMat->EmissiveColor = Quantity_Color (anEmi.r(), anEmi.g(), anEmi.b(), Quantity_TOC_sRGB); |
0a419c51 |
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 | { |
ba00aab7 |
520 | theMat->SpecularColor = Quantity_Color (aSpec.r(), aSpec.g(), aSpec.b(), Quantity_TOC_sRGB); |
0a419c51 |
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 | // ======================================================================= |
539 | bool 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"); |
a4815d55 |
556 | const RWGltf_JsonValue* aDoubleSidedVal = findObjectMember (theMatNode, "doubleSided"); |
557 | const RWGltf_JsonValue* anAlphaModeVal = findObjectMember (theMatNode, "alphaMode"); |
558 | const RWGltf_JsonValue* anAlphaCutoffVal = findObjectMember (theMatNode, "alphaCutoff"); |
0858125f |
559 | // TODO ADOBE_materials_thin_transparency extension can be used to read IOR (Index of Refraction for transparent materials) |
0a419c51 |
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 | |
a4815d55 |
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 | |
0a419c51 |
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 | // ======================================================================= |
665 | bool 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 | // ======================================================================= |
691 | bool 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 | { |
0a419c51 |
753 | if (aBufferViewName == NULL) |
754 | { |
755 | reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source."); |
756 | return false; |
757 | } |
0a419c51 |
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 | } |
6216ed57 |
765 | return gltfParseTexturInGlbBuffer (theTexture, *aBinVal, getKeyString (*aBufferViewName), *aBufferView); |
766 | } |
767 | } |
0a419c51 |
768 | |
6216ed57 |
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 | } |
0a419c51 |
778 | |
6216ed57 |
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; |
0a419c51 |
785 | } |
6216ed57 |
786 | return gltfParseTextureInBufferView (theTexture, getKeyString (*aSrcVal), getKeyString (*aBufferViewName), *aBufferView); |
0a419c51 |
787 | } |
788 | |
6216ed57 |
789 | if (!anUriVal->IsString()) |
0a419c51 |
790 | { |
6216ed57 |
791 | reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source."); |
0a419c51 |
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 | } |
a87b1b37 |
813 | Message::SendWarning ("glTF reader - embedded image has been skipped"); |
0a419c51 |
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 | |
6216ed57 |
826 | // ======================================================================= |
827 | // function : gltfParseTexturInGlbBuffer |
828 | // purpose : |
829 | // ======================================================================= |
830 | bool 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 | // ======================================================================= |
878 | bool 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 | |
0a419c51 |
970 | // ======================================================================= |
971 | // function : gltfParseScene |
972 | // purpose : |
973 | // ======================================================================= |
7e785937 |
974 | bool RWGltf_GltfJsonParser::gltfParseScene (const Message_ProgressRange& theProgress) |
0a419c51 |
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 | // ======================================================================= |
999 | bool RWGltf_GltfJsonParser::gltfParseSceneNodes (TopTools_SequenceOfShape& theShapeSeq, |
1000 | const RWGltf_JsonValue& theSceneNodes, |
7e785937 |
1001 | const Message_ProgressRange& theProgress) |
0a419c51 |
1002 | { |
1003 | if (!theSceneNodes.IsArray()) |
1004 | { |
1005 | reportGltfError ("Scene nodes is not array."); |
1006 | return false; |
1007 | } |
1008 | |
7e785937 |
1009 | Message_ProgressScope aPS (theProgress, "Reading scene nodes", theSceneNodes.Size()); |
0a419c51 |
1010 | for (rapidjson::Value::ConstValueIterator aSceneNodeIter = theSceneNodes.Begin(); |
7e785937 |
1011 | aSceneNodeIter != theSceneNodes.End() && aPS.More(); ++aSceneNodeIter) |
0a419c51 |
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; |
7e785937 |
1021 | if (!gltfParseSceneNode (aNodeShape, getKeyString (*aSceneNodeIter), *aSceneNode, aPS.Next())) |
0a419c51 |
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 | // ======================================================================= |
1045 | bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape, |
1046 | const TCollection_AsciiString& theSceneNodeId, |
1047 | const RWGltf_JsonValue& theSceneNode, |
7e785937 |
1048 | const Message_ProgressRange& theProgress) |
0a419c51 |
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 | |
a87b1b37 |
1208 | Message::SendWarning (TCollection_AsciiString ("glTF reader, scene node '") |
1209 | + theSceneNodeId + "' defines unsupported scaling " + aScaleVec.x() + " " + aScaleVec.y() + " " + aScaleVec.z()); |
0a419c51 |
1210 | } |
1211 | else if (Abs (aScaleVec.x() - 1.0) > Precision::Confusion()) |
1212 | { |
1213 | aTrsf.SetScaleFactor (aScaleVec.x()); |
1214 | } |
1215 | } |
1216 | |
1217 | myCSTrsf.TransformTransformation (aTrsf); |
1218 | if (aTrsf.Form() != gp_Identity) |
1219 | { |
1220 | aNodeLoc = TopLoc_Location (aTrsf); |
1221 | } |
1222 | } |
1223 | |
1224 | BRep_Builder aBuilder; |
1225 | TopoDS_Compound aNodeShape; |
1226 | aBuilder.MakeCompound (aNodeShape); |
1227 | TopTools_SequenceOfShape aChildShapes; |
1228 | int aNbSubShapes = 0; |
1229 | if (aChildren != NULL |
1230 | && !gltfParseSceneNodes (aChildShapes, *aChildren, theProgress)) |
1231 | { |
1232 | theNodeShape = aNodeShape; |
1233 | bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName); |
1234 | return false; |
1235 | } |
1236 | for (TopTools_SequenceOfShape::Iterator aChildShapeIter (aChildShapes); aChildShapeIter.More(); aChildShapeIter.Next()) |
1237 | { |
1238 | aBuilder.Add (aNodeShape, aChildShapeIter.Value()); |
1239 | ++aNbSubShapes; |
1240 | } |
1241 | |
1242 | if (aMeshes_1 != NULL |
1243 | && aMeshes_1->IsArray()) |
1244 | { |
1245 | // glTF 1.0 |
0a419c51 |
1246 | for (rapidjson::Value::ConstValueIterator aMeshIter = aMeshes_1->Begin(); |
28828566 |
1247 | aMeshIter != aMeshes_1->End(); ++aMeshIter) |
0a419c51 |
1248 | { |
1249 | const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMeshIter); |
1250 | if (aMesh == NULL) |
1251 | { |
1252 | theNodeShape = aNodeShape; |
1253 | bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName); |
1254 | reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh."); |
1255 | return false; |
1256 | } |
1257 | |
1258 | TopoDS_Shape aMeshShape; |
28828566 |
1259 | if (!gltfParseMesh (aMeshShape, getKeyString (*aMeshIter), *aMesh)) |
0a419c51 |
1260 | { |
1261 | theNodeShape = aNodeShape; |
1262 | bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName); |
1263 | return false; |
1264 | } |
1265 | if (!aMeshShape.IsNull()) |
1266 | { |
1267 | aBuilder.Add (aNodeShape, aMeshShape); |
1268 | ++aNbSubShapes; |
1269 | } |
1270 | } |
1271 | } |
1272 | if (aMesh_2 != NULL) |
1273 | { |
1274 | // glTF 2.0 |
1275 | const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMesh_2); |
1276 | if (aMesh == NULL) |
1277 | { |
1278 | theNodeShape = aNodeShape; |
1279 | bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName); |
1280 | reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh."); |
1281 | return false; |
1282 | } |
1283 | |
1284 | TopoDS_Shape aMeshShape; |
28828566 |
1285 | if (!gltfParseMesh (aMeshShape, getKeyString (*aMesh_2), *aMesh)) |
0a419c51 |
1286 | { |
1287 | theNodeShape = aNodeShape; |
1288 | bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName); |
1289 | return false; |
1290 | } |
1291 | if (!aMeshShape.IsNull()) |
1292 | { |
1293 | aBuilder.Add (aNodeShape, aMeshShape); |
1294 | ++aNbSubShapes; |
1295 | } |
1296 | } |
1297 | |
1298 | if (aNbSubShapes == 1) |
1299 | { |
1300 | theNodeShape = TopoDS_Iterator (aNodeShape).Value(); |
1301 | } |
1302 | else |
1303 | { |
1304 | theNodeShape = aNodeShape; |
1305 | } |
1306 | bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName); |
1307 | return true; |
1308 | } |
1309 | |
1310 | // ======================================================================= |
1311 | // function : gltfParseMesh |
1312 | // purpose : |
1313 | // ======================================================================= |
1314 | bool RWGltf_GltfJsonParser::gltfParseMesh (TopoDS_Shape& theMeshShape, |
1315 | const TCollection_AsciiString& theMeshId, |
28828566 |
1316 | const RWGltf_JsonValue& theMesh) |
0a419c51 |
1317 | { |
1318 | const RWGltf_JsonValue* aName = findObjectMember (theMesh, "name"); |
1319 | const RWGltf_JsonValue* aPrims = findObjectMember (theMesh, "primitives"); |
49259460 |
1320 | if (aPrims == NULL |
1321 | || !aPrims->IsArray()) |
0a419c51 |
1322 | { |
1323 | reportGltfError ("Primitive array attributes within Mesh '" + theMeshId + "' is not an array."); |
1324 | return false; |
1325 | } |
1326 | |
1327 | if (findMeshShape (theMeshShape, theMeshId)) |
1328 | { |
1329 | return true; |
1330 | } |
1331 | |
1332 | BRep_Builder aBuilder; |
1333 | TopoDS_Compound aMeshShape; |
1334 | int aNbFaces = 0; |
1335 | for (rapidjson::Value::ConstValueIterator aPrimArrIter = aPrims->Begin(); |
1336 | aPrimArrIter != aPrims->End(); ++aPrimArrIter) |
1337 | { |
1338 | TCollection_AsciiString aUserName; |
1339 | if (aName != NULL |
1340 | && aName->IsString()) |
1341 | { |
1342 | aUserName = aName->GetString(); |
1343 | } |
1344 | |
1345 | Handle(RWGltf_GltfLatePrimitiveArray) aMeshData = new RWGltf_GltfLatePrimitiveArray (theMeshId, aUserName); |
28828566 |
1346 | if (!gltfParsePrimArray (aMeshData, theMeshId, *aPrimArrIter)) |
0a419c51 |
1347 | { |
1348 | return false; |
1349 | } |
1350 | |
1351 | if (!aMeshData->Data().IsEmpty()) |
1352 | { |
1353 | if (aMeshShape.IsNull()) |
1354 | { |
1355 | aBuilder.MakeCompound (aMeshShape); |
1356 | } |
1357 | |
1358 | TopoDS_Face aFace; |
1359 | aBuilder.MakeFace (aFace, aMeshData); |
1360 | aBuilder.Add (aMeshShape, aFace); |
1361 | if (myAttribMap != NULL |
1362 | && aMeshData->HasStyle()) |
1363 | { |
1364 | RWMesh_NodeAttributes aShapeAttribs; |
1365 | aShapeAttribs.RawName = aUserName; |
a4815d55 |
1366 | |
1367 | // assign material and not color |
1368 | //aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor()); |
1369 | |
1370 | Handle(XCAFDoc_VisMaterial) aMat; |
1371 | myMaterials.Find (!aMeshData->MaterialPbr().IsNull() ? aMeshData->MaterialPbr()->Id : aMeshData->MaterialCommon()->Id, aMat); |
1372 | aShapeAttribs.Style.SetMaterial (aMat); |
1373 | |
0a419c51 |
1374 | myAttribMap->Bind (aFace, aShapeAttribs); |
1375 | } |
1376 | myFaceList.Append (aFace); |
1377 | ++aNbFaces; |
1378 | } |
1379 | } |
1380 | |
1381 | if (aNbFaces == 1) |
1382 | { |
1383 | theMeshShape = TopoDS_Iterator (aMeshShape).Value(); |
1384 | } |
1385 | else |
1386 | { |
1387 | theMeshShape = aMeshShape; |
1388 | } |
1389 | bindMeshShape (theMeshShape, theMeshId, aName); |
1390 | return true; |
1391 | } |
1392 | |
1393 | // ======================================================================= |
1394 | // function : gltfParsePrimArray |
1395 | // purpose : |
1396 | // ======================================================================= |
1397 | bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData, |
1398 | const TCollection_AsciiString& theMeshId, |
28828566 |
1399 | const RWGltf_JsonValue& thePrimArray) |
0a419c51 |
1400 | { |
1401 | const RWGltf_JsonValue* anAttribs = findObjectMember (thePrimArray, "attributes"); |
1402 | const RWGltf_JsonValue* anIndices = findObjectMember (thePrimArray, "indices"); |
1403 | const RWGltf_JsonValue* aMaterial = findObjectMember (thePrimArray, "material"); |
1404 | const RWGltf_JsonValue* aModeVal = findObjectMember (thePrimArray, "mode"); |
1405 | RWGltf_GltfPrimitiveMode aMode = RWGltf_GltfPrimitiveMode_Triangles; |
1406 | if (anAttribs == NULL |
1407 | || !anAttribs->IsObject()) |
1408 | { |
1409 | reportGltfError ("Primitive array within Mesh '" + theMeshId + "' defines no attributes."); |
1410 | return false; |
1411 | } |
1412 | else if (aModeVal != NULL) |
1413 | { |
1414 | aMode = RWGltf_GltfPrimitiveMode_UNKNOWN; |
1415 | if (aModeVal->IsInt()) |
1416 | { |
1417 | aMode = (RWGltf_GltfPrimitiveMode )aModeVal->GetInt(); |
1418 | } |
1419 | if (aMode < RWGltf_GltfPrimitiveMode_Points |
1420 | || aMode > RWGltf_GltfPrimitiveMode_TriangleFan) |
1421 | { |
1422 | reportGltfError ("Primitive array within Mesh '" + theMeshId + "' has unknown mode."); |
1423 | return false; |
1424 | } |
1425 | } |
1426 | if (aMode != RWGltf_GltfPrimitiveMode_Triangles) |
1427 | { |
a87b1b37 |
1428 | Message::SendWarning (TCollection_AsciiString() + "Primitive array within Mesh '" + theMeshId + "' skipped due to unsupported mode"); |
0a419c51 |
1429 | return true; |
1430 | } |
1431 | theMeshData->SetPrimitiveMode (aMode); |
1432 | |
1433 | // assign material |
1434 | if (aMaterial != NULL) |
1435 | { |
1436 | Handle(RWGltf_MaterialMetallicRoughness) aMatPbr; |
1437 | if (myMaterialsPbr.Find (getKeyString (*aMaterial), aMatPbr)) |
1438 | { |
1439 | theMeshData->SetMaterialPbr (aMatPbr); |
1440 | } |
1441 | |
1442 | Handle(RWGltf_MaterialCommon) aMatCommon; |
1443 | if (myMaterialsCommon.Find (getKeyString (*aMaterial), aMatCommon)) |
1444 | { |
1445 | theMeshData->SetMaterialCommon (aMatCommon); |
1446 | } |
1447 | } |
1448 | |
1449 | bool hasPositions = false; |
1450 | for (rapidjson::Value::ConstMemberIterator anAttribIter = anAttribs->MemberBegin(); |
1451 | anAttribIter != anAttribs->MemberEnd(); ++anAttribIter) |
1452 | { |
1453 | const TCollection_AsciiString anAttribId = getKeyString (anAttribIter->value); |
1454 | if (anAttribId.IsEmpty()) |
1455 | { |
1456 | reportGltfError ("Primitive array attribute accessor key within Mesh '" + theMeshId + "' is not a string."); |
1457 | return false; |
1458 | } |
1459 | |
1460 | RWGltf_GltfArrayType aType = RWGltf_GltfParseAttribType (anAttribIter->name.GetString()); |
1461 | if (aType == RWGltf_GltfArrayType_UNKNOWN) |
1462 | { |
1463 | // just ignore unknown attributes |
1464 | continue; |
1465 | } |
1466 | |
1467 | const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (anAttribIter->value); |
1468 | if (anAccessor == NULL |
1469 | || !anAccessor->IsObject()) |
1470 | { |
1471 | reportGltfError ("Primitive array attribute accessor key '" + anAttribId + "' points to non-existing object."); |
1472 | return false; |
1473 | } |
1474 | else if (!gltfParseAccessor (theMeshData, anAttribId, *anAccessor, aType)) |
1475 | { |
1476 | return false; |
1477 | } |
1478 | else if (aType == RWGltf_GltfArrayType_Position) |
1479 | { |
1480 | hasPositions = true; |
1481 | } |
1482 | } |
1483 | if (!hasPositions) |
1484 | { |
1485 | reportGltfError ("Primitive array within Mesh '" + theMeshId + "' does not define vertex positions."); |
1486 | return false; |
1487 | } |
1488 | |
1489 | if (anIndices != NULL) |
1490 | { |
1491 | const TCollection_AsciiString anIndicesId = getKeyString (*anIndices); |
1492 | const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (*anIndices); |
1493 | if (anAccessor == NULL |
1494 | || !anAccessor->IsObject()) |
1495 | { |
1496 | reportGltfError ("Primitive array indices accessor key '" + anIndicesId + "' points to non-existing object."); |
1497 | return false; |
1498 | } |
1499 | else if (!gltfParseAccessor (theMeshData, anIndicesId, *anAccessor, RWGltf_GltfArrayType_Indices)) |
1500 | { |
1501 | return false; |
1502 | } |
1503 | } |
1504 | |
1505 | return true; |
1506 | } |
1507 | |
1508 | // ======================================================================= |
1509 | // function : gltfParseAccessor |
1510 | // purpose : |
1511 | // ======================================================================= |
1512 | bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData, |
1513 | const TCollection_AsciiString& theName, |
1514 | const RWGltf_JsonValue& theAccessor, |
1515 | const RWGltf_GltfArrayType theType) |
1516 | { |
1517 | RWGltf_GltfAccessor aStruct; |
1518 | const RWGltf_JsonValue* aTypeStr = findObjectMember (theAccessor, "type"); |
1519 | const RWGltf_JsonValue* aBufferViewName = findObjectMember (theAccessor, "bufferView"); |
1520 | const RWGltf_JsonValue* aByteOffset = findObjectMember (theAccessor, "byteOffset"); |
8811d6f2 |
1521 | const RWGltf_JsonValue* aByteStride = findObjectMember (theAccessor, "byteStride"); // byteStride was part of bufferView in glTF 1.0 |
0a419c51 |
1522 | const RWGltf_JsonValue* aCompType = findObjectMember (theAccessor, "componentType"); |
1523 | const RWGltf_JsonValue* aCount = findObjectMember (theAccessor, "count"); |
1524 | if (aTypeStr == NULL |
1525 | || !aTypeStr->IsString()) |
1526 | { |
1527 | reportGltfError ("Accessor '" + theName + "' does not define type."); |
1528 | return false; |
1529 | } |
1530 | aStruct.Type = RWGltf_GltfParseAccessorType (aTypeStr->GetString()); |
1531 | if (aStruct.Type == RWGltf_GltfAccessorLayout_UNKNOWN) |
1532 | { |
1533 | reportGltfError ("Accessor '" + theName + "' has invalid type."); |
1534 | return false; |
1535 | } |
1536 | |
1537 | if (aBufferViewName == NULL) |
1538 | { |
1539 | reportGltfError ("Accessor '" + theName + "' does not define bufferView."); |
1540 | return false; |
1541 | } |
1542 | if (aCompType == NULL |
1543 | || !aCompType->IsInt()) |
1544 | { |
1545 | reportGltfError ("Accessor '" + theName + "' does not define componentType."); |
1546 | return false; |
1547 | } |
1548 | aStruct.ComponentType = (RWGltf_GltfAccessorCompType )aCompType->GetInt(); |
1549 | if (aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int8 |
1550 | && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt8 |
1551 | && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int16 |
1552 | && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt16 |
1553 | && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt32 |
1554 | && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Float32) |
1555 | { |
1556 | reportGltfError ("Accessor '" + theName + "' defines invalid componentType value."); |
1557 | return false; |
1558 | } |
1559 | |
1560 | if (aCount == NULL |
1561 | || !aCount->IsNumber()) |
1562 | { |
1563 | reportGltfError ("Accessor '" + theName + "' does not define count."); |
1564 | return false; |
1565 | } |
1566 | |
1567 | aStruct.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber() |
1568 | ? (int64_t )aByteOffset->GetDouble() |
1569 | : 0; |
1570 | aStruct.ByteStride = aByteStride != NULL && aByteStride->IsInt() |
1571 | ? aByteStride->GetInt() |
1572 | : 0; |
1573 | aStruct.Count = (int64_t )aCount->GetDouble(); |
1574 | |
1575 | if (aStruct.ByteOffset < 0) |
1576 | { |
1577 | reportGltfError ("Accessor '" + theName + "' defines invalid byteOffset."); |
1578 | return false; |
1579 | } |
1580 | else if (aStruct.ByteStride < 0 |
1581 | || aStruct.ByteStride > 255) |
1582 | { |
1583 | reportGltfError ("Accessor '" + theName + "' defines invalid byteStride."); |
1584 | return false; |
1585 | } |
1586 | else if (aStruct.Count < 1) |
1587 | { |
1588 | reportGltfError ("Accessor '" + theName + "' defines invalid count."); |
1589 | return false; |
1590 | } |
1591 | |
1592 | // Read Min/Max values for POSITION type. It is used for bounding boxes |
1593 | if (theType == RWGltf_GltfArrayType_Position) |
1594 | { |
1595 | const RWGltf_JsonValue* aMin = findObjectMember (theAccessor, "min"); |
1596 | const RWGltf_JsonValue* aMax = findObjectMember (theAccessor, "max"); |
1597 | if (aMin != NULL && aMax != NULL) |
1598 | { |
1599 | // Note: Min/Max values can be not defined in glTF file. |
1600 | // In this case it is not used only. |
1601 | if (!aMin->IsArray() || !aMax->IsArray() || |
1602 | aMin->Size() != 3 || aMax->Size() != 3) |
1603 | { |
1604 | reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max values."); |
1605 | } |
1606 | else |
1607 | { |
1608 | bool isValidMinMax = true; |
1609 | gp_Pnt aMinPnt, aMaxPnt; |
1610 | for (int anIter = 0; anIter < 3; ++anIter) |
1611 | { |
1612 | const RWGltf_JsonValue& aMinVal = (*aMin)[anIter]; |
1613 | const RWGltf_JsonValue& aMaxVal = (*aMax)[anIter]; |
1614 | if (!aMinVal.IsNumber() || !aMaxVal.IsNumber()) |
1615 | { |
1616 | reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max value."); |
1617 | isValidMinMax = false; |
1618 | break; |
1619 | } |
1620 | aMinPnt.SetCoord (anIter + 1, aMinVal.GetDouble()); |
1621 | aMinPnt.SetCoord (anIter + 1, aMaxVal.GetDouble()); |
1622 | } |
1623 | if (isValidMinMax) |
1624 | { |
1625 | myCSTrsf.TransformPosition (aMinPnt.ChangeCoord()); |
1626 | myCSTrsf.TransformPosition (aMaxPnt.ChangeCoord()); |
1627 | |
1628 | Bnd_Box aBox; |
1629 | aBox.Add (aMinPnt); |
1630 | aBox.Add (aMaxPnt); |
1631 | |
1632 | theMeshData->SetBoundingBox (aBox); |
1633 | } |
1634 | } |
1635 | } |
1636 | } |
1637 | |
1638 | const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName); |
1639 | if (aBufferView == NULL |
1640 | || !aBufferView->IsObject()) |
1641 | { |
1642 | reportGltfError ("Accessor '" + theName + "' refers to non-existing bufferView."); |
1643 | return false; |
1644 | } |
1645 | |
1646 | return gltfParseBufferView (theMeshData, getKeyString (*aBufferViewName), *aBufferView, aStruct, theType); |
1647 | } |
1648 | |
1649 | // ======================================================================= |
1650 | // function : gltfParseBufferView |
1651 | // purpose : |
1652 | // ======================================================================= |
1653 | bool RWGltf_GltfJsonParser::gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData, |
1654 | const TCollection_AsciiString& theName, |
1655 | const RWGltf_JsonValue& theBufferView, |
1656 | const RWGltf_GltfAccessor& theAccessor, |
1657 | const RWGltf_GltfArrayType theType) |
1658 | { |
1659 | RWGltf_GltfBufferView aBuffView; |
1660 | const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer"); |
1661 | const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength"); |
1662 | const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset"); |
8811d6f2 |
1663 | const RWGltf_JsonValue* aByteStride = findObjectMember (theBufferView, "byteStride"); // byteStride is part of bufferView since glTF 2.0 |
0a419c51 |
1664 | const RWGltf_JsonValue* aTarget = findObjectMember (theBufferView, "target"); |
1665 | if (aBufferName == NULL) |
1666 | { |
1667 | reportGltfError ("BufferView '" + theName + "' does not define buffer."); |
1668 | return false; |
1669 | } |
1670 | |
1671 | aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber() |
1672 | ? (int64_t )aByteOffset->GetDouble() |
1673 | : 0; |
1674 | aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber() |
1675 | ? (int64_t )aByteLength->GetDouble() |
1676 | : 0; |
8811d6f2 |
1677 | aBuffView.ByteStride = aByteStride != NULL && aByteStride->IsInt() |
1678 | ? aByteStride->GetInt() |
1679 | : 0; |
0a419c51 |
1680 | if (aTarget != NULL && aTarget->IsInt()) |
1681 | { |
1682 | aBuffView.Target = (RWGltf_GltfBufferViewTarget )aTarget->GetInt(); |
1683 | if (aBuffView.Target != RWGltf_GltfBufferViewTarget_ARRAY_BUFFER |
1684 | && aBuffView.Target != RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER) |
1685 | { |
1686 | reportGltfError ("BufferView '" + theName + "' defines invalid target."); |
1687 | return false; |
1688 | } |
1689 | } |
1690 | |
6216ed57 |
1691 | if (aBuffView.ByteLength <= 0) |
0a419c51 |
1692 | { |
1693 | reportGltfError ("BufferView '" + theName + "' defines invalid byteLength."); |
1694 | return false; |
1695 | } |
1696 | else if (aBuffView.ByteOffset < 0) |
1697 | { |
1698 | reportGltfError ("BufferView '" + theName + "' defines invalid byteOffset."); |
1699 | return false; |
1700 | } |
8811d6f2 |
1701 | else if (aBuffView.ByteStride < 0 |
1702 | || aBuffView.ByteStride > 255) |
1703 | { |
1704 | reportGltfError ("BufferView '" + theName + "' defines invalid byteStride."); |
1705 | return false; |
1706 | } |
0a419c51 |
1707 | |
1708 | const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName); |
1709 | if (aBuffer == NULL |
1710 | || !aBuffer->IsObject()) |
1711 | { |
1712 | reportGltfError ("BufferView '" + theName + "' refers to non-existing buffer."); |
1713 | return false; |
1714 | } |
1715 | |
1716 | return gltfParseBuffer (theMeshData, getKeyString (*aBufferName), *aBuffer, theAccessor, aBuffView, theType); |
1717 | } |
1718 | |
1719 | // ======================================================================= |
1720 | // function : gltfParseBuffer |
1721 | // purpose : |
1722 | // ======================================================================= |
1723 | bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData, |
1724 | const TCollection_AsciiString& theName, |
1725 | const RWGltf_JsonValue& theBuffer, |
1726 | const RWGltf_GltfAccessor& theAccessor, |
1727 | const RWGltf_GltfBufferView& theView, |
1728 | const RWGltf_GltfArrayType theType) |
1729 | { |
1730 | //const RWGltf_JsonValue* aType = findObjectMember (theBuffer, "type"); |
1731 | //const RWGltf_JsonValue* aByteLength = findObjectMember (theBuffer, "byteLength"); |
1732 | const RWGltf_JsonValue* anUriVal = findObjectMember (theBuffer, "uri"); |
1733 | |
1734 | int64_t anOffset = theView.ByteOffset + theAccessor.ByteOffset; |
8811d6f2 |
1735 | const int32_t aByteStride = theAccessor.ByteStride != 0 ? theView.ByteStride : theView.ByteStride; |
0a419c51 |
1736 | bool isBinary = false; |
1737 | if (myIsBinary) |
1738 | { |
1739 | isBinary = IsEqual ("binary_glTF", theName) // glTF 1.0 |
1740 | || anUriVal == NULL; // glTF 2.0 |
1741 | } |
1742 | if (isBinary) |
1743 | { |
1744 | anOffset += myBinBodyOffset; |
1745 | |
1746 | RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType); |
1747 | aData.Accessor = theAccessor; |
8811d6f2 |
1748 | aData.Accessor.ByteStride = aByteStride; |
0a419c51 |
1749 | aData.StreamOffset = anOffset; |
1750 | aData.StreamUri = myFilePath; |
1751 | return true; |
1752 | } |
1753 | |
1754 | if (anUriVal == NULL |
1755 | || !anUriVal->IsString()) |
1756 | { |
1757 | reportGltfError ("Buffer '" + theName + "' does not define uri."); |
1758 | return false; |
1759 | } |
1760 | |
1761 | const char* anUriData = anUriVal->GetString(); |
1762 | if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0) |
1763 | { |
1764 | RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType); |
1765 | aData.Accessor = theAccessor; |
8811d6f2 |
1766 | aData.Accessor.ByteStride = aByteStride; |
0a419c51 |
1767 | aData.StreamOffset = anOffset; |
1768 | if (!myDecodedBuffers.Find (theName, aData.StreamData)) |
1769 | { |
1770 | // it is better decoding in multiple threads |
1771 | aData.StreamData = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37); |
1772 | myDecodedBuffers.Bind (theName, aData.StreamData); |
1773 | } |
1774 | return true; |
1775 | } |
1776 | else |
1777 | { |
1778 | TCollection_AsciiString anUri = anUriData; |
1779 | if (anUri.IsEmpty()) |
1780 | { |
1781 | reportGltfError ("Buffer '" + theName + "' does not define uri."); |
1782 | return false; |
1783 | } |
1784 | |
1785 | TCollection_AsciiString aPath = myFolder + anUri; |
1786 | bool isFileExist = false; |
1787 | if (!myProbedFiles.Find (aPath, isFileExist)) |
1788 | { |
1789 | isFileExist = OSD_File (aPath).Exists(); |
1790 | myProbedFiles.Bind (aPath, isFileExist); |
1791 | } |
1792 | if (!isFileExist) |
1793 | { |
1794 | reportGltfError ("Buffer '" + theName + "' refers to non-existing file '" + anUri + "'."); |
1795 | return false; |
1796 | } |
1797 | |
1798 | RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType); |
1799 | aData.Accessor = theAccessor; |
8811d6f2 |
1800 | aData.Accessor.ByteStride = aByteStride; |
0a419c51 |
1801 | aData.StreamOffset = anOffset; |
1802 | aData.StreamUri = myFolder + anUri; |
1803 | if (myExternalFiles != NULL) |
1804 | { |
1805 | myExternalFiles->Add (aData.StreamUri); |
1806 | } |
1807 | return true; |
1808 | } |
1809 | } |
1810 | |
1811 | // ======================================================================= |
1812 | // function : bindNamedShape |
1813 | // purpose : |
1814 | // ======================================================================= |
1815 | void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape, |
1816 | ShapeMapGroup theGroup, |
1817 | const TopLoc_Location& theLoc, |
1818 | const TCollection_AsciiString& theId, |
1819 | const RWGltf_JsonValue* theUserName) |
1820 | { |
1821 | if (theShape.IsNull()) |
1822 | { |
1823 | return; |
1824 | } |
1825 | |
1826 | if (!theLoc.IsIdentity()) |
1827 | { |
af7fa143 |
1828 | if (!theShape.Location().IsIdentity()) |
1829 | { |
1830 | theShape.Location (theLoc * theShape.Location()); |
1831 | } |
1832 | else |
1833 | { |
1834 | theShape.Location (theLoc); |
1835 | } |
0a419c51 |
1836 | } |
1837 | |
1838 | TCollection_AsciiString aUserName; |
1839 | if (theUserName != NULL |
1840 | && theUserName->IsString()) |
1841 | { |
1842 | aUserName = theUserName->GetString(); |
1843 | } |
1844 | else if (myIsGltf1) |
1845 | { |
1846 | aUserName = theId; |
1847 | } |
1848 | |
1849 | myShapeMap[theGroup].Bind (theId, theShape); |
1850 | if (myAttribMap != NULL) |
1851 | { |
1852 | RWMesh_NodeAttributes aShapeAttribs; |
1853 | aShapeAttribs.Name = aUserName; |
1854 | aShapeAttribs.RawName = theId; |
1855 | if (theShape.ShapeType() == TopAbs_FACE) |
1856 | { |
1857 | TopLoc_Location aDummy; |
1858 | if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (theShape), aDummy))) |
1859 | { |
1860 | if (aLateData->HasStyle()) |
1861 | { |
a4815d55 |
1862 | // assign material and not color |
1863 | //aShapeAttribs.Style.SetColorSurf (aLateData->BaseColor()); |
1864 | |
1865 | Handle(XCAFDoc_VisMaterial) aMat; |
1866 | myMaterials.Find (!aLateData->MaterialPbr().IsNull() ? aLateData->MaterialPbr()->Id : aLateData->MaterialCommon()->Id, aMat); |
1867 | aShapeAttribs.Style.SetMaterial (aMat); |
0a419c51 |
1868 | } |
803bdcdf |
1869 | if (aShapeAttribs.Name.IsEmpty() |
1870 | && myUseMeshNameAsFallback) |
1871 | { |
1872 | // fallback using Mesh name |
1873 | aShapeAttribs.Name = aLateData->Name(); |
1874 | } |
1875 | } |
1876 | } |
1877 | else if (aShapeAttribs.Name.IsEmpty() |
1878 | && myUseMeshNameAsFallback) |
1879 | { |
1880 | // fallback using Mesh name |
1881 | TopLoc_Location aDummy; |
1882 | TCollection_AsciiString aMeshName; |
1883 | for (TopExp_Explorer aFaceIter (theShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next()) |
1884 | { |
1885 | if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (aFaceIter.Value()), aDummy))) |
1886 | { |
1887 | if (aLateData->Name().IsEmpty()) |
1888 | { |
1889 | aMeshName.Clear(); |
1890 | break; |
1891 | } |
1892 | else if (aMeshName.IsEmpty()) |
1893 | { |
1894 | aMeshName = aLateData->Name(); |
1895 | } |
1896 | else if (!aMeshName.IsEqual (aLateData->Name())) |
1897 | { |
1898 | aMeshName.Clear(); |
1899 | break; |
1900 | } |
1901 | } |
1902 | } |
1903 | if (!aMeshName.IsEmpty()) |
1904 | { |
1905 | aShapeAttribs.Name = aMeshName; |
0a419c51 |
1906 | } |
1907 | } |
1908 | myAttribMap->Bind (theShape, aShapeAttribs); |
1909 | } |
1910 | } |
1911 | #endif |
1912 | |
1913 | // ======================================================================= |
1914 | // function : Parse |
1915 | // purpose : |
1916 | // ======================================================================= |
7e785937 |
1917 | bool RWGltf_GltfJsonParser::Parse (const Message_ProgressRange& theProgress) |
0a419c51 |
1918 | { |
7e785937 |
1919 | Message_ProgressScope aPS (theProgress, "Parsing glTF", 1); |
0a419c51 |
1920 | #ifdef HAVE_RAPIDJSON |
1921 | { |
1922 | if (!gltfParseRoots()) |
1923 | { |
1924 | return false; |
1925 | } |
1926 | |
1927 | gltfParseAsset(); |
1928 | gltfParseMaterials(); |
7e785937 |
1929 | if (!gltfParseScene (aPS.Next())) |
0a419c51 |
1930 | { |
1931 | return false; |
1932 | } |
1933 | } |
7e785937 |
1934 | if (!aPS.More()) |
0a419c51 |
1935 | { |
1936 | return false; |
1937 | } |
1938 | return true; |
1939 | #else |
a87b1b37 |
1940 | Message::SendFail ("Error: glTF reader is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined]"); |
0a419c51 |
1941 | return false; |
1942 | #endif |
1943 | } |