Fix ugly extrusions (Quaternions)?

I'm using the extrusion routine in the Unity3D procedural examples. I have a path of points along which I am extruding a "tube" based on a circle profile.

I get these ugly "turnovers" (pictures below) that I reckon are a result of this section of the extrusion code:

        if (sections.length < 2) return;

    var worldToLocal = transform.worldToLocalMatrix;
    var finalSections = new Matrix4x4[sections.length];
    var previousRotation : Quaternion;

    for (var i=0;i<sections.length;i++)
    {
        if (autoCalculateOrientation)
        {
            if (i == 0)
            {
                var direction = sections[0].point - sections[1].point;
                var rotation = Quaternion.LookRotation(direction, Vector3.up);

                previousRotation = rotation;
                finalSections _= worldToLocal * Matrix4x4.TRS(newPosition, rotation, Vector3.one);_ 
 *}*
 *else if (i != sections.length - 1)*
 *{* 
 _direction = sections*.point - sections[i+1].point;*_
 _*rotation = Quaternion.LookRotation(direction, Vector3.up);*_
 _*if (Quaternion.Angle (previousRotation, rotation) > 20)*_
 _*rotation = Quaternion.Slerp(previousRotation, rotation, 0.5);*_
 _*previousRotation = rotation;*_
 <em><em>finalSections _= worldToLocal * Matrix4x4.TRS(sections*.point, rotation, Vector3.one);*_</em></em>
 <em><em>_*}*_</em></em>
 <em><em><em>_else finalSections *= finalSections[i-1];*_</em></em></em>
 <em><em><em>_*}*_</em></em></em>
 <em><em><em>_*else*_</em></em></em>
 <em><em><em>_*{*_</em></em></em>
 <em><em><em>_*if (i == 0)*_</em></em></em>
 <em><em><em>_*{*_</em></em></em>
 <em><em><em><em>_finalSections *= Matrix4x4.identity;*_</em></em></em></em>
 <em><em><em><em>_*}*_</em></em></em></em>
 <em><em><em><em><em><em>else finalSections _= worldToLocal * sections*.matrix;*_</em></em></em></em></em></em>
 <em><em><em><em><em><em>_*}*_</em></em></em></em></em></em>
 <em><em><em><em><em><em>_*}*_</em></em></em></em></em></em>
<em><em><em><em><em><em>_*```*_</em></em></em></em></em></em>
<em><em><em><em><em><em>_*<p><img src="http://img138.imageshack.us/img138/8603/uglyturnover.png" alt="alt text"></p>*_</em></em></em></em></em></em>
<em><em><em><em><em><em>_*<p><img src="http://img231.imageshack.us/img231/4564/uglyturnover02.png" alt="alt text"></p>*_</em></em></em></em></em></em>
<em><em><em><em><em><em>_*<p>I'm wondering if there's a way to prevent these turnovers.  Possibly reverse the rotation specified by the Quaternion?  I've tried changing the parameter of the quaternion - vector.right, vector.up, vector.forward, etc.  That doesn't correct anything.</p>*_</em></em></em></em></em></em>
<em><em><em><em><em><em>_*<p>Thanks for any suggestions or corrections!</p>*_</em></em></em></em></em></em>

The 'parallel transport frame' should solve your problem. (For details, search online for 'parallel transport frame', or search the forum archives over at gamedev.net, as it's been discussed quite a few times there.)

Hi All,

I took the liberty of translating the PTF logic from Cinder (Github) as I have a similar problem. Let me know if this helps:

	public static class ParallelTransportFrame
	{	 
		 // Parallel Transport Frames
		 //
		 //  These methods compute a set of reference frames, defined by their
		 //  transformation matrix, along a curve. It is designed so that the 
		 //  array of points and the array of matrices used to fetch these routines 
		 //  don't need to be ordered as the curve.
		 //  
		 //  A typical usage would be :
		 //
		 //      m[0] = Imath::firstFrame( p[0], p[1], p[2] );
		 //      for( int i = 1; i < n - 1; i++ )
		 //      {
		 //          m <em>= Imath::nextFrame( m[i-1], p[i-1], p_, t[i-1], t *);*_</em>

* // }*
* // m[n-1] = Imath::lastFrame( m[n-2], p[n-2], p[n-1] );*
* //*
* // See Graphics Gems I for the underlying algorithm.*
* // These are also called Parallel Transport Frames*
* // see Game Programming Gems 2, Section 2.5*

* public static Matrix4x4 FirstFrame(*
* Vector3 firstPoint,*
* Vector3 secondPoint,*
* Vector3 thirdPoint )*
* {*
* Vector3 t = ( secondPoint - firstPoint ).normalized;*

* Vector3 n = Vector3.Cross(t, thirdPoint - firstPoint ).normalized;*
* if( n == Vector3.zero )*
* {*
* int i = Mathf.Abs( t[0] ) < Mathf.Abs( t[1] ) ? 0 : 1;*
_ if( Mathf.Abs( t[2] ) < Mathf.Abs( t ) ) i = 2;_

* Vector3 v = Vector3.zero;*
_ v = 1.0f;
* n = Vector3.Cross( t, v ).normalized;
}*_

* Vector3 b = Vector3.Cross(t, n);*

* Matrix4x4 M = Matrix4x4.zero;*
* M[0] = b[0]; M[1] = b[1]; M[2] = b[2]; M[3] = 0.0f;*
* M[4] = n[0]; M[5] = n[1]; M[6] = n[2]; M[7] = 0.0f;*
* M[8] = t[0]; M[9] = t[1]; M[10] = t[2]; M[11] = 0.0f;*
* M[12] = firstPoint[0]; M[13] = firstPoint[1]; M[14] = firstPoint[2]; M[15] = 1.0f;*
* return M;*
* }*

* public static Matrix4x4 CreateRotation(this Vector3 axis, float angle)*
* {*

* Vector3 unit = axis.normalized;*
* float sine = Mathf.Sin( angle );*
* float cosine = Mathf.Cos( angle );*

* Matrix4x4 ret = Matrix4x4.zero;*

_ ret[ 0] = unit.x * unit.x * (1 - cosine) + cosine;
ret[ 1] = unit.x * unit.y * (1 - cosine) + unit.z * sine;
ret[ 2] = unit.x * unit.z * (1 - cosine) - unit.y * sine;
* ret[ 3] = 0;*_

_ ret[ 4] = unit.x * unit.y * (1 - cosine) - unit.z * sine;
ret[ 5] = unit.y * unit.y * (1 - cosine) + cosine;
ret[ 6] = unit.y * unit.z * (1 - cosine) + unit.x * sine;
* ret[ 7] = 0;*_

_ ret[ 8] = unit.x * unit.z * (1 - cosine) + unit.y * sine;
ret[ 9] = unit.y * unit.z * (1 - cosine) - unit.x * sine;
ret[10] = unit.z * unit.z * (1 - cosine) + cosine;
* ret[11] = 0;*_

* ret[12] = 0;*
* ret[13] = 0;*
* ret[14] = 0;*
* ret[15] = 1;*

* return ret;*
* }*

* public static Matrix4x4 CreateTranslation( this Vector3 v, float w = 1f )*
* {*
* Matrix4x4 ret = Matrix4x4.zero;*
* ret[12] = v.x;*
* ret[13] = v.y;*
* ret[14] = v.z;*
* ret[15] = w;*

* return ret;*
* }*

* public static Matrix4x4 NextFrame(*
* this Matrix4x4 prevMatrix,*
* Vector3 prevPoint,*
* Vector3 curPoint,*
* Vector3 prevTangent,*
* Vector3 curTangent)*
* {*
* Vector3 a = Vector3.zero; // Rotation axis.*
* float r = 0; // Rotation angle.*

* if( ( prevTangent != Vector3.zero ) && ( curTangent != Vector3.zero ) )*
* {*
* prevTangent.Normalize();*
* curTangent.Normalize();*
* float dot = Vector3.Dot ( prevTangent, curTangent );*

* if( dot > 1.0f ) dot = 1.0f;*
* else if( dot < -1.0f ) dot = -1.0f;*

* r = Mathf.Acos(dot);*
* a = Vector3.Cross( prevTangent, curTangent );*
* }*

* if ( ( a != Vector3.zero ) && ( r != 0.0f ) )*
* {*

* Matrix4x4 R = CreateRotation(a, r); //Matrix44::createRotation( a, r );*
* Matrix4x4 Tj = CreateTranslation( curPoint );*
* Matrix4x4 Ti = CreateTranslation( -prevPoint );*

* // Original order of operation:*
_ //return prevMatrix * Ti * R * Tj;
* //
// Cinder’s order of operation*

return TjRTiprevMatrix;
}
else {
Matrix4x4 Tr = CreateTranslation( curPoint - prevPoint );
// Original order of operation:
//return prevMatrixTr;

* //
// Cinder’s order of operation*

return TrprevMatrix;
}
}*_

* public static Matrix4x4 LastFrame(*
* Matrix4x4 prevMatrix,*
* Vector3 prevPoint,*
* Vector3 lastPoint )*
* {*
* // Original order of operation:*
_ //return prevMatrix * Matrix44::createTranslation( lastPoint - prevPoint );
* //
// Cinder’s order of operation*

return CreateTranslation( lastPoint - prevPoint )prevMatrix;
}
}*_

instead of matrix, i simply rotated the extruded plane using quaternion lookat relative the the previous version of itself. this prevents any next version of the extruded object from being too much rotated compared to the previous… i found the complete post here: