/*
								+----------------------------------+
								|                                  |
								| ***  Simple spline surfaces  *** |
								|                                  |
								|   Copyright © -tHE SWINe- 2008   |
								|                                  |
								|           SplineSurf.h           |
								|                                  |
								+----------------------------------+
*/

#pragma once
#ifndef __SPLINE_SURFACE_INCLUDED
#define __SPLINE_SURFACE_INCLUDED

/**
 *	@file dev/SplineSurf.h
 *	@date 2008
 *	@author -tHE SWINe-
 *	@brief Simple spline surfaces
 *
 *	@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 "PolyMesh.h"
#include "PlatPrim.h"
#include "Spline.h"

#define SPLINE_SURF_GENERATE_TEXCOORDS
// if defined, 2D texture coordinates are automatically generated in channel 0
// when creating mesh, unless specified otherwise

/*
 *	class CSplineSurf
 *		- basic spline surface triangulator
 *		- note this doesn't triangulate spline patches, use ffd instead or write specialized
 *		  function, in überlame spline class there's no interface to evaluate equation for patch
 */
class CSplineSurf {
public:
	/*
	 *	static bool CSplineSurf::MakeExtrudeSurf(CPolyMesh &r_t_mesh,
	 *		const CSpline<Vector3f> &r_spline, Vector3f v_extrude, int n_tess_spline,
	 *		int n_tess_extrusion = 1, bool b_flip = false)
	 *		- creates surface of spline r_spline extruded in direction v_extrude
	 *		- n_tess_spline is number of points on spline and n_tess_extrusion is
	 *		  number of points along extrusion direction (over-tesselation)
	 *		- setting b_flip flips surface faces
	 *		- returns true on success, false on failure
	 */
	static bool MakeExtrudeSurf(CPolyMesh &r_t_mesh, const CSpline<Vector3f> &r_spline,
		Vector3f v_extrude, int n_tess_spline, int n_tess_extrusion = 1, bool b_flip = false);

	/*
	 *	static bool CSplineSurf::MakeRuledSurf(CPolyMesh &r_t_mesh,
	 *		const CSpline<Vector3f> &r_left_spline, const CSpline<Vector3f> &r_right_spline,
	 *		int n_tess_left_spline, int n_tess_right_spline, int n_tess_cross_section = 1,
	 *		bool b_flip = false)
	 *		- creates surface of moving line connecting points of r_left_spline with
	 *		  corrseponding points of r_right_spline (points at the same position on the spline)
	 *		- n_tess_left_spline is number of points on left spline, n_tess_right_spline
	 *		  is number of points on right spline and n_tess_cross_section is
	 *		  number of points along the line between splines (over-tesselation)
	 *		- setting b_flip flips surface faces
	 *		- returns true on success, false on failure
	 */
	static bool MakeRuledSurf(CPolyMesh &r_t_mesh, const CSpline<Vector3f> &r_left_spline,
		const CSpline<Vector3f> &r_right_spline, int n_tess_left_spline, int n_tess_right_spline,
		int n_tess_cross_section = 1, bool b_flip = false);

	/*
	 *	static bool CSplineSurf::MakeRevolutionSurf(CPolyMesh &r_t_mesh,
	 *		const CSpline<Vector2f> &r_spline, int n_tess_spline, int n_tess_radius,
	 *		bool b_bottom_cap, bool b_top_cap, bool b_weld_top_vertex = false,
	 *		bool b_weld_bottom_vertex = false, bool b_flip = false)
	 *		- creates surface of spline r_spline revolution arround
	 *		  y-axis and stores it to r_t_mesh
	 *		- n_tess_spline is number of points on spline and
	 *		  n_tess_radius is number of revolution steps
	 *		- b_bottom_cap or b_top_cap enables construction of flat
	 *		  cap on bottom or top end of spline, respectively
	 *		  (cap is triangle fan, perpendicular to y-axis)
	 *		- b_weld_top_vertex or b_weld_bottom_vertex tells the function
	 *		  spline top or bottom vertex, respectively, lies at y-axis
	 *		  and therefore is identical for all revolution steps, so
	 *		  top or bottom part of mesh, respectively, is triangle fan.
	 *		  note these options can not be used with b_bottom_cap
	 *		  or b_top_cap, respectively
	 *		- note 'bottom' refers to starting point of spline and 'top' to endpoint
	 *		- setting b_flip flips surface faces
	 *		- returns true on success, false on failure
	 */
	static bool MakeRevolutionSurf(CPolyMesh &r_t_mesh, const CSpline<Vector2f> &r_spline,
		int n_tess_spline, int n_tess_radius, bool b_bottom_cap, bool b_top_cap,
		bool b_weld_top_vertex = false, bool b_weld_bottom_vertex = false, bool b_flip = false);

	/*
	 *	static bool CSplineSurf::MakeRailSweep(CPolyMesh &r_t_mesh,
	 *		const CSpline<Vector3f> &r_left_rail, const CSpline<Vector3f> &r_right_rail,
	 *		const CSpline<Vector3f> &r_connect_spline, float f_connect_spline_position,
	 *		int n_tess_rail, int n_tess_connect_spline_begin, int n_tess_connect_spline_end,
	 *		bool b_flip = false, bool b_scale = true, bool b_unskew = false)
	 *		- creates surface by sweeping spline r_connect_spline connecting corresponding points
	 *		  on the left and the right spline (points at the same position on the spline)
	 *		- f_connect_spline_position is position where connection spline is attached to rails
	 *		- n_tess_rail is number of points on left and right spline (rails),
	 *		  n_tess_connect_spline_begin and n_tess_connect_spline_end is number of points
	 *		  on the connecting spline on the beginning and on the end of rails, respectively
	 *		- setting b_flip flips surface faces
	 *		- setting b_scale scales connection spline depending on distance it connects
	 *		- setting b_unskew forces orthogonal matrices for transforming spline position
	 *		  which may lead to some unexpected surface shapes
	 *		- returns true on success, false on failure
	 *		- note this doesn't calculate normals (lot of blending involved, it's cheaper
	 *		  to use CPolyMesh::CalcVertexNormals_Simple())
	 */
	static bool MakeRailSweep(CPolyMesh &r_t_mesh, const CSpline<Vector3f> &r_left_rail,
		const CSpline<Vector3f> &r_right_rail, const CSpline<Vector3f> &r_connect_spline,
		float f_connect_spline_position, int n_tess_rail, int n_tess_connect_spline_begin,
		int n_tess_connect_spline_end, bool b_flip = false, bool b_scale = true,
		bool b_unskew = false);

	/*
	 *	static bool CSplineSurf::MakeRailSweep2(CPolyMesh &r_t_mesh,
	 *		const CSpline<Vector3f> &r_left_rail, const CSpline<Vector3f> &r_right_rail,
	 *		const CSpline<Vector3f> &r_conn_spline_0, const CSpline<Vector3f> &r_conn_spline_1,
	 *		int n_tess_rail, int n_tess_connect_spline_begin, int n_tess_connect_spline_end,
	 *		bool b_flip = false, bool b_scale = true, bool b_unskew = false)
	 *		- creates surface by sweeping spline connecting corresponding points on left
	 *		  and right spline (points at the same position on the spline)
	 *		- r_conn_spline_0 and r_conn_spline_1 are morph targets of connection spline
	 *		  on the beginning and on the end of rails
	 *		- n_tess_rail is number of points on left and right spline (rails),
	 *		  n_tess_connect_spline_begin and n_tess_connect_spline_end is number of points
	 *		  on the connecting spline on the beginning and on the end of rails, respectively
	 *		- setting b_flip flips surface faces
	 *		- setting b_scale scales connection spline depending on distance it connects
	 *		- setting b_unskew forces orthogonal matrices for transforming spline position
	 *		  which may lead to some unexpected surface shapes
	 *		- returns true on success, false on failure
	 *		- note this doesn't calculate normals (lot of blending involved, it's cheaper
	 *		  to use CPolyMesh::CalcVertexNormals_Simple())
	 */
	static bool MakeRailSweep2(CPolyMesh &r_t_mesh, const CSpline<Vector3f> &r_left_rail,
		const CSpline<Vector3f> &r_right_rail, const CSpline<Vector3f> &r_conn_spline_0,
		const CSpline<Vector3f> &r_conn_spline_1, int n_tess_rail,
		int n_tess_connect_spline_begin, int n_tess_connect_spline_end, bool b_flip = false,
		bool b_scale = true, bool b_unskew = false);

	/*
	CapSurface (triangulate inside of closed curve)
	*/

	/*
	 *	static void CSplineSurf::RailBases(const CSpline<Vector3f> &r_left_spline,
	 *		const CSpline<Vector3f> &r_right_spline, float f_t, bool b_dist_scaling,
	 *		bool b_unskew, Matrix4f &r_t_left_basis, Matrix4f &r_t_right_basis)
	 *		- calculates two basis matrices for point on r_left_spline and on r_right_spline,
	 *		  both on position f_t on the splines. bases are aligned with spline tangents and
	 *		  with line connecting the points. setting b_dist_scaling scales matrices with
	 *		  distance between points, setting b_unskew assures both matrices define orthogonal
	 *		  coordinate bases.
	 *		- note the bases cannot be constructed if the points are identical and
	 *		  spline tangents on them are collinear (but that should be rather rare)
	 */
	static void RailBases(const CSpline<Vector3f> &r_left_spline,
		const CSpline<Vector3f> &r_right_spline, float f_t, bool b_dist_scaling,
		bool b_unskew, Matrix4f &r_t_left_basis, Matrix4f &r_t_right_basis);

	/*
	 *	class CSplineSurf::CSkewGridSubdivision
	 *		- simple skewed grid subdivision
	 *		- skewed grid in this context means number of rows is varying
	 *		  linearly from bottom row to top row
	 *		- this class is used to calculate numbers of vertices and triangles
	 *		  at different grid lines and in total and to create grid winding
	 */
	class CSkewGridSubdivision {
	protected:
		int m_n_height, m_n_bottom_width, m_n_top_width;
		int m_n_vertex_num, m_n_triangle_num;

	public:
		/*
		 *	CSplineSurf::CSkewGridSubdivision::CSkewGridSubdivision(int n_height,
		 *		int n_bottom_width, int n_top_width)
		 *		- default constructor; n_height is number of grid lines,
		 *		  n_bottom_width is number of columns in the first line
		 *		  and n_top_width is number of columns in the last line
		 *		- calculates total vertex and triangle num for later use
		 */
		CSkewGridSubdivision(int n_height, int n_bottom_width, int n_top_width);

		/*
		 *	int CSplineSurf::CSkewGridSubdivision::n_Width(int n_row) const
		 *		- returns number of grid columns at line with zero-based index n_row
		 *		  (equals to number of vertices - 1 in that line)
		 *		- note this is just linear interpolation
		 */
		int n_Width(int n_row) const;

		/*
		 *	inline int CSplineSurf::CSkewGridSubdivision::n_Vertex_Num() const
		 *		- returns total number of vertices required for grid mesh
		 */
		inline int n_Vertex_Num() const
		{
			return m_n_vertex_num;
		}

		/*
		 *	inline int CSplineSurf::CSkewGridSubdivision::n_Triangle_Num() const
		 *		- returns total number of triangles required for grid mesh
		 */
		inline int n_Triangle_Num() const
		{
			return m_n_triangle_num;
		}

		/*
		 *	inline int CSplineSurf::CSkewGridSubdivision::n_Height() const
		 *		- returns grid height (number of lines)
		 */
		inline int n_Height() const
		{
			return m_n_height;
		}

		/*
		 *	inline int CSplineSurf::CSkewGridSubdivision::n_Bottom_Width() const
		 *		- returns grid width at bottom (number of columns in the first line)
		 */
		inline int n_Bottom_Width() const
		{
			return m_n_bottom_width;
		}

		/*
		 *	inline int CSplineSurf::CSkewGridSubdivision::n_Top_Width() const
		 *		- returns grid width at top (number of columns in the last line)
		 */
		inline int n_Top_Width() const
		{
			return m_n_top_width;
		}

		/*
		 *	bool CSplineSurf::CSkewGridSubdivision::CreateWinding(CPolyMesh &r_t_mesh,
		 *		int n_face, int n_vertex, bool b_flip = false)
		 *		- creates grid winding in mesh r_t_mesh (must have allocated sufficient number
		 *		  of vertices and polygons), n_face is index of first face to be used, n_vertex
		 *		  is index of first grid vertex
		 *		- setting b_flip reverses face vertex order
		 *		- returns true on success, false on failure
		 *		- note this type of grid operates on triangles
		 */
		bool CreateWinding(CPolyMesh &r_t_mesh, int n_face, int n_vertex, bool b_flip = false);
	};
};

#endif // __SPLINE_SURFACE_INCLUDED
