0031312: Data Exchange - RWGltf_CafReader fails reading texture embedded into base64...
[occt.git] / src / RWGltf / RWGltf_GltfJsonParser.pxx
1 // Author: Kirill Gavrilov
2 // Copyright (c) 2016-2019 OPEN CASCADE SAS
3 //
4 // This file is part of Open CASCADE Technology software library.
5 //
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
11 //
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
14
15 #ifndef _RWGltf_GltfJsonParser_HeaderFile
16 #define _RWGltf_GltfJsonParser_HeaderFile
17
18 #include <Graphic3d_Vec.hxx>
19 #include <Message_Gravity.hxx>
20 #include <NCollection_DataMap.hxx>
21 #include <NCollection_IndexedMap.hxx>
22 #include <RWGltf_GltfLatePrimitiveArray.hxx>
23 #include <RWGltf_GltfBufferView.hxx>
24 #include <RWGltf_GltfRootElement.hxx>
25 #include <RWGltf_MaterialCommon.hxx>
26 #include <RWGltf_MaterialMetallicRoughness.hxx>
27 #include <RWMesh_CoordinateSystemConverter.hxx>
28 #include <RWMesh_NodeAttributes.hxx>
29 #include <TColStd_IndexedDataMapOfStringString.hxx>
30 #include <TopoDS_Face.hxx>
31 #include <TopTools_SequenceOfShape.hxx>
32
33 // workaround name collisions with XLib
34 #ifdef None
35   #undef None
36 #endif
37 #ifdef Bool
38   #undef Bool
39 #endif
40
41 #ifdef HAVE_RAPIDJSON
42   //#define RAPIDJSON_ASSERT
43   #include <rapidjson/document.h>
44   #include <rapidjson/prettywriter.h>
45   #include <rapidjson/stringbuffer.h>
46   #include <rapidjson/istreamwrapper.h>
47   #include <rapidjson/ostreamwrapper.h>
48
49   typedef rapidjson::Document::ValueType RWGltf_JsonValue;
50 #endif
51
52 class Message_ProgressIndicator;
53
54 //! INTERNAL tool for parsing glTF document (JSON structure).
55 class RWGltf_GltfJsonParser
56 #ifdef HAVE_RAPIDJSON
57 : public rapidjson::Document
58 #endif
59 {
60 public:
61
62 #ifdef HAVE_RAPIDJSON
63   //! Auxiliary method for formatting error code.
64   Standard_EXPORT static const char* FormatParseError (rapidjson::ParseErrorCode theCode);
65 #endif
66
67 public:
68
69   //! Empty constructor.
70   Standard_EXPORT RWGltf_GltfJsonParser (TopTools_SequenceOfShape& theRootShapes);
71
72   //! Set file path.
73   Standard_EXPORT void SetFilePath (const TCollection_AsciiString& theFilePath);
74
75   //! Set flag for probing file without complete reading.
76   void SetProbeHeader (bool theToProbe) { myToProbeHeader = theToProbe; }
77
78   //! Return prefix for reporting issues.
79   const TCollection_AsciiString& ErrorPrefix() const { return myErrorPrefix; }
80
81   //! Set prefix for reporting issues.
82   void SetErrorPrefix (const TCollection_AsciiString& theErrPrefix) { myErrorPrefix = theErrPrefix; }
83
84   //! Set map for storing node attributes.
85   void SetAttributeMap (RWMesh_NodeAttributeMap& theAttribMap) { myAttribMap = &theAttribMap; }
86
87   //! Set list for storing external files.
88   void SetExternalFiles (NCollection_IndexedMap<TCollection_AsciiString>& theExternalFiles) { myExternalFiles = &theExternalFiles; }
89
90   //! Set metadata map.
91   void SetMetadata (TColStd_IndexedDataMapOfStringString& theMetadata) { myMetadata = &theMetadata; }
92
93   //! Return transformation from glTF to OCCT coordinate system.
94   const RWMesh_CoordinateSystemConverter& CoordinateSystemConverter() const { return myCSTrsf; }
95
96   //! Set transformation from glTF to OCCT coordinate system.
97   void SetCoordinateSystemConverter (const RWMesh_CoordinateSystemConverter& theConverter) { myCSTrsf = theConverter; }
98
99   //! Initialize binary format.
100   void SetBinaryFormat (int64_t theBinBodyOffset,
101                         int64_t theBinBodyLen)
102   {
103     myIsBinary      = true;
104     myBinBodyOffset = theBinBodyOffset;
105     myBinBodyLen    = theBinBodyLen;
106   }
107
108   //! Set flag to ignore nodes without Geometry, TRUE by default.
109   void SetSkipEmptyNodes (bool theToSkip) { myToSkipEmptyNodes = theToSkip; }
110
111   //! Set flag to use Mesh name in case if Node name is empty, TRUE by default.
112   void SetMeshNameAsFallback (bool theToFallback) { myUseMeshNameAsFallback = theToFallback; }
113
114   //! Parse glTF document.
115   Standard_EXPORT bool Parse (const Handle(Message_ProgressIndicator)& theProgress);
116
117   //! Return face list for loading triangulation.
118   NCollection_Vector<TopoDS_Face>& FaceList() { return myFaceList; }
119
120 protected:
121 #ifdef HAVE_RAPIDJSON
122   //! Search mandatory root elements in the document.
123   //! Return FALSE if some mandatory element is missing.
124   Standard_EXPORT bool gltfParseRoots();
125
126   //! Parse default scene.
127   Standard_EXPORT bool gltfParseScene (const Handle(Message_ProgressIndicator)& theProgress);
128
129   //! Parse document metadata.
130   Standard_EXPORT void gltfParseAsset();
131
132 protected:
133
134   //! Parse materials defined in the document.
135   Standard_EXPORT void gltfParseMaterials();
136
137   //! Parse standard material.
138   Standard_EXPORT bool gltfParseStdMaterial (Handle(RWGltf_MaterialCommon)& theMat,
139                                              const RWGltf_JsonValue& theMatNode);
140
141   //! Parse pbrMetallicRoughness material.
142   Standard_EXPORT bool gltfParsePbrMaterial (Handle(RWGltf_MaterialMetallicRoughness)& theMat,
143                                              const RWGltf_JsonValue& theMatNode);
144
145   //! Parse common material (KHR_materials_common extension).
146   Standard_EXPORT bool gltfParseCommonMaterial (Handle(RWGltf_MaterialCommon)& theMat,
147                                                 const RWGltf_JsonValue& theMatNode);
148
149   //! Parse texture definition.
150   Standard_EXPORT bool gltfParseTexture (Handle(Image_Texture)& theTexture,
151                                          const RWGltf_JsonValue* theTextureId);
152
153   //! Parse texture definition in binary buffer of GLB file.
154   Standard_EXPORT bool gltfParseTexturInGlbBuffer (Handle(Image_Texture)& theTexture,
155                                                    const RWGltf_JsonValue& theBinVal,
156                                                    const TCollection_AsciiString& theBufferViewId,
157                                                    const RWGltf_JsonValue& theBufferViewName);
158
159   //! Parse texture definition in binary buffer of glTF file.
160   Standard_EXPORT bool gltfParseTextureInBufferView (Handle(Image_Texture)& theTexture,
161                                                      const TCollection_AsciiString& theSourceId,
162                                                      const TCollection_AsciiString& theBufferViewhId,
163                                                      const RWGltf_JsonValue& theBufferView);
164
165   //! Bind material definition to the map.
166   Standard_EXPORT void gltfBindMaterial (const Handle(RWGltf_MaterialMetallicRoughness)& theMatPbr,
167                                          const Handle(RWGltf_MaterialCommon)& theMatCommon);
168
169 protected:
170
171   //! Parse scene array of nodes recursively.
172   Standard_EXPORT bool gltfParseSceneNodes (TopTools_SequenceOfShape& theShapeSeq,
173                                             const RWGltf_JsonValue& theSceneNodes,
174                                             const Handle(Message_ProgressIndicator)& theProgress);
175
176   //! Parse scene node recursively.
177   Standard_EXPORT bool gltfParseSceneNode (TopoDS_Shape& theNodeShape,
178                                            const TCollection_AsciiString& theSceneNodeId,
179                                            const RWGltf_JsonValue& theSceneNode,
180                                            const Handle(Message_ProgressIndicator)& theProgress);
181
182   //! Parse mesh element.
183   Standard_EXPORT bool gltfParseMesh (TopoDS_Shape& theMeshShape,
184                                       const TCollection_AsciiString& theMeshId,
185                                       const RWGltf_JsonValue& theMesh,
186                                       const Handle(Message_ProgressIndicator)& theProgress);
187
188   //! Parse primitive array.
189   Standard_EXPORT bool gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
190                                            const TCollection_AsciiString& theMeshName,
191                                            const RWGltf_JsonValue& thePrimArray,
192                                            const Handle(Message_ProgressIndicator)& theProgress);
193
194   //! Parse accessor.
195   Standard_EXPORT bool gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
196                                           const TCollection_AsciiString& theName,
197                                           const RWGltf_JsonValue& theAccessor,
198                                           const RWGltf_GltfArrayType theType);
199
200   //! Parse buffer view.
201   Standard_EXPORT bool gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
202                                             const TCollection_AsciiString& theName,
203                                             const RWGltf_JsonValue& theBufferView,
204                                             const RWGltf_GltfAccessor& theAccessor,
205                                             const RWGltf_GltfArrayType theType);
206
207   //! Parse buffer.
208   Standard_EXPORT bool gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
209                                         const TCollection_AsciiString& theName,
210                                         const RWGltf_JsonValue& theBuffer,
211                                         const RWGltf_GltfAccessor&   theAccessor,
212                                         const RWGltf_GltfBufferView& theView,
213                                         const RWGltf_GltfArrayType   theType);
214
215 protected:
216
217   //! Read vec4 from specified item.
218   static bool gltfReadVec4 (Graphic3d_Vec4d& theVec4,
219                             const RWGltf_JsonValue* theVal)
220   {
221     if (theVal == NULL
222     || !theVal->IsArray()
223     ||  theVal->Size() != 4)
224     {
225       return false;
226     }
227
228     for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
229     {
230       const RWGltf_JsonValue& aGenVal = (*theVal)[aCompIter];
231       if (!aGenVal.IsNumber())
232       {
233         return false;
234       }
235       theVec4[aCompIter] = aGenVal.GetDouble();
236     }
237     return true;
238   }
239
240   //! Validate color
241   static bool validateColor4 (const Graphic3d_Vec4d& theVec)
242   {
243     return theVec.r() >= 0.0 && theVec.r() <= 1.0
244         && theVec.g() >= 0.0 && theVec.g() <= 1.0
245         && theVec.b() >= 0.0 && theVec.b() <= 1.0
246         && theVec.a() >= 0.0 && theVec.a() <= 1.0;
247   }
248
249   //! Read vec3 from specified item.
250   static bool gltfReadVec3 (Graphic3d_Vec3d& theVec3,
251                             const RWGltf_JsonValue* theVal)
252   {
253     if (theVal == NULL
254     || !theVal->IsArray()
255     ||  theVal->Size() != 3)
256     {
257       return false;
258     }
259
260     for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
261     {
262       const RWGltf_JsonValue& aGenVal = (*theVal)[aCompIter];
263       if (!aGenVal.IsNumber())
264       {
265         return false;
266       }
267       theVec3[aCompIter] = aGenVal.GetDouble();
268     }
269     return true;
270   }
271
272   //! Validate color
273   static bool validateColor3 (const Graphic3d_Vec3d& theVec)
274   {
275     return theVec.r() >= 0.0 && theVec.r() <= 1.0
276         && theVec.g() >= 0.0 && theVec.g() <= 1.0
277         && theVec.b() >= 0.0 && theVec.b() <= 1.0;
278   }
279
280 protected:
281
282   //! Groups for re-using shapes.
283   enum ShapeMapGroup
284   {
285     ShapeMapGroup_Nodes,  //!< nodes
286     ShapeMapGroup_Meshes, //!< meshes
287   };
288
289   //! Bind name attribute.
290   void bindNodeShape (TopoDS_Shape& theShape,
291                       const TopLoc_Location& theLoc,
292                       const TCollection_AsciiString& theNodeId,
293                       const RWGltf_JsonValue* theUserName)
294   {
295     bindNamedShape (theShape, ShapeMapGroup_Nodes, theLoc, theNodeId, theUserName);
296   }
297
298   //! Bind name attribute.
299   void bindMeshShape (TopoDS_Shape& theShape,
300                       const TCollection_AsciiString& theMeshId,
301                       const RWGltf_JsonValue* theUserName)
302   {
303     bindNamedShape (theShape, ShapeMapGroup_Meshes, TopLoc_Location(), theMeshId, theUserName);
304   }
305
306   //! Find named shape.
307   bool findNodeShape (TopoDS_Shape& theShape,
308                       const TCollection_AsciiString& theNodeId) const
309   {
310     return findNamedShape (theShape, ShapeMapGroup_Nodes, theNodeId);
311   }
312
313   //! Find named shape.
314   bool findMeshShape (TopoDS_Shape& theShape,
315                       const TCollection_AsciiString& theMeshId) const
316   {
317     return findNamedShape (theShape, ShapeMapGroup_Meshes, theMeshId);
318   }
319
320   //! Bind name attribute.
321   Standard_EXPORT void bindNamedShape (TopoDS_Shape& theShape,
322                                        ShapeMapGroup theGroup,
323                                        const TopLoc_Location& theLoc,
324                                        const TCollection_AsciiString& theId,
325                                        const RWGltf_JsonValue* theUserName);
326
327   //! Find named shape.
328   bool findNamedShape (TopoDS_Shape& theShape,
329                        ShapeMapGroup theGroup,
330                        const TCollection_AsciiString& theId) const
331   {
332     return myShapeMap[theGroup].Find (theId, theShape);
333   }
334
335   //! Return the string representation of the key.
336   static TCollection_AsciiString getKeyString (const RWGltf_JsonValue& theValue)
337   {
338     if (theValue.IsString())
339     {
340       return TCollection_AsciiString (theValue.GetString());
341     }
342     else if (theValue.IsInt())
343     {
344       return TCollection_AsciiString (theValue.GetInt());
345     }
346     return TCollection_AsciiString();
347   }
348
349 protected:
350
351   //! Auxiliary structure for fast look-up of document sub-nodes of specified node.
352   class GltfElementMap
353   {
354   public:
355
356     //! Empty constructor.
357     GltfElementMap() : myRoot (NULL) {}
358
359     //! Return TRUE if this element is NULL.
360     bool IsNull() const { return myRoot == NULL; }
361
362     //! Access this node.
363     const RWGltf_JsonValue* Root() const { return myRoot; }
364
365     //! Find the child node with specified key.
366     const RWGltf_JsonValue* FindChild (const TCollection_AsciiString& theKey)
367     {
368       const RWGltf_JsonValue* aNode = NULL;
369       return myChildren.Find (theKey, aNode)
370            ? aNode
371            : NULL;
372     }
373
374     //! Find the child node with specified key.
375     const RWGltf_JsonValue* FindChild (const RWGltf_JsonValue& theKey)
376     {
377       const TCollection_AsciiString aKey = getKeyString (theKey);
378       if (aKey.IsEmpty())
379       {
380         return NULL;
381       }
382
383       const RWGltf_JsonValue* aNode = NULL;
384       return myChildren.Find (aKey, aNode)
385            ? aNode
386            : NULL;
387     }
388
389     //! Initialize the element.
390     void Init (const TCollection_AsciiString& theRootName,
391                const RWGltf_JsonValue* theRoot);
392
393   private:
394
395     NCollection_DataMap<TCollection_AsciiString, const RWGltf_JsonValue*, TCollection_AsciiString> myChildren;
396     const RWGltf_JsonValue* myRoot;
397
398   };
399 #endif
400 protected:
401
402   //! Print message about invalid glTF syntax.
403   void reportGltfSyntaxProblem (const TCollection_AsciiString& theMsg, Message_Gravity theGravity);
404
405 protected:
406
407   TopTools_SequenceOfShape*        myRootShapes;    //!< sequence of result root shapes
408   RWMesh_NodeAttributeMap*         myAttribMap;     //!< shape attributes
409   NCollection_IndexedMap<TCollection_AsciiString>*
410                                    myExternalFiles; //!< list of external file references
411   RWMesh_CoordinateSystemConverter myCSTrsf;        //!< transformation from glTF to OCCT coordinate system
412   TColStd_IndexedDataMapOfStringString* myMetadata; //!< file metadata
413
414   NCollection_DataMap<TCollection_AsciiString, Handle(RWGltf_MaterialMetallicRoughness)> myMaterialsPbr;
415   NCollection_DataMap<TCollection_AsciiString, Handle(RWGltf_MaterialCommon)> myMaterialsCommon;
416   NCollection_DataMap<TCollection_AsciiString, Handle(XCAFDoc_VisMaterial)> myMaterials;
417   NCollection_DataMap<TCollection_AsciiString, TopoDS_Shape> myShapeMap[2];
418
419   NCollection_DataMap<TCollection_AsciiString, bool> myProbedFiles;
420   NCollection_DataMap<TCollection_AsciiString, Handle(NCollection_Buffer)> myDecodedBuffers;
421   NCollection_Vector<TopoDS_Face> myFaceList; //!< face list for loading triangulation
422
423   TCollection_AsciiString   myFilePath;       //!< file path
424   TCollection_AsciiString   myFolder;         //!< folder
425   TCollection_AsciiString   myErrorPrefix;    //!< invalid syntax error prefix
426   int64_t                   myBinBodyOffset;  //!< offset to binary body
427   int64_t                   myBinBodyLen;     //!< binary body length
428   bool                      myIsBinary;       //!< binary document
429   bool                      myIsGltf1;        //!< obsolete glTF 1.0 version format
430   bool                      myToSkipEmptyNodes; //!< ignore nodes without Geometry
431   bool                      myUseMeshNameAsFallback; //!< flag to use Mesh name in case if Node name is empty, TRUE by default
432   bool                      myToProbeHeader;  //!< flag to probe header without full reading, FALSE by default
433
434 #ifdef HAVE_RAPIDJSON
435   GltfElementMap myGltfRoots[RWGltf_GltfRootElement_NB]; //!< glTF format root elements
436 #endif
437
438 };
439
440 #endif // _RWGltf_GltfJsonParser_HeaderFile