0033661: Data Exchange, Step Import - Tessellated GDTs are not imported
[occt.git] / src / RWObj / RWObj_Reader.hxx
CommitLineData
4151c94d 1// Author: Kirill Gavrilov
2// Copyright (c) 2015-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 _RWObj_Reader_HeaderFile
16#define _RWObj_Reader_HeaderFile
17
4151c94d 18#include <Message.hxx>
19#include <Message_Messenger.hxx>
7e785937 20#include <Message_ProgressRange.hxx>
4151c94d 21#include <NCollection_Array1.hxx>
22#include <NCollection_DataMap.hxx>
23#include <NCollection_IndexedMap.hxx>
24#include <NCollection_Vector.hxx>
25#include <NCollection_Shared.hxx>
0435edfe 26#include <OSD_OpenFile.hxx>
4151c94d 27#include <RWMesh_CoordinateSystemConverter.hxx>
28#include <RWObj_Material.hxx>
29#include <RWObj_SubMesh.hxx>
30#include <RWObj_SubMeshReason.hxx>
31#include <RWObj_Tools.hxx>
1103eb60 32#include <Standard_HashUtils.hxx>
4151c94d 33
34#include <vector>
35
36//! An abstract class implementing procedure to read OBJ file.
37//!
38//! This class is not bound to particular data structure
39//! and can be used to read the file directly into arbitrary data model.
40//! To use it, create descendant class and implement interface methods.
41//!
42//! Call method Read() to read the file.
43class RWObj_Reader : public Standard_Transient
44{
45 DEFINE_STANDARD_RTTIEXT(RWObj_Reader, Standard_Transient)
46public:
47
48 //! Empty constructor.
49 Standard_EXPORT RWObj_Reader();
50
0435edfe 51 //! Open stream and pass it to Read method
52 //! Returns true if success, false on error.
53 Standard_Boolean Read (const TCollection_AsciiString& theFile,
54 const Message_ProgressRange& theProgress)
55 {
56 std::ifstream aStream;
57 OSD_OpenStream(aStream, theFile, std::ios_base::in | std::ios_base::binary);
58 return Read(aStream, theFile, theProgress);
59 }
60
4151c94d 61 //! Reads data from OBJ file.
62 //! Unicode paths can be given in UTF-8 encoding.
63 //! Returns true if success, false on error or user break.
0435edfe 64 Standard_Boolean Read (std::istream& theStream,
65 const TCollection_AsciiString& theFile,
7e785937 66 const Message_ProgressRange& theProgress)
4151c94d 67 {
0435edfe 68 return read(theStream, theFile, theProgress, Standard_False);
69 }
70
71 //! Open stream and pass it to Probe method.
72 //! @param theFile path to the file
73 //! @param theProgress progress indicator
74 //! @return TRUE if success, FALSE on error or user break.
75 //! @sa FileComments(), ExternalFiles(), NbProbeNodes(), NbProbeElems().
76 Standard_Boolean Probe (const TCollection_AsciiString& theFile,
77 const Message_ProgressRange& theProgress)
78 {
79 std::ifstream aStream;
80 OSD_OpenStream(aStream, theFile, std::ios_base::in | std::ios_base::binary);
81 return Probe(aStream, theFile, theProgress);
4151c94d 82 }
83
84 //! Probe data from OBJ file (comments, external references) without actually reading mesh data.
85 //! Although mesh data will not be collected, the full file content will be parsed, due to OBJ format limitations.
0435edfe 86 //! @param theStream input stream
4151c94d 87 //! @param theFile path to the file
88 //! @param theProgress progress indicator
89 //! @return TRUE if success, FALSE on error or user break.
90 //! @sa FileComments(), ExternalFiles(), NbProbeNodes(), NbProbeElems().
0435edfe 91 Standard_Boolean Probe (std::istream& theStream,
92 const TCollection_AsciiString& theFile,
7e785937 93 const Message_ProgressRange& theProgress)
4151c94d 94 {
0435edfe 95 return read(theStream, theFile, theProgress, Standard_True);
4151c94d 96 }
97
98 //! Returns file comments (lines starting with # at the beginning of file).
99 const TCollection_AsciiString& FileComments() const { return myFileComments; }
100
101 //! Return the list of external file references.
102 const NCollection_IndexedMap<TCollection_AsciiString>& ExternalFiles() const { return myExternalFiles; }
103
104 //! Number of probed nodes.
105 Standard_Integer NbProbeNodes() const { return myNbProbeNodes; }
106
107 //!< number of probed polygon elements (of unknown size).
108 Standard_Integer NbProbeElems() const { return myNbProbeElems; }
109
110 //! Returns memory limit in bytes; -1 (no limit) by default.
111 Standard_Size MemoryLimit() const { return myMemLimitBytes; }
112
113 //! Specify memory limit in bytes, so that import will be aborted
114 //! by specified limit before memory allocation error occurs.
115 void SetMemoryLimit (Standard_Size theMemLimit) { myMemLimitBytes = theMemLimit; }
116
117 //! Return transformation from one coordinate system to another; no transformation by default.
118 const RWMesh_CoordinateSystemConverter& Transformation() const { return myCSTrsf; }
119
120 //! Setup transformation from one coordinate system to another.
121 //! OBJ file might be exported following various coordinate system conventions,
122 //! so that it might be useful automatically transform data during file reading.
123 void SetTransformation (const RWMesh_CoordinateSystemConverter& theCSConverter) { myCSTrsf = theCSConverter; }
124
125 //! Return single precision flag for reading vertex data (coordinates); FALSE by default.
126 Standard_Boolean IsSinglePrecision() const { return myObjVerts.IsSinglePrecision(); }
127
128 //! Setup single/double precision flag for reading vertex data (coordinates).
129 void SetSinglePrecision (Standard_Boolean theIsSinglePrecision) { myObjVerts.SetSinglePrecision (theIsSinglePrecision); }
130
131protected:
132
133 //! Reads data from OBJ file.
134 //! Unicode paths can be given in UTF-8 encoding.
135 //! Returns true if success, false on error or user break.
0435edfe 136 Standard_EXPORT Standard_Boolean read (std::istream& theStream,
137 const TCollection_AsciiString& theFile,
7e785937 138 const Message_ProgressRange& theProgress,
4151c94d 139 const Standard_Boolean theToProbe);
140
141//! @name interface methods which should be implemented by sub-class
142protected:
143
144 //! Add new sub-mesh.
145 //! Basically, this method will be called multiple times for the same group with different reason,
146 //! so that implementation should decide if previously allocated sub-mesh should be used or new one to be allocated.
147 //! Sub-mesh command can be skipped if previous sub-mesh is empty,
148 //! or if the reason is out of interest for particular reader
149 //! (e.g. if materials are ignored, reader may ignore RWObj_SubMeshReason_NewMaterial reason).
150 //! @param theMesh mesh definition
151 //! @param theReason reason to create new sub-mesh
152 //! @return TRUE if new sub-mesh should be started since this point
153 virtual Standard_Boolean addMesh (const RWObj_SubMesh& theMesh,
154 const RWObj_SubMeshReason theReason) = 0;
155
156 //! Retrieve sub-mesh node position, added by addNode().
157 virtual gp_Pnt getNode (Standard_Integer theIndex) const = 0;
158
159 //! Callback function to be implemented in descendant.
160 //! Should create new node with specified coordinates in the target model, and return its ID as integer.
161 virtual Standard_Integer addNode (const gp_Pnt& thePnt) = 0;
162
163 //! Callback function to be implemented in descendant.
164 //! Should set normal coordinates for specified node.
165 //! @param theIndex node ID as returned by addNode()
166 //! @param theNorm normal vector
167 virtual void setNodeNormal (const Standard_Integer theIndex,
168 const Graphic3d_Vec3& theNorm) = 0;
169
170 //! Callback function to be implemented in descendant.
171 //! Should set texture coordinates for specified node.
172 //! @param theIndex node ID as returned by addNode()
173 //! @param theUV UV texture coordinates
174 virtual void setNodeUV (const Standard_Integer theIndex,
175 const Graphic3d_Vec2& theUV) = 0;
176
177 //! Callback function to be implemented in descendant.
178 //! Should create new element (triangle or quad if 4th index is != -1) built on specified nodes in the target model.
179 virtual void addElement (Standard_Integer theN1,
180 Standard_Integer theN2,
181 Standard_Integer theN3,
182 Standard_Integer theN4) = 0;
183
184//! @name implementation details
185private:
186
187 //! Handle "v X Y Z".
188 void pushVertex (const char* theXYZ)
189 {
190 char* aNext = NULL;
191 gp_Pnt anXYZ;
192 RWObj_Tools::ReadVec3 (theXYZ, aNext, anXYZ.ChangeCoord());
193 myCSTrsf.TransformPosition (anXYZ.ChangeCoord());
194
195 myMemEstim += myObjVerts.IsSinglePrecision() ? sizeof(Graphic3d_Vec3) : sizeof(gp_Pnt);
196 myObjVerts.Append (anXYZ);
197 }
198
199 //! Handle "vn NX NY NZ".
200 void pushNormal (const char* theXYZ)
201 {
202 char* aNext = NULL;
203 Graphic3d_Vec3 aNorm;
204 RWObj_Tools::ReadVec3 (theXYZ, aNext, aNorm);
205 myCSTrsf.TransformNormal (aNorm);
206
207 myMemEstim += sizeof(Graphic3d_Vec3);
208 myObjNorms.Append (aNorm);
209 }
210
211 //! Handle "vt U V".
212 void pushTexel (const char* theUV)
213 {
214 char* aNext = NULL;
215 Graphic3d_Vec2 anUV;
216 anUV.x() = (float )Strtod (theUV, &aNext);
217 theUV = aNext;
218 anUV.y() = (float )Strtod (theUV, &aNext);
219
220 myMemEstim += sizeof(Graphic3d_Vec2);
221 myObjVertsUV.Append (anUV);
222 }
223
224 //! Handle "f indices".
225 void pushIndices (const char* thePos);
226
227 //! Compute the center of planar polygon.
228 //! @param theIndices polygon indices
229 //! @return center of polygon
230 gp_XYZ polygonCenter (const NCollection_Array1<Standard_Integer>& theIndices);
231
232 //! Compute the normal to planar polygon.
233 //! The logic is similar to ShapeAnalysis_Curve::IsPlanar().
234 //! @param theIndices polygon indices
235 //! @return polygon normal
236 gp_XYZ polygonNormal (const NCollection_Array1<Standard_Integer>& theIndices);
237
238 //! Create triangle fan from specified polygon.
239 //! @param theIndices polygon nodes
240 //! @return number of added triangles
241 Standard_Integer triangulatePolygonFan (const NCollection_Array1<Standard_Integer>& theIndices);
242
243 //! Triangulate specified polygon.
244 //! @param theIndices polygon nodes
245 //! @return number of added triangles
246 Standard_Integer triangulatePolygon (const NCollection_Array1<Standard_Integer>& theIndices);
247
248 //! Handle "o ObjectName".
249 void pushObject (const char* theObjectName);
250
251 //! Handle "g GroupName".
252 void pushGroup (const char* theGroupName);
253
254 //! Handle "s SmoothGroupIndex".
255 void pushSmoothGroup (const char* theSmoothGroupIndex);
256
257 //! Handle "usemtl MaterialName".
258 void pushMaterial (const char* theMaterialName);
259
260 //! Handle "mtllib FileName".
261 void readMaterialLib (const char* theFileName);
262
263 //! Check memory limits.
264 //! @return FALSE on out of memory
265 bool checkMemory();
266
267protected:
268
269 //! Hasher for 3 ordered integers.
270 struct ObjVec3iHasher
271 {
1103eb60 272 std::size_t operator()(const Graphic3d_Vec3i& theKey) const noexcept
4151c94d 273 {
1103eb60 274 return opencascade::hashBytes(&theKey[0], 3 * sizeof(int));
4151c94d 275 }
276
1103eb60 277 bool operator()(const Graphic3d_Vec3i& theKey1,
278 const Graphic3d_Vec3i& theKey2) const noexcept
4151c94d 279 {
280 return theKey1[0] == theKey2[0]
281 && theKey1[1] == theKey2[1]
282 && theKey1[2] == theKey2[2];
283 }
284 };
285
286 //! Auxiliary structure holding vertex data either with single or double floating point precision.
287 class VectorOfVertices
288 {
289 public:
290 //! Empty constructor.
291 VectorOfVertices() : myIsSinglePrecision (Standard_False) {}
292
293 //! Return single precision flag; FALSE by default.
294 bool IsSinglePrecision() const { return myIsSinglePrecision; }
295
296 //! Setup single/double precision flag.
297 void SetSinglePrecision (Standard_Boolean theIsSinglePrecision)
298 {
299 myIsSinglePrecision = theIsSinglePrecision;
300 myPntVec.Nullify();
301 myVec3Vec.Nullify();
302 }
303
304 //! Reset and (re)allocate buffer.
305 void Reset()
306 {
307 if (myIsSinglePrecision)
308 {
309 myVec3Vec = new NCollection_Shared<NCollection_Vector<Graphic3d_Vec3> >();
310 }
311 else
312 {
313 myPntVec = new NCollection_Shared<NCollection_Vector<gp_Pnt> >();
314 }
315 }
316
317 //! Return vector lower index.
318 Standard_Integer Lower() const { return 0; }
319
320 //! Return vector upper index.
321 Standard_Integer Upper() const { return myIsSinglePrecision ? myVec3Vec->Upper() : myPntVec->Upper(); }
322
323 //! Return point with the given index.
324 gp_Pnt Value (Standard_Integer theIndex) const
325 {
326 if (myIsSinglePrecision)
327 {
328 const Graphic3d_Vec3& aPnt = myVec3Vec->Value (theIndex);
329 return gp_Pnt (aPnt.x(), aPnt.y(), aPnt.z());
330 }
331 else
332 {
333 return myPntVec->Value (theIndex);
334 }
335 }
336
337 //! Append new point.
338 void Append (const gp_Pnt& thePnt)
339 {
340 if (myIsSinglePrecision)
341 {
342 myVec3Vec->Append (Graphic3d_Vec3 ((float )thePnt.X(), (float )thePnt.Y(), (float )thePnt.Z()));
343 }
344 else
345 {
346 myPntVec->Append (thePnt);
347 }
348 }
349 private:
350 Handle(NCollection_Shared<NCollection_Vector<gp_Pnt> >) myPntVec;
351 Handle(NCollection_Shared<NCollection_Vector<Graphic3d_Vec3> >) myVec3Vec;
352 Standard_Boolean myIsSinglePrecision;
353 };
354
355protected:
356
357 NCollection_IndexedMap<TCollection_AsciiString>
358 myExternalFiles; //!< list of external file references
359 TCollection_AsciiString myFileComments; //!< file header comments
360 TCollection_AsciiString myFolder; //!< folder containing the OBJ file
361 RWMesh_CoordinateSystemConverter myCSTrsf; //!< coordinate system flipper
362 Standard_Size myMemLimitBytes; //!< memory limit in bytes
363 Standard_Size myMemEstim; //!< estimated memory occupation in bytes
364 Standard_Integer myNbLines; //!< number of parsed lines (e.g. current line)
365 Standard_Integer myNbProbeNodes; //!< number of probed nodes
366 Standard_Integer myNbProbeElems; //!< number of probed elements
367 Standard_Integer myNbElemsBig; //!< number of big elements (polygons with 5+ nodes)
368 Standard_Boolean myToAbort; //!< flag indicating abort state (e.g. syntax error)
369
370 // Each node in the Element specifies independent indices of Vertex position, Texture coordinates and Normal.
371 // This scheme does not match natural definition of Primitive Array
372 // where each unique set of nodal properties defines Vertex
373 // (thus node at the same location but with different normal should be duplicated).
374 // The following code converts OBJ definition of nodal properties to Primitive Array definition.
375 VectorOfVertices myObjVerts; //!< temporary vector of vertices
376 NCollection_Vector<Graphic3d_Vec2> myObjVertsUV; //!< temporary vector of UV parameters
377 NCollection_Vector<Graphic3d_Vec3> myObjNorms; //!< temporary vector of normals
378 NCollection_DataMap<Graphic3d_Vec3i, Standard_Integer, ObjVec3iHasher>
379 myPackedIndices;
380 NCollection_DataMap<TCollection_AsciiString, RWObj_Material>
381 myMaterials; //!< map of known materials
382
383 RWObj_SubMesh myActiveSubMesh; //!< active sub-mesh definition
384 std::vector<Standard_Integer> myCurrElem; //!< indices for the current element
4151c94d 385};
386
387#endif // _RWObj_Reader_HeaderFile