0030691: Data Exchange - implement import of mesh data from files in glTF format
[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   //! Return transformation from glTF to OCCT coordinate system.
91   const RWMesh_CoordinateSystemConverter& CoordinateSystemConverter() const { return myCSTrsf; }
92
93   //! Set transformation from glTF to OCCT coordinate system.
94   void SetCoordinateSystemConverter (const RWMesh_CoordinateSystemConverter& theConverter) { myCSTrsf = theConverter; }
95
96   //! Initialize binary format.
97   void SetBinaryFormat (int64_t theBinBodyOffset,
98                         int64_t theBinBodyLen)
99   {
100     myIsBinary      = true;
101     myBinBodyOffset = theBinBodyOffset;
102     myBinBodyLen    = theBinBodyLen;
103   }
104
105   //! Parse glTF document.
106   Standard_EXPORT bool Parse (const Handle(Message_ProgressIndicator)& theProgress);
107
108   //! Return metadata map.
109   const TColStd_IndexedDataMapOfStringString& Metadata() const { return myMetadata; }
110
111   //! Return face list for loading triangulation.
112   NCollection_Vector<TopoDS_Face>& FaceList() { return myFaceList; }
113
114 protected:
115 #ifdef HAVE_RAPIDJSON
116   //! Search mandatory root elements in the document.
117   //! Return FALSE if some mandatory element is missing.
118   Standard_EXPORT bool gltfParseRoots();
119
120   //! Parse default scene.
121   Standard_EXPORT bool gltfParseScene (const Handle(Message_ProgressIndicator)& theProgress);
122
123   //! Parse document metadata.
124   Standard_EXPORT void gltfParseAsset();
125
126 protected:
127
128   //! Parse materials defined in the document.
129   Standard_EXPORT void gltfParseMaterials();
130
131   //! Parse standard material.
132   Standard_EXPORT bool gltfParseStdMaterial (Handle(RWGltf_MaterialCommon)& theMat,
133                                              const RWGltf_JsonValue& theMatNode);
134
135   //! Parse pbrMetallicRoughness material.
136   Standard_EXPORT bool gltfParsePbrMaterial (Handle(RWGltf_MaterialMetallicRoughness)& theMat,
137                                              const RWGltf_JsonValue& theMatNode);
138
139   //! Parse common material (KHR_materials_common extension).
140   Standard_EXPORT bool gltfParseCommonMaterial (Handle(RWGltf_MaterialCommon)& theMat,
141                                                 const RWGltf_JsonValue& theMatNode);
142
143   //! Parse texture definition.
144   Standard_EXPORT bool gltfParseTexture (Handle(Image_Texture)& theTexture,
145                                          const RWGltf_JsonValue* theTextureId);
146
147 protected:
148
149   //! Parse scene array of nodes recursively.
150   Standard_EXPORT bool gltfParseSceneNodes (TopTools_SequenceOfShape& theShapeSeq,
151                                             const RWGltf_JsonValue& theSceneNodes,
152                                             const Handle(Message_ProgressIndicator)& theProgress);
153
154   //! Parse scene node recursively.
155   Standard_EXPORT bool gltfParseSceneNode (TopoDS_Shape& theNodeShape,
156                                            const TCollection_AsciiString& theSceneNodeId,
157                                            const RWGltf_JsonValue& theSceneNode,
158                                            const Handle(Message_ProgressIndicator)& theProgress);
159
160   //! Parse mesh element.
161   Standard_EXPORT bool gltfParseMesh (TopoDS_Shape& theMeshShape,
162                                       const TCollection_AsciiString& theMeshId,
163                                       const RWGltf_JsonValue& theMesh,
164                                       const Handle(Message_ProgressIndicator)& theProgress);
165
166   //! Parse primitive array.
167   Standard_EXPORT bool gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
168                                            const TCollection_AsciiString& theMeshName,
169                                            const RWGltf_JsonValue& thePrimArray,
170                                            const Handle(Message_ProgressIndicator)& theProgress);
171
172   //! Parse accessor.
173   Standard_EXPORT bool gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
174                                           const TCollection_AsciiString& theName,
175                                           const RWGltf_JsonValue& theAccessor,
176                                           const RWGltf_GltfArrayType theType);
177
178   //! Parse buffer view.
179   Standard_EXPORT bool gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
180                                             const TCollection_AsciiString& theName,
181                                             const RWGltf_JsonValue& theBufferView,
182                                             const RWGltf_GltfAccessor& theAccessor,
183                                             const RWGltf_GltfArrayType theType);
184
185   //! Parse buffer.
186   Standard_EXPORT bool gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
187                                         const TCollection_AsciiString& theName,
188                                         const RWGltf_JsonValue& theBuffer,
189                                         const RWGltf_GltfAccessor&   theAccessor,
190                                         const RWGltf_GltfBufferView& theView,
191                                         const RWGltf_GltfArrayType   theType);
192
193 protected:
194
195   //! Read vec4 from specified item.
196   static bool gltfReadVec4 (Graphic3d_Vec4d& theVec4,
197                             const RWGltf_JsonValue* theVal)
198   {
199     if (theVal == NULL
200     || !theVal->IsArray()
201     ||  theVal->Size() != 4)
202     {
203       return false;
204     }
205
206     for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
207     {
208       const RWGltf_JsonValue& aGenVal = (*theVal)[aCompIter];
209       if (!aGenVal.IsNumber())
210       {
211         return false;
212       }
213       theVec4[aCompIter] = aGenVal.GetDouble();
214     }
215     return true;
216   }
217
218   //! Validate color
219   static bool validateColor4 (const Graphic3d_Vec4d& theVec)
220   {
221     return theVec.r() >= 0.0 && theVec.r() <= 1.0
222         && theVec.g() >= 0.0 && theVec.g() <= 1.0
223         && theVec.b() >= 0.0 && theVec.b() <= 1.0
224         && theVec.a() >= 0.0 && theVec.a() <= 1.0;
225   }
226
227   //! Read vec3 from specified item.
228   static bool gltfReadVec3 (Graphic3d_Vec3d& theVec3,
229                             const RWGltf_JsonValue* theVal)
230   {
231     if (theVal == NULL
232     || !theVal->IsArray()
233     ||  theVal->Size() != 3)
234     {
235       return false;
236     }
237
238     for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
239     {
240       const RWGltf_JsonValue& aGenVal = (*theVal)[aCompIter];
241       if (!aGenVal.IsNumber())
242       {
243         return false;
244       }
245       theVec3[aCompIter] = aGenVal.GetDouble();
246     }
247     return true;
248   }
249
250   //! Validate color
251   static bool validateColor3 (const Graphic3d_Vec3d& theVec)
252   {
253     return theVec.r() >= 0.0 && theVec.r() <= 1.0
254         && theVec.g() >= 0.0 && theVec.g() <= 1.0
255         && theVec.b() >= 0.0 && theVec.b() <= 1.0;
256   }
257
258 protected:
259
260   //! Groups for re-using shapes.
261   enum ShapeMapGroup
262   {
263     ShapeMapGroup_Nodes,  //!< nodes
264     ShapeMapGroup_Meshes, //!< meshes
265   };
266
267   //! Bind name attribute.
268   void bindNodeShape (TopoDS_Shape& theShape,
269                       const TopLoc_Location& theLoc,
270                       const TCollection_AsciiString& theNodeId,
271                       const RWGltf_JsonValue* theUserName)
272   {
273     bindNamedShape (theShape, ShapeMapGroup_Nodes, theLoc, theNodeId, theUserName);
274   }
275
276   //! Bind name attribute.
277   void bindMeshShape (TopoDS_Shape& theShape,
278                       const TCollection_AsciiString& theMeshId,
279                       const RWGltf_JsonValue* theUserName)
280   {
281     bindNamedShape (theShape, ShapeMapGroup_Meshes, TopLoc_Location(), theMeshId, theUserName);
282   }
283
284   //! Find named shape.
285   bool findNodeShape (TopoDS_Shape& theShape,
286                       const TCollection_AsciiString& theNodeId) const
287   {
288     return findNamedShape (theShape, ShapeMapGroup_Nodes, theNodeId);
289   }
290
291   //! Find named shape.
292   bool findMeshShape (TopoDS_Shape& theShape,
293                       const TCollection_AsciiString& theMeshId) const
294   {
295     return findNamedShape (theShape, ShapeMapGroup_Meshes, theMeshId);
296   }
297
298   //! Bind name attribute.
299   Standard_EXPORT void bindNamedShape (TopoDS_Shape& theShape,
300                                        ShapeMapGroup theGroup,
301                                        const TopLoc_Location& theLoc,
302                                        const TCollection_AsciiString& theId,
303                                        const RWGltf_JsonValue* theUserName);
304
305   //! Find named shape.
306   bool findNamedShape (TopoDS_Shape& theShape,
307                        ShapeMapGroup theGroup,
308                        const TCollection_AsciiString& theId) const
309   {
310     return myShapeMap[theGroup].Find (theId, theShape);
311   }
312
313   //! Return the string representation of the key.
314   static TCollection_AsciiString getKeyString (const RWGltf_JsonValue& theValue)
315   {
316     if (theValue.IsString())
317     {
318       return TCollection_AsciiString (theValue.GetString());
319     }
320     else if (theValue.IsInt())
321     {
322       return TCollection_AsciiString (theValue.GetInt());
323     }
324     return TCollection_AsciiString();
325   }
326
327 protected:
328
329   //! Auxiliary structure for fast look-up of document sub-nodes of specified node.
330   class GltfElementMap
331   {
332   public:
333
334     //! Empty constructor.
335     GltfElementMap() : myRoot (NULL) {}
336
337     //! Return TRUE if this element is NULL.
338     bool IsNull() const { return myRoot == NULL; }
339
340     //! Access this node.
341     const RWGltf_JsonValue* Root() const { return myRoot; }
342
343     //! Find the child node with specified key.
344     const RWGltf_JsonValue* FindChild (const TCollection_AsciiString& theKey)
345     {
346       const RWGltf_JsonValue* aNode = NULL;
347       return myChildren.Find (theKey, aNode)
348            ? aNode
349            : NULL;
350     }
351
352     //! Find the child node with specified key.
353     const RWGltf_JsonValue* FindChild (const RWGltf_JsonValue& theKey)
354     {
355       const TCollection_AsciiString aKey = getKeyString (theKey);
356       if (aKey.IsEmpty())
357       {
358         return NULL;
359       }
360
361       const RWGltf_JsonValue* aNode = NULL;
362       return myChildren.Find (aKey, aNode)
363            ? aNode
364            : NULL;
365     }
366
367     //! Initialize the element.
368     void Init (const TCollection_AsciiString& theRootName,
369                const RWGltf_JsonValue* theRoot);
370
371   private:
372
373     NCollection_DataMap<TCollection_AsciiString, const RWGltf_JsonValue*, TCollection_AsciiString> myChildren;
374     const RWGltf_JsonValue* myRoot;
375
376   };
377 #endif
378 protected:
379
380   //! Print message about invalid glTF syntax.
381   void reportGltfSyntaxProblem (const TCollection_AsciiString& theMsg, Message_Gravity theGravity);
382
383 protected:
384
385   TopTools_SequenceOfShape*        myRootShapes;    //!< sequence of result root shapes
386   RWMesh_NodeAttributeMap*         myAttribMap;     //!< shape attributes
387   NCollection_IndexedMap<TCollection_AsciiString>*
388                                    myExternalFiles; //!< list of external file references
389   RWMesh_CoordinateSystemConverter myCSTrsf;        //!< transformation from glTF to OCCT coordinate system
390
391   TColStd_IndexedDataMapOfStringString myMetadata; //!< file metadata
392   NCollection_DataMap<TCollection_AsciiString, Handle(RWGltf_MaterialMetallicRoughness)> myMaterialsPbr;
393   NCollection_DataMap<TCollection_AsciiString, Handle(RWGltf_MaterialCommon)> myMaterialsCommon;
394   NCollection_DataMap<TCollection_AsciiString, TopoDS_Shape> myShapeMap[2];
395
396   NCollection_DataMap<TCollection_AsciiString, bool> myProbedFiles;
397   NCollection_DataMap<TCollection_AsciiString, Handle(NCollection_Buffer)> myDecodedBuffers;
398   NCollection_Vector<TopoDS_Face> myFaceList; //!< face list for loading triangulation
399
400   TCollection_AsciiString   myFilePath;       //!< file path
401   TCollection_AsciiString   myFolder;         //!< folder
402   TCollection_AsciiString   myErrorPrefix;    //!< invalid syntax error prefix
403   int64_t                   myBinBodyOffset;  //!< offset to binary body
404   int64_t                   myBinBodyLen;     //!< binary body length
405   bool                      myIsBinary;       //!< binary document
406   bool                      myIsGltf1;        //!< obsolete glTF 1.0 version format
407   bool                      myToSkipEmptyNodes; //!< ignore nodes without Geometry
408   bool                      myToProbeHeader;  //!< flag to probe header without full reading, FALSE by default
409
410 #ifdef HAVE_RAPIDJSON
411   GltfElementMap myGltfRoots[RWGltf_GltfRootElement_NB]; //!< glTF format root elements
412 #endif
413
414 };
415
416 #endif // _RWGltf_GltfJsonParser_HeaderFile