0031124: Configuration - linker errors when building with CLang on Windows
[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
18#include <gp_XYZ.hxx>
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>
29
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>
35
36#include <vector>
37
38//! An abstract class implementing procedure to read OBJ file.
39//!
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.
43//!
44//! Call method Read() to read the file.
45class RWObj_Reader : public Standard_Transient
46{
47 DEFINE_STANDARD_RTTIEXT(RWObj_Reader, Standard_Transient)
48public:
49
50 //! Empty constructor.
51 Standard_EXPORT RWObj_Reader();
52
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)
58 {
59 return read (theFile, theProgress, Standard_False);
60 }
61
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)
70 {
71 return read (theFile, theProgress, Standard_True);
72 }
73
74 //! Returns file comments (lines starting with # at the beginning of file).
75 const TCollection_AsciiString& FileComments() const { return myFileComments; }
76
77 //! Return the list of external file references.
78 const NCollection_IndexedMap<TCollection_AsciiString>& ExternalFiles() const { return myExternalFiles; }
79
80 //! Number of probed nodes.
81 Standard_Integer NbProbeNodes() const { return myNbProbeNodes; }
82
83 //!< number of probed polygon elements (of unknown size).
84 Standard_Integer NbProbeElems() const { return myNbProbeElems; }
85
86 //! Returns memory limit in bytes; -1 (no limit) by default.
87 Standard_Size MemoryLimit() const { return myMemLimitBytes; }
88
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; }
92
93 //! Return transformation from one coordinate system to another; no transformation by default.
94 const RWMesh_CoordinateSystemConverter& Transformation() const { return myCSTrsf; }
95
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; }
100
101 //! Return single precision flag for reading vertex data (coordinates); FALSE by default.
102 Standard_Boolean IsSinglePrecision() const { return myObjVerts.IsSinglePrecision(); }
103
104 //! Setup single/double precision flag for reading vertex data (coordinates).
105 void SetSinglePrecision (Standard_Boolean theIsSinglePrecision) { myObjVerts.SetSinglePrecision (theIsSinglePrecision); }
106
107protected:
108
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);
115
116//! @name interface methods which should be implemented by sub-class
117protected:
118
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;
130
131 //! Retrieve sub-mesh node position, added by addNode().
132 virtual gp_Pnt getNode (Standard_Integer theIndex) const = 0;
133
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;
137
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;
144
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;
151
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;
158
159//! @name implementation details
160private:
161
162 //! Handle "v X Y Z".
163 void pushVertex (const char* theXYZ)
164 {
165 char* aNext = NULL;
166 gp_Pnt anXYZ;
167 RWObj_Tools::ReadVec3 (theXYZ, aNext, anXYZ.ChangeCoord());
168 myCSTrsf.TransformPosition (anXYZ.ChangeCoord());
169
170 myMemEstim += myObjVerts.IsSinglePrecision() ? sizeof(Graphic3d_Vec3) : sizeof(gp_Pnt);
171 myObjVerts.Append (anXYZ);
172 }
173
174 //! Handle "vn NX NY NZ".
175 void pushNormal (const char* theXYZ)
176 {
177 char* aNext = NULL;
178 Graphic3d_Vec3 aNorm;
179 RWObj_Tools::ReadVec3 (theXYZ, aNext, aNorm);
180 myCSTrsf.TransformNormal (aNorm);
181
182 myMemEstim += sizeof(Graphic3d_Vec3);
183 myObjNorms.Append (aNorm);
184 }
185
186 //! Handle "vt U V".
187 void pushTexel (const char* theUV)
188 {
189 char* aNext = NULL;
190 Graphic3d_Vec2 anUV;
191 anUV.x() = (float )Strtod (theUV, &aNext);
192 theUV = aNext;
193 anUV.y() = (float )Strtod (theUV, &aNext);
194
195 myMemEstim += sizeof(Graphic3d_Vec2);
196 myObjVertsUV.Append (anUV);
197 }
198
199 //! Handle "f indices".
200 void pushIndices (const char* thePos);
201
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);
206
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);
212
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);
217
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);
222
223 //! Handle "o ObjectName".
224 void pushObject (const char* theObjectName);
225
226 //! Handle "g GroupName".
227 void pushGroup (const char* theGroupName);
228
229 //! Handle "s SmoothGroupIndex".
230 void pushSmoothGroup (const char* theSmoothGroupIndex);
231
232 //! Handle "usemtl MaterialName".
233 void pushMaterial (const char* theMaterialName);
234
235 //! Handle "mtllib FileName".
236 void readMaterialLib (const char* theFileName);
237
238 //! Check memory limits.
239 //! @return FALSE on out of memory
240 bool checkMemory();
241
242protected:
243
244 //! Hasher for 3 ordered integers.
245 struct ObjVec3iHasher
246 {
247 static Standard_Integer HashCode (const Graphic3d_Vec3i& theKey,
248 const Standard_Integer theUpper)
249 {
250 return ::HashCode (::HashCodes ((Standard_CString )&theKey, sizeof(Graphic3d_Vec3i)), theUpper);
251 }
252
253 static Standard_Boolean IsEqual (const Graphic3d_Vec3i& theKey1,
254 const Graphic3d_Vec3i& theKey2)
255 {
256 return theKey1[0] == theKey2[0]
257 && theKey1[1] == theKey2[1]
258 && theKey1[2] == theKey2[2];
259 }
260 };
261
262 //! Auxiliary structure holding vertex data either with single or double floating point precision.
263 class VectorOfVertices
264 {
265 public:
266 //! Empty constructor.
267 VectorOfVertices() : myIsSinglePrecision (Standard_False) {}
268
269 //! Return single precision flag; FALSE by default.
270 bool IsSinglePrecision() const { return myIsSinglePrecision; }
271
272 //! Setup single/double precision flag.
273 void SetSinglePrecision (Standard_Boolean theIsSinglePrecision)
274 {
275 myIsSinglePrecision = theIsSinglePrecision;
276 myPntVec.Nullify();
277 myVec3Vec.Nullify();
278 }
279
280 //! Reset and (re)allocate buffer.
281 void Reset()
282 {
283 if (myIsSinglePrecision)
284 {
285 myVec3Vec = new NCollection_Shared<NCollection_Vector<Graphic3d_Vec3> >();
286 }
287 else
288 {
289 myPntVec = new NCollection_Shared<NCollection_Vector<gp_Pnt> >();
290 }
291 }
292
293 //! Return vector lower index.
294 Standard_Integer Lower() const { return 0; }
295
296 //! Return vector upper index.
297 Standard_Integer Upper() const { return myIsSinglePrecision ? myVec3Vec->Upper() : myPntVec->Upper(); }
298
299 //! Return point with the given index.
300 gp_Pnt Value (Standard_Integer theIndex) const
301 {
302 if (myIsSinglePrecision)
303 {
304 const Graphic3d_Vec3& aPnt = myVec3Vec->Value (theIndex);
305 return gp_Pnt (aPnt.x(), aPnt.y(), aPnt.z());
306 }
307 else
308 {
309 return myPntVec->Value (theIndex);
310 }
311 }
312
313 //! Append new point.
314 void Append (const gp_Pnt& thePnt)
315 {
316 if (myIsSinglePrecision)
317 {
318 myVec3Vec->Append (Graphic3d_Vec3 ((float )thePnt.X(), (float )thePnt.Y(), (float )thePnt.Z()));
319 }
320 else
321 {
322 myPntVec->Append (thePnt);
323 }
324 }
325 private:
326 Handle(NCollection_Shared<NCollection_Vector<gp_Pnt> >) myPntVec;
327 Handle(NCollection_Shared<NCollection_Vector<Graphic3d_Vec3> >) myVec3Vec;
328 Standard_Boolean myIsSinglePrecision;
329 };
330
331protected:
332
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)
345
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>
355 myPackedIndices;
356 NCollection_DataMap<TCollection_AsciiString, RWObj_Material>
357 myMaterials; //!< map of known materials
358
359 RWObj_SubMesh myActiveSubMesh; //!< active sub-mesh definition
360 std::vector<Standard_Integer> myCurrElem; //!< indices for the current element
361
362};
363
364#endif // _RWObj_Reader_HeaderFile