//########################################################################## //# # //# CLOUDCOMPARE # //# # //# This program is free software; you can redistribute it and/or modify # //# it under the terms of the GNU General Public License as published by # //# the Free Software Foundation; version 2 or later of the License. # //# # //# This program is distributed in the hope that it will be useful, # //# but WITHOUT ANY WARRANTY; without even the implied warranty of # //# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # //# GNU General Public License for more details. # //# # //# COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI) # //# # //########################################################################## #ifndef CC_MESH_HEADER #define CC_MESH_HEADER //CCCoreLib #include #include //Local #include "ccGenericMesh.h" class ccProgressDialog; class ccPolyline; //! Triangular mesh class QCC_DB_LIB_API ccMesh : public ccGenericMesh { public: //! Default ccMesh constructor /** \param vertices the vertices cloud \param uniqueID unique ID (handle with care) **/ explicit ccMesh(ccGenericPointCloud* vertices, unsigned uniqueID = ccUniqueIDGenerator::InvalidUniqueID); //! ccMesh constructor (from a CCCoreLib::GenericIndexedMesh) /** The GenericIndexedMesh must be associated to a valid ccGenericPointCloud instance. \param giMesh the mesh to 'copy' \param giVertices associated vertices (can technically be different from the input mesh vertices, but should have at least as many points) **/ explicit ccMesh(CCCoreLib::GenericIndexedMesh* giMesh, ccGenericPointCloud* giVertices); //! Default destructor ~ccMesh() override; //! Returns class ID inline CC_CLASS_ENUM getClassID() const override { return CC_TYPES::MESH; } //! Sets the associated vertices cloud (warning) void setAssociatedCloud(ccGenericPointCloud* cloud); //! Clones this entity /** All the main features of the entity are cloned, except from the octree \param vertices vertices set to use (will be automatically - AND OPTIMALLY - cloned if nullptr) \param clonedMaterials for internal use \param clonedNormsTable for internal use \param cloneTexCoords for internal use \return a copy of this entity **/ ccMesh* cloneMesh( ccGenericPointCloud* vertices = nullptr, ccMaterialSet* clonedMaterials = nullptr, NormsIndexesTableType* clonedNormsTable = nullptr, TextureCoordsContainer* cloneTexCoords = nullptr); //! Creates a Delaunay 2.5D mesh from a point cloud /** See CCCoreLib::PointProjectionTools::computeTriangulation. **/ static ccMesh* Triangulate( ccGenericPointCloud* cloud, CCCoreLib::TRIANGULATION_TYPES type, bool updateNormals = false, PointCoordinateType maxEdgeLength = 0, unsigned char dim = 2); //! Creates a Delaunay 2.5D mesh from two polylines static ccMesh* TriangulateTwoPolylines(ccPolyline* p1, ccPolyline* p2, CCVector3* projectionDir = nullptr); //! Merges another mesh into this one /** \param mesh mesh to be merged in this one \param createSubMesh whether to create a submesh entity corresponding to the added mesh \return success **/ bool merge(const ccMesh* mesh, bool createSubMesh); //inherited methods (ccHObject) ccBBox getOwnBB(bool withGLFeatures = false) override; bool isSerializable() const override { return true; } const ccGLMatrix& getGLTransformationHistory() const override; //inherited methods (ccGenericMesh) inline ccGenericPointCloud* getAssociatedCloud() const override { return m_associatedCloud; } void refreshBB() override; bool interpolateNormalsBC(unsigned triIndex, const CCVector3d& w, CCVector3& N) override; bool interpolateColors(unsigned triIndex, const CCVector3& P, ccColor::Rgb& C) override; bool interpolateColorsBC(unsigned triIndex, const CCVector3d& w, ccColor::Rgb& C) override; bool interpolateColors(unsigned triIndex, const CCVector3& P, ccColor::Rgba& C) override; bool interpolateColorsBC(unsigned triIndex, const CCVector3d& w, ccColor::Rgba& C) override; void computeInterpolationWeights(unsigned triIndex, const CCVector3& P, CCVector3d& weights) const override; bool getColorFromMaterial(unsigned triIndex, const CCVector3& P, ccColor::Rgba& C, bool interpolateColorIfNoTexture) override; bool getVertexColorFromMaterial(unsigned triIndex, unsigned char vertIndex, ccColor::Rgba& color, bool returnColorIfNoTexture) override; unsigned capacity() const override; //inherited methods (GenericIndexedMesh) void forEach(genericTriangleAction action) override; void placeIteratorAtBeginning() override; CCCoreLib::GenericTriangle* _getNextTriangle() override; //temporary CCCoreLib::GenericTriangle* _getTriangle(unsigned triangleIndex) override; //temporary CCCoreLib::VerticesIndexes* getNextTriangleVertIndexes() override; CCCoreLib::VerticesIndexes* getTriangleVertIndexes(unsigned triangleIndex) override; void getTriangleVertices(unsigned triangleIndex, CCVector3& A, CCVector3& B, CCVector3& C) const override; unsigned size() const override; void getBoundingBox(CCVector3& bbMin, CCVector3& bbMax) override; bool normalsAvailable() const override { return hasNormals(); } bool interpolateNormals(unsigned triIndex, const CCVector3& P, CCVector3& N) override; //const version of getTriangleVertIndexes const virtual CCCoreLib::VerticesIndexes* getTriangleVertIndexes(unsigned triangleIndex) const; //inherited methods (ccDrawableObject) bool hasColors() const override; bool hasNormals() const override; bool hasScalarFields() const override; bool hasDisplayedScalarField() const override; bool normalsShown() const override; void toggleMaterials() override { showMaterials(!materialsShown()); } //! Inverts normals (if any) /** Either the per-triangle normals, or the per-vertex ones **/ void invertNormals(); //! Shifts all triangles indexes /** \param shift index shift (positive) **/ void shiftTriangleIndexes(unsigned shift); //! Flips the triangle /** Swaps the second and third vertices indexes **/ void flipTriangles(); //! Adds a triangle to the mesh /** \warning Bounding-box validity is broken after a call to this method. However, for the sake of performance, no call to notifyGeometryUpdate is made automatically. Make sure to do so when all modifications are done! \param i1 first vertex index (relatively to the vertex cloud) \param i2 second vertex index (relatively to the vertex cloud) \param i3 third vertex index (relatively to the vertex cloud) **/ void addTriangle(unsigned i1, unsigned i2, unsigned i3); //! Reserves the memory to store the vertex indexes (3 per triangle) /** \param n the number of triangles to reserve \return true if the method succeeds, false otherwise **/ bool reserve(size_t n); //! Resizes the array of vertex indexes (3 per triangle) /** If the new number of elements is smaller than the actual size, the overflooding elements will be deleted. \param n the new number of triangles \return true if the method succeeds, false otherwise **/ bool resize(size_t n); //! Removes unused capacity inline void shrinkToFit() { if (size() < capacity()) resize(size()); } /*********************************************************/ /************** PER-TRIANGLE NORMALS ***************/ /*********************************************************/ //inherited from ccGenericMesh bool hasTriNormals() const override; void getTriangleNormalIndexes(unsigned triangleIndex, int& i1, int& i2, int& i3) const override; bool getTriangleNormals(unsigned triangleIndex, CCVector3& Na, CCVector3& Nb, CCVector3& Nc) const override; NormsIndexesTableType* getTriNormsTable() const override { return m_triNormals; } //! Sets per-triangle normals array (may be shared) void setTriNormsTable(NormsIndexesTableType* triNormsTable, bool autoReleaseOldTable = true); //! Removes per-triangle normals void clearTriNormals() { setTriNormsTable(nullptr); } //! Returns whether per triangle normals are enabled /** To enable per triangle normals, you should: - first, reserve memory for triangles (this is always the first thing to do) - associate this mesh to a triangle normals array (see ccMesh::setTriNormsTable) - reserve memory to store per-triangle normal indexes with ccMesh::reservePerTriangleNormalIndexes - add for each triangle a triplet of indexes (referring to stored normals) **/ bool arePerTriangleNormalsEnabled() const; //! Reserves memory to store per-triangle triplets of normal indexes /** Before adding per-triangle normal indexes triplets to the mesh (with ccMesh::addTriangleNormalsIndexes()) be sure to reserve the necessary amount of memory with this method. This method reserves memory for as many normals indexes triplets as the number of triangles in the mesh (effectively stored or reserved - a call to ccMesh::reserve prior to this one is mandatory). \return true if ok, false if there's not enough memory **/ bool reservePerTriangleNormalIndexes(); //! Adds a triplet of normal indexes for next triangle /** Make sure per-triangle normal indexes array is allocated (see reservePerTriangleNormalIndexes) \param i1 first vertex normal index \param i2 second vertex normal index \param i3 third vertex normal index **/ void addTriangleNormalIndexes(int i1, int i2, int i3); //! Sets a triplet of normal indexes for a given triangle /** \param triangleIndex triangle index \param i1 first vertex normal index \param i2 second vertex normal index \param i3 third vertex normal index **/ void setTriangleNormalIndexes(unsigned triangleIndex, int i1, int i2, int i3); //! Removes any per-triangle triplets of normal indexes void removePerTriangleNormalIndexes(); //! Invert per-triangle normals void invertPerTriangleNormals(); /********************************************************/ /************ PER-TRIANGLE MATERIAL ***************/ /********************************************************/ //inherited from ccGenericMesh bool hasMaterials() const override; const ccMaterialSet* getMaterialSet() const override { return m_materials; } int getTriangleMtlIndex(unsigned triangleIndex) const override; //! Converts materials to vertex colors /** Warning: this method will overwrite colors (if any) **/ bool convertMaterialsToVertexColors(); //! Returns whether this mesh as per-triangle material index bool hasPerTriangleMtlIndexes() const { return m_triMtlIndexes && m_triMtlIndexes->isAllocated(); } //! Reserves memory to store per-triangle material index /** Before adding per-triangle material index to the mesh (with ccMesh::addTriangleMtlIndex()) be sure to reserve the necessary amount of memory with this method. This method reserves memory for as many material descriptors as the number of triangles in the mesh (effectively stored or reserved - a call to ccMesh::reserve prior to this one is mandatory). \return true if ok, false if there's not enough memory **/ bool reservePerTriangleMtlIndexes(); //! Removes any per-triangle material indexes void removePerTriangleMtlIndexes(); //! Adds triangle material index for next triangle /** Cf. ccMesh::reservePerTriangleMtlIndexes. \param mtlIndex triangle material index **/ void addTriangleMtlIndex(int mtlIndex); //! Container of per-triangle material descriptors using triangleMaterialIndexesSet = ccArray; //! Sets per-triangle material indexes array void setTriangleMtlIndexesTable(triangleMaterialIndexesSet* matIndexesTable, bool autoReleaseOldTable = true); //! Returns the per-triangle material indexes array inline const triangleMaterialIndexesSet* getTriangleMtlIndexesTable() const { return m_triMtlIndexes; } //! Sets triangle material indexes /** Cf. ccMesh::reservePerTriangleMtlIndexes. \param triangleIndex triangle index \param mtlIndex triangle material index **/ void setTriangleMtlIndex(unsigned triangleIndex, int mtlIndex); //! Sets associated material set (may be shared) void setMaterialSet(ccMaterialSet* materialSet, bool autoReleaseOldMaterialSet = true); /******************************************************************/ /************ PER-TRIANGLE TEXTURE COORDINATE ***************/ /******************************************************************/ //inherited from ccGenericMesh bool hasTextures() const override; TextureCoordsContainer* getTexCoordinatesTable() const override { return m_texCoords; } void getTriangleTexCoordinates(unsigned triIndex, TexCoords2D* &tx1, TexCoords2D* &tx2, TexCoords2D* &tx3) const override; bool hasPerTriangleTexCoordIndexes() const override { return m_texCoordIndexes && m_texCoordIndexes->isAllocated(); } void getTriangleTexCoordinatesIndexes(unsigned triangleIndex, int& i1, int& i2, int& i3) const override; //! Sets per-triangle texture coordinates array (may be shared) void setTexCoordinatesTable(TextureCoordsContainer* texCoordsTable, bool autoReleaseOldTable = true); //! Reserves memory to store per-triangle triplets of tex coords indexes /** Before adding per-triangle tex coords indexes triplets to the mesh (with ccMesh::addTriangleTexCoordIndexes()) be sure to reserve the necessary amount of memory with this method. This method reserves memory for as many tex coords indexes triplets as the number of triangles in the mesh (effectively stored or reserved - a call to ccMesh::reserve prior to this one is mandatory). \return true if ok, false if there's not enough memory **/ bool reservePerTriangleTexCoordIndexes(); //! Remove per-triangle tex coords indexes void removePerTriangleTexCoordIndexes(); //! Adds a triplet of tex coords indexes for next triangle /** Make sure per-triangle tex coords indexes array is allocated (see reservePerTriangleTexCoordIndexes) \param i1 first vertex tex coords index \param i2 second vertex tex coords index \param i3 third vertex tex coords index **/ void addTriangleTexCoordIndexes(int i1, int i2, int i3); //! Sets a triplet of tex coords indexes for a given triangle /** \param triangleIndex triangle index \param i1 first vertex tex coords index \param i2 second vertex tex coords index \param i3 third vertex tex coords index **/ void setTriangleTexCoordIndexes(unsigned triangleIndex, int i1, int i2, int i3); //! Computes normals /** \param perVertex whether normals should be computed per-vertex or per-triangle \return success **/ bool computeNormals(bool perVertex); //! Computes per-vertex normals bool computePerVertexNormals(); //! Computes per-triangle normals bool computePerTriangleNormals(); //! Laplacian smoothing /** \param nbIteration smoothing iterations \param factor smoothing 'force' \param progressCb progress dialog callback **/ bool laplacianSmooth( unsigned nbIteration = 100, PointCoordinateType factor = static_cast(0.01), ccProgressDialog* progressCb = nullptr); //! Mesh scalar field processes enum MESH_SCALAR_FIELD_PROCESS { SMOOTH_MESH_SF, /**< Smooth **/ ENHANCE_MESH_SF, /**< Enhance **/ }; //! Applies process to the mesh scalar field (the one associated to its vertices in fact) /** A very simple smoothing/enhancement algorithm based on each vertex direct neighbours. Prior to calling this method, one should check first that the vertices are associated to a scalar field. Warning: the processed scalar field must be enabled for both INPUT & OUTPUT! (see ccGenericCloud::setCurrentScalarField) \param process either 'smooth' or 'enhance' **/ bool processScalarField(MESH_SCALAR_FIELD_PROCESS process); //! Subdivides mesh (so as to ensure that all triangles are falls below 'maxArea') /** \return subdivided mesh (if successful) **/ ccMesh* subdivide(PointCoordinateType maxArea) const; //! Creates a new mesh with the selected vertices only /** This method is called after a graphical segmentation. It creates a new mesh structure with the vertices that are tagged as "visible" (see ccGenericPointCloud::visibilityArray). This method will also update this mesh if removeSelectedFaces is true. In this case, all "selected" triangles will be removed from this mesh's instance. \param removeSelectedTriangles specifies if the faces composed only of 'selected' vertices should be removed or not. If true, the visibility array will be automatically unallocated on completion \param newIndexesOfRemainingTriangles the new indexes of the remaining triangles (if removeSelectedTriangles is true - optional). Must be initially empty or have the same size as the original mesh. \param withChildEntities whether child entities should be transferred as well (see ccHObjectCaster::CloneChildren) \return the new mesh (if successful) or itself if all vertices were visible/selected **/ ccMesh* createNewMeshFromSelection( bool removeSelectedTriangles, std::vector* newIndexesOfRemainingTriangles = nullptr, bool withChildEntities = false ); //! Swaps two triangles /** Automatically updates internal structures (i.e. lookup tables for material, normals, etc.). **/ void swapTriangles(unsigned index1, unsigned index2); //! Transforms the mesh per-triangle normals void transformTriNormals(const ccGLMatrix& trans); //! Default octree level for the 'mergeDuplicatedVertices' algorithm static const unsigned char DefaultMergeDuplicateVerticesLevel = 10; //! Merges duplicated vertices bool mergeDuplicatedVertices(unsigned char octreeLevel = DefaultMergeDuplicateVerticesLevel, QWidget* parentWidget = nullptr); protected: //methods //inherited from ccHObject void drawMeOnly(CC_DRAW_CONTEXT& context) override; bool toFile_MeOnly(QFile& out, short dataVersion) const override; bool fromFile_MeOnly(QFile& in, short dataVersion, int flags, LoadedIDMap& oldToNewIDMap) override; short minimumFileVersion_MeOnly() const override; void applyGLTransformation(const ccGLMatrix& trans) override; void onUpdateOf(ccHObject* obj) override; void onDeletionOf(const ccHObject* obj) override; //! Same as other 'computeInterpolationWeights' method with a set of 3 vertices indexes void computeInterpolationWeights(const CCCoreLib::VerticesIndexes& vertIndexes, const CCVector3& P, CCVector3d& weights) const; //! Same as other 'interpolateNormals' method with a set of 3 vertices indexes bool interpolateNormals(const CCCoreLib::VerticesIndexes& vertIndexes, const CCVector3d& w, CCVector3& N, const Tuple3i* triNormIndexes = nullptr); //! Same as other 'interpolateColors' method with a set of 3 vertices indexes bool interpolateColors(const CCCoreLib::VerticesIndexes& vertIndexes, const CCVector3d& w, ccColor::Rgb& C); //! Same as other 'interpolateColors' method with a set of 3 vertices indexes bool interpolateColors(const CCCoreLib::VerticesIndexes& vertIndexes, const CCVector3d& w, ccColor::Rgba& C); //! Used internally by 'subdivide' bool pushSubdivide(/*PointCoordinateType maxArea, */unsigned indexA, unsigned indexB, unsigned indexC); /*** EXTENDED CALL SCRIPTS (FOR CC_SUB_MESHES) ***/ //0 parameter #define ccMesh_extended_call0(baseName,recursiveName) \ inline virtual void recursiveName() \ { \ baseName(); \ for (Container::iterator it = m_children.begin(); it != m_children.end(); ++it) \ if ((*it)->isA(CC_TYPES::SUB_MESH)) \ static_cast(*it)->baseName(); \ } \ //1 parameter #define ccMesh_extended_call1(baseName,param1Type,recursiveName) \ inline virtual void recursiveName(param1Type p) \ { \ baseName(p); \ for (Container::iterator it = m_children.begin(); it != m_children.end(); ++it) \ if ((*it)->isA(CC_TYPES::SUB_MESH)) \ static_cast(*it)->baseName(p); \ } \ //recursive equivalents of some of ccGenericMesh methods (applied to sub-meshes as well) ccMesh_extended_call1(showNormals, bool, showNormals_extended) protected: //members //! associated cloud (vertices) ccGenericPointCloud* m_associatedCloud; //! Per-triangle normals NormsIndexesTableType* m_triNormals; //! Texture coordinates TextureCoordsContainer* m_texCoords; //! Materials ccMaterialSet* m_materials; //! Container of per-triangle vertices indexes (3) using triangleIndexesContainer = ccArray; //! Triangles' vertices indexes (3 per triangle) triangleIndexesContainer* m_triVertIndexes; //! Iterator on the list of triangles unsigned m_globalIterator; //! Dump triangle structure to transmit temporary data CCCoreLib::SimpleRefTriangle m_currentTriangle; //! Bounding-box ccBBox m_bBox; //! Per-triangle material indexes triangleMaterialIndexesSet* m_triMtlIndexes; //! Set of triplets of indexes referring to mesh texture coordinates using triangleTexCoordIndexesSet = ccArray; //! Mesh tex coords indexes (per-triangle) triangleTexCoordIndexesSet* m_texCoordIndexes; //! Set of triplets of indexes referring to mesh normals using triangleNormalsIndexesSet = ccArray; //! Mesh normals indexes (per-triangle) triangleNormalsIndexesSet* m_triNormalIndexes; private: //! Copy of a ccMesh instance is not supported (because of all the pointers to the members) ccMesh(const ccMesh&) {} }; #endif //CC_MESH_HEADER