Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:04:43

0001 // Author: Kirill Gavrilov
0002 // Copyright (c) 2015-2019 OPEN CASCADE SAS
0003 //
0004 // This file is part of Open CASCADE Technology software library.
0005 //
0006 // This library is free software; you can redistribute it and/or modify it under
0007 // the terms of the GNU Lesser General Public License version 2.1 as published
0008 // by the Free Software Foundation, with special exception defined in the file
0009 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
0010 // distribution for complete text of the license and disclaimer of any warranty.
0011 //
0012 // Alternatively, this file may be used under the terms of Open CASCADE
0013 // commercial license or contractual agreement.
0014 
0015 #ifndef _RWObj_Reader_HeaderFile
0016 #define _RWObj_Reader_HeaderFile
0017 
0018 #include <Message.hxx>
0019 #include <Message_Messenger.hxx>
0020 #include <Message_ProgressRange.hxx>
0021 #include <NCollection_Array1.hxx>
0022 #include <NCollection_DataMap.hxx>
0023 #include <NCollection_IndexedMap.hxx>
0024 #include <NCollection_Vector.hxx>
0025 #include <NCollection_Shared.hxx>
0026 #include <OSD_OpenFile.hxx>
0027 #include <RWMesh_CoordinateSystemConverter.hxx>
0028 #include <RWObj_Material.hxx>
0029 #include <RWObj_SubMesh.hxx>
0030 #include <RWObj_SubMeshReason.hxx>
0031 #include <RWObj_Tools.hxx>
0032 #include <Standard_HashUtils.hxx>
0033 
0034 #include <vector>
0035 
0036 //! An abstract class implementing procedure to read OBJ file.
0037 //!
0038 //! This class is not bound to particular data structure
0039 //! and can be used to read the file directly into arbitrary data model.
0040 //! To use it, create descendant class and implement interface methods.
0041 //!
0042 //! Call method Read() to read the file.
0043 class RWObj_Reader : public Standard_Transient
0044 {
0045   DEFINE_STANDARD_RTTIEXT(RWObj_Reader, Standard_Transient)
0046 public:
0047 
0048   //! Empty constructor.
0049   Standard_EXPORT RWObj_Reader();
0050 
0051   //! Open stream and pass it to Read method
0052   //! Returns true if success, false on error.
0053   Standard_Boolean Read (const TCollection_AsciiString& theFile,
0054                          const Message_ProgressRange& theProgress)
0055   {
0056     std::ifstream aStream;
0057     OSD_OpenStream(aStream, theFile, std::ios_base::in | std::ios_base::binary);
0058     return Read(aStream, theFile, theProgress);
0059   }
0060 
0061   //! Reads data from OBJ file.
0062   //! Unicode paths can be given in UTF-8 encoding.
0063   //! Returns true if success, false on error or user break.
0064   Standard_Boolean Read (std::istream& theStream,
0065                          const TCollection_AsciiString& theFile,
0066                          const Message_ProgressRange& theProgress)
0067   {
0068     return read(theStream, theFile, theProgress, Standard_False);
0069   }
0070 
0071   //! Open stream and pass it to Probe method.
0072   //! @param theFile     path to the file
0073   //! @param theProgress progress indicator
0074   //! @return TRUE if success, FALSE on error or user break.
0075   //! @sa FileComments(), ExternalFiles(), NbProbeNodes(), NbProbeElems().
0076   Standard_Boolean Probe (const TCollection_AsciiString& theFile,
0077                           const Message_ProgressRange& theProgress)
0078   {
0079     std::ifstream aStream;
0080     OSD_OpenStream(aStream, theFile, std::ios_base::in | std::ios_base::binary);
0081     return Probe(aStream, theFile, theProgress);
0082   }
0083 
0084   //! Probe data from OBJ file (comments, external references) without actually reading mesh data.
0085   //! Although mesh data will not be collected, the full file content will be parsed, due to OBJ format limitations.
0086   //! @param theStream   input stream
0087   //! @param theFile     path to the file
0088   //! @param theProgress progress indicator
0089   //! @return TRUE if success, FALSE on error or user break.
0090   //! @sa FileComments(), ExternalFiles(), NbProbeNodes(), NbProbeElems().
0091   Standard_Boolean Probe (std::istream& theStream,
0092                           const TCollection_AsciiString& theFile,
0093                           const Message_ProgressRange& theProgress)
0094   {
0095     return read(theStream, theFile, theProgress, Standard_True);
0096   }
0097 
0098   //! Returns file comments (lines starting with # at the beginning of file).
0099   const TCollection_AsciiString& FileComments() const { return myFileComments; }
0100 
0101   //! Return the list of external file references.
0102   const NCollection_IndexedMap<TCollection_AsciiString>& ExternalFiles() const { return myExternalFiles; }
0103 
0104   //! Number of probed nodes.
0105   Standard_Integer NbProbeNodes() const { return myNbProbeNodes; }
0106 
0107   //!< number of probed polygon elements (of unknown size).
0108   Standard_Integer NbProbeElems() const { return myNbProbeElems; }
0109 
0110   //! Returns memory limit in bytes; -1 (no limit) by default.
0111   Standard_Size MemoryLimit() const { return myMemLimitBytes; }
0112 
0113   //! Specify memory limit in bytes, so that import will be aborted
0114   //! by specified limit before memory allocation error occurs.
0115   void SetMemoryLimit (Standard_Size theMemLimit) { myMemLimitBytes = theMemLimit; }
0116 
0117   //! Return transformation from one coordinate system to another; no transformation by default.
0118   const RWMesh_CoordinateSystemConverter& Transformation() const { return myCSTrsf; }
0119 
0120   //! Setup transformation from one coordinate system to another.
0121   //! OBJ file might be exported following various coordinate system conventions,
0122   //! so that it might be useful automatically transform data during file reading.
0123   void SetTransformation (const RWMesh_CoordinateSystemConverter& theCSConverter) { myCSTrsf = theCSConverter; }
0124 
0125   //! Return single precision flag for reading vertex data (coordinates); FALSE by default.
0126   Standard_Boolean IsSinglePrecision() const { return myObjVerts.IsSinglePrecision(); }
0127 
0128   //! Setup single/double precision flag for reading vertex data (coordinates).
0129   void SetSinglePrecision (Standard_Boolean theIsSinglePrecision) { myObjVerts.SetSinglePrecision (theIsSinglePrecision); }
0130 
0131 protected:
0132 
0133   //! Reads data from OBJ file.
0134   //! Unicode paths can be given in UTF-8 encoding.
0135   //! Returns true if success, false on error or user break.
0136   Standard_EXPORT Standard_Boolean read (std::istream& theStream,
0137                                          const TCollection_AsciiString& theFile,
0138                                          const Message_ProgressRange& theProgress,
0139                                          const Standard_Boolean theToProbe);
0140 
0141 //! @name interface methods which should be implemented by sub-class
0142 protected:
0143 
0144   //! Add new sub-mesh.
0145   //! Basically, this method will be called multiple times for the same group with different reason,
0146   //! so that implementation should decide if previously allocated sub-mesh should be used or new one to be allocated.
0147   //! Sub-mesh command can be skipped if previous sub-mesh is empty,
0148   //! or if the reason is out of interest for particular reader
0149   //! (e.g. if materials are ignored, reader may ignore RWObj_SubMeshReason_NewMaterial reason).
0150   //! @param theMesh   mesh definition
0151   //! @param theReason reason to create new sub-mesh
0152   //! @return TRUE if new sub-mesh should be started since this point
0153   virtual Standard_Boolean addMesh (const RWObj_SubMesh& theMesh,
0154                                     const RWObj_SubMeshReason theReason) = 0;
0155 
0156   //! Retrieve sub-mesh node position, added by addNode().
0157   virtual gp_Pnt getNode (Standard_Integer theIndex) const = 0;
0158 
0159   //! Callback function to be implemented in descendant.
0160   //! Should create new node with specified coordinates in the target model, and return its ID as integer.
0161   virtual Standard_Integer addNode (const gp_Pnt& thePnt) = 0;
0162 
0163   //! Callback function to be implemented in descendant.
0164   //! Should set normal coordinates for specified node.
0165   //! @param theIndex node ID as returned by addNode()
0166   //! @param theNorm  normal vector
0167   virtual void setNodeNormal (const Standard_Integer theIndex,
0168                               const Graphic3d_Vec3& theNorm) = 0;
0169 
0170   //! Callback function to be implemented in descendant.
0171   //! Should set texture coordinates for specified node.
0172   //! @param theIndex node ID as returned by addNode()
0173   //! @param theUV    UV texture coordinates
0174   virtual void setNodeUV (const Standard_Integer theIndex,
0175                           const Graphic3d_Vec2& theUV) = 0;
0176 
0177   //! Callback function to be implemented in descendant.
0178   //! Should create new element (triangle or quad if 4th index is != -1) built on specified nodes in the target model.
0179   virtual void addElement (Standard_Integer theN1,
0180                            Standard_Integer theN2,
0181                            Standard_Integer theN3,
0182                            Standard_Integer theN4) = 0;
0183 
0184 //! @name implementation details
0185 private:
0186 
0187   //! Handle "v X Y Z".
0188   void pushVertex (const char* theXYZ)
0189   {
0190     char* aNext = NULL;
0191     gp_Pnt anXYZ;
0192     RWObj_Tools::ReadVec3 (theXYZ, aNext, anXYZ.ChangeCoord());
0193     myCSTrsf.TransformPosition (anXYZ.ChangeCoord());
0194 
0195     myMemEstim += myObjVerts.IsSinglePrecision() ? sizeof(Graphic3d_Vec3) : sizeof(gp_Pnt);
0196     myObjVerts.Append (anXYZ);
0197   }
0198 
0199   //! Handle "vn NX NY NZ".
0200   void pushNormal (const char* theXYZ)
0201   {
0202     char* aNext = NULL;
0203     Graphic3d_Vec3 aNorm;
0204     RWObj_Tools::ReadVec3 (theXYZ, aNext, aNorm);
0205     myCSTrsf.TransformNormal (aNorm);
0206 
0207     myMemEstim += sizeof(Graphic3d_Vec3);
0208     myObjNorms.Append (aNorm);
0209   }
0210 
0211   //! Handle "vt U V".
0212   void pushTexel (const char* theUV)
0213   {
0214     char* aNext = NULL;
0215     Graphic3d_Vec2 anUV;
0216     anUV.x() = (float )Strtod (theUV, &aNext);
0217     theUV = aNext;
0218     anUV.y() = (float )Strtod (theUV, &aNext);
0219 
0220     myMemEstim += sizeof(Graphic3d_Vec2);
0221     myObjVertsUV.Append (anUV);
0222   }
0223 
0224   //! Handle "f indices".
0225   void pushIndices (const char* thePos);
0226 
0227   //! Compute the center of planar polygon.
0228   //! @param theIndices polygon indices
0229   //! @return center of polygon
0230   gp_XYZ polygonCenter (const NCollection_Array1<Standard_Integer>& theIndices);
0231 
0232   //! Compute the normal to planar polygon.
0233   //! The logic is similar to ShapeAnalysis_Curve::IsPlanar().
0234   //! @param theIndices polygon indices
0235   //! @return polygon normal
0236   gp_XYZ polygonNormal (const NCollection_Array1<Standard_Integer>& theIndices);
0237 
0238   //! Create triangle fan from specified polygon.
0239   //! @param theIndices polygon nodes
0240   //! @return number of added triangles
0241   Standard_Integer triangulatePolygonFan (const NCollection_Array1<Standard_Integer>& theIndices);
0242 
0243   //! Triangulate specified polygon.
0244   //! @param theIndices polygon nodes
0245   //! @return number of added triangles
0246   Standard_Integer triangulatePolygon (const NCollection_Array1<Standard_Integer>& theIndices);
0247 
0248   //! Handle "o ObjectName".
0249   void pushObject (const char* theObjectName);
0250 
0251   //! Handle "g GroupName".
0252   void pushGroup (const char* theGroupName);
0253 
0254   //! Handle "s SmoothGroupIndex".
0255   void pushSmoothGroup (const char* theSmoothGroupIndex);
0256 
0257   //! Handle "usemtl MaterialName".
0258   void pushMaterial (const char* theMaterialName);
0259 
0260   //! Handle "mtllib FileName".
0261   void readMaterialLib (const char* theFileName);
0262 
0263   //! Check memory limits.
0264   //! @return FALSE on out of memory
0265   bool checkMemory();
0266 
0267 protected:
0268 
0269   //! Hasher for 3 ordered integers.
0270   struct ObjVec3iHasher
0271   {
0272     std::size_t operator()(const Graphic3d_Vec3i& theKey) const noexcept
0273     {
0274       return opencascade::hashBytes(&theKey[0], 3 * sizeof(int));
0275     }
0276 
0277     bool operator()(const Graphic3d_Vec3i& theKey1,
0278                     const Graphic3d_Vec3i& theKey2) const noexcept
0279     {
0280       return theKey1[0] == theKey2[0]
0281           && theKey1[1] == theKey2[1]
0282           && theKey1[2] == theKey2[2];
0283     }
0284   };
0285 
0286   //! Auxiliary structure holding vertex data either with single or double floating point precision.
0287   class VectorOfVertices
0288   {
0289   public:
0290     //! Empty constructor.
0291     VectorOfVertices() : myIsSinglePrecision (Standard_False) {}
0292 
0293     //! Return single precision flag; FALSE by default.
0294     bool IsSinglePrecision() const { return myIsSinglePrecision; }
0295 
0296     //! Setup single/double precision flag.
0297     void SetSinglePrecision (Standard_Boolean theIsSinglePrecision)
0298     {
0299       myIsSinglePrecision = theIsSinglePrecision;
0300       myPntVec.Nullify();
0301       myVec3Vec.Nullify();
0302     }
0303 
0304     //! Reset and (re)allocate buffer.
0305     void Reset()
0306     {
0307       if (myIsSinglePrecision)
0308       {
0309         myVec3Vec = new NCollection_Shared<NCollection_Vector<Graphic3d_Vec3> >();
0310       }
0311       else
0312       {
0313         myPntVec = new NCollection_Shared<NCollection_Vector<gp_Pnt> >();
0314       }
0315     }
0316 
0317     //! Return vector lower index.
0318     Standard_Integer Lower() const { return 0; }
0319 
0320     //! Return vector upper index.
0321     Standard_Integer Upper() const { return myIsSinglePrecision ? myVec3Vec->Upper() : myPntVec->Upper(); }
0322 
0323     //! Return point with the given index.
0324     gp_Pnt Value (Standard_Integer theIndex) const
0325     {
0326       if (myIsSinglePrecision)
0327       {
0328         const Graphic3d_Vec3& aPnt = myVec3Vec->Value (theIndex);
0329         return gp_Pnt (aPnt.x(), aPnt.y(), aPnt.z());
0330       }
0331       else
0332       {
0333         return myPntVec->Value (theIndex);
0334       }
0335     }
0336 
0337     //! Append new point.
0338     void Append (const gp_Pnt& thePnt)
0339     {
0340       if (myIsSinglePrecision)
0341       {
0342         myVec3Vec->Append (Graphic3d_Vec3 ((float )thePnt.X(), (float )thePnt.Y(), (float )thePnt.Z()));
0343       }
0344       else
0345       {
0346         myPntVec->Append (thePnt);
0347       }
0348     }
0349   private:
0350     Handle(NCollection_Shared<NCollection_Vector<gp_Pnt> >)         myPntVec;
0351     Handle(NCollection_Shared<NCollection_Vector<Graphic3d_Vec3> >) myVec3Vec;
0352     Standard_Boolean myIsSinglePrecision;
0353   };
0354 
0355 protected:
0356 
0357   NCollection_IndexedMap<TCollection_AsciiString>
0358                                      myExternalFiles; //!< list of external file references
0359   TCollection_AsciiString            myFileComments;  //!< file header comments
0360   TCollection_AsciiString            myFolder;        //!< folder containing the OBJ file
0361   RWMesh_CoordinateSystemConverter   myCSTrsf;        //!< coordinate system flipper
0362   Standard_Size                      myMemLimitBytes; //!< memory limit in bytes
0363   Standard_Size                      myMemEstim;      //!< estimated memory occupation in bytes
0364   Standard_Integer                   myNbLines;       //!< number of parsed lines (e.g. current line)
0365   Standard_Integer                   myNbProbeNodes;  //!< number of probed nodes
0366   Standard_Integer                   myNbProbeElems;  //!< number of probed elements
0367   Standard_Integer                   myNbElemsBig;    //!< number of big elements (polygons with 5+ nodes)
0368   Standard_Boolean                   myToAbort;       //!< flag indicating abort state (e.g. syntax error)
0369 
0370   // Each node in the Element specifies independent indices of Vertex position, Texture coordinates and Normal.
0371   // This scheme does not match natural definition of Primitive Array
0372   // where each unique set of nodal properties defines Vertex
0373   // (thus node at the same location but with different normal should be duplicated).
0374   // The following code converts OBJ definition of nodal properties to Primitive Array definition.
0375   VectorOfVertices                   myObjVerts;      //!< temporary vector of vertices
0376   NCollection_Vector<Graphic3d_Vec2> myObjVertsUV;    //!< temporary vector of UV parameters
0377   NCollection_Vector<Graphic3d_Vec3> myObjNorms;      //!< temporary vector of normals
0378   NCollection_DataMap<Graphic3d_Vec3i, Standard_Integer, ObjVec3iHasher>
0379                                      myPackedIndices;
0380   NCollection_DataMap<TCollection_AsciiString, RWObj_Material>
0381                                      myMaterials;     //!< map of known materials
0382 
0383   RWObj_SubMesh                      myActiveSubMesh; //!< active sub-mesh definition
0384   std::vector<Standard_Integer>      myCurrElem;      //!< indices for the current element
0385 };
0386 
0387 #endif // _RWObj_Reader_HeaderFile