1 // Author: Kirill Gavrilov
2 // Copyright (c) 2015-2019 OPEN CASCADE SAS
4 // This file is part of Open CASCADE Technology software library.
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
15 #ifndef _RWObj_Reader_HeaderFile
16 #define _RWObj_Reader_HeaderFile
19 #include <Graphic3d_Vec2.hxx>
20 #include <Graphic3d_Vec4.hxx>
21 #include <Message.hxx>
22 #include <Message_Messenger.hxx>
23 #include <Message_ProgressIndicator.hxx>
24 #include <NCollection_Array1.hxx>
25 #include <NCollection_DataMap.hxx>
26 #include <NCollection_IndexedMap.hxx>
27 #include <NCollection_Vector.hxx>
28 #include <NCollection_Shared.hxx>
30 #include <RWMesh_CoordinateSystemConverter.hxx>
31 #include <RWObj_Material.hxx>
32 #include <RWObj_SubMesh.hxx>
33 #include <RWObj_SubMeshReason.hxx>
34 #include <RWObj_Tools.hxx>
38 //! An abstract class implementing procedure to read OBJ file.
40 //! This class is not bound to particular data structure
41 //! and can be used to read the file directly into arbitrary data model.
42 //! To use it, create descendant class and implement interface methods.
44 //! Call method Read() to read the file.
45 class RWObj_Reader : public Standard_Transient
47 DEFINE_STANDARD_RTTIEXT(RWObj_Reader, Standard_Transient)
50 //! Empty constructor.
51 Standard_EXPORT RWObj_Reader();
53 //! Reads data from OBJ file.
54 //! Unicode paths can be given in UTF-8 encoding.
55 //! Returns true if success, false on error or user break.
56 Standard_Boolean Read (const TCollection_AsciiString& theFile,
57 const Handle(Message_ProgressIndicator)& theProgress)
59 return read (theFile, theProgress, Standard_False);
62 //! Probe data from OBJ file (comments, external references) without actually reading mesh data.
63 //! Although mesh data will not be collected, the full file content will be parsed, due to OBJ format limitations.
64 //! @param theFile path to the file
65 //! @param theProgress progress indicator
66 //! @return TRUE if success, FALSE on error or user break.
67 //! @sa FileComments(), ExternalFiles(), NbProbeNodes(), NbProbeElems().
68 Standard_Boolean Probe (const TCollection_AsciiString& theFile,
69 const Handle(Message_ProgressIndicator)& theProgress)
71 return read (theFile, theProgress, Standard_True);
74 //! Returns file comments (lines starting with # at the beginning of file).
75 const TCollection_AsciiString& FileComments() const { return myFileComments; }
77 //! Return the list of external file references.
78 const NCollection_IndexedMap<TCollection_AsciiString>& ExternalFiles() const { return myExternalFiles; }
80 //! Number of probed nodes.
81 Standard_Integer NbProbeNodes() const { return myNbProbeNodes; }
83 //!< number of probed polygon elements (of unknown size).
84 Standard_Integer NbProbeElems() const { return myNbProbeElems; }
86 //! Returns memory limit in bytes; -1 (no limit) by default.
87 Standard_Size MemoryLimit() const { return myMemLimitBytes; }
89 //! Specify memory limit in bytes, so that import will be aborted
90 //! by specified limit before memory allocation error occurs.
91 void SetMemoryLimit (Standard_Size theMemLimit) { myMemLimitBytes = theMemLimit; }
93 //! Return transformation from one coordinate system to another; no transformation by default.
94 const RWMesh_CoordinateSystemConverter& Transformation() const { return myCSTrsf; }
96 //! Setup transformation from one coordinate system to another.
97 //! OBJ file might be exported following various coordinate system conventions,
98 //! so that it might be useful automatically transform data during file reading.
99 void SetTransformation (const RWMesh_CoordinateSystemConverter& theCSConverter) { myCSTrsf = theCSConverter; }
101 //! Return single precision flag for reading vertex data (coordinates); FALSE by default.
102 Standard_Boolean IsSinglePrecision() const { return myObjVerts.IsSinglePrecision(); }
104 //! Setup single/double precision flag for reading vertex data (coordinates).
105 void SetSinglePrecision (Standard_Boolean theIsSinglePrecision) { myObjVerts.SetSinglePrecision (theIsSinglePrecision); }
109 //! Reads data from OBJ file.
110 //! Unicode paths can be given in UTF-8 encoding.
111 //! Returns true if success, false on error or user break.
112 Standard_EXPORT Standard_Boolean read (const TCollection_AsciiString& theFile,
113 const Handle(Message_ProgressIndicator)& theProgress,
114 const Standard_Boolean theToProbe);
116 //! @name interface methods which should be implemented by sub-class
119 //! Add new sub-mesh.
120 //! Basically, this method will be called multiple times for the same group with different reason,
121 //! so that implementation should decide if previously allocated sub-mesh should be used or new one to be allocated.
122 //! Sub-mesh command can be skipped if previous sub-mesh is empty,
123 //! or if the reason is out of interest for particular reader
124 //! (e.g. if materials are ignored, reader may ignore RWObj_SubMeshReason_NewMaterial reason).
125 //! @param theMesh mesh definition
126 //! @param theReason reason to create new sub-mesh
127 //! @return TRUE if new sub-mesh should be started since this point
128 virtual Standard_Boolean addMesh (const RWObj_SubMesh& theMesh,
129 const RWObj_SubMeshReason theReason) = 0;
131 //! Retrieve sub-mesh node position, added by addNode().
132 virtual gp_Pnt getNode (Standard_Integer theIndex) const = 0;
134 //! Callback function to be implemented in descendant.
135 //! Should create new node with specified coordinates in the target model, and return its ID as integer.
136 virtual Standard_Integer addNode (const gp_Pnt& thePnt) = 0;
138 //! Callback function to be implemented in descendant.
139 //! Should set normal coordinates for specified node.
140 //! @param theIndex node ID as returned by addNode()
141 //! @param theNorm normal vector
142 virtual void setNodeNormal (const Standard_Integer theIndex,
143 const Graphic3d_Vec3& theNorm) = 0;
145 //! Callback function to be implemented in descendant.
146 //! Should set texture coordinates for specified node.
147 //! @param theIndex node ID as returned by addNode()
148 //! @param theUV UV texture coordinates
149 virtual void setNodeUV (const Standard_Integer theIndex,
150 const Graphic3d_Vec2& theUV) = 0;
152 //! Callback function to be implemented in descendant.
153 //! Should create new element (triangle or quad if 4th index is != -1) built on specified nodes in the target model.
154 virtual void addElement (Standard_Integer theN1,
155 Standard_Integer theN2,
156 Standard_Integer theN3,
157 Standard_Integer theN4) = 0;
159 //! @name implementation details
162 //! Handle "v X Y Z".
163 void pushVertex (const char* theXYZ)
167 RWObj_Tools::ReadVec3 (theXYZ, aNext, anXYZ.ChangeCoord());
168 myCSTrsf.TransformPosition (anXYZ.ChangeCoord());
170 myMemEstim += myObjVerts.IsSinglePrecision() ? sizeof(Graphic3d_Vec3) : sizeof(gp_Pnt);
171 myObjVerts.Append (anXYZ);
174 //! Handle "vn NX NY NZ".
175 void pushNormal (const char* theXYZ)
178 Graphic3d_Vec3 aNorm;
179 RWObj_Tools::ReadVec3 (theXYZ, aNext, aNorm);
180 myCSTrsf.TransformNormal (aNorm);
182 myMemEstim += sizeof(Graphic3d_Vec3);
183 myObjNorms.Append (aNorm);
187 void pushTexel (const char* theUV)
191 anUV.x() = (float )Strtod (theUV, &aNext);
193 anUV.y() = (float )Strtod (theUV, &aNext);
195 myMemEstim += sizeof(Graphic3d_Vec2);
196 myObjVertsUV.Append (anUV);
199 //! Handle "f indices".
200 void pushIndices (const char* thePos);
202 //! Compute the center of planar polygon.
203 //! @param theIndices polygon indices
204 //! @return center of polygon
205 gp_XYZ polygonCenter (const NCollection_Array1<Standard_Integer>& theIndices);
207 //! Compute the normal to planar polygon.
208 //! The logic is similar to ShapeAnalysis_Curve::IsPlanar().
209 //! @param theIndices polygon indices
210 //! @return polygon normal
211 gp_XYZ polygonNormal (const NCollection_Array1<Standard_Integer>& theIndices);
213 //! Create triangle fan from specified polygon.
214 //! @param theIndices polygon nodes
215 //! @return number of added triangles
216 Standard_Integer triangulatePolygonFan (const NCollection_Array1<Standard_Integer>& theIndices);
218 //! Triangulate specified polygon.
219 //! @param theIndices polygon nodes
220 //! @return number of added triangles
221 Standard_Integer triangulatePolygon (const NCollection_Array1<Standard_Integer>& theIndices);
223 //! Handle "o ObjectName".
224 void pushObject (const char* theObjectName);
226 //! Handle "g GroupName".
227 void pushGroup (const char* theGroupName);
229 //! Handle "s SmoothGroupIndex".
230 void pushSmoothGroup (const char* theSmoothGroupIndex);
232 //! Handle "usemtl MaterialName".
233 void pushMaterial (const char* theMaterialName);
235 //! Handle "mtllib FileName".
236 void readMaterialLib (const char* theFileName);
238 //! Check memory limits.
239 //! @return FALSE on out of memory
244 //! Hasher for 3 ordered integers.
245 struct ObjVec3iHasher
247 static Standard_Integer HashCode (const Graphic3d_Vec3i& theKey,
248 const Standard_Integer theUpper)
250 return ::HashCode (::HashCodes ((Standard_CString )&theKey, sizeof(Graphic3d_Vec3i)), theUpper);
253 static Standard_Boolean IsEqual (const Graphic3d_Vec3i& theKey1,
254 const Graphic3d_Vec3i& theKey2)
256 return theKey1[0] == theKey2[0]
257 && theKey1[1] == theKey2[1]
258 && theKey1[2] == theKey2[2];
262 //! Auxiliary structure holding vertex data either with single or double floating point precision.
263 class VectorOfVertices
266 //! Empty constructor.
267 VectorOfVertices() : myIsSinglePrecision (Standard_False) {}
269 //! Return single precision flag; FALSE by default.
270 bool IsSinglePrecision() const { return myIsSinglePrecision; }
272 //! Setup single/double precision flag.
273 void SetSinglePrecision (Standard_Boolean theIsSinglePrecision)
275 myIsSinglePrecision = theIsSinglePrecision;
280 //! Reset and (re)allocate buffer.
283 if (myIsSinglePrecision)
285 myVec3Vec = new NCollection_Shared<NCollection_Vector<Graphic3d_Vec3> >();
289 myPntVec = new NCollection_Shared<NCollection_Vector<gp_Pnt> >();
293 //! Return vector lower index.
294 Standard_Integer Lower() const { return 0; }
296 //! Return vector upper index.
297 Standard_Integer Upper() const { return myIsSinglePrecision ? myVec3Vec->Upper() : myPntVec->Upper(); }
299 //! Return point with the given index.
300 gp_Pnt Value (Standard_Integer theIndex) const
302 if (myIsSinglePrecision)
304 const Graphic3d_Vec3& aPnt = myVec3Vec->Value (theIndex);
305 return gp_Pnt (aPnt.x(), aPnt.y(), aPnt.z());
309 return myPntVec->Value (theIndex);
313 //! Append new point.
314 void Append (const gp_Pnt& thePnt)
316 if (myIsSinglePrecision)
318 myVec3Vec->Append (Graphic3d_Vec3 ((float )thePnt.X(), (float )thePnt.Y(), (float )thePnt.Z()));
322 myPntVec->Append (thePnt);
326 Handle(NCollection_Shared<NCollection_Vector<gp_Pnt> >) myPntVec;
327 Handle(NCollection_Shared<NCollection_Vector<Graphic3d_Vec3> >) myVec3Vec;
328 Standard_Boolean myIsSinglePrecision;
333 NCollection_IndexedMap<TCollection_AsciiString>
334 myExternalFiles; //!< list of external file references
335 TCollection_AsciiString myFileComments; //!< file header comments
336 TCollection_AsciiString myFolder; //!< folder containing the OBJ file
337 RWMesh_CoordinateSystemConverter myCSTrsf; //!< coordinate system flipper
338 Standard_Size myMemLimitBytes; //!< memory limit in bytes
339 Standard_Size myMemEstim; //!< estimated memory occupation in bytes
340 Standard_Integer myNbLines; //!< number of parsed lines (e.g. current line)
341 Standard_Integer myNbProbeNodes; //!< number of probed nodes
342 Standard_Integer myNbProbeElems; //!< number of probed elements
343 Standard_Integer myNbElemsBig; //!< number of big elements (polygons with 5+ nodes)
344 Standard_Boolean myToAbort; //!< flag indicating abort state (e.g. syntax error)
346 // Each node in the Element specifies independent indices of Vertex position, Texture coordinates and Normal.
347 // This scheme does not match natural definition of Primitive Array
348 // where each unique set of nodal properties defines Vertex
349 // (thus node at the same location but with different normal should be duplicated).
350 // The following code converts OBJ definition of nodal properties to Primitive Array definition.
351 VectorOfVertices myObjVerts; //!< temporary vector of vertices
352 NCollection_Vector<Graphic3d_Vec2> myObjVertsUV; //!< temporary vector of UV parameters
353 NCollection_Vector<Graphic3d_Vec3> myObjNorms; //!< temporary vector of normals
354 NCollection_DataMap<Graphic3d_Vec3i, Standard_Integer, ObjVec3iHasher>
356 NCollection_DataMap<TCollection_AsciiString, RWObj_Material>
357 myMaterials; //!< map of known materials
359 RWObj_SubMesh myActiveSubMesh; //!< active sub-mesh definition
360 std::vector<Standard_Integer> myCurrElem; //!< indices for the current element
364 #endif // _RWObj_Reader_HeaderFile