2009-10-24 10:07:43 +00:00
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 ] ;
2009-11-04 20:48:31 +00:00
Vector3d . Subtract ( ref PathPositions [ pathsteps - 1 ] , ref PathPositions [ 0 ] , out result ) ;
2009-10-24 10:07:43 +00:00
Lengths [ 0 ] = result . Length ;
double TotalLength = result . Length ;
for ( int i = 1 ; i < pathsteps ; i + + ) // skipping
{
2009-11-04 20:48:31 +00:00
Vector3d . Subtract ( ref PathPositions [ i - 1 ] , ref PathPositions [ i ] , out result ) ;
2009-10-24 10:07:43 +00:00
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 ] ;
2009-11-04 20:48:31 +00:00
Vector3d . Subtract ( ref next , ref last , out tangent ) ; // Guesstimate tangent
2009-10-24 10:07:43 +00:00
tangent . Normalize ( ) ;
Vector3d . Add ( ref next , ref last , out normal ) ; // Approximate N
normal . Normalize ( ) ;
2009-11-04 20:48:31 +00:00
Vector3d . Multiply ( ref normal , radius , out normal ) ; // scale the shape to desired radius
2009-10-24 10:07:43 +00:00
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
2009-11-04 20:48:31 +00:00
Vector3d . Subtract ( ref VertexArray [ index ] . Position , ref PathPositions [ i ] , out VertexArray [ index ] . Normal ) ;
2009-10-24 10:07:43 +00:00
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
}
}
}