// Copyright (c) 2018 GeometryFactory Sarl (France). // All rights reserved. // // This file is part of CGAL (www.cgal.org). // You can redistribute it and/or modify it under the terms of the GNU // General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // Licensees holding a valid commercial license may use this file in // accordance with the commercial license agreement provided with the software. // // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // $URL$ // $Id$ // SPDX-License-Identifier: GPL-3.0+ // // // Author(s) : Guillaume Damiand #ifndef CGAL_VBO_BUFFER_FILLER_H #define CGAL_VBO_BUFFER_FILLER_H #include #include #include #include #include #include #include #include #include #include #include #include namespace CGAL { typedef CGAL::Exact_predicates_inexact_constructions_kernel Local_kernel; typedef Local_kernel::Point_3 Local_point; typedef Local_kernel::Vector_3 Local_vector; //------------------------------------------------------------------------------ namespace internal { template void newell_single_step_3(const Point& p, const Point& q, Vector& n) { // Compute normal of the face by using Newell's method: for each edge PQ // Nx += (Py - Qy) * (Pz + Qz); // Ny += (Pz - Qz) * (Px + Qx); // Nz += (Px - Qx) * (Py + Qy); n = Vector(n.x()+((p.y()-q.y())*(p.z()+q.z())), n.y()+((p.z()-q.z())*(p.x()+q.x())), n.z()+((p.x()-q.x())*(p.y()+q.y()))); } inline Local_vector compute_normal_of_face(const std::vector& points) { Local_vector normal(CGAL::NULL_VECTOR); unsigned int nb = 0; for (std::size_t i=0; i0); return (Local_kernel::Construct_scaled_vector_3()(normal, 1.0/nb)); } //////////////////////////////////////////////////////////////// // Structs to transform any CGAL point/vector into a Local_point/Local_vector template struct Geom_utils { static Local_point get_local_point(const typename K::Point_2& p) { CGAL::Cartesian_converter converter; return Local_point(converter(p.x()), 0, converter(p.y())); } static Local_point get_local_point(const typename K::Weighted_point_2& p) { typename K::Point_2 lp(p); return Geom_utils::get_local_point(lp); } static Local_point get_local_point(const typename K::Point_3& p) { CGAL::Cartesian_converter converter; return converter(p); } static Local_point get_local_point(const typename K::Weighted_point_3& p) { typename K::Point_3 lp(p); return Geom_utils::get_local_point(lp); } static Local_vector get_local_vector(const typename K::Vector_2& v) { CGAL::Cartesian_converter converter; return Local_vector(converter(v.x()), 0, converter(v.y())); } static Local_vector get_local_vector(const typename K::Vector_3& v) { CGAL::Cartesian_converter converter; return converter(v); } }; // Specialization for Local_kernel, because there is no need of convertion here. template<> struct Geom_utils { static Local_point get_local_point(const Local_kernel::Point_2& p) { return Local_point(p.x(), 0, p.y()); } static Local_point get_local_point(const Local_kernel::Weighted_point_2& p) { return Local_point(p.point().x(), 0, p.point().y());} static const Local_point & get_local_point(const Local_kernel::Point_3& p) { return p; } static Local_point get_local_point(const Local_kernel::Weighted_point_3& p) { return Local_point(p);} static Local_vector get_local_vector(const Local_kernel::Vector_2& v) { return Local_vector(v.x(), 0, v.y()); } static const Local_vector& get_local_vector(const Local_kernel::Vector_3& v) { return v; } }; //////////////////////////////////////////////////////////////// // Global function to simplify function calls. template Local_point get_local_point(const KPoint& p) { return Geom_utils::Kernel>:: get_local_point(p); } template Local_vector get_local_vector(const KVector& v) { return Geom_utils::Kernel>:: get_local_vector(v); } } // End namespace internal //------------------------------------------------------------------------------ template class Buffer_for_vao { public: Buffer_for_vao(std::vector* pos=NULL, std::vector* indices=NULL, CGAL::Bbox_3* bbox=NULL, std::vector* color=NULL, std::vector* flat_normal=NULL, std::vector* gouraud_normal=NULL) : m_pos_buffer(pos), m_index_buffer(indices), m_color_buffer(color), m_flat_normal_buffer(flat_normal), m_gouraud_normal_buffer(gouraud_normal), m_bb(bbox), m_face_started(false) {} void clear() { if (m_pos_buffer!=NULL) { m_pos_buffer->clear(); } if (m_color_buffer!=NULL) { m_color_buffer->clear(); } if (m_index_buffer!=NULL) { m_index_buffer->clear(); } if (m_flat_normal_buffer!=NULL) { m_flat_normal_buffer->clear(); } if (m_gouraud_normal_buffer!=NULL) { m_gouraud_normal_buffer->clear(); } } bool is_empty() const { return (m_pos_buffer!=NULL && m_pos_buffer->empty()) && (m_color_buffer!=NULL || m_color_buffer->empty()) && (m_flat_normal_buffer!=NULL || m_flat_normal_buffer->empty()) && (m_gouraud_normal_buffer!=NULL || m_gouraud_normal_buffer->empty()) && (m_index_buffer!=NULL || m_index_buffer->empty()); } bool has_position() const { return m_pos_buffer!=NULL; } bool has_indices() const { return m_index_buffer!=NULL; } bool has_color() const { return m_color_buffer!=NULL; } bool has_flat_normal() const { return m_flat_normal_buffer!=NULL; } bool has_gouraud_normal() const { return m_gouraud_normal_buffer!=NULL; } // 1.1) Add a point, without color. Return the index of the added point. template std::size_t add_point(const KPoint& kp) { if (!has_position()) return (std::size_t)-1; Local_point p=internal::get_local_point(kp); add_point_in_buffer(p, *m_pos_buffer); if (m_bb!=NULL) { (*m_bb)=(*m_bb)+p.bbox(); } return m_pos_buffer->size()-3; } // 1.2) Add a point, with color. template void add_point(const KPoint& kp, const CGAL::Color& c) { add_point(kp); add_color(c); } // 1.3) Add an indexed point, without color. template void add_indexed_point(T index) { if (!has_indices()) return; m_index_buffer->push_back((IndexType)index); } // 2.1) Add a segment, without color. template void add_segment(const KPoint& kp1, const KPoint& kp2) { add_point(kp1); add_point(kp2); } // 2.2) Add a segment, with color. template void add_segment(const KPoint& kp1, const KPoint& kp2, const CGAL::Color& c) { add_segment(kp1, kp2); add_color(c); add_color(c); } // 2.3) Add an indexed segment, without color. template void add_indexed_segment(T index1, T index2) { add_indexed_point(index1); add_indexed_point(index2); } /// @return true iff a face has begun. bool is_a_face_started() const { return m_face_started; } // 3.1) Add a face, without color, without normal. void face_begin() { face_begin_internal(false, false); } // 3.2) Add a face, with a color, without normal. void face_begin(const CGAL::Color& c) { m_color_of_face=c; face_begin_internal(true, false); } // 3.3) Add a face, without a color, with a normal. template void face_begin(const KNormal& kv) { m_normal_of_face=internal::get_local_vector(kv); face_begin_internal(false, true); } // 3.3) Add a face, with a color and with a normal. template void face_begin(const CGAL::Color& c, const KNormal& kv) { m_color_of_face=c; m_normal_of_face=internal::get_local_vector(kv); face_begin_internal(true, true); } /// Add a point at the end of the current face, without giving the vertex normal. /// When this method is used, it is not possible to use the Gouraud shading. /// @param p the point to add template bool add_point_in_face(const KPoint& kp) { if (!is_a_face_started()) return false; Local_point p=internal::get_local_point(kp); if (m_points_of_face.empty() || m_points_of_face.back()!=p) // TODO test if the distance between prev point and kp is smaller than an epsilon (?? not sure ??) { m_points_of_face.push_back(p); return true; } return false; } /// Add a point at the end of the current face /// @param p the point to add /// @p_normal the vertex normal at this point (for Gouraud shading) template bool add_point_in_face(const KPoint& kp, const KVector& p_normal) { if (add_point_in_face(kp)) { m_vertex_normals_for_face.push_back(internal::get_local_vector(p_normal)); return true; } return false; } /// Add an indexed point at the end of the current face, without giving the vertex normal. /// When Indexation is used, it is not possible to use flat shading or multiple colors /// for face sor edges. /// Note that we still need the point itself, in order to triangulate the face when necessary. template bool add_indexed_point_in_face(T index, const KPoint& kp) { if (add_point_in_face(kp)) { m_indices_of_points_of_face.push_back(index); return true; } return false; } /// End the face: compute the triangulation. void face_end() { if (!is_a_face_started()) return; if (m_points_of_face.size()<3) { std::cerr<<"PB: you try to triangulate a face with "<0 && m_indices_of_points_of_face.size()!=m_points_of_face.size()) { std::cerr<<"PB: you mixed some add_point_in_face(...) and some add_indexed_point_in_face(...)" <<" for a same face. Indices for this face are ignored."<0 && m_vertex_normals_for_face.size()!=m_points_of_face.size()) { std::cerr<<"PB: you only gave some vertex normals (and not all) for a same face. " <<"All vertex normal are ignored and thus it is not possible to use Gouraud " <<"shading for this face." < 4 vertices } else { // Non convex and more than 3 points: we triangulate nonconvex_face_end_internal(normal); } m_face_started=false; } /// adds `kp` coordinates to `buffer` template static void add_point_in_buffer(const KPoint& kp, std::vector& buffer) { Local_point p=internal::get_local_point(kp); buffer.push_back(p.x()); buffer.push_back(p.y()); buffer.push_back(p.z()); } /// adds `kv` coordinates to `buffer` template static void add_normal_in_buffer(const KVector& kv, std::vector& buffer) { Local_vector n=internal::get_local_vector(kv); buffer.push_back(n.x()); buffer.push_back(n.y()); buffer.push_back(n.z()); } ///adds `acolor` RGB components to `buffer` static void add_color_in_buffer(const CGAL::Color& acolor, std::vector& buffer) { buffer.push_back((float)acolor.red()/(float)255); buffer.push_back((float)acolor.green()/(float)255); buffer.push_back((float)acolor.blue()/(float)255); } /// @return true iff the points of 'facet' form a convex face static bool is_facet_convex(const std::vector& facet, const Local_vector& normal) { Local_kernel::Orientation orientation, local_orientation; std::size_t id=0; do { const Local_point& S=facet[id]; const Local_point& T=facet[(id+1==facet.size())?0:id+1]; Local_vector V1=Local_vector((T-S).x(), (T-S).y(), (T-S).z()); const Local_point& U=facet[(id+2==facet.size())?0:id+2]; Local_vector V2=Local_vector((U-T).x(), (U-T).y(), (U-T).z()); orientation = Local_kernel::Orientation_3()(V1, V2, normal); // Is it possible that orientation==COPLANAR ? Maybe if V1 or V2 is very small ? } while(++id!=facet.size() && (orientation==CGAL::COPLANAR || orientation==CGAL::ZERO)); //Here, all orientations were COPLANAR. Not sure this case is possible, // but we stop here. if (orientation==CGAL::COPLANAR || orientation==CGAL::ZERO) { return false; } // Now we compute convexness for(id=0; id0) { add_indexed_point(m_indices_of_points_of_face[i]); } else { add_point(m_points_of_face[i]); // Add the position of the point if (m_started_face_is_colored) { add_color(m_color_of_face); } // Add the color add_flat_normal(normal); // Add the flat normal // Its smooth normal (if given by the user) if (m_vertex_normals_for_face.size()>0) { // Here we have 3 vertex normals; we can use Gouraud add_gouraud_normal(m_vertex_normals_for_face[i]); } else { // Here user does not provide all vertex normals: we use face normal istead // and thus we will not be able to use Gouraud add_gouraud_normal(normal); } } } } void convex_quadrangular_face_end_internal(const Local_vector& normal) { // Add indices when they exist if (m_indices_of_points_of_face.size()>0) { add_indexed_point(m_indices_of_points_of_face[0]); add_indexed_point(m_indices_of_points_of_face[1]); add_indexed_point(m_indices_of_points_of_face[2]); add_indexed_point(m_indices_of_points_of_face[0]); add_indexed_point(m_indices_of_points_of_face[2]); add_indexed_point(m_indices_of_points_of_face[3]); } else { // (1) add points add_point(m_points_of_face[0]); add_point(m_points_of_face[1]); add_point(m_points_of_face[2]); add_point(m_points_of_face[0]); add_point(m_points_of_face[2]); add_point(m_points_of_face[3]); // (2) Add flat and smooth normals and color for(unsigned int i=0; i<6; ++i) { if (m_started_face_is_colored) { add_color(m_color_of_face); } add_flat_normal(normal); if (m_vertex_normals_for_face.size()==0) { add_gouraud_normal(normal); } } if (m_vertex_normals_for_face.size()>0) { add_gouraud_normal(m_vertex_normals_for_face[0]); add_gouraud_normal(m_vertex_normals_for_face[1]); add_gouraud_normal(m_vertex_normals_for_face[2]); add_gouraud_normal(m_vertex_normals_for_face[0]); add_gouraud_normal(m_vertex_normals_for_face[2]); add_gouraud_normal(m_vertex_normals_for_face[3]); } } } void convex_face_end_internal(const Local_vector& normal) { for(std::size_t i=1; i0) { add_indexed_point(m_indices_of_points_of_face[0]); add_indexed_point(m_indices_of_points_of_face[i]); add_indexed_point(m_indices_of_points_of_face[i+1]); } else { Local_point& p0 = m_points_of_face[0]; Local_point& p1 = m_points_of_face[i]; Local_point& p2 = m_points_of_face[i+1]; // (1) add points add_point(p0); add_point(p1); add_point(p2); // (2) Add flat normal and color for(unsigned int j=0; j<3; ++j) { if (m_started_face_is_colored) { add_color(m_color_of_face); } add_flat_normal(normal); if (m_vertex_normals_for_face.size()==0) { add_gouraud_normal(normal); } // No smooth normal, we use the flat one instead } // (3) Add smooth normals if they exist if (m_vertex_normals_for_face.size()>0) { add_gouraud_normal(m_vertex_normals_for_face[0]); add_gouraud_normal(m_vertex_normals_for_face[i]); add_gouraud_normal(m_vertex_normals_for_face[i+1]); } } } } void nonconvex_face_end_internal(const Local_vector& normal) { try { P_traits cdt_traits(normal); CDT cdt(cdt_traits); bool with_vertex_normal=(m_vertex_normals_for_face.size()==m_points_of_face.size()); // (1) We insert all the edges as contraint in the CDT. typename CDT::Vertex_handle previous=NULL, first=NULL; for (unsigned int i=0; iinfo().v=m_vertex_normals_for_face[i]; } else { vh->info().v=normal; } if (m_indices_of_points_of_face.size()>0) { vh->info().index=m_indices_of_points_of_face[i]; } if(previous!=NULL && previous!=vh) { cdt.insert_constraint(previous, vh); } previous=vh; } if (previous!=NULL && previous!=first) { cdt.insert_constraint(previous, first); } // (2) We mark all external triangles // (2.1) We initialize is_external and is_process values for(typename CDT::All_faces_iterator fit = cdt.all_faces_begin(), fitend = cdt.all_faces_end(); fit!=fitend; ++fit) { fit->info().is_external = true; fit->info().is_process = false; } // (2.2) We check if the facet is external or internal std::queue face_queue; typename CDT::Face_handle face_internal = NULL; if (cdt.infinite_vertex()->face()!=NULL) { face_queue.push(cdt.infinite_vertex()->face()); } while(!face_queue.empty()) { typename CDT::Face_handle fh = face_queue.front(); face_queue.pop(); if(!fh->info().is_process) { fh->info().is_process = true; for(int i=0; i<3; ++i) { if(!cdt.is_constrained(std::make_pair(fh, i))) { if (fh->neighbor(i)!=NULL) { face_queue.push(fh->neighbor(i)); } } else if (face_internal==NULL) { face_internal = fh->neighbor(i); } } } } if ( face_internal!=NULL ) { face_queue.push(face_internal); } while(!face_queue.empty()) { typename CDT::Face_handle fh = face_queue.front(); face_queue.pop(); if(!fh->info().is_process) { fh->info().is_process = true; fh->info().is_external = false; for(unsigned int i=0; i<3; ++i) { if(!cdt.is_constrained(std::make_pair(fh, i))) { if (fh->neighbor(i)!=NULL) { face_queue.push(fh->neighbor(i)); } } } } } // (3) Now we iterates on the internal faces to add the vertices // and the normals to the appropriate vectors for(typename CDT::Finite_faces_iterator ffit=cdt.finite_faces_begin(), ffitend = cdt.finite_faces_end(); ffit!=ffitend; ++ffit) { if(!ffit->info().is_external) { for(unsigned int i=0; i<3; ++i) { // Add indices when they exist if (m_indices_of_points_of_face.size()>0) { add_indexed_point(ffit->vertex(i)->info().index); } else { // (1) add point add_point(ffit->vertex(i)->point()); // (2) Add face color if (m_started_face_is_colored) { add_color(m_color_of_face); } // (3) Add flat normal add_flat_normal(normal); // (4) Add smooth normals (or flat if smooth normals do not exist) add_gouraud_normal(ffit->vertex(i)->info().v); } } } } } catch(...) { // Triangulation crash: the face is not filled std::cerr<<"Catch: face not filled."< void add_flat_normal(const KVector& kv) { if(m_flat_normal_buffer != NULL) { add_normal_in_buffer(kv, *m_flat_normal_buffer); } } template void add_gouraud_normal(const KVector& kv) { if(m_gouraud_normal_buffer != NULL) { add_normal_in_buffer(kv, *m_gouraud_normal_buffer); } } protected: // Types usefull for triangulation struct Vertex_info { Local_vector v; std::size_t index; }; struct Face_info { bool exist_edge[3]; bool is_external; bool is_process; }; typedef CGAL::Triangulation_2_projection_traits_3 P_traits; typedef CGAL::Triangulation_vertex_base_with_info_2 Vb; typedef CGAL::Triangulation_face_base_with_info_2 Fb1; typedef CGAL::Constrained_triangulation_face_base_2 Fb; typedef CGAL::Triangulation_data_structure_2 TDS; typedef CGAL::Exact_predicates_tag Itag; typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; protected: std::vector* m_pos_buffer; std::vector* m_index_buffer; std::vector* m_color_buffer; std::vector* m_flat_normal_buffer; std::vector* m_gouraud_normal_buffer; CGAL::Bbox_3* m_bb; // Local variables, used when we started a new face. bool m_face_started; bool m_started_face_is_colored; bool m_started_face_has_normal; std::vector m_points_of_face; std::vector m_vertex_normals_for_face; std::vector m_indices_of_points_of_face; CGAL::Color m_color_of_face; Local_vector m_normal_of_face; }; } // End namespace CGAL #endif // CGAL_VBO_BUFFER_FILLER_H