/*
								+----------------------------------+
								|                                  |
								|  ***  Polygonal mesh class  ***  |
								|                                  |
								|   Copyright © -tHE SWINe- 2008   |
								|                                  |
								|            PolyMesh.h            |
								|                                  |
								+----------------------------------+
*/

#pragma once
#ifndef __POLYGON_MESH_INCLUDED
#define __POLYGON_MESH_INCLUDED

/**
 *	@file dev/PolyMesh.h
 *	@date 2008
 *	@author -tHE SWINe-
 *	@brief polygonal mesh class
 *
 *	@date 2008-06-04
 *
 *	fixed error in CPolyMesh::BoundingBox() (it returned minimal corner coordinates in maximum)
 *
 *	conducted series of experiments to find out what is the most efficient vertex hash
 *	cell size. it was found that for unit sphere with 655362 vertices there is ratio
 *	2:3 between required vertices per cell and real number of vertices / cell. this ratio
 *	is however independent of vertex cell size. timing of vertex hash / vertex merge table
 *	operations is independent as well, aparts from CVertexMergeTable constructor which
 *	runs in linear time proportional to number of vertices / cell. memory trashing
 *	is logarithmic, the bend is approximately at 11 so this value has been chosen as default.
 *
 *	@date 2008-06-24
 *
 *	renamed CPolygon2::r_t_Vertex() and CPolyMesh::r_t_Vertex() to
 *	CPolygon2::r_Vertex() and CPolyMesh::r_Vertex() respectively
 *
 *	added vertex color comparison to CPolyMesh::CVertexMergeTable() and dependent functions
 *	(CPolyMesh::OptimizeVertices(), CPolyMesh::CalcVertexNormals_Thresh())
 *
 *	@date 2008-08-08
 *
 *	added \#ifdef for windows 64, added \#define for GL_GLEXT_LEGACY (for linux builds)
 *
 *	@date 2009-05-23
 *
 *	removed all instances of std::vector::reserve and replaced them by stl_ut::Reserve_*
 *
 *	@date 2009-10-20
 *
 *	fixed some warnings when compiling under VC 2005, implemented "Security
 *	Enhancements in the CRT" for VC 2008. compare against MyProjects_2009-10-19_
 *
 *	@date 2012-06-19
 *
 *	Moved multiple inclusion guard before file documentation comment.
 *
 */

#include "MyVertex.h"
#include "VertexPool.h"
#include "Polygon.h"

/*
 *								=== CPolyMesh ===
 */

class CPolyMesh {
public:
	typedef uint32_t TPolygonFlags;
	typedef int TMaterialId;
	typedef TMyVertex TVertex;
	typedef TRefVertex2<TVertex> TRefVertex;
	typedef CPolygon2<TRefVertex, TPolygonFlags, TMaterialId> CPolygon;

	/*
	 *	class CTransform
	 *		- utility class for transforming (the whole)
	 *		  mesh using CVertexPool::ForEach
	 */
	class CTransform {
	protected:
		Matrix4f m_t_transform;

	public:
		inline CTransform(const Matrix4f &r_t_transform)
			:m_t_transform(r_t_transform)
		{}

		inline void operator ()(TVertex *p_vertex) const
		{
			p_vertex->v_position = m_t_transform.v_Transform_Pos(p_vertex->v_position);
		}
	};

protected:
	std::vector<CPolygon*> m_poly_list;
	CVertexPool<TVertex> m_vertex_list;

public:
	/*
	 *	CPolyMesh::CPolyMesh()
	 *		- default constructor, creates empty mesh
	 */
	CPolyMesh();

	/*
	 *	CPolyMesh::CPolyMesh(size_t n_vertex_num, size_t n_polygon_num)
	 *		- allocates clear mesh with n_vertex_num vertices and n_polygon_num polygons
	 *		- if it fails, it's guaranteed there are no vertices nor polygons
	 */
	CPolyMesh(size_t n_vertex_num, size_t n_polygon_num);

	/*
	 *	CPolyMesh::CPolyMesh(const CPolyMesh &r_t_mesh)
	 *		- copy-constructor
	 *		- if it fails, it's guaranteed there are no vertices nor polygons
	 */
	CPolyMesh(const CPolyMesh &r_t_mesh);

	/*
	 *	CPolyMesh::~CPolyMesh()
	 *		- destructor
	 */
	~CPolyMesh();

	/*
	 *	void CPolyMesh::Swap(CPolyMesh &r_poly_mesh)
	 *		- swap contents of this mesh and r_poly_mesh
	 */
	void Swap(CPolyMesh &r_poly_mesh);

	/*
	 *	bool CPolyMesh::Alloc(size_t n_vertex_num, size_t n_polygon_num)
	 *		- allocates clear mesh with n_vertex_num vertices and n_polygon_num polygons,
	 *		  if there were any vertices or polygons before calling this, they will be deleted
	 *		- returns true on success, false on failure
	 *		- if it fails, it's guaranteed there are no vertices nor polygons
	 */
	bool Alloc(size_t n_vertex_num, size_t n_polygon_num);

	/*
	 *	bool CPolyMesh::Copy(const CPolyMesh &r_poly_mesh)
	 *		- copies contents of r_poly_mesh
	 *		- returns true on success, false on failure
	 *		- if it fails, it's guaranteed there are no vertices nor polygons
	 */
	bool Copy(const CPolyMesh &r_poly_mesh);

	/*
	 *	bool CPolyMesh::Merge(const CPolyMesh &r_poly_mesh)
	 *		- adds contents of r_poly_mesh to this mesh
	 *		- returns true on success, false on failure
	 *		- if it fails, this mesh might contain some of r_poly_mesh
	 *		  vertices and polygon (but not all of them)
	 */
	bool Merge(const CPolyMesh &r_poly_mesh);

	/*
	 *	bool CPolyMesh::Merge(const CPolyMesh &r_poly_mesh, Matrix4f t_transform)
	 *		- adds contents of r_poly_mesh to this mesh,
	 *		  with transformation given by t_transform
	 *		- returns true on success, false on failure
	 *		- if it fails, this mesh might contain some of r_poly_mesh
	 *		  vertices and polygon (but not all of them)
	 */
	bool Merge(const CPolyMesh &r_poly_mesh, Matrix4f t_transform);

	/*
	 *	inline CPolyMesh &CPolyMesh::operator =(const CPolyMesh &r_t_object)
	 *		- copy-operator
	 *		- if it fails, it's guaranteed there are no vertices nor polygons
	 *		- returns reference to this mesh
	 *		- note this just calls copy
	 */
	inline CPolyMesh &operator =(const CPolyMesh &r_t_object)
	{
		if(&r_t_object == this)
			return *this;
		// handle self-assignment

		Copy(r_t_object);
		return *this;
	}

	/*
	 *	inline bool CPolyMesh::operator +=(const CPolyMesh &r_t_object)
	 *		- merges this mesh with r_t_object
	 *		- returns true on success, false on failure (not enough memory)
	 *		- note this just calls Merge()
	 */
	inline bool operator +=(const CPolyMesh &r_t_object)
	{
		return Merge(r_t_object);
	}

	/*
	 *	bool BoundingBox(Vector3f &r_v_min, Vector3f &r_v_max) const
	 *		- finds mesh bounding box, output is in r_v_min and r_v_max
	 *		- in case there are no vertices, both r_v_min and r_v_max are set to (0, 0, 0)
	 *		  and function returns false; otherwise returns true.
	 */
	bool BoundingBox(Vector3f &r_v_min, Vector3f &r_v_max) const;

	/*
	 *	const CVertexPool<TVertex> &CPolyMesh::Vertex_Pool() const
	 *		- returns const reference to vertex list
	 */
	const CVertexPool<TVertex> &Vertex_Pool() const;

	/*
	 *	CVertexPool<TVertex> &CPolyMesh::Vertex_Pool()
	 *		- returns reference to vertex list (to create new vertices)
	 */
	CVertexPool<TVertex> &Vertex_Pool();

	/*
	 *	class CPolyMesh::CVertexHash
	 *		- vertex hash-table; associates position to list of nearby vertices
	 */
	class CVertexHash {
	protected:
		float m_f_cell_side, m_f_inv_cell_side;
		size_t m_n_cells_x;
		size_t m_n_cells_y;
		size_t m_n_cells_z;
		std::vector<TVertex*> *m_p_vertex_hash;

		template <class CPredicate, class CFunctor>
		class CForEachVertexAdaptor {
		protected:
			CPredicate m_pred;
			CFunctor m_functor;

		public:
			CForEachVertexAdaptor(CPredicate pred, CFunctor functor)
				:m_pred(pred), m_functor(functor)
			{}

			inline void operator ()(TVertex *p_vertex)
			{
				if(m_pred(p_vertex))
					m_functor(p_vertex);
			}

			inline CFunctor GetFunctor() const
			{
				return m_functor;
			}
		};

	public:
		/*
		 *	CPolyMesh::CVertexHash::CVertexHash(CPolyMesh &r_object, size_t n_vertices_per_node = 11)
		 *		- default constructor
		 *		- r_object is object to be hashed
		 *		- n_vertices_per_node is desired number of vertices per node
		 *		  (to determine uniform grid size)
		 *		- call b_Status() to determine wheter constructor was successful
		 *		- note default of 11 vertices / node was chosen to be memory efficient
		 *		  while reasonably fast
		 */
		CVertexHash(CPolyMesh &r_object, size_t n_vertices_per_node = 11);

		/*
		 *	CPolyMesh::CVertexHash::~CVertexHash()
		 *		- default destructor
		 */
		~CVertexHash();

		/*
		 *	bool CPolyMesh::CVertexHash::b_Status() const
		 *		- returns true in case constructor succeeded and vertex hash
		 *		  is ready to be used, otherwise returns false
		 */
		bool b_Status() const;

		/*
		 *	const std::vector<TVertex*> &CPolyMesh::CVertexHash::r_Hash(Vector3f v_pos)
		 *		- returns list of vertices in cell corresponding to position v_pos
		 */
		const std::vector<TVertex*> &r_Hash(Vector3f v_pos);

		/*
		 *	inline size_t CPolyMesh::CVertexHash::n_Cell_X_Num() const
		 *		- returns grid resolution along x-axis
		 */
		inline size_t n_Cell_X_Num() const
		{
			return m_n_cells_x;
		}

		/*
		 *	inline size_t CPolyMesh::CVertexHash::n_Cell_Y_Num() const
		 *		- returns grid resolution along y-axis
		 */
		inline size_t n_Cell_Y_Num() const
		{
			return m_n_cells_y;
		}

		/*
		 *	inline size_t CPolyMesh::CVertexHash::n_Cell_Z_Num() const
		 *		- returns grid resolution along z-axis
		 */
		inline size_t n_Cell_Z_Num() const
		{
			return m_n_cells_z;
		}

		/*
		 *	float CPolyMesh::CVertexHash::f_Avg_CellOccupation() const
		 *		- returns average cell occupation
		 *		- returns -1 if there's too much vertices so they can't be counted
		 */
		float f_Avg_CellOccupation() const;

		/*
		 *	class CPolyMesh::CVertexHash::CVertexAlwaysPredicate
		 *		- predicate for ForEachVertex template function
		 *		- causes all vertices are iterated
		 */
		class CVertexAlwaysPredicate {
		public:
			inline bool operator ()(const TVertex *p_vertex) const
			{ return true; }
		};

		/*
		 *	class CPolyMesh::CVertexHash::CVertexPosPredicate
		 *		- predicate for ForEachVertex template function
		 *		- causes vertices close enough to given vertex are iterated
		 */
		class CVertexPosPredicate {
		protected:
			float m_f_epsilon_ex2;
			const TVertex &m_r_ref_vertex;

		public:
			/*
			 *	CPolyMesh::CVertexHash::CVertexPosPredicate::CVertexPosPredicate(
			 *		const TVertex &r_ref_vertex, float f_epsilon_ex = f_epsilon)
			 *		- default constructor; r_ref_vertex is reference vertex
			 *		- f_epsilon_ex is maximal allowed distance (note the grid cell overlap of
			 *		  vertex hash is limit to this distance)
			 */
			CVertexPosPredicate(const TVertex &r_ref_vertex, float f_epsilon_ex = f_epsilon)
				:m_r_ref_vertex(r_ref_vertex), m_f_epsilon_ex2(f_epsilon_ex * f_epsilon_ex)
			{}

			inline bool operator ()(const TVertex *p_vertex) const
			{ return (m_r_ref_vertex.v_position - p_vertex->v_position).f_Length2() <= m_f_epsilon_ex2; }
		};

		/*
		 *	class CPolyMesh::CVertexHash::CVertexGenPredicate
		 *		- predicate for ForEachVertex template function
		 *		- generic vertex comparison
		 */
		class CVertexGenPredicate {
		protected:
			bool m_b_compare_normal;
			bool m_b_compare_color;
			bool m_b_compare_texcoord;
			float m_f_epsilon_ex2;
			float m_f_normal_epsilon2;
			float m_f_color_epsilon2;
			float m_f_texcoord_epsilon2;
			const TVertex &m_r_ref_vertex;

		public:
			/*
			 *	CPolyMesh::CVertexHash::CVertexGenPredicate::CVertexGenPredicate(
			 *		const TVertex &r_ref_vertex, bool b_compare_normal,
			 *		bool b_compare_texcoord, float f_epsilon_ex, float f_normal_epsilon,
			 *		float f_texcoord_epsilon)
			 *		- default constructor; r_ref_vertex is reference vertex
			 *		- b_compare_normal is normal comparison flag
			 *		- b_compare_color is color comparison flag
			 *		- b_compare_texcoord is texture coordinate comparison flag
			 *		  (compares all coords of all texture units!)
			 *		- f_epsilon_ex is maximal allowed distance (note the grid cell overlap of
			 *		  vertex hash is limit to this distance)
			 *		- f_normal_epsilon is maximal allowed normal distance
			 *		- f_color_epsilon is maximal allowed color distance (euclidean)
			 *		- f_texcoord_epsilon is maximal allowed texcoord distance
			 */
			CVertexGenPredicate(const TVertex &r_ref_vertex,
				bool b_compare_normal, bool b_compare_color, bool b_compare_texcoord,
				float f_epsilon_ex, float f_normal_epsilon, float f_color_epsilon,
				float f_texcoord_epsilon)
				:m_r_ref_vertex(r_ref_vertex), m_b_compare_normal(b_compare_normal),
				m_b_compare_color(b_compare_color), m_b_compare_texcoord(b_compare_texcoord),
				m_f_epsilon_ex2(f_epsilon_ex * f_epsilon_ex),
				m_f_normal_epsilon2(f_normal_epsilon * f_normal_epsilon),
				m_f_color_epsilon2(f_color_epsilon * f_color_epsilon),
				m_f_texcoord_epsilon2(f_texcoord_epsilon * f_texcoord_epsilon)
			{}

			inline bool operator ()(const TVertex *p_vertex) const
			{
				if((m_r_ref_vertex.v_position -
				   p_vertex->v_position).f_Length2() > m_f_epsilon_ex2)
					return false;
				if(m_b_compare_normal && (m_r_ref_vertex.v_normal -
				   p_vertex->v_normal).f_Length2() > m_f_normal_epsilon2)
					return false;
				if(m_b_compare_color && (m_r_ref_vertex.v_color -
				   p_vertex->v_color).f_Length2() > m_f_color_epsilon2)
					return false;
				if(m_b_compare_texcoord) {
					const Vector4f *p_ref_texcoord = m_r_ref_vertex.p_texture_coord;
					const Vector4f *p_texcoord = p_vertex->p_texture_coord;
					for(int i = 0; i < TVertex::n_Texcoord_Num; ++ i) {
						if((p_ref_texcoord[i] - p_texcoord[i]).f_Length2() > m_f_texcoord_epsilon2)
							return false;
					}
				}
				return true;
			}
		};

		/*
		 *	template <class CPredicate, class CFunctor>
		 *	CFunctor CPolyMesh::CVertexHash::ForEachVertexNear(Vector3f v_pos,
		 *		CPredicate pred, CFunctor functor)
		 *		- for-each iterator for vertices near v_pos, filtered by vertex predicate pred
		 *		  and passed to function object functor
		 *		- returns the function object
		 */
		template <class CPredicate, class CFunctor>
		CFunctor ForEachVertexNear(Vector3f v_pos, CPredicate pred, CFunctor functor)
		{
			const std::vector<TVertex*> &r_vertex_list = r_Hash(v_pos);
			return std::for_each(r_vertex_list.begin(), r_vertex_list.end(),
				CForEachVertexAdaptor<CPredicate, CFunctor>(pred, functor)).GetFunctor();
		}
	};

	/*
	 *	class CPolyMesh::CVertexMergeTable
	 *		- vertex-merge table class
	 *		- essentialy table of equivalent vertices; use for
	 *		  face neighbour searching or vertex optimization
	 */
	class CVertexMergeTable {
	protected:
		std::vector<std::vector<TVertex*>*> m_table;
		CPolyMesh &m_r_object;
		bool m_b_status;

		class CGroupVertices;
		class CCreateIndexTable;

	public:
		/*
		 *	CPolyMesh::CVertexMergeTable::CVertexMergeTable(CPolyMesh &r_object,
		 *		CVertexHash &r_vertex_hash, bool b_compare_normal = false,
		 *		bool b_compare_color = false, bool b_compare_texcoord = true,
		 *		float f_epsilon_ex = f_epsilon, float f_normal_epsilon = f_epsilon,
		 *		float f_color_epsilon = f_epsilon, float f_texcoord_epsilon = f_epsilon)
		 *		- default constructor
		 *		- r_object is object for which this merge table is constructed
		 *		- r_vertex_hash is current vertex hash for the same object
		 *		- b_compare_normal is normal comparison flag
		 *		- b_compare_color is color comparison flag
		 *		- b_compare_texcoord is texture coordinate comparison flag
		 *		  (compares all coords of all texture units!)
		 *		- f_epsilon_ex is maximal allowed distance (note the grid cell overlap of
		 *		  vertex hash is limit to this distance)
		 *		- f_normal_epsilon is maximal allowed normal distance
		 *		- f_color_epsilon is maximal allowed color distance (euclidean)
		 *		- f_texcoord_epsilon is maximal allowed texcoord distance
		 *		- call b_Status() to see wheter constructor succeeded
		 */
		CVertexMergeTable(CPolyMesh &r_object, CVertexHash &r_vertex_hash,
			bool b_compare_normal = false, bool b_compare_color = false,
			bool b_compare_texcoord = true, float f_epsilon_ex = f_epsilon,
			float f_normal_epsilon = f_epsilon, float f_color_epsilon = f_epsilon,
			float f_texcoord_epsilon = f_epsilon);

		/*
		 *	CPolyMesh::CVertexMergeTable::~CVertexMergeTable
		 *		- default destructor
		 */
		~CVertexMergeTable();

		/*
		 *	bool CPolyMesh::CVertexMergeTable::b_Status() const
		 *		- returns true in case constructor succeeded and
		 *		  table is ready to use, otherwise returns false
		 */
		bool b_Status() const;

		/*
		 *	inline const std::vector<std::vector<TVertex*>*> &CPolyMesh::CVertexMergeTable::r_Table()
		 *		- returns merge table contents (list of lists of equivalent vertices)
		 */
		inline const std::vector<std::vector<TVertex*>*> &r_Table()
		{
			return m_table;
		}

		/*
		 *	inline size_t CPolyMesh::CVertexMergeTable::n_Group_Num() const
		 *		- returns number of groups of equivalent vertices
		 */
		inline size_t n_Group_Num() const
		{
			return m_table.size();
		}

		/*
		 *	size_t *CPolyMesh::CVertexMergeTable::p_VertexIndexTable() const
		 *		- generates list of indices as long as vertex list
		 *		- contains indices of vertex groups for each vertex in object
		 *		- returns pointer to index list or 0 in case there was not enough memory
		 */
		size_t *p_VertexIndexTable() const;

		/*
		 *	inline TVertex *CPolyMesh::CVertexMergeTable::p_FirstVertex(size_t n_index)
		 *		- returns pointer to first vertex of n_index-th group
		 */
		inline TVertex *p_FirstVertex(size_t n_index)
		{
			return m_table[n_index]->front();
		}

	protected:
		bool Create(CPolyMesh &r_object, CVertexHash &r_vertex_hash, bool b_compare_normal = false,
			bool b_compare_color = false, bool b_compare_texcoord = true, float f_epsilon_ex = f_epsilon,
			float f_normal_epsilon = f_epsilon, float f_color_epsilon = f_epsilon,
			float f_texcoord_epsilon = f_epsilon);
		static inline void DeleteVector(std::vector<TVertex*> *p_vector);
	};

	/*
	 *	inline size_t CPolyMesh::n_Vertex_Num() const
	 *		- returns number of mesh vertices
	 */
	inline size_t n_Vertex_Num() const
	{
		return m_vertex_list.n_Vertex_Num();
	}

	/*
	 *	inline const TVertex &CPolyMesh::r_Vertex(size_t n_index) const
	 *		- returns reference to vertex with index n_index
	 *		- do not assume vertices are in linear block of memory, they are not
	 */
	inline TVertex &r_Vertex(size_t n_index)
	{
		return *m_vertex_list[n_index];
	}

	/*
	 *	inline TRefVertex CPolyMesh::t_RefVertex(size_t n_index)
	 *		- returns reference vertex with index n_index
	 */
	inline TRefVertex t_RefVertex(size_t n_index)
	{
		return TRefVertex(m_vertex_list[n_index], m_vertex_list);
	}

	/*
	 *	inline const TVertex &CPolyMesh::t_Vertex(size_t n_index) const
	 *		- returns const reference to vertex with index n_index
	 *		- do not assume vertices are in linear block of memory, they are not
	 */
	inline const TVertex &t_Vertex(size_t n_index) const
	{
		return *m_vertex_list[n_index];
	}

	/*
	 *	inline const TVertex *CPolyMesh::p_Vertex(size_t n_index) const
	 *		- returns const pointer to vertex with index n_index
	 *		- do not assume vertices are in linear block of memory, they are not
	 */
	inline const TVertex *p_Vertex(size_t n_index) const
	{
		return m_vertex_list[n_index];
	}

	/*
	 *	inline size_t CPolyMesh::n_Poly_Num() const
	 *		- returns number of mesh polygons		
	 */
	inline size_t n_Poly_Num() const
	{
		return m_poly_list.size();
	}

	/*
	 *	inline CPolyMesh::CPolygon &CPolyMesh::r_Polygon(size_t n_index)
	 *		- returns reference to polygon with index n_index
	 *		- do not assume polygons are in linear block of memory, they are not
	 */
	inline CPolygon &r_Polygon(size_t n_index)
	{
		return *m_poly_list[n_index];
	}

	/*
	 *	inline const CPolyMesh::CPolygon &CPolyMesh::r_Polygon(size_t n_index) const
	 *		- returns const reference to polygon with index n_index
	 *		- do not assume polygons are in linear block of memory, they are not
	 */
	inline const CPolygon &r_Polygon(size_t n_index) const
	{
		return *m_poly_list[n_index];
	}

	/*
	 *	inline const CPolyMesh::CPolygon *CPolyMesh::p_Polygon(size_t n_index) const
	 *		- returns const pointer to polygon with index n_index
	 *		- do not assume polygons are in linear block of memory, they are not
	 */
	inline const CPolygon *p_Polygon(size_t n_index) const
	{
		return m_poly_list[n_index];
	}

	/*
	 *	std::vector<size_t> *CPolyMesh::p_VertexReference_Table(size_t *p_vertex_index_table,
	 *		const CVertexMergeTable &r_merge_table) const
	 *		- create table of vertex references
	 *		- table has same length as number of vertex groups
	 *		  (number of distinct vertex positions) and contains indices of all polygons
	 *		  referencing vertices in this group
	 *		- returns pointer to vertex reference table on success or 0 on failure
	 */
	std::vector<size_t> *p_VertexReference_Table(size_t *p_vertex_index_table,
		const CVertexMergeTable &r_merge_table) const;

	/*
	 *	bool CPolyMesh::OptimizeVertices(bool b_compare_normal = false,
	 *		bool b_compare_color = false, bool b_compare_texcoord = true)
	 *		- optimizes vertices (ie. merges all equivalent vertices)
	 *		- b_compare_normal is normal comparison flag
	 *		- b_compare_color is color comparison flag
	 *		- b_compare_texcoord is texcoord comparison flag
	 *		  (note all coords of all texture units are compared!)
	 *		- note all epsilons are equal to f_epsilon from Vector.cpp
	 *		- returns true on success, false on failure
	 */
	bool OptimizeVertices(bool b_compare_normal,
		bool b_compare_color, bool b_compare_texcoord);

	/*
	 *	bool CPolyMesh::ExplodeVertices()
	 *		- explodes vertices (ie. makes sure every face has three unique vertices)
	 *		- returns true on success, false on failure
	 */
	bool ExplodeVertices();

	/*
	 *	bool CPolyMesh::MakeTris()
	 *		- break down polygons to triangles
	 *		- returns true on success, false on failure
	 *		- note this removes degenerate polygons with < 3 vertices
	 */
	bool MakeTris();

	/*
	 *	bool CPolyMesh::CalcFaceNormals()
	 *		- calculates face normals
	 *		- returns false in case some polygons failed to
	 *		  calculate their normal (it is set to zero),
	 *		  otherwise returns true
	 */
	bool CalcFaceNormals();

	/*
	 *	void CPolyMesh::CalcVertexNormals_Simple()
	 *		- calculates vertex normals by averaging normals
	 *		  of faces which references the vertex
	 *		- note this does not make any changes to mesh topology
	 */
	void CalcVertexNormals_Simple();

	/*
	 *	bool CPolyMesh::CalcVertexNormals_Thresh(float f_angle_thresh = 30 * f_pi / 180)
	 *		- calculates vertex normals, normals of faces holding angle under
	 *		  f_angle_thresh are averaged only
	 *		- note this explodes vertices
	 */
	bool CalcVertexNormals_Thresh(float f_angle_thresh = 30 * f_pi / 180);

	/*
	 *	void CPolyMesh::DeleteVertices()
	 *		- helper func, deletes all vertices
	 */
	void DeleteVertices();

	/*
	 *	void CPolyMesh::DeletePolygons(size_t n_begin = 0, size_t n_end = -1)
	 *		- helper func, deletes polygons in range n_begin to n_end
	 */
	void DeletePolygons(size_t n_begin = 0, size_t n_end = -1);

protected:
	static inline void DeletePolygon(CPolygon *p_polygon);
};

/*
 *								=== ~CPolyMesh ===
 */

#endif // __POLYGON_MESH_INCLUDED
