8dcb8601a2
Hopefully this is the first and last time we have to do this.
128 lines
5.7 KiB
C#
128 lines
5.7 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
|
|
}
|
|
|
|
}
|
|
}
|