/*
								+----------------------------------+
								|                                  |
								|  ***  Platonic primitives  ***   |
								|                                  |
								|   Copyright © -tHE SWINe- 2007   |
								|                                  |
								|           PlatPrim.cpp           |
								|                                  |
								+----------------------------------+
*/

/**
 *	@file dev/PlatPrim.cpp
 *	@date 2007
 *	@author -tHE SWINe-
 *	@brief platonic primitives
 *
 *	@date 2008-06-24
 *
 *	renamed CPolygon2::r_t_Vertex() and CPolyMesh::r_t_Vertex() to
 *	CPolygon2::r_Vertex() and CPolyMesh::r_Vertex() respectively
 *
 *	@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_
 *
 */

#if defined(_MSC_VER) && !defined(__MWERKS__)
#pragma warning(disable:4786)
#endif
// disable warning C4786 "identifier was truncated to '255' characters in the debug information"

#include "../NewFix.h"

#include "../CallStack.h"
#include <algorithm>
#include <math.h>
#include "../Vector.h"
#include "PolyMesh.h"
#include "PlatPrim.h"

/*
 *								=== CPlatonicPrimitives ===
 */

/*
 *	static bool CPlatonicPrimitives::MakeSphere(CPolyMesh &r_t_mesh,
 *		int n_tess_axis, int n_tess_radius)
 *		- makes unit sphere mesh (sphere has center at O and radius 1)
 *		- mesh is tesselated to n_tess_axis segments along y-axis and
 *		  to n_tess_radius arround y-axis
 *		- returns true on success, false on failure
 */
bool CPlatonicPrimitives::MakeSphere(CPolyMesh &r_t_mesh, int n_tess_axis, int n_tess_radius)
{
	if(n_tess_axis < 2)
		n_tess_axis = 2;
	if(n_tess_radius < 3)
		n_tess_radius = 3;
	// min tesselation check

	int n_coat_vertex_num = (n_tess_radius + 1) * (n_tess_axis - 1);
	int n_coat_face_num = (n_tess_axis - 2) * n_tess_radius;
	int n_cap_face_num = n_tess_radius;
	if(!r_t_mesh.Alloc(n_coat_vertex_num + 2, n_coat_face_num + n_cap_face_num * 2))
		return false;
	// alloc vertices and faces

	float f_r_step = 2.0f * f_pi / n_tess_radius;
	float f_h_step = f_pi / n_tess_axis;
	float f_angle = f_h_step;
	int n_out_vertex = 0;
	for(int j = 1; j < n_tess_axis; ++ j, f_angle += f_h_step) {
		float f_sin_angle = (float)sin(f_angle);
		float f_cos_angle = (float)cos(f_angle);
		float f_angle_r = 0;
		for(int i = 0; i <= n_tess_radius; ++ i, f_angle_r += f_r_step, ++ n_out_vertex) {
			CPolyMesh::TVertex t_tmp((float)sin(f_angle_r) * f_sin_angle, f_cos_angle,
				(float)cos(f_angle_r) * f_sin_angle);
			t_tmp.v_normal = t_tmp.v_position;
#ifdef PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
			t_tmp.p_texture_coord[0] = Vector4f(f_angle_r / (2 * f_pi) * 3, f_angle / f_pi, 0, 1);
#endif // PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
			r_t_mesh.r_Vertex(n_out_vertex) = t_tmp;
			// generate vertex
		}
	}
	// generate grid of (n_tess_radius + 1) * (n_tess_axis - 1) vertices

	r_t_mesh.r_Vertex(n_out_vertex) = CPolyMesh::TVertex(0, 1, 0);
	r_t_mesh.r_Vertex(n_out_vertex).v_normal = Vector3f(0, 1, 0);
#ifdef PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
	r_t_mesh.r_Vertex(n_out_vertex).p_texture_coord[0] = Vector4f(0, 0, 0, 1);
#endif // PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
	r_t_mesh.r_Vertex(n_out_vertex + 1) = CPolyMesh::TVertex(0, -1, 0);
	r_t_mesh.r_Vertex(n_out_vertex + 1).v_normal = Vector3f(0, -1, 0);
#ifdef PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
	r_t_mesh.r_Vertex(n_out_vertex + 1).p_texture_coord[0] = Vector4f(0, 1, 0, 1);
#endif // PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
	// generate top-most and bottom-most vertices

	if(!WindGrid(r_t_mesh, 0, 0, n_tess_radius, n_tess_axis - 2, false))
		return false;
	// wind grid of quads (sphere coat)

	if(!WindFan(r_t_mesh, n_coat_face_num, n_out_vertex,
	   0, n_tess_radius + 1, false, true) ||
	   !WindFan(r_t_mesh, n_coat_face_num + n_cap_face_num,
	   n_out_vertex + 1, n_coat_vertex_num - n_tess_radius - 1,
	   n_tess_radius + 1, false, false))
		return false;
	// wind triangle fans (sphere caps)

	return true;
}

/*
 *	static bool CPlatonicPrimitives::MakeCube(CPolyMesh &r_t_mesh,
 *		int n_tess_x, int n_tess_y, int n_tess_z)
 *		- makes unit cube mesh (axis-aligned cube with min vertex
 *		  at (-1, -1, -1) and max vertex at (1, 1, 1))
 *		- mesh is tesselated to n_tess_x segments along x-axis,
 *		  n_tess_y along y-axis and n_tess_z along z-axis
 *		- returns true on success, false on failure
 */
bool CPlatonicPrimitives::MakeCube(CPolyMesh &r_t_mesh, int n_tess_x, int n_tess_y, int n_tess_z)
{
	int p_tess[3] = {(n_tess_x >= 1)? n_tess_x : 1,
		(n_tess_y >= 1)? n_tess_y : 1, (n_tess_z >= 1)? n_tess_z : 1};
	// min tesselation check

	int n_vertex_num = 0;
	int n_face_num = 0;
	for(int n_coord = 0; n_coord < 3; ++ n_coord) {
		int n_coord1 = (n_coord + 1) % 3;
		int n_tess_s = p_tess[n_coord];
		int n_tess_t = p_tess[n_coord1];
		n_vertex_num += (n_tess_s + 1) * (n_tess_t + 1) * 2;
		n_face_num += n_tess_s * n_tess_t * 2;
	}
	// determine number of faces (loop should optimize away)

	if(!r_t_mesh.Alloc(n_vertex_num, n_face_num))
		return false;
	// alloc vertices and faces

	int n_out_vertex = 0;
	int n_in_vertex = 0;
	int n_out_face = 0;
	for(int n_coord = 0; n_coord < 3; ++ n_coord) {
		int n_coord1 = (n_coord + 1) % 3;
		int n_coord2 = (n_coord + 2) % 3;
		// determine index of other coordinates

		int n_tess_s = p_tess[n_coord];
		int n_tess_t = p_tess[n_coord1];
		float f_step_s = 2.0f / n_tess_s;
		float f_step_t = 2.0f / n_tess_t;
		// determine tesselations and stepping

		for(float n_side = -1; n_side < 2; n_side += 2) {
			float f_t = -1;
			for(int y = 0; y <= n_tess_t; ++ y, f_t += f_step_t) {
				float f_s = -1;
				for(int x = 0; x <= n_tess_s; ++ x, f_s += f_step_s, ++ n_out_vertex) {
					CPolyMesh::TVertex t_tmp(0, 0, 0);
					t_tmp.v_position[n_coord] = f_s;
					t_tmp.v_position[n_coord1] = f_t;
					t_tmp.v_position[n_coord2] = n_side;
					t_tmp.v_normal = Vector3f(0, 0, 0);
					t_tmp.v_normal[n_coord2] = n_side;
#ifdef PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
					if(n_coord2 == 2) {
						t_tmp.p_texture_coord[0] = Vector4f(
							n_side * (t_tmp.v_position[n_coord] + 1) * .5f,
							-(t_tmp.v_position[n_coord1] + 1) * .5f, 0, 1);
					} else {
						t_tmp.p_texture_coord[0] = Vector4f(
							-n_side * (t_tmp.v_position[n_coord1] + 1) * .5f,
							-(t_tmp.v_position[n_coord] + 1) * .5f, 0, 1);
					}
					// produces results, identical to box mapping implemented in TexCoords.h
#endif // PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
					r_t_mesh.r_Vertex(n_out_vertex) = t_tmp;
					// generate vertex
				}
			}
		}
		// generate vertices on n_coord - n_coord1 plane (both cube sides)

		for(int n_side = 0; n_side < 2; ++ n_side) {
			if(!WindGrid(r_t_mesh, n_out_face, n_in_vertex,
			   n_tess_s, n_tess_t, (n_side)? true : false))
				return false;
			n_out_face += n_tess_s * n_tess_t;
			n_in_vertex += (n_tess_s + 1) * (n_tess_t + 1);
		}
		// wind polygons
	}

	return true;
}

/*
 *	static bool CPlatonicPrimitives::MakePlane(CPolyMesh &r_t_mesh,
 *		int n_tess_x, int n_tess_y)
 *		- makes unit plane mesh (x-z plane with min vertex
 *		  at (-1, 0, -1) and max vertex at (1, 0, 1))
 *		- mesh is tesselated to n_tess_x segments along x-axis and
 *		  n_tess_y segments along y-axis
 *		- returns true on success, false on failure
 */
bool CPlatonicPrimitives::MakePlane(CPolyMesh &r_t_mesh, int n_tess_x, int n_tess_y)
{
	if(n_tess_x < 1)
		n_tess_x = 1;
	if(n_tess_y < 1)
		n_tess_y = 1;
	// min tesselation check

	if(!r_t_mesh.Alloc((n_tess_x + 1) * (n_tess_y + 1), n_tess_x * n_tess_y))
		return false;
	// alloc vertices and faces

	int n_out_vertex = 0;
	float f_step_x = 2.0f / n_tess_x;
	float f_step_y = 2.0f / n_tess_y;
	float f_t = -1;
	for(int y = 0; y <= n_tess_y; ++ y, f_t += f_step_y) {
		float f_s = -1;
		for(int x = 0; x <= n_tess_x; ++ x, f_s += f_step_x, ++ n_out_vertex) {
			CPolyMesh::TVertex t_tmp(f_s, 0, f_t);
			t_tmp.v_normal = Vector3f(0, 1, 0);
#ifdef PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
			t_tmp.p_texture_coord[0] = Vector4f((f_s + 1) * .5f, (f_t + 1) * .5f, 0, 1);
#endif // PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
			r_t_mesh.r_Vertex(n_out_vertex) = t_tmp;
			// generate vertex
		}
	}
	// generate vertices

	if(!WindGrid(r_t_mesh, 0, 0, n_tess_x, n_tess_y, false))
		return false;
	// wind polygons

	return true;
}

/*
 *	static bool CPlatonicPrimitives::MakeCylinder(CPolyMesh &r_t_mesh, int n_tess_axis,
 *		int n_tess_radius, int n_cap_tess)
 *		- creates cylinder mesh (cylinder axis is identical to y-axis, radius is 1,
 *		  bottom cap is at y = -1, top cap is at y = 1)
 *		- mesh is tesselated to n_tess_axis segments along cylinder axis and
 *		  to n_tess_radius arround cylinder axis
 *		- in case n_cap_tess is greater than zero, cap faces are generated
 *		- returns true on success, false on failure
 */
bool CPlatonicPrimitives::MakeCylinder(CPolyMesh &r_t_mesh,
	int n_tess_axis, int n_tess_radius, int n_cap_tess)
{
	if(n_tess_axis < 1)
		n_tess_axis = 1;
	if(n_tess_radius < 3)
		n_tess_radius = 3;
	if(n_cap_tess < 0)
		n_cap_tess = 0;
	// min tesselation check

	int n_coat_face_num = n_tess_axis * n_tess_radius;
	int n_coat_vertex_num = (n_tess_axis + 1) * (n_tess_radius + 1);
	int n_cap_face_num = n_tess_radius * n_cap_tess;
	int n_cap_vertex_num = (n_cap_tess)? n_tess_radius * n_cap_tess + 1 : 0;
	if(!r_t_mesh.Alloc(n_coat_vertex_num + 2 * n_cap_vertex_num,
	   n_coat_face_num + 2 * n_cap_face_num))
		return false;
	// alloc vertices and faces

	int n_out_vertex = 0;
	float f_r_step = 2.0f * f_pi / n_tess_radius;
	float f_y_step = 2.0f / n_tess_axis;
	float f_y = -1;
	for(int j = 0; j <= n_tess_axis; ++ j, f_y += f_y_step) {
		float f_angle = 0;
		for(int i = 0; i <= n_tess_radius; ++ i, f_angle += f_r_step, ++ n_out_vertex) {
			CPolyMesh::TVertex t_tmp((float)sin(f_angle), f_y, (float)cos(f_angle));
			t_tmp.v_normal = t_tmp.v_position;
			t_tmp.v_normal.y = 0;
#ifdef PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
			t_tmp.p_texture_coord[0] = Vector4f(f_angle / (2 * f_pi) * 3, (1 - f_y) * .5f, 0, 1);
#endif // PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
			r_t_mesh.r_Vertex(n_out_vertex) = t_tmp;
			// generate vertex
		}
	}
	// generate grid of (n_tess_radius + 1) * (n_tess_axis + 1) coat vertices

	if(!WindGrid(r_t_mesh, 0, 0, n_tess_radius, n_tess_axis, true))
		return false;
	// wind coat polygons

	if(n_cap_tess) {
		int n_out_vertex = n_coat_vertex_num;
		int n_in_vertex = n_coat_vertex_num;
		int n_out_face = n_coat_face_num;
		for(int y = -1; y < 2; y += 2) {
			float f_radius_step = -1.0f / n_cap_tess;
			float f_radius = 1.0f;
			for(int i = 0; i < n_cap_tess; ++ i, f_radius += f_radius_step) {
				float f_angle = 0;
				for(int i = 0; i < n_tess_radius; ++ i, f_angle += f_r_step, ++ n_out_vertex) {
					CPolyMesh::TVertex t_tmp((float)sin(f_angle), float(y), (float)cos(f_angle));
					t_tmp.v_normal = Vector3f(0, float(y), 0);
					t_tmp.v_position.x *= f_radius;
					t_tmp.v_position.z *= f_radius;
#ifdef PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
					t_tmp.p_texture_coord[0] = Vector4f((t_tmp.v_position.x + 1) * .5f,
						(t_tmp.v_position.z + 1) * y * .5f, 0, 1);
#endif // PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
					r_t_mesh.r_Vertex(n_out_vertex) = t_tmp;
					// generate vertex
				}
			}
			// extra cap vertices (in case cap is over-tesselated)

			CPolyMesh::TVertex t_tmp(0, float(y), 0);
			t_tmp.v_normal = t_tmp.v_position;
#ifdef PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
			t_tmp.p_texture_coord[0] = Vector4f(.5f, .5f * y, 0, 1);
#endif // PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
			r_t_mesh.r_Vertex(n_out_vertex) = t_tmp;
			// cap center vertex

			for(int i = 1; i < n_cap_tess; ++ i) {
				if(!WindSection(r_t_mesh, n_out_face, n_in_vertex + n_tess_radius * (i - 1),
				   n_in_vertex + n_tess_radius * i, n_tess_radius, true, y > 0))
					return false;
				n_out_face += n_tess_radius;
			}
			// wind extra cap sections (in case cap is over-tesselated)

			if(!WindFan(r_t_mesh, n_out_face, n_out_vertex, n_in_vertex +
				n_tess_radius * (n_cap_tess - 1), n_tess_radius, true, y > 0))
				return false;
			n_out_face += n_tess_radius;
			n_in_vertex += n_cap_vertex_num;
			// wind fan in center of cap

			++ n_out_vertex;
			// skip past center vertex
		}
	}
	// make caps

	return true;
}

/*
 *	static bool CPlatonicPrimitives::MakeCone(CPolyMesh &r_t_mesh, int n_tess_axis,
 *		int n_tess_radius, int n_cap_tess)
 *		- creates cone mesh (cone axis is identical to y-axis, base radius is 1,
 *		  base lies at y = -1, top vertex is at y = 1)
 *		- mesh is tesselated to n_tess_axis segments along cylinder axis and
 *		  to n_tess_radius arround cylinder axis
 *		- in case n_cap_tess is greater than zero, cap faces are generated
 *		- returns true on success, false on failure
 */
bool CPlatonicPrimitives::MakeCone(CPolyMesh &r_t_mesh, int n_tess_axis,
	int n_tess_radius, int n_cap_tess)
{
	if(n_tess_axis < 1)
		n_tess_axis = 1;
	if(n_tess_radius < 3)
		n_tess_radius = 3;
	if(n_cap_tess < 0)
		n_cap_tess = 0;
	// min tesselation check

	int n_coat_face_num = n_tess_axis * n_tess_radius;
	int n_coat_vertex_num = n_tess_axis * (n_tess_radius + 1) + 1;
	int n_cap_face_num = n_tess_radius * n_cap_tess;
	int n_cap_vertex_num = (n_cap_tess)? n_tess_radius * n_cap_tess + 1 : 0;
	if(!r_t_mesh.Alloc(n_coat_vertex_num + n_cap_vertex_num,
	   n_coat_face_num + n_cap_face_num))
		return false;
	// alloc vertices and faces

	int n_out_vertex = 0;
	float f_r_step = 2.0f * f_pi / n_tess_radius;
	float f_y_step = 2.0f / n_tess_axis;
	float f_y = -1;
	float f_radius = 1;
	float f_radius_step = -1.0f / n_tess_axis;
	for(int j = 0; j < n_tess_axis; ++ j, f_y += f_y_step, f_radius += f_radius_step) {
		float f_angle = 0;
		for(int i = 0; i <= n_tess_radius; ++ i, f_angle += f_r_step, ++ n_out_vertex) {
			CPolyMesh::TVertex t_tmp((float)sin(f_angle), f_y, (float)cos(f_angle));
			t_tmp.v_normal = t_tmp.v_position;
			t_tmp.v_normal.y = 0;
			t_tmp.v_position.x *= f_radius;
			t_tmp.v_position.z *= f_radius;
#ifdef PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
			t_tmp.p_texture_coord[0] = Vector4f(f_angle / (2 * f_pi) * 3, (1 - f_y) * .5f, 0, 1);
#endif // PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
			r_t_mesh.r_Vertex(n_out_vertex) = t_tmp;
			// generate vertex
		}
	}
	// generate grid of (n_tess_radius + 1) * n_tess_axis coat vertices

	CPolyMesh::TVertex t_tmp(0, 1, 0);
	t_tmp.v_normal = Vector3f(0, 1, 0);
#ifdef PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
	t_tmp.p_texture_coord[0] = Vector4f(0, 0, 0, 1);
#endif // PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
	r_t_mesh.r_Vertex(n_out_vertex) = t_tmp;
	// generate top vertex

	if(!WindGrid(r_t_mesh, 0, 0, n_tess_radius, n_tess_axis - 1, true) ||
	   !WindFan(r_t_mesh, n_tess_radius * (n_tess_axis - 1),
	   n_out_vertex, (n_tess_radius + 1) * (n_tess_axis - 1),
	   n_tess_radius + 1, false, true))
		return false;
	// generate coat winding

	if(n_cap_tess) {
		int n_out_vertex = n_coat_vertex_num;
		int n_in_vertex = n_coat_vertex_num;
		int n_out_face = n_coat_face_num;
		float f_radius_step = -1.0f / n_cap_tess;
		float f_radius = 1.0f;
		for(int i = 0; i < n_cap_tess; ++ i, f_radius += f_radius_step) {
			float f_angle = 0;
			for(int i = 0; i < n_tess_radius; ++ i, f_angle += f_r_step, ++ n_out_vertex) {
				CPolyMesh::TVertex t_tmp((float)sin(f_angle), -1, (float)cos(f_angle));
				t_tmp.v_normal = Vector3f(0, -1, 0);
				t_tmp.v_position.x *= f_radius;
				t_tmp.v_position.z *= f_radius;
#ifdef PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
				t_tmp.p_texture_coord[0] = Vector4f((t_tmp.v_position.x + 1) * .5f,
					(1 - t_tmp.v_position.z) * .5f, 0, 1);
#endif // PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
				r_t_mesh.r_Vertex(n_out_vertex) = t_tmp;
				// generate vertex
			}
		}
		// extra cap vertices (in case cap is over-tesselated)

		CPolyMesh::TVertex t_tmp(0, -1, 0);
		t_tmp.v_normal = Vector3f(0, -1, 0);
#ifdef PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
		t_tmp.p_texture_coord[0] = Vector4f(.5f, .5f, 0, 1);
#endif // PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
		r_t_mesh.r_Vertex(n_out_vertex) = t_tmp;
		// cap center vertex

		for(int i = 1; i < n_cap_tess; ++ i) {
			if(!WindSection(r_t_mesh, n_out_face, n_in_vertex + n_tess_radius * (i - 1),
			   n_in_vertex + n_tess_radius * i, n_tess_radius, true, false))
				return false;
			n_out_face += n_tess_radius;
		}
		// wind extra cap sections (in case cap is over-tesselated)

		if(!WindFan(r_t_mesh, n_out_face, n_out_vertex, n_in_vertex +
		   n_tess_radius * (n_cap_tess - 1), n_tess_radius, true, false))
			return false;
		// wind fan in center of cap
	}
	// make cap

	return true;
}

/*
 *	static bool CPlatonicPrimitives::MakeTube(CPolyMesh &r_t_mesh, int n_tess_axis,
 *		int n_tess_radius, float f_inner_radius)
 *		- creates tube mesh (tube axis is identical to y-axis, radius is 1,
 *		  inner radius is f_inner_radius, bottom cap is at y = -1, top cap is at y = 1)
 *		- mesh is tesselated to n_tess_axis segments along cylinder axis and
 *		  to n_tess_radius arround cylinder axis
 *		- in case n_cap_tess is greater than zero, cap faces are generated
 *		- returns true on success, false on failure
 */
bool CPlatonicPrimitives::MakeTube(CPolyMesh &r_t_mesh, int n_tess_axis,
	int n_tess_radius, float f_inner_radius)
{
	if(n_tess_axis < 1)
		n_tess_axis = 1;
	if(n_tess_radius < 3)
		n_tess_radius = 3;
	// min tesselation check

	int n_coat_face_num = n_tess_axis * n_tess_radius;
	int n_coat_vertex_num = (n_tess_axis + 1) * (n_tess_radius + 1);
	int n_cap_face_num = n_tess_radius;
	int n_cap_vertex_num = 2 * n_tess_radius;
	if(!r_t_mesh.Alloc(2 * n_coat_vertex_num + 2 * n_cap_vertex_num,
	   2 * n_coat_face_num + 2 * n_cap_face_num))
		return false;
	// alloc vertices and faces

	int n_out_vertex = 0;
	float f_r_step = 2.0f * f_pi / n_tess_radius;
	float f_y_step = 2.0f / n_tess_axis;
	float f_r = 1;
	for(int n_pass = -1; n_pass < 2; n_pass += 2) {
		float f_y = -1;
		for(int j = 0; j <= n_tess_axis; ++ j, f_y += f_y_step) {
			float f_angle = 0;
			for(int i = 0; i <= n_tess_radius; ++ i, f_angle += f_r_step, ++ n_out_vertex) {
				CPolyMesh::TVertex t_tmp((float)sin(f_angle), f_y, (float)cos(f_angle));
				t_tmp.v_normal = t_tmp.v_position * float(-n_pass);
				t_tmp.v_normal.y = 0;
				t_tmp.v_position.x *= f_r;
				t_tmp.v_position.z *= f_r;
#ifdef PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
				t_tmp.p_texture_coord[0] =
					Vector4f(f_angle / (2 * f_pi) * 3 * -n_pass, (1 - f_y) * .5f, 0, 1);
#endif // PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
				r_t_mesh.r_Vertex(n_out_vertex) = t_tmp;
				// generate vertex
			}
		}
		f_r = f_inner_radius;
	}
	// generate two grids of (n_tess_radius + 1) * (n_tess_axis + 1) coat vertices

	if(!WindGrid(r_t_mesh, 0, 0, n_tess_radius, n_tess_axis, true) ||
	   !WindGrid(r_t_mesh, n_coat_face_num, n_coat_vertex_num, n_tess_radius, n_tess_axis, false))
		return false;
	// wind coat polygons

	{float f_r = 1;
	for(int n_pass = -1; n_pass < 2; n_pass += 2) {
		for(int n_y = -1; n_y < 2; n_y += 2) {
			float f_angle = 0;
			for(int i = 0; i < n_tess_radius; ++ i, f_angle += f_r_step, ++ n_out_vertex) {
				CPolyMesh::TVertex t_tmp((float)sin(f_angle), float(n_y), (float)cos(f_angle));
				t_tmp.v_normal = Vector3f(0, float(n_y), 0);
				t_tmp.v_position.x *= f_r;
				t_tmp.v_position.z *= f_r;
#ifdef PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
				t_tmp.p_texture_coord[0] = Vector4f((t_tmp.v_position.x + 1) * .5f,
					(t_tmp.v_position.z + 1) * .5f * n_y, 0, 1);
#endif // PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
				r_t_mesh.r_Vertex(n_out_vertex) = t_tmp;
				// generate vertex
			}
		}
		f_r = f_inner_radius;
	}}
	// generate two grids of (n_tess_radius + 1) * (n_tess_axis + 1) coat vertices

	if(!WindSection(r_t_mesh, 2 * n_coat_face_num, 2 * n_coat_vertex_num,
	   2 * n_coat_vertex_num + 2 * n_tess_radius, n_tess_radius, true, false) ||
	   !WindSection(r_t_mesh, 2 * n_coat_face_num + n_cap_face_num,
	   2 * n_coat_vertex_num + n_tess_radius,
	   2 * n_coat_vertex_num + 3 * n_tess_radius, n_tess_radius, true, true))
		return false;
	// wind cap polygons

	return true;
}

/*
 *	static bool CPlatonicPrimitives::MakeTorus(CPolyMesh &r_t_mesh, int n_tess_major,
 *		int n_tess_minor, float f_minor_radius)
 *		- creates torus (annuloid); center is at O, major radius is 1 and lies on x-z plane
 *		- minor radius is f_minor_radius
 *		- torus is tesselated to n_tess_major segments arround major radius and
 *		  to n_tess_minor segments arround minor radius
 *		- returns true on success, false on failure
 */
bool CPlatonicPrimitives::MakeTorus(CPolyMesh &r_t_mesh, int n_tess_major,
	int n_tess_minor, float f_minor_radius)
{
	int n_coat_vertex_num = (n_tess_minor + 1) * (n_tess_major + 1);
	int n_coat_face_num = n_tess_minor * n_tess_major;
	if(!r_t_mesh.Alloc(n_coat_vertex_num, n_coat_face_num))
		return false;
	// alloc vertices and faces

	int n_out_vertex = 0;
	float f_r_step = 2.0f * f_pi / n_tess_major;
	float f_sub_r_step = 2.0f * f_pi / n_tess_minor;
	float f_angle_r = 0;
	for(int i = 0; i <= n_tess_major; ++ i, f_angle_r += f_r_step) {
		Vector3f v_local_org(sinf(f_angle_r), 0, cosf(f_angle_r));
		Vector3f v_local_x = v_local_org * f_minor_radius;
		Vector3f v_local_y(0, f_minor_radius, 0);
		float f_angle = 0;
		for(int j = 0; j <= n_tess_minor; ++ j, f_angle += f_sub_r_step, ++ n_out_vertex) {
			CPolyMesh::TVertex t_tmp((v_local_x * sinf(f_angle) +
				v_local_y * cosf(f_angle)) + v_local_org);
			t_tmp.v_normal = t_tmp.v_position - v_local_org;
			t_tmp.v_normal.Normalize();
#ifdef PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
			t_tmp.p_texture_coord[0] =
				Vector4f(-f_angle / (2 * f_pi), f_angle_r / (2 * f_pi), 0, 1);
#endif // PLATONIC_PRIMITIVES_GENERATE_TEXCOORDS
			r_t_mesh.r_Vertex(n_out_vertex) = t_tmp;
		}
	}
	// calc vertices

	if(!WindGrid(r_t_mesh, 0, 0, n_tess_minor, n_tess_major, true))
		return false;
	// wind grid of quads (torus coat)

	return true;
}

/*
 *	static bool CPlatonicPrimitives::WindGrid(CPolyMesh &r_t_mesh, int n_face, int n_vertex,
 *		int n_size_x, int n_size_y, bool b_flip = false)
 *		- creates winding of grid of n_size_x * n_size_y quads (requires
 *		  (n_size_x + 1) * (n_size_y + 1) vertices and n_size_x * n_size_y quads)
 *		- r_t_mesh is mesh where grid should be generated
 *		- n_face is the first face of grid
 *		- n_vertex is the first vertex of grid
 *		- n_size_x and n_size_y are grid dimensions
 *		- in case b_flip is true, faces are wound with reversed vertex order
 */
bool CPlatonicPrimitives::WindGrid(CPolyMesh &r_t_mesh, int n_face, int n_vertex,
	int n_size_x, int n_size_y, bool b_flip)
{
	for(int y = 0; y < n_size_y; ++ y) {
		for(int x = 0; x < n_size_x; ++ x, ++ n_face) {
			CPolyMesh::TRefVertex p_vertex_ptr[4] = {
				r_t_mesh.t_RefVertex(n_vertex + x + y * (n_size_x + 1)),
				r_t_mesh.t_RefVertex(n_vertex + x + (y + 1) * (n_size_x + 1)),
				r_t_mesh.t_RefVertex(n_vertex + (x + 1) + (y + 1) * (n_size_x + 1)),
				r_t_mesh.t_RefVertex(n_vertex + (x + 1) + y * (n_size_x + 1))};
			// take ref vertices of grid quad

			if(b_flip) {
				CPolyMesh::TRefVertex tmp(p_vertex_ptr[3]);
				p_vertex_ptr[3] = p_vertex_ptr[0];
				p_vertex_ptr[0] = tmp;
				CPolyMesh::TRefVertex tmp2(p_vertex_ptr[2]);
				p_vertex_ptr[2] = p_vertex_ptr[1];
				p_vertex_ptr[1] = tmp2;
			}
			// flip?

			CPolyMesh::CPolygon &r_face = r_t_mesh.r_Polygon(n_face);
			r_face.Delete_Vertices(0, r_face.n_Vertex_Num());
			if(!r_face.Add_Vertex(0, p_vertex_ptr, 4))
				return false;
			// creates a new polygon

			r_face.SetFlags(0);
			r_face.SetMaterial(-1);
			// set no material
		}
	}

	return true;
}

/*
 *	static bool CPlatonicPrimitives::WindFan(CPolyMesh &r_t_mesh, int n_face,
 *		int n_center_vertex, int n_fan_vertex, int n_fan_length,
 *		bool b_loop = true, bool b_flip = false);
 *		- creates winding of triangle fan
 *		- n_face points to first face of triangle fan
 *		- n_center_vertex points to shared vertex of triangle fan
 *		- n_fan_vertex points to satellite vertices of triangle fan
 *		- n_fan_length is number of satellite vertices forming fan
 *		- in case b_loop is true, face between first and last fan vertex is created
 *		  (n_fan_length faces is created; otherwise n_fan_length - 1)
 *		- in case b_flip is true, faces are wound with reversed vertex order
 */
bool CPlatonicPrimitives::WindFan(CPolyMesh &r_t_mesh, int n_face, int n_center_vertex,
	int n_fan_vertex, int n_fan_length, bool b_loop, bool b_flip)
{
	int n_flip = int(b_flip);
	int n_not_flip = 1 - n_flip;
	for(int i = 1; i < n_fan_length; ++ i, ++ n_face) {
		CPolyMesh::TRefVertex p_vertex_ptr[3] = {
			r_t_mesh.t_RefVertex(n_center_vertex),
			r_t_mesh.t_RefVertex(n_fan_vertex + i - n_flip),
			r_t_mesh.t_RefVertex(n_fan_vertex + i - n_not_flip)};
		// take ref vertices of triangle

		CPolyMesh::CPolygon &r_face = r_t_mesh.r_Polygon(n_face);
		r_face.Delete_Vertices(0, r_face.n_Vertex_Num());
		if(!r_face.Add_Vertex(0, p_vertex_ptr, 3))
			return false;
		// creates a new polygon

		r_face.SetFlags(0);
		r_face.SetMaterial(-1);
		// set no material
	}
	if(b_loop) {
		CPolyMesh::TRefVertex p_vertex_ptr[3] = {
			r_t_mesh.t_RefVertex(n_center_vertex),
			r_t_mesh.t_RefVertex(n_fan_vertex + n_flip * (n_fan_length - 1)),
			r_t_mesh.t_RefVertex(n_fan_vertex + n_not_flip * (n_fan_length - 1))};
		// take ref vertices of triangle

		CPolyMesh::CPolygon &r_face = r_t_mesh.r_Polygon(n_face);
		r_face.Delete_Vertices(0, r_face.n_Vertex_Num());
		if(!r_face.Add_Vertex(0, p_vertex_ptr, 3))
			return false;
		// creates a new polygon

		r_face.SetFlags(0);
		r_face.SetMaterial(-1);
		// set no material
	}

	return true;
}

/*
 *	static bool CPlatonicPrimitives::WindSection(CPolyMesh &r_t_mesh, int n_face,
 *		int n_left_vertex, int n_right_vertex, int n_section_length,
 *		bool b_loop = true, bool b_flip = false)
 *		- wind quadriliteral section between left and right vertices
 *		- n_face points to first face of section
 *		- n_left_vertex points to left vertices of section
 *		- n_right_vertex points to right vertices of section
 *		- n_section_length is number of section vertices (there's
 *		  n_section_length vertices in both left and right array)
 *		- in case b_loop is true, quad between first and last section vertices is created
 *		  (n_section_length quads is created; otherwise n_section_length - 1)
 *		- in case b_flip is true, faces are wound with reversed vertex order
 */
bool CPlatonicPrimitives::WindSection(CPolyMesh &r_t_mesh, int n_face, int n_left_vertex,
	int n_right_vertex, int n_section_length, bool b_loop, bool b_flip)
{
	int n_flip = int(b_flip);
	int n_not_flip = 1 - n_flip;
	for(int i = 1; i < n_section_length; ++ i, ++ n_face) {
		CPolyMesh::TRefVertex p_vertex_ptr[4] = {
			r_t_mesh.t_RefVertex(n_left_vertex + i - n_flip),
			r_t_mesh.t_RefVertex(n_left_vertex + i - n_not_flip),
			r_t_mesh.t_RefVertex(n_right_vertex + i - n_not_flip),
			r_t_mesh.t_RefVertex(n_right_vertex + i - n_flip)
		};
		// vertices of a single quad

		CPolyMesh::CPolygon &r_face = r_t_mesh.r_Polygon(n_face);
		r_face.Delete_Vertices(0, r_face.n_Vertex_Num());
		if(!r_face.Add_Vertex(0, p_vertex_ptr, 4))
			return false;
		// creates a new polygon

		r_face.SetFlags(0);
		r_face.SetMaterial(-1);
		// set no material
	}
	if(b_loop) {
		CPolyMesh::TRefVertex p_vertex_ptr[4] = {
			r_t_mesh.t_RefVertex(n_left_vertex + n_flip * (n_section_length - 1)),
			r_t_mesh.t_RefVertex(n_left_vertex + n_not_flip * (n_section_length - 1)),
			r_t_mesh.t_RefVertex(n_right_vertex + n_not_flip * (n_section_length - 1)),
			r_t_mesh.t_RefVertex(n_right_vertex + n_flip * (n_section_length - 1))
		};
		// vertices of a single quad

		CPolyMesh::CPolygon &r_face = r_t_mesh.r_Polygon(n_face);
		r_face.Delete_Vertices(0, r_face.n_Vertex_Num());
		if(!r_face.Add_Vertex(0, p_vertex_ptr, 4))
			return false;
		// creates a new polygon

		r_face.SetFlags(0);
		r_face.SetMaterial(-1);
		// set no material
	}

	return true;
}

/*
 *								=== ~CPlatonicPrimitives ===
 */
