Opentk/Source/Examples/Shapes/TorusKnot.cs

129 lines
5.9 KiB
C#

using System;
using System.Diagnostics;
using OpenTK;
namespace Examples.Shapes
{
public sealed class TorusKnot: DrawableShape
{
#region Constants
// hard minimums to make sure the created Torusknot is 3D
private const int MINShapeVertices = 3;
private const int MINPathSteps = 32;
private const double TwoPi = ( 2.0 * System.Math.PI );
#endregion Constants
public TorusKnot( int pathsteps, int shapevertices, double radius, int p, int q, int TexCount, bool useDL )
: base( useDL )
{
Trace.Assert( pathsteps >= MINPathSteps, "A Path must have at least " + MINPathSteps + " Steps to form a volume." );
Trace.Assert( shapevertices >= MINShapeVertices, "A Shape must contain at least " + MINShapeVertices + " Vertices to be considered valid and create a volume." );
Trace.Assert( TexCount >= 1, "at least 1 Texture set is required." );
PrimitiveMode = OpenTK.Graphics.OpenGL.BeginMode.TriangleStrip;
Vector3d[] PathPositions = new Vector3d[pathsteps];
#region Find the center Points for each step on the path
for ( int i = 0; i < pathsteps; i++ )
{
double Angle = ( i / (double)pathsteps ) * TwoPi;
double AngleTimesP = Angle * p;
double AngleTimesQ = Angle * q;
double r = ( 0.5 * ( 2.0 + System.Math.Sin( AngleTimesQ ) ) );
PathPositions[i] = new Vector3d( ( r * System.Math.Cos( AngleTimesP ) ),
( r * System.Math.Cos( AngleTimesQ ) ),
( r * System.Math.Sin( AngleTimesP ) ) );
}
#endregion Find the center Points for each step on the path
#region Find the Torus length
Vector3d result;
double[] Lengths = new double[pathsteps];
Vector3d.Subtract( ref PathPositions[pathsteps - 1], ref PathPositions[0], out result );
Lengths[0] = result.Length;
double TotalLength = result.Length;
for ( int i = 1; i < pathsteps; i++ ) // skipping
{
Vector3d.Subtract( ref PathPositions[i - 1], ref PathPositions[i], out result );
Lengths[i] = result.Length;
TotalLength += result.Length;
}
Trace.WriteLine( "the TorusKnot's length is: " + TotalLength + " " );
#endregion Find the Torus length
VertexArray = new VertexT2dN3dV3d[pathsteps * shapevertices];
#region Loft a circle Shape along the path
double TwoPiThroughVert = TwoPi / shapevertices; // precalc for reuse
for ( uint i = 0; i < pathsteps; i++ )
{
Vector3d last, next, normal, tangent;
if ( i == pathsteps - 1 )
next = PathPositions[0];
else
next = PathPositions[i + 1];
if ( i == 0 )
last = PathPositions[pathsteps - 1];
else
last = PathPositions[i - 1];
Vector3d.Subtract( ref next, ref last, out tangent ); // Guesstimate tangent
tangent.Normalize();
Vector3d.Add( ref next, ref last, out normal ); // Approximate N
normal.Normalize();
Vector3d.Multiply( ref normal, radius, out normal );// scale the shape to desired radius
for ( uint j = 0; j < shapevertices; j++ )
{
uint index = i * (uint)shapevertices + j;
// Create a point on the plane and rotate it
Matrix4d RotationMatrix = Matrix4d.Rotate( tangent, -( j * TwoPiThroughVert ) );
Vector3d point = Vector3d.TransformVector( normal, RotationMatrix );
Vector3d.Add( ref PathPositions[i], ref point, out VertexArray[index].Position );
// Since the used shape is a circle, the Vertex normal's heading is easy to find
Vector3d.Subtract( ref VertexArray[index].Position, ref PathPositions[i], out VertexArray[index].Normal );
VertexArray[index].Normal.Normalize();
// just generate some semi-useful UVs to fill blanks
VertexArray[index].TexCoord = new Vector2d( (double)( i / TotalLength/ TexCount ), j / ( shapevertices - 1.0 ) );
}
}
#endregion Loft a circle Shape along the path
PathPositions = null; // not needed anymore
uint currentindex = 0;
#region Build a Triangle strip from the Vertices
IndexArray = new uint[pathsteps * ( shapevertices * 2 + 2 )]; // 2 triangles per vertex, +2 due to added degenerate triangles
for ( uint i = 0; i < pathsteps; i++ )
{
uint RowCurrent = i * (uint)shapevertices;
uint RowBelow;
if ( i == pathsteps - 1 )
RowBelow = 0; // for the last row, the first row is the following
else
RowBelow = ( i + 1 ) * (uint)shapevertices;
// new ring begins here
for ( uint j = 0; j < shapevertices; j++ )
{
IndexArray[currentindex++] = RowCurrent + j;
IndexArray[currentindex++] = RowBelow + j;
}
// ring ends here, repeat first 2 vertices to insert 2 degenerate triangles to reach following ring
IndexArray[currentindex++] = RowCurrent;
IndexArray[currentindex++] = RowBelow;
}
#endregion Build a Triangle strip from the Vertices
}
}
}