#pragma once #include "Vector.h" #include #include #include #include #include #include "fmt/format.h" namespace Aftr { //Author Scott Nykl // // BLUF: Use the make_iter_* methods declared at the *bottom* of this file -- a user will rarely have to use // any of the classes/types defined here directly. Just expect iterator semantics and things should work. // // Example: // // //NOTE: use the non-const make_*_iter functions to mutate the vertices if each triangle. // // auto idx = getIndexData(); //returns a std::vector // auto vts = getVertData(); //returns a std::vector // auto it = make_begin_const_iter( vts, idx ); //iterator pointing to beginning of triangles // auto end = make_end_const_iter( vts, idx ); //end iterator // fmt::print( ss, "Number of Triangles {:d}\n", it.numTriangles() ); // fmt::print( ss, "Number of Indices {:d}\n", it.numIndices() ); // fmt::print( ss, "Number of Vertices {:d}\n", it.numVertices() ); // fmt::print( ss, "Number of Triangles in [it,end) {:d}\n", std::distance( it, end ) ); // for( auto it = make_begin_const_iter( vts, idx ); it != end; it++ ) // { // fmt::print( ss, "Visiting Triangle {:s}", it->toString() ); // } // // ----- Iterate over a WO, like the F16 model loaded from disk // auto begin = Aftr::make_begin_iter( f16a->getModel()->getCompositeVertexList(), f16a->getModel()->getCompositeIndexList() ); // auto end = Aftr::make_end_iter( f16a->getModel()->getCompositeVertexList(), f16a->getModel()->getCompositeIndexList() ); // for( int i = 0; begin != end; begin++, i++ ) // { // fmt::print( "Triangle {:d} is {:s}\n", i, begin->toString() ); // } // // If the user wants to create an iterator around RAW pointers, ie Vector* and GLshort*, for example, // one must wrap the raw pointer with a lightweight ptr_iterVert_wrapper or ptr_iterIdx_wrapper. The // simplest way to do this is to call the make_*_iter_raw_ptr(...) functions defined at the bottom of // this file. // // This files provides a generic set of functions to iterate across indexed geometry. // Indexed Geometry is a set of Vertices that are indexed via a list of indices. // It is assumed the Vertices are in one random access list and the indices // are in another random access list. This could be two std::vectors of various types or just raw pointers. // See the helper make_iter_* at the end of this file. // See engine/src/aftr/gtest/AftrUtil_iter_test.cpp for simple examples on how to walk across // triangles that are fed to OpenGL. //This type_trait is used to determine the constness of the in passed std container iterators. // //USAGE -- Computes if an STL iterator is const_iterator or iterator: // Sample Usage: // using VI = std::vector::iterator; // using CVI = std::vector::const_iterator; // static_assert(!is_const_iterator_v, ""); // VI is NOT const // static_assert(is_const_iterator_v, ""); // CVI *is* const // // //https://stackoverflow.com/questions/16498674/check-modify-iterator-constness template struct is_const_iterator : std::false_type { }; template struct is_const_iterator::pointer >::type >::value >::type> : std::true_type { }; template inline constexpr bool is_const_iterator_v = is_const_iterator::value; //A simple copy of a Tri_ref or const_Tri_ref. Since this is a copy, the user //owns the data and it no longer references into the Mesh. This is a simple //helper class that may be useful to a user. template< typename VEC3 > struct Tri { VEC3 va; VEC3 vb; VEC3 vc; constexpr std::string toString() const { return fmt::format( "[ {:s}->{:s}->{:s} ]", va.toString( 3 ), vb.toString( 3 ), vc.toString( 3 ) ); } }; //A Single Triangle (mutable) within a Mesh -- stores mutable references to the //three Vertices comprising the triangle's corners. Includes helper methods to //print, etc. VEC3 is a generic type but is most commonly expected to be a //VectorT& such as a Vector& or VectorD&. template struct Tri_ref { using value_type = VEC3&; using pointer = VEC3*; //used inside Face_Iter operator*() value_type va; value_type vb; value_type vc; constexpr std::string toString() const { return fmt::format( "[ {:s}->{:s}->{:s} ]", va.toString( 3 ), vb.toString( 3 ), vc.toString( 3 ) ); } constexpr Tri toTri() const noexcept { return Tri{ va,vb,vc }; } }; //A Single Triangle (non-mutable / const) within a Mesh -- stores const references to the //three Vertices comprising the triangle's corners. Includes helper methods to //print, etc. VEC3 is a generic type but is most commonly expected to be a //VectorT& such as a Vector& or VectorD&. template struct const_Tri_ref { using value_type = VEC3 const&; using pointer = VEC3 const*; //used inside Face_Iter operator*() value_type va; value_type vb; value_type vc; constexpr std::string toString() const { return fmt::format( "[ {:s}->{:s}->{:s} ]", va.toString( 3 ), vb.toString( 3 ), vc.toString( 3 ) ); } constexpr Tri toTri() const noexcept { return Tri{ va, vb, vc }; } }; //Simple tag to unambiguously differentiate between creating a begin iterator //and an end iterator. namespace Face_of_tris_iterator_detail { enum class END_ITER_FLAG { end }; } //When the user iterators over indexed geometry using RAW POINTERS, this is a lightweight //proxy class that wraps the raw pointer to the vertices to provide the internally //desired operator[] semantics for uniform access to the vertices. A user may //only use these when manually creating an iterator. See make_begin_iter_raw() or //make_end_iter_raw below for a usage example. template< typename ptr_to_array > struct ptr_iterVert_wrapper : public boost::stl_interfaces::iterator_interface < ptr_iterVert_wrapper, //CRTP, static compile-time inheritance std::random_access_iterator_tag, //IteratorConcept used for this iterator std::remove_pointer_t //this is the value_type > { private: ptr_to_array ptr; public: using pointer = ptr_to_array; using value_type = std::remove_pointer_t; using reference = value_type&; constexpr ptr_iterVert_wrapper() noexcept : ptr( nullptr ) {} constexpr ptr_iterVert_wrapper( ptr_to_array ptr ) noexcept : ptr( ptr ) {} constexpr ptr_to_array const& operator*() const noexcept { return ptr; } constexpr ptr_to_array& operator*() noexcept { return ptr; } constexpr reference const& operator[]( size_t idx ) const { pointer va = this->ptr + idx; return *va; } constexpr reference operator[]( size_t idx ) { pointer va = this->ptr + idx; //auto will be some type of VectorT return *va; } constexpr ptr_iterVert_wrapper& operator+=( std::ptrdiff_t i ) noexcept { ptr += i; return *this; } constexpr auto operator-( ptr_iterVert_wrapper other ) const noexcept { return ptr - other.ptr; } using base_type = boost::stl_interfaces::iterator_interface < ptr_iterVert_wrapper, //make base_type visible in derived subclass std::random_access_iterator_tag, std::remove_pointer_t >; using base_type::operator++; }; //When the user iterators over indexed geometry using RAW POINTERS, this is a lightweight //proxy class that wraps the raw pointer to the indices to provide the internally //desired operator[] semantics for uniform access to the indices. A user may //only use these when manually creating an iterator. See make_begin_iter_raw() or //make_end_iter_raw below for a usage example. template< typename ptr_to_array > struct ptr_iterIdx_wrapper : public boost::stl_interfaces::iterator_interface < ptr_iterIdx_wrapper, //CRTP, static compile-time inheritance std::random_access_iterator_tag, //IteratorConcept used for this iterator std::remove_pointer_t > { private: ptr_to_array ptr; public: using pointer = ptr_to_array; using value_type = std::remove_pointer_t; constexpr ptr_iterIdx_wrapper() noexcept : ptr( nullptr ) {} constexpr ptr_iterIdx_wrapper( ptr_to_array ptr ) noexcept : ptr( ptr ) {} constexpr ptr_to_array const& operator*() const noexcept { return ptr; } constexpr ptr_to_array& operator*() noexcept { return ptr; } //This particular iter wraps a pointer. This dereference operator returns //the idx'th element inside the array pointed to by the wrapped pointer. constexpr auto operator[]( size_t idx ) const { return this->ptr[idx]; } constexpr auto operator[]( size_t idx ) { return this->ptr[idx]; } constexpr ptr_iterIdx_wrapper& operator+=( std::ptrdiff_t i ) noexcept { ptr += i; return *this; } constexpr auto operator-( ptr_iterIdx_wrapper other ) const noexcept { return ptr - other.ptr; } using base_type = boost::stl_interfaces::iterator_interface < ptr_iterIdx_wrapper, //make base_type visible in derived subclass std::random_access_iterator_tag, std::remove_pointer_t >; using base_type::operator++; }; //This is the PRIMARY CLASS within this file. To use this class, see the make_*_iter method defined //at the bottom of this file. //This iterator walks each triangle of a set of vertices in the order specified by the index list. This //iterators over a canonical list of OpenGL-esque indexed geometry. // Assumes a set of parallel arrays (contiguous memory w/ size) for verts, colors, normals, tangents, etc. // Assumes a integral index list that indexes into the parallel arrays to describe all // properties of Vertex N at indexLst[N] // This uses boot's stl_interface wrapper to write stl compliant iterators //https://github.com/boostorg/stl_interfaces/blob/develop/example/node_iterator.cpp and //https://www.boost.org/doc/libs/1_80_0/doc/html/boost_stlinterfaces/tutorial___iterator_interface_.html template< typename Vert_Iter, typename Idx_Iter > struct Face_of_tris_iterator : public boost::stl_interfaces::iterator_interface < Face_of_tris_iterator, //CRTP, static compile-time inheritance std::random_access_iterator_tag, //IteratorConcept used for this iterator //The template expression below evaluates to the value_type for our iterator. //If the Vert_Iter is const, then the value_type of a dereference will be a const_Tri_ref. //If the Vert_Iter is non-const, then the value_type of a dereference will be a Tri_ref. std::conditional_t< is_const_iterator_v, const_Tri_ref< typename Vert_Iter::value_type >, Tri_ref< typename Vert_Iter::value_type > > > { using Tri_refVEC3 = std::conditional_t< is_const_iterator_v, const_Tri_ref< typename Vert_Iter::value_type >, Tri_ref< typename Vert_Iter::value_type > >; using reference = Tri_refVEC3; constexpr Face_of_tris_iterator( Vert_Iter vert_iter_begin, Vert_Iter vert_iter_end, Idx_Iter idx_iter_begin, Idx_Iter idx_iter_end ) noexcept : vert_it( vert_iter_begin ), vert_it_end( vert_iter_end ), idx_it( idx_iter_begin ), idx_it_end( idx_iter_end ) { //creating a begin iterator with nullptr containers -- this is an invalid iterator, but shouldn't crash //if the user passes in any nullptr containers, we set the begin iterator's idx to -1 (which is also //used by the end iterator) -- this way, a loop will not crash or perform UB, it will simply never //iterate if( vert_it == vert_it_end ) this->idx = -1; } constexpr Face_of_tris_iterator( Face_of_tris_iterator_detail::END_ITER_FLAG, Vert_Iter vert_iter_begin, Vert_Iter vert_iter_end, Idx_Iter idx_iter_begin, Idx_Iter idx_iter_end ) noexcept : vert_it( vert_iter_begin ), vert_it_end( vert_iter_end ), idx_it( idx_iter_begin ), idx_it_end( idx_iter_end ) { if( vert_it == vert_it_end ) this->idx = -1; else this->idx = std::distance( idx_it, idx_it_end ); //one idx larger than valid index } // Implicit conversion from an existing random_access_iterator with a // possibly different value type. The enable_if logic here just enforces // that this constructor only participates in overload resolution when the // expression it_ = other.it_ is well-formed. //template< // typename Vert_Iter, typename Idx_Iter, // typename E = std::enable_if_t< // std::is_convertible::value> > ////copy constructor constexpr Face_of_tris_iterator( Face_of_tris_iterator const& toCopy ) noexcept : vert_it( toCopy.vert_it ), vert_it_end( toCopy.vert_it_end ), idx_it( toCopy.idx_it ), idx_it_end( toCopy.idx_it_end ), idx( toCopy.idx ) { } constexpr Face_of_tris_iterator& operator=( Face_of_tris_iterator const& toAssign ) { if( this != &toAssign ) [[likely]] { vert_it = toAssign.vert_it; vert_it_end = toAssign.vert_it_end; idx_it = toAssign.idx_it; idx_it_end = toAssign.idx_it_end; idx = toAssign.idx; } return *this; } //When the iterator is dereferenced: A non-const iterator returns a Tri_ref. A const iterator // returns a const_Tri_ref. Internally, a [const_]Tri_ref contains three references to // VectorT [const]&. constexpr Tri_refVEC3& operator*() const noexcept { auto ia = (this->idx_it)[idx + 0]; auto ib = (this->idx_it)[idx + 1]; auto ic = (this->idx_it)[idx + 2]; this->curTri.emplace( (this->vert_it)[ia], (this->vert_it)[ib], (this->vert_it)[ic] ); return *curTri; } //This iterator operators in terms of Triangles. So incrementing by ++ will move to the //next triangle -- which is three indices higher. So internally, idx always grows by //three which is the next adjacent face in the index list. constexpr Face_of_tris_iterator& operator++() noexcept { this->idx += 3; return *this; } //Moves the iterator forward by i Triangles. constexpr Face_of_tris_iterator& operator+=( std::ptrdiff_t i ) noexcept { //random access "move forward by i triangles". Which maps to i * 3 indices in the index list this->idx += i * 3; if( this->idx_it + idx > this->idx_it_end ) [[unlikely]] { fmt::print( "Walked beyond end of index list -- ilist[{:d}]\n", this->idx ); std::abort(); } return *this; } //Returns the number of triangles (distance) between this iterator and other. constexpr auto operator-( Face_of_tris_iterator other ) const noexcept { //returns the distance between this and other std::ptrdiff_t num_triangles_between = (this->idx - other.idx) / 3; if( other.idx_it != this->idx_it ) [[unlikely]] { fmt::print( "ERROR: Two iterators are from different spans.\n" ); std::abort(); } return num_triangles_between; } //Always returns the number of triangles between the initially passed begin and end iterators. //For any given instance of this Face iterator, this value will always be the same. constexpr auto numTriangles() const noexcept { return std::distance( idx_it, idx_it_end ) / 3; } //Always returns the number of indices contained in the initially passed begin and end iterators. //For any given instance of this Face iterator, this value will always be the same. constexpr auto numIndices() const noexcept { return std::distance( idx_it, idx_it_end ); } //Always returns the number of Vertices (x,y,z triplets) contained in the initially //passed begin and end iterators. //For any given instance of this Face iterator, this value will always be the same. constexpr auto numVertices() const noexcept { return std::distance( vert_it, vert_it_end ); } //template< // typename Vert_Iter, typename Idx_Iter, // typename E = std::enable_if_t< // std::is_convertible::value> > friend constexpr bool operator==( Face_of_tris_iterator lhs, Face_of_tris_iterator rhs ) noexcept { return lhs.idx == rhs.idx; } // node_iterator_using_declaration using base_type = boost::stl_interfaces::iterator_interface < Face_of_tris_iterator, //make base_type visible in derived subclass std::random_access_iterator_tag, Tri_refVEC3 >; using base_type::operator++; private: Vert_Iter vert_it; Vert_Iter vert_it_end; Idx_Iter idx_it; Idx_Iter idx_it_end; mutable std::optional< Tri_refVEC3 > curTri; //evaluates to either Tri_ref or const_Tri_ref> std::int64_t idx = 0; // This friendship is necessary to enable the implicit conversion // constructor above to work. template< typename Const_Vert_Iter, typename Const_Idx_Iter> friend struct Face_of_tris_iterator; }; //Helpful user functions to traverse via stl-compliant iterators //----------------- Iterator-based const and non-const factory make_iter funcs ///Returns a BEGIN iterator to first triangle in collection. ///If all in passed iterators are non-const, returned iterater is non-const. ///If all in passed iterators are const, returned iterator is const. /// If your data is stored in a std::vector> verts and std::vector indices, /// consider using the simpler make_*_iter( std::vector<...> [const]& verts, std::vector<...> [const]& indices ) template< typename Vert_Iter_m, typename Idx_Iter_m> auto make_begin_iter( Vert_Iter_m vert_begin_iter, Vert_Iter_m vert_end_iter, Idx_Iter_m idx_begin_iter, Idx_Iter_m idx_end_iter ) { return Face_of_tris_iterator( vert_begin_iter, vert_end_iter, idx_begin_iter, idx_end_iter ); } ///Returns an END iterator beyond the last triangle in collection. ///If all in passed iterators are non-const, returned iterater is non-const. ///If all in passed iterators are const, returned iterator is const. /// If your data is stored in a std::vector> verts and std::vector indices, /// consider using the simpler make_*_iter( std::vector<...> [const]& verts, std::vector<...> [const]& indices ) template< typename Vert_Iter_m, typename Idx_Iter_m> auto make_end_iter( Vert_Iter_m vert_begin_iter, Vert_Iter_m vert_end_iter, Idx_Iter_m idx_begin_iter, Idx_Iter_m idx_end_iter ) { return Face_of_tris_iterator( Face_of_tris_iterator_detail::END_ITER_FLAG{}, vert_begin_iter, vert_end_iter, idx_begin_iter, idx_end_iter ); } //----------------- //----------------- Iterator-based const and non-const factory make_iter funcs FOR RAW POINTERS ///Returns a BEGIN iterator to first triangle in collection. ///If all in passed iterators are non-const, returned iterater is non-const. ///If all in passed iterators are const, returned iterator is const. /// If your data is stored in a std::vector> verts and std::vector indices, /// consider using the simpler make_*_iter( std::vector<...> [const]& verts, std::vector<...> [const]& indices ) template< typename Vert_Ptr, typename Idx_Ptr> auto make_begin_iter_raw( Vert_Ptr vert_begin, Vert_Ptr vert_end, Idx_Ptr idx_begin, Idx_Ptr idx_end ) { return Face_of_tris_iterator( ptr_iterVert_wrapper(vert_begin), ptr_iterVert_wrapper( vert_end ), ptr_iterIdx_wrapper( idx_begin ), ptr_iterIdx_wrapper( idx_end ) ); } ///Returns an END iterator beyond the last triangle in collection. ///If all in passed iterators are non-const, returned iterater is non-const. ///If all in passed iterators are const, returned iterator is const. /// If your data is stored in a std::vector> verts and std::vector indices, /// consider using the simpler make_*_iter( std::vector<...> [const]& verts, std::vector<...> [const]& indices ) template< typename Vert_Ptr, typename Idx_Ptr> auto make_end_iter_raw( Vert_Ptr vert_begin, Vert_Ptr vert_end, Idx_Ptr idx_begin, Idx_Ptr idx_end ) { return Face_of_tris_iterator( Face_of_tris_iterator_detail::END_ITER_FLAG{}, ptr_iterVert_wrapper( vert_begin ), ptr_iterVert_wrapper( vert_end ), ptr_iterIdx_wrapper( idx_begin ), ptr_iterIdx_wrapper( idx_end ) ); } //----------------- //----------------- std::vector<...>-based const and non-const factory make_iter funcs ///Returns a BEGIN iterator that will traverse the triangles specified ///in the vertex & index lists passed in. If the containers are const, the iterator /// **WILL be CONST**. If the containers are mutable, this method will return a /// mutable iterator allowing the user to mutate the triangles. template< typename Vert_Cont_m, typename Idx_Cont_m > auto make_begin_iter( Vert_Cont_m& vert_container, Idx_Cont_m& idx_container ) { return Face_of_tris_iterator( vert_container.begin(), vert_container.end(), idx_container.begin(), idx_container.end() ); } ///Returns an END iterator pointing beyond the last triangle specified ///in the vertex & index lists passed in. If the containers are const, the iterator /// **WILL be CONST**. If the containers are mutable, this method will return a /// mutable iterator allowing the user to mutate the triangles template< typename Vert_Cont_m, typename Idx_Cont_m > auto make_end_iter( Vert_Cont_m& vert_container, Idx_Cont_m& idx_container ) { return Face_of_tris_iterator( Face_of_tris_iterator_detail::END_ITER_FLAG{}, vert_container.begin(), vert_container.end(), idx_container.begin(), idx_container.end() ); } //----------------- //----------------- ///Returns a CONST BEGIN iterator over STL container template< typename Vert_Cont_m, typename Idx_Cont_m > auto make_begin_const_iter( Vert_Cont_m& vert_container, Idx_Cont_m& idx_container ) { return Face_of_tris_iterator( vert_container.cbegin(), vert_container.cend(), idx_container.cbegin(), idx_container.cend() ); } ///Returns a CONST END iterator over STL container template< typename Vert_Cont_m, typename Idx_Cont_m > auto make_end_const_iter( Vert_Cont_m& vert_container, Idx_Cont_m& idx_container ) { return Face_of_tris_iterator( Face_of_tris_iterator_detail::END_ITER_FLAG{}, vert_container.cbegin(), vert_container.cend(), idx_container.cbegin(), idx_container.cend() ); } //----------------- }