+void Patch::constructPlane( const AABB& aabb, int axis, std::size_t width, std::size_t height ){
+ setDims( width, height );
+
+ int x, y, z;
+ switch ( axis )
+ {
+ case 2: x = 0; y = 1; z = 2; break;
+ case 1: x = 0; y = 2; z = 1; break;
+ case 0: x = 1; y = 2; z = 0; break;
+ default:
+ ERROR_MESSAGE( "invalid view-type" );
+ return;
+ }
+
+ if ( m_width < MIN_PATCH_WIDTH || m_width > MAX_PATCH_WIDTH ) {
+ m_width = 3;
+ }
+ if ( m_height < MIN_PATCH_HEIGHT || m_height > MAX_PATCH_HEIGHT ) {
+ m_height = 3;
+ }
+
+ Vector3 vStart;
+ vStart[x] = aabb.origin[x] - aabb.extents[x];
+ vStart[y] = aabb.origin[y] - aabb.extents[y];
+ vStart[z] = aabb.origin[z];
+
+ float xAdj = fabsf( ( vStart[x] - ( aabb.origin[x] + aabb.extents[x] ) ) / (float)( m_width - 1 ) );
+ float yAdj = fabsf( ( vStart[y] - ( aabb.origin[y] + aabb.extents[y] ) ) / (float)( m_height - 1 ) );
+
+ Vector3 vTmp;
+ vTmp[z] = vStart[z];
+ PatchControl* pCtrl = m_ctrl.data();
+
+ vTmp[y] = vStart[y];
+ for ( std::size_t h = 0; h < m_height; h++ )
+ {
+ vTmp[x] = vStart[x];
+ for ( std::size_t w = 0; w < m_width; w++, ++pCtrl )
+ {
+ pCtrl->m_vertex = vTmp;
+ vTmp[x] += xAdj;
+ }
+ vTmp[y] += yAdj;
+ }
+
+ NaturalTexture();
+}
+
+void Patch::ConstructPrefab( const AABB& aabb, EPatchPrefab eType, int axis, std::size_t width, std::size_t height ){
+ Vector3 vPos[3];
+
+ if ( eType != ePlane ) {
+ vPos[0] = vector3_subtracted( aabb.origin, aabb.extents );
+ vPos[1] = aabb.origin;
+ vPos[2] = vector3_added( aabb.origin, aabb.extents );
+ }
+
+ if ( eType == ePlane ) {
+ constructPlane( aabb, axis, width, height );
+ }
+ else if ( eType == eSqCylinder
+ || eType == eCylinder
+ || eType == eDenseCylinder
+ || eType == eVeryDenseCylinder
+ || eType == eCone
+ || eType == eSphere ) {
+ unsigned char *pIndex;
+ unsigned char pCylIndex[] =
+ {
+ 0, 0,
+ 1, 0,
+ 2, 0,
+ 2, 1,
+ 2, 2,
+ 1, 2,
+ 0, 2,
+ 0, 1,
+ 0, 0
+ };
+
+
+ PatchControl *pStart;
+ switch ( eType )
+ {
+ case eSqCylinder: setDims( 9, 3 );
+ pStart = m_ctrl.data();
+ break;
+ case eDenseCylinder:
+ case eVeryDenseCylinder:
+ case eCylinder:
+ setDims( 9, 3 );
+ pStart = m_ctrl.data() + 1;
+ break;
+ case eCone: setDims( 9, 3 );
+ pStart = m_ctrl.data() + 1;
+ break;
+ case eSphere:
+ setDims( 9, 5 );
+ pStart = m_ctrl.data() + ( 9 + 1 );
+ break;
+ default:
+ ERROR_MESSAGE( "this should be unreachable" );
+ return;
+ }
+
+ for ( std::size_t h = 0; h < 3; h++, pStart += 9 )
+ {
+ pIndex = pCylIndex;
+ PatchControl* pCtrl = pStart;
+ for ( std::size_t w = 0; w < 8; w++, pCtrl++ )
+ {
+ pCtrl->m_vertex[0] = vPos[pIndex[0]][0];
+ pCtrl->m_vertex[1] = vPos[pIndex[1]][1];
+ pCtrl->m_vertex[2] = vPos[h][2];
+ pIndex += 2;
+ }
+ }
+
+ switch ( eType )
+ {
+ case eSqCylinder:
+ {
+ PatchControl* pCtrl = m_ctrl.data();
+ for ( std::size_t h = 0; h < 3; h++, pCtrl += 9 )
+ {
+ pCtrl[8].m_vertex = pCtrl[0].m_vertex;
+ }
+ }
+ break;
+ case eDenseCylinder:
+ case eVeryDenseCylinder:
+ case eCylinder:
+ {
+ PatchControl* pCtrl = m_ctrl.data();
+ for ( std::size_t h = 0; h < 3; h++, pCtrl += 9 )
+ {
+ pCtrl[0].m_vertex = pCtrl[8].m_vertex;
+ }
+ }
+ break;
+ case eCone:
+ {
+ PatchControl* pCtrl = m_ctrl.data();
+ for ( std::size_t h = 0; h < 2; h++, pCtrl += 9 )
+ {
+ pCtrl[0].m_vertex = pCtrl[8].m_vertex;
+ }
+ }
+ {
+ PatchControl* pCtrl = m_ctrl.data() + 9 * 2;
+ for ( std::size_t w = 0; w < 9; w++, pCtrl++ )
+ {
+ pCtrl->m_vertex[0] = vPos[1][0];
+ pCtrl->m_vertex[1] = vPos[1][1];
+ pCtrl->m_vertex[2] = vPos[2][2];
+ }
+ }
+ break;
+ case eSphere:
+ {
+ PatchControl* pCtrl = m_ctrl.data() + 9;
+ for ( std::size_t h = 0; h < 3; h++, pCtrl += 9 )
+ {
+ pCtrl[0].m_vertex = pCtrl[8].m_vertex;
+ }
+ }
+ {
+ PatchControl* pCtrl = m_ctrl.data();
+ for ( std::size_t w = 0; w < 9; w++, pCtrl++ )
+ {
+ pCtrl->m_vertex[0] = vPos[1][0];
+ pCtrl->m_vertex[1] = vPos[1][1];
+ pCtrl->m_vertex[2] = vPos[0][2];
+ }
+ }
+ {
+ PatchControl* pCtrl = m_ctrl.data() + ( 9 * 4 );
+ for ( std::size_t w = 0; w < 9; w++, pCtrl++ )
+ {
+ pCtrl->m_vertex[0] = vPos[1][0];
+ pCtrl->m_vertex[1] = vPos[1][1];
+ pCtrl->m_vertex[2] = vPos[2][2];
+ }
+ }
+ break;
+ default:
+ ERROR_MESSAGE( "this should be unreachable" );
+ return;
+ }
+ }
+ else if ( eType == eXactCylinder ) {
+ int n = ( width - 1 ) / 2; // n = number of segments
+ setDims( width, height );
+
+ // vPos[0] = vector3_subtracted(aabb.origin, aabb.extents);
+ // vPos[1] = aabb.origin;
+ // vPos[2] = vector3_added(aabb.origin, aabb.extents);
+
+ float f = 1 / cos( M_PI / n );
+ for ( std::size_t i = 0; i < width; ++i )
+ {
+ float angle = ( M_PI * i ) / n; // 0 to 2pi
+ float x = vPos[1][0] + ( vPos[2][0] - vPos[1][0] ) * cos( angle ) * ( ( i & 1 ) ? f : 1.0f );
+ float y = vPos[1][1] + ( vPos[2][1] - vPos[1][1] ) * sin( angle ) * ( ( i & 1 ) ? f : 1.0f );
+ for ( std::size_t j = 0; j < height; ++j )
+ {
+ float z = vPos[0][2] + ( vPos[2][2] - vPos[0][2] ) * ( j / (float)( height - 1 ) );
+ PatchControl *v;
+ v = &m_ctrl.data()[j * width + i];
+ v->m_vertex[0] = x;
+ v->m_vertex[1] = y;
+ v->m_vertex[2] = z;
+ }
+ }
+ }
+ else if ( eType == eXactCone ) {
+ int n = ( width - 1 ) / 2; // n = number of segments
+ setDims( width, height );
+
+ // vPos[0] = vector3_subtracted(aabb.origin, aabb.extents);
+ // vPos[1] = aabb.origin;
+ // vPos[2] = vector3_added(aabb.origin, aabb.extents);
+
+ float f = 1 / cos( M_PI / n );
+ for ( std::size_t i = 0; i < width; ++i )
+ {
+ float angle = ( M_PI * i ) / n;
+ for ( std::size_t j = 0; j < height; ++j )
+ {
+ float x = vPos[1][0] + ( 1.0f - ( j / (float)( height - 1 ) ) ) * ( vPos[2][0] - vPos[1][0] ) * cos( angle ) * ( ( i & 1 ) ? f : 1.0f );
+ float y = vPos[1][1] + ( 1.0f - ( j / (float)( height - 1 ) ) ) * ( vPos[2][1] - vPos[1][1] ) * sin( angle ) * ( ( i & 1 ) ? f : 1.0f );
+ float z = vPos[0][2] + ( vPos[2][2] - vPos[0][2] ) * ( j / (float)( height - 1 ) );
+ PatchControl *v;
+ v = &m_ctrl.data()[j * width + i];
+ v->m_vertex[0] = x;
+ v->m_vertex[1] = y;
+ v->m_vertex[2] = z;
+ }
+ }
+ }
+ else if ( eType == eXactSphere ) {
+ int n = ( width - 1 ) / 2; // n = number of segments (yaw)
+ int m = ( height - 1 ) / 2; // m = number of segments (pitch)
+ setDims( width, height );
+
+ // vPos[0] = vector3_subtracted(aabb.origin, aabb.extents);
+ // vPos[1] = aabb.origin;
+ // vPos[2] = vector3_added(aabb.origin, aabb.extents);
+
+ float f = 1 / cos( M_PI / n );
+ float g = 1 / cos( M_PI / ( 2 * m ) );
+ for ( std::size_t i = 0; i < width; ++i )
+ {
+ float angle = ( M_PI * i ) / n;
+ for ( std::size_t j = 0; j < height; ++j )
+ {
+ float angle2 = ( M_PI * j ) / ( 2 * m );
+ float x = vPos[1][0] + ( vPos[2][0] - vPos[1][0] ) * sin( angle2 ) * ( ( j & 1 ) ? g : 1.0f ) * cos( angle ) * ( ( i & 1 ) ? f : 1.0f );
+ float y = vPos[1][1] + ( vPos[2][1] - vPos[1][1] ) * sin( angle2 ) * ( ( j & 1 ) ? g : 1.0f ) * sin( angle ) * ( ( i & 1 ) ? f : 1.0f );
+ float z = vPos[1][2] + ( vPos[2][2] - vPos[1][2] ) * -cos( angle2 ) * ( ( j & 1 ) ? g : 1.0f );
+ PatchControl *v;
+ v = &m_ctrl.data()[j * width + i];
+ v->m_vertex[0] = x;
+ v->m_vertex[1] = y;
+ v->m_vertex[2] = z;
+ }
+ }
+ }
+ else if ( eType == eBevel ) {
+ unsigned char *pIndex;
+ unsigned char pBevIndex[] =
+ {
+ 0, 0,
+ 2, 0,
+ 2, 2,
+ };
+
+ setDims( 3, 3 );
+
+ PatchControl* pCtrl = m_ctrl.data();
+ for ( std::size_t h = 0; h < 3; h++ )
+ {
+ pIndex = pBevIndex;
+ for ( std::size_t w = 0; w < 3; w++, pIndex += 2, pCtrl++ )
+ {
+ pCtrl->m_vertex[0] = vPos[pIndex[0]][0];
+ pCtrl->m_vertex[1] = vPos[pIndex[1]][1];
+ pCtrl->m_vertex[2] = vPos[h][2];
+ }
+ }
+ }
+ else if ( eType == eEndCap ) {
+ unsigned char *pIndex;
+ unsigned char pEndIndex[] =
+ {
+ 2, 0,
+ 2, 2,
+ 1, 2,
+ 0, 2,
+ 0, 0,
+ };
+
+ setDims( 5, 3 );
+
+ PatchControl* pCtrl = m_ctrl.data();
+ for ( std::size_t h = 0; h < 3; h++ )
+ {
+ pIndex = pEndIndex;
+ for ( std::size_t w = 0; w < 5; w++, pIndex += 2, pCtrl++ )
+ {
+ pCtrl->m_vertex[0] = vPos[pIndex[0]][0];
+ pCtrl->m_vertex[1] = vPos[pIndex[1]][1];
+ pCtrl->m_vertex[2] = vPos[h][2];
+ }
+ }
+ }
+
+ if ( eType == eDenseCylinder ) {
+ InsertRemove( true, false, true );
+ }
+
+ if ( eType == eVeryDenseCylinder ) {
+ InsertRemove( true, false, false );
+ InsertRemove( true, false, true );
+ }
+
+ NaturalTexture();
+}
+
+void Patch::RenderDebug( RenderStateFlags state ) const {
+ for ( std::size_t i = 0; i < m_tess.m_numStrips; i++ )
+ {
+ glBegin( GL_QUAD_STRIP );
+ for ( std::size_t j = 0; j < m_tess.m_lenStrips; j++ )
+ {
+ glNormal3fv( normal3f_to_array( ( m_tess.m_vertices.data() + m_tess.m_indices[i * m_tess.m_lenStrips + j] )->normal ) );
+ glTexCoord2fv( texcoord2f_to_array( ( m_tess.m_vertices.data() + m_tess.m_indices[i * m_tess.m_lenStrips + j] )->texcoord ) );
+ glVertex3fv( vertex3f_to_array( ( m_tess.m_vertices.data() + m_tess.m_indices[i * m_tess.m_lenStrips + j] )->vertex ) );
+ }
+ glEnd();
+ }
+}
+
+void RenderablePatchSolid::RenderNormals() const {
+ const std::size_t width = m_tess.m_numStrips + 1;
+ const std::size_t height = m_tess.m_lenStrips >> 1;
+ glBegin( GL_LINES );
+ for ( std::size_t i = 0; i < width; i++ )
+ {
+ for ( std::size_t j = 0; j < height; j++ )
+ {
+ {
+ Vector3 vNormal(
+ vector3_added(
+ vertex3f_to_vector3( ( m_tess.m_vertices.data() + ( j * width + i ) )->vertex ),
+ vector3_scaled( normal3f_to_vector3( ( m_tess.m_vertices.data() + ( j * width + i ) )->normal ), 8 )
+ )
+ );
+ glVertex3fv( vertex3f_to_array( ( m_tess.m_vertices.data() + ( j * width + i ) )->vertex ) );
+ glVertex3fv( &vNormal[0] );
+ }
+ {
+ Vector3 vNormal(
+ vector3_added(
+ vertex3f_to_vector3( ( m_tess.m_vertices.data() + ( j * width + i ) )->vertex ),
+ vector3_scaled( normal3f_to_vector3( ( m_tess.m_vertices.data() + ( j * width + i ) )->tangent ), 8 )
+ )
+ );
+ glVertex3fv( vertex3f_to_array( ( m_tess.m_vertices.data() + ( j * width + i ) )->vertex ) );
+ glVertex3fv( &vNormal[0] );
+ }
+ {
+ Vector3 vNormal(
+ vector3_added(
+ vertex3f_to_vector3( ( m_tess.m_vertices.data() + ( j * width + i ) )->vertex ),
+ vector3_scaled( normal3f_to_vector3( ( m_tess.m_vertices.data() + ( j * width + i ) )->bitangent ), 8 )
+ )
+ );
+ glVertex3fv( vertex3f_to_array( ( m_tess.m_vertices.data() + ( j * width + i ) )->vertex ) );
+ glVertex3fv( &vNormal[0] );
+ }
+ }
+ }
+ glEnd();
+}
+
+const int DEGEN_0a = 0x01;
+const int DEGEN_1a = 0x02;
+const int DEGEN_2a = 0x04;
+const int DEGEN_0b = 0x08;
+const int DEGEN_1b = 0x10;
+const int DEGEN_2b = 0x20;
+const int SPLIT = 0x40;
+const int AVERAGE = 0x80;
+
+
+unsigned int subarray_get_degen( PatchControlIter subarray, std::size_t strideU, std::size_t strideV ){
+ unsigned int nDegen = 0;
+ const PatchControl* p1;
+ const PatchControl* p2;
+
+ p1 = subarray;
+ p2 = p1 + strideU;
+ if ( vector3_equal( p1->m_vertex, p2->m_vertex ) ) {
+ nDegen |= DEGEN_0a;
+ }
+ p1 = p2;
+ p2 = p1 + strideU;
+ if ( vector3_equal( p1->m_vertex, p2->m_vertex ) ) {
+ nDegen |= DEGEN_0b;
+ }
+
+ p1 = subarray + strideV;
+ p2 = p1 + strideU;
+ if ( vector3_equal( p1->m_vertex, p2->m_vertex ) ) {
+ nDegen |= DEGEN_1a;
+ }
+ p1 = p2;
+ p2 = p1 + strideU;
+ if ( vector3_equal( p1->m_vertex, p2->m_vertex ) ) {
+ nDegen |= DEGEN_1b;
+ }
+
+ p1 = subarray + ( strideV << 1 );
+ p2 = p1 + strideU;
+ if ( vector3_equal( p1->m_vertex, p2->m_vertex ) ) {
+ nDegen |= DEGEN_2a;
+ }
+ p1 = p2;
+ p2 = p1 + strideU;
+ if ( vector3_equal( p1->m_vertex, p2->m_vertex ) ) {
+ nDegen |= DEGEN_2b;
+ }
+
+ return nDegen;
+}
+
+
+inline void deCasteljau3( const Vector3& P0, const Vector3& P1, const Vector3& P2, Vector3& P01, Vector3& P12, Vector3& P012 ){
+ P01 = vector3_mid( P0, P1 );
+ P12 = vector3_mid( P1, P2 );
+ P012 = vector3_mid( P01, P12 );
+}
+
+inline void BezierInterpolate3( const Vector3& start, Vector3& left, Vector3& mid, Vector3& right, const Vector3& end ){
+ left = vector3_mid( start, mid );
+ right = vector3_mid( mid, end );
+ mid = vector3_mid( left, right );
+}
+
+inline void BezierInterpolate2( const Vector2& start, Vector2& left, Vector2& mid, Vector2& right, const Vector2& end ){
+ left[0] = float_mid( start[0], mid[0] );
+ left[1] = float_mid( start[1], mid[1] );
+ right[0] = float_mid( mid[0], end[0] );
+ right[1] = float_mid( mid[1], end[1] );
+ mid[0] = float_mid( left[0], right[0] );
+ mid[1] = float_mid( left[1], right[1] );
+}
+
+
+inline Vector2& texcoord_for_index( Array<ArbitraryMeshVertex>& vertices, std::size_t index ){
+ return reinterpret_cast<Vector2&>( vertices[index].texcoord );
+}
+
+inline Vector3& vertex_for_index( Array<ArbitraryMeshVertex>& vertices, std::size_t index ){
+ return reinterpret_cast<Vector3&>( vertices[index].vertex );
+}
+
+inline Vector3& normal_for_index( Array<ArbitraryMeshVertex>& vertices, std::size_t index ){
+ return reinterpret_cast<Vector3&>( vertices[index].normal );
+}
+
+inline Vector3& tangent_for_index( Array<ArbitraryMeshVertex>& vertices, std::size_t index ){
+ return reinterpret_cast<Vector3&>( vertices[index].tangent );
+}
+
+inline Vector3& bitangent_for_index( Array<ArbitraryMeshVertex>& vertices, std::size_t index ){
+ return reinterpret_cast<Vector3&>( vertices[index].bitangent );
+}
+
+inline const Vector2& texcoord_for_index( const Array<ArbitraryMeshVertex>& vertices, std::size_t index ){
+ return reinterpret_cast<const Vector2&>( vertices[index].texcoord );
+}
+
+inline const Vector3& vertex_for_index( const Array<ArbitraryMeshVertex>& vertices, std::size_t index ){
+ return reinterpret_cast<const Vector3&>( vertices[index].vertex );
+}
+
+inline const Vector3& normal_for_index( const Array<ArbitraryMeshVertex>& vertices, std::size_t index ){
+ return reinterpret_cast<const Vector3&>( vertices[index].normal );
+}
+
+inline const Vector3& tangent_for_index( const Array<ArbitraryMeshVertex>& vertices, std::size_t index ){
+ return reinterpret_cast<const Vector3&>( vertices[index].tangent );
+}
+
+inline const Vector3& bitangent_for_index( const Array<ArbitraryMeshVertex>& vertices, std::size_t index ){
+ return reinterpret_cast<const Vector3&>( vertices[index].bitangent );
+}
+
+#include "math/curve.h"
+
+inline PatchControl QuadraticBezier_evaluate( const PatchControl* firstPoint, double t ){
+ PatchControl result = { Vector3( 0, 0, 0 ), Vector2( 0, 0 ) };
+ double denominator = 0;
+
+ {
+ double weight = BernsteinPolynomial<Zero, Two>::apply( t );
+ vector3_add( result.m_vertex, vector3_scaled( firstPoint[0].m_vertex, weight ) );
+ vector2_add( result.m_texcoord, vector2_scaled( firstPoint[0].m_texcoord, weight ) );
+ denominator += weight;
+ }
+ {
+ double weight = BernsteinPolynomial<One, Two>::apply( t );
+ vector3_add( result.m_vertex, vector3_scaled( firstPoint[1].m_vertex, weight ) );
+ vector2_add( result.m_texcoord, vector2_scaled( firstPoint[1].m_texcoord, weight ) );
+ denominator += weight;
+ }
+ {
+ double weight = BernsteinPolynomial<Two, Two>::apply( t );
+ vector3_add( result.m_vertex, vector3_scaled( firstPoint[2].m_vertex, weight ) );
+ vector2_add( result.m_texcoord, vector2_scaled( firstPoint[2].m_texcoord, weight ) );
+ denominator += weight;
+ }
+
+ vector3_divide( result.m_vertex, denominator );
+ vector2_divide( result.m_texcoord, denominator );
+ return result;
+}
+
+inline Vector3 vector3_linear_interpolated( const Vector3& a, const Vector3& b, double t ){
+ return vector3_added( vector3_scaled( a, 1.0 - t ), vector3_scaled( b, t ) );
+}
+
+inline Vector2 vector2_linear_interpolated( const Vector2& a, const Vector2& b, double t ){
+ return vector2_added( vector2_scaled( a, 1.0 - t ), vector2_scaled( b, t ) );
+}
+
+void normalise_safe( Vector3& normal ){
+ if ( !vector3_equal( normal, g_vector3_identity ) ) {
+ vector3_normalise( normal );
+ }
+}
+
+inline void QuadraticBezier_evaluate( const PatchControl& a, const PatchControl& b, const PatchControl& c, double t, PatchControl& point, PatchControl& left, PatchControl& right ){
+ left.m_vertex = vector3_linear_interpolated( a.m_vertex, b.m_vertex, t );
+ left.m_texcoord = vector2_linear_interpolated( a.m_texcoord, b.m_texcoord, t );
+ right.m_vertex = vector3_linear_interpolated( b.m_vertex, c.m_vertex, t );
+ right.m_texcoord = vector2_linear_interpolated( b.m_texcoord, c.m_texcoord, t );
+ point.m_vertex = vector3_linear_interpolated( left.m_vertex, right.m_vertex, t );
+ point.m_texcoord = vector2_linear_interpolated( left.m_texcoord, right.m_texcoord, t );
+}
+
+void Patch::TesselateSubMatrixFixed( ArbitraryMeshVertex* vertices, std::size_t strideX, std::size_t strideY, unsigned int nFlagsX, unsigned int nFlagsY, PatchControl* subMatrix[3][3] ){
+ double incrementU = 1.0 / m_subdivisions_x;
+ double incrementV = 1.0 / m_subdivisions_y;
+ const std::size_t width = m_subdivisions_x + 1;
+ const std::size_t height = m_subdivisions_y + 1;
+
+ for ( std::size_t i = 0; i != width; ++i )
+ {
+ double tU = ( i + 1 == width ) ? 1 : i * incrementU;
+ PatchControl pointX[3];
+ PatchControl leftX[3];
+ PatchControl rightX[3];
+ QuadraticBezier_evaluate( *subMatrix[0][0], *subMatrix[0][1], *subMatrix[0][2], tU, pointX[0], leftX[0], rightX[0] );
+ QuadraticBezier_evaluate( *subMatrix[1][0], *subMatrix[1][1], *subMatrix[1][2], tU, pointX[1], leftX[1], rightX[1] );
+ QuadraticBezier_evaluate( *subMatrix[2][0], *subMatrix[2][1], *subMatrix[2][2], tU, pointX[2], leftX[2], rightX[2] );
+
+ ArbitraryMeshVertex* p = vertices + i * strideX;
+ for ( std::size_t j = 0; j != height; ++j )
+ {
+ if ( ( j == 0 || j + 1 == height ) && ( i == 0 || i + 1 == width ) ) {
+ }
+ else
+ {
+ double tV = ( j + 1 == height ) ? 1 : j * incrementV;
+
+ PatchControl pointY[3];
+ PatchControl leftY[3];
+ PatchControl rightY[3];
+ QuadraticBezier_evaluate( *subMatrix[0][0], *subMatrix[1][0], *subMatrix[2][0], tV, pointY[0], leftY[0], rightY[0] );
+ QuadraticBezier_evaluate( *subMatrix[0][1], *subMatrix[1][1], *subMatrix[2][1], tV, pointY[1], leftY[1], rightY[1] );
+ QuadraticBezier_evaluate( *subMatrix[0][2], *subMatrix[1][2], *subMatrix[2][2], tV, pointY[2], leftY[2], rightY[2] );
+
+ PatchControl point;
+ PatchControl left;
+ PatchControl right;
+ QuadraticBezier_evaluate( pointX[0], pointX[1], pointX[2], tV, point, left, right );
+ PatchControl up;
+ PatchControl down;
+ QuadraticBezier_evaluate( pointY[0], pointY[1], pointY[2], tU, point, up, down );
+
+ vertex3f_to_vector3( p->vertex ) = point.m_vertex;
+ texcoord2f_to_vector2( p->texcoord ) = point.m_texcoord;
+
+ ArbitraryMeshVertex a, b, c;
+
+ a.vertex = vertex3f_for_vector3( left.m_vertex );
+ a.texcoord = texcoord2f_for_vector2( left.m_texcoord );
+ b.vertex = vertex3f_for_vector3( right.m_vertex );
+ b.texcoord = texcoord2f_for_vector2( right.m_texcoord );
+
+ if ( i != 0 ) {
+ c.vertex = vertex3f_for_vector3( up.m_vertex );
+ c.texcoord = texcoord2f_for_vector2( up.m_texcoord );
+ }
+ else
+ {
+ c.vertex = vertex3f_for_vector3( down.m_vertex );
+ c.texcoord = texcoord2f_for_vector2( down.m_texcoord );
+ }
+
+ Vector3 normal = vector3_normalised( vector3_cross( right.m_vertex - left.m_vertex, up.m_vertex - down.m_vertex ) );
+
+ Vector3 tangent, bitangent;
+ ArbitraryMeshTriangle_calcTangents( a, b, c, tangent, bitangent );
+ vector3_normalise( tangent );
+ vector3_normalise( bitangent );
+
+ if ( ( ( nFlagsX & AVERAGE ) != 0 && i == 0 ) || ( ( nFlagsY & AVERAGE ) != 0 && j == 0 ) ) {
+ normal3f_to_vector3( p->normal ) = vector3_normalised( vector3_added( normal3f_to_vector3( p->normal ), normal ) );
+ normal3f_to_vector3( p->tangent ) = vector3_normalised( vector3_added( normal3f_to_vector3( p->tangent ), tangent ) );
+ normal3f_to_vector3( p->bitangent ) = vector3_normalised( vector3_added( normal3f_to_vector3( p->bitangent ), bitangent ) );
+ }
+ else
+ {
+ normal3f_to_vector3( p->normal ) = normal;
+ normal3f_to_vector3( p->tangent ) = tangent;
+ normal3f_to_vector3( p->bitangent ) = bitangent;
+ }
+ }
+
+ p += strideY;
+ }
+ }
+}
+
+void Patch::TesselateSubMatrix( const BezierCurveTree *BX, const BezierCurveTree *BY,
+ std::size_t offStartX, std::size_t offStartY,
+ std::size_t offEndX, std::size_t offEndY,
+ std::size_t nFlagsX, std::size_t nFlagsY,
+ Vector3& left, Vector3& mid, Vector3& right,
+ Vector2& texLeft, Vector2& texMid, Vector2& texRight,
+ bool bTranspose ){
+ int newFlagsX, newFlagsY;
+
+ Vector3 tmp;
+ Vector3 vertex_0_0, vertex_0_1, vertex_1_0, vertex_1_1, vertex_2_0, vertex_2_1;
+ Vector2 texTmp;
+ Vector2 texcoord_0_0, texcoord_0_1, texcoord_1_0, texcoord_1_1, texcoord_2_0, texcoord_2_1;
+
+ {
+ // texcoords
+
+ BezierInterpolate2( texcoord_for_index( m_tess.m_vertices, offStartX + offStartY ),
+ texcoord_0_0,
+ texcoord_for_index( m_tess.m_vertices, BX->index + offStartY ),
+ texcoord_0_1,
+ texcoord_for_index( m_tess.m_vertices, offEndX + offStartY ) );
+
+
+ BezierInterpolate2( texcoord_for_index( m_tess.m_vertices, offStartX + offEndY ),
+ texcoord_2_0,
+ texcoord_for_index( m_tess.m_vertices, BX->index + offEndY ),
+ texcoord_2_1,
+ texcoord_for_index( m_tess.m_vertices, offEndX + offEndY ) );
+
+ texTmp = texMid;
+
+ BezierInterpolate2( texLeft,
+ texcoord_1_0,
+ texTmp,
+ texcoord_1_1,
+ texRight );
+
+ if ( !BezierCurveTree_isLeaf( BY ) ) {
+ texcoord_for_index( m_tess.m_vertices, BX->index + BY->index ) = texTmp;
+ }
+
+
+ if ( !BezierCurveTree_isLeaf( BX->left ) ) {
+ texcoord_for_index( m_tess.m_vertices, BX->left->index + offStartY ) = texcoord_0_0;
+ texcoord_for_index( m_tess.m_vertices, BX->left->index + offEndY ) = texcoord_2_0;
+
+ if ( !BezierCurveTree_isLeaf( BY ) ) {
+ texcoord_for_index( m_tess.m_vertices, BX->left->index + BY->index ) = texcoord_1_0;
+ }
+ }
+ if ( !BezierCurveTree_isLeaf( BX->right ) ) {
+ texcoord_for_index( m_tess.m_vertices, BX->right->index + offStartY ) = texcoord_0_1;
+ texcoord_for_index( m_tess.m_vertices, BX->right->index + offEndY ) = texcoord_2_1;
+
+ if ( !BezierCurveTree_isLeaf( BY ) ) {
+ texcoord_for_index( m_tess.m_vertices, BX->right->index + BY->index ) = texcoord_1_1;
+ }
+ }
+
+
+ // verts
+
+ BezierInterpolate3( vertex_for_index( m_tess.m_vertices, offStartX + offStartY ),
+ vertex_0_0,
+ vertex_for_index( m_tess.m_vertices, BX->index + offStartY ),
+ vertex_0_1,
+ vertex_for_index( m_tess.m_vertices, offEndX + offStartY ) );
+
+
+ BezierInterpolate3( vertex_for_index( m_tess.m_vertices, offStartX + offEndY ),
+ vertex_2_0,
+ vertex_for_index( m_tess.m_vertices, BX->index + offEndY ),
+ vertex_2_1,
+ vertex_for_index( m_tess.m_vertices, offEndX + offEndY ) );
+
+
+ tmp = mid;
+
+ BezierInterpolate3( left,
+ vertex_1_0,
+ tmp,
+ vertex_1_1,
+ right );
+
+ if ( !BezierCurveTree_isLeaf( BY ) ) {
+ vertex_for_index( m_tess.m_vertices, BX->index + BY->index ) = tmp;
+ }
+
+
+ if ( !BezierCurveTree_isLeaf( BX->left ) ) {
+ vertex_for_index( m_tess.m_vertices, BX->left->index + offStartY ) = vertex_0_0;
+ vertex_for_index( m_tess.m_vertices, BX->left->index + offEndY ) = vertex_2_0;
+
+ if ( !BezierCurveTree_isLeaf( BY ) ) {
+ vertex_for_index( m_tess.m_vertices, BX->left->index + BY->index ) = vertex_1_0;
+ }
+ }
+ if ( !BezierCurveTree_isLeaf( BX->right ) ) {
+ vertex_for_index( m_tess.m_vertices, BX->right->index + offStartY ) = vertex_0_1;
+ vertex_for_index( m_tess.m_vertices, BX->right->index + offEndY ) = vertex_2_1;
+
+ if ( !BezierCurveTree_isLeaf( BY ) ) {
+ vertex_for_index( m_tess.m_vertices, BX->right->index + BY->index ) = vertex_1_1;
+ }
+ }
+
+ // normals
+
+ if ( nFlagsX & SPLIT ) {
+ ArbitraryMeshVertex a, b, c;
+ Vector3 tangentU;
+
+ if ( !( nFlagsX & DEGEN_0a ) || !( nFlagsX & DEGEN_0b ) ) {
+ tangentU = vector3_subtracted( vertex_0_1, vertex_0_0 );
+ a.vertex = vertex3f_for_vector3( vertex_0_0 );
+ a.texcoord = texcoord2f_for_vector2( texcoord_0_0 );
+ c.vertex = vertex3f_for_vector3( vertex_0_1 );
+ c.texcoord = texcoord2f_for_vector2( texcoord_0_1 );
+ }
+ else if ( !( nFlagsX & DEGEN_1a ) || !( nFlagsX & DEGEN_1b ) ) {
+ tangentU = vector3_subtracted( vertex_1_1, vertex_1_0 );
+ a.vertex = vertex3f_for_vector3( vertex_1_0 );
+ a.texcoord = texcoord2f_for_vector2( texcoord_1_0 );
+ c.vertex = vertex3f_for_vector3( vertex_1_1 );
+ c.texcoord = texcoord2f_for_vector2( texcoord_1_1 );
+ }
+ else
+ {
+ tangentU = vector3_subtracted( vertex_2_1, vertex_2_0 );
+ a.vertex = vertex3f_for_vector3( vertex_2_0 );
+ a.texcoord = texcoord2f_for_vector2( texcoord_2_0 );
+ c.vertex = vertex3f_for_vector3( vertex_2_1 );
+ c.texcoord = texcoord2f_for_vector2( texcoord_2_1 );
+ }
+
+ Vector3 tangentV;
+
+ if ( ( nFlagsY & DEGEN_0a ) && ( nFlagsY & DEGEN_1a ) && ( nFlagsY & DEGEN_2a ) ) {
+ tangentV = vector3_subtracted( vertex_for_index( m_tess.m_vertices, BX->index + offEndY ), tmp );
+ b.vertex = vertex3f_for_vector3( tmp ); //m_tess.m_vertices[BX->index + offEndY].vertex;
+ b.texcoord = texcoord2f_for_vector2( texTmp ); //m_tess.m_vertices[BX->index + offEndY].texcoord;
+ }
+ else
+ {
+ tangentV = vector3_subtracted( tmp, vertex_for_index( m_tess.m_vertices, BX->index + offStartY ) );
+ b.vertex = vertex3f_for_vector3( tmp ); //m_tess.m_vertices[BX->index + offStartY].vertex;
+ b.texcoord = texcoord2f_for_vector2( texTmp ); //m_tess.m_vertices[BX->index + offStartY].texcoord;
+ }
+
+
+ Vector3 normal, s, t;
+ ArbitraryMeshVertex& v = m_tess.m_vertices[offStartY + BX->index];
+ Vector3& p = normal3f_to_vector3( v.normal );
+ Vector3& ps = normal3f_to_vector3( v.tangent );
+ Vector3& pt = normal3f_to_vector3( v.bitangent );
+
+ if ( bTranspose ) {
+ normal = vector3_cross( tangentV, tangentU );
+ }
+ else
+ {
+ normal = vector3_cross( tangentU, tangentV );
+ }
+ normalise_safe( normal );
+
+ ArbitraryMeshTriangle_calcTangents( a, b, c, s, t );
+ normalise_safe( s );
+ normalise_safe( t );
+
+ if ( nFlagsX & AVERAGE ) {
+ p = vector3_normalised( vector3_added( p, normal ) );
+ ps = vector3_normalised( vector3_added( ps, s ) );
+ pt = vector3_normalised( vector3_added( pt, t ) );
+ }
+ else
+ {
+ p = normal;
+ ps = s;
+ pt = t;
+ }
+ }
+
+ {
+ ArbitraryMeshVertex a, b, c;
+ Vector3 tangentU;
+
+ if ( !( nFlagsX & DEGEN_2a ) || !( nFlagsX & DEGEN_2b ) ) {
+ tangentU = vector3_subtracted( vertex_2_1, vertex_2_0 );
+ a.vertex = vertex3f_for_vector3( vertex_2_0 );
+ a.texcoord = texcoord2f_for_vector2( texcoord_2_0 );
+ c.vertex = vertex3f_for_vector3( vertex_2_1 );
+ c.texcoord = texcoord2f_for_vector2( texcoord_2_1 );
+ }
+ else if ( !( nFlagsX & DEGEN_1a ) || !( nFlagsX & DEGEN_1b ) ) {
+ tangentU = vector3_subtracted( vertex_1_1, vertex_1_0 );
+ a.vertex = vertex3f_for_vector3( vertex_1_0 );
+ a.texcoord = texcoord2f_for_vector2( texcoord_1_0 );
+ c.vertex = vertex3f_for_vector3( vertex_1_1 );
+ c.texcoord = texcoord2f_for_vector2( texcoord_1_1 );
+ }
+ else
+ {
+ tangentU = vector3_subtracted( vertex_0_1, vertex_0_0 );
+ a.vertex = vertex3f_for_vector3( vertex_0_0 );
+ a.texcoord = texcoord2f_for_vector2( texcoord_0_0 );
+ c.vertex = vertex3f_for_vector3( vertex_0_1 );
+ c.texcoord = texcoord2f_for_vector2( texcoord_0_1 );
+ }
+
+ Vector3 tangentV;
+
+ if ( ( nFlagsY & DEGEN_0b ) && ( nFlagsY & DEGEN_1b ) && ( nFlagsY & DEGEN_2b ) ) {
+ tangentV = vector3_subtracted( tmp, vertex_for_index( m_tess.m_vertices, BX->index + offStartY ) );
+ b.vertex = vertex3f_for_vector3( tmp ); //m_tess.m_vertices[BX->index + offStartY].vertex;
+ b.texcoord = texcoord2f_for_vector2( texTmp ); //m_tess.m_vertices[BX->index + offStartY].texcoord;
+ }
+ else
+ {
+ tangentV = vector3_subtracted( vertex_for_index( m_tess.m_vertices, BX->index + offEndY ), tmp );
+ b.vertex = vertex3f_for_vector3( tmp ); //m_tess.m_vertices[BX->index + offEndY].vertex;
+ b.texcoord = texcoord2f_for_vector2( texTmp ); //m_tess.m_vertices[BX->index + offEndY].texcoord;
+ }
+
+ ArbitraryMeshVertex& v = m_tess.m_vertices[offEndY + BX->index];
+ Vector3& p = normal3f_to_vector3( v.normal );
+ Vector3& ps = normal3f_to_vector3( v.tangent );
+ Vector3& pt = normal3f_to_vector3( v.bitangent );
+
+ if ( bTranspose ) {
+ p = vector3_cross( tangentV, tangentU );
+ }
+ else
+ {
+ p = vector3_cross( tangentU, tangentV );
+ }
+ normalise_safe( p );
+
+ ArbitraryMeshTriangle_calcTangents( a, b, c, ps, pt );
+ normalise_safe( ps );
+ normalise_safe( pt );
+ }
+ }
+
+
+ newFlagsX = newFlagsY = 0;
+
+ if ( ( nFlagsX & DEGEN_0a ) && ( nFlagsX & DEGEN_0b ) ) {
+ newFlagsX |= DEGEN_0a;
+ newFlagsX |= DEGEN_0b;
+ }
+ if ( ( nFlagsX & DEGEN_1a ) && ( nFlagsX & DEGEN_1b ) ) {
+ newFlagsX |= DEGEN_1a;
+ newFlagsX |= DEGEN_1b;
+ }
+ if ( ( nFlagsX & DEGEN_2a ) && ( nFlagsX & DEGEN_2b ) ) {
+ newFlagsX |= DEGEN_2a;
+ newFlagsX |= DEGEN_2b;
+ }
+ if ( ( nFlagsY & DEGEN_0a ) && ( nFlagsY & DEGEN_1a ) && ( nFlagsY & DEGEN_2a ) ) {
+ newFlagsY |= DEGEN_0a;
+ newFlagsY |= DEGEN_1a;
+ newFlagsY |= DEGEN_2a;
+ }
+ if ( ( nFlagsY & DEGEN_0b ) && ( nFlagsY & DEGEN_1b ) && ( nFlagsY & DEGEN_2b ) ) {
+ newFlagsY |= DEGEN_0b;
+ newFlagsY |= DEGEN_1b;
+ newFlagsY |= DEGEN_2b;
+ }
+
+
+ //if((nFlagsX & DEGEN_0a) && (nFlagsX & DEGEN_1a) && (nFlagsX & DEGEN_2a)) { newFlagsX |= DEGEN_0a; newFlagsX |= DEGEN_1a; newFlagsX |= DEGEN_2a; }
+ //if((nFlagsX & DEGEN_0b) && (nFlagsX & DEGEN_1b) && (nFlagsX & DEGEN_2b)) { newFlagsX |= DEGEN_0b; newFlagsX |= DEGEN_1b; newFlagsX |= DEGEN_2b; }
+
+ newFlagsX |= ( nFlagsX & SPLIT );
+ newFlagsX |= ( nFlagsX & AVERAGE );
+
+ if ( !BezierCurveTree_isLeaf( BY ) ) {
+ {
+ int nTemp = newFlagsY;
+
+ if ( ( nFlagsY & DEGEN_0a ) && ( nFlagsY & DEGEN_0b ) ) {
+ newFlagsY |= DEGEN_0a;
+ newFlagsY |= DEGEN_0b;
+ }
+ newFlagsY |= ( nFlagsY & SPLIT );
+ newFlagsY |= ( nFlagsY & AVERAGE );
+
+ Vector3& p = vertex_for_index( m_tess.m_vertices, BX->index + BY->index );
+ Vector3 vTemp( p );
+
+ Vector2& p2 = texcoord_for_index( m_tess.m_vertices, BX->index + BY->index );
+ Vector2 stTemp( p2 );
+
+ TesselateSubMatrix( BY, BX->left,
+ offStartY, offStartX,
+ offEndY, BX->index,
+ newFlagsY, newFlagsX,
+ vertex_0_0, vertex_1_0, vertex_2_0,
+ texcoord_0_0, texcoord_1_0, texcoord_2_0,
+ !bTranspose );
+
+ newFlagsY = nTemp;
+ p = vTemp;
+ p2 = stTemp;
+ }
+
+ if ( ( nFlagsY & DEGEN_2a ) && ( nFlagsY & DEGEN_2b ) ) {
+ newFlagsY |= DEGEN_2a; newFlagsY |= DEGEN_2b;
+ }
+
+ TesselateSubMatrix( BY, BX->right,
+ offStartY, BX->index,
+ offEndY, offEndX,
+ newFlagsY, newFlagsX,
+ vertex_0_1, vertex_1_1, vertex_2_1,
+ texcoord_0_1, texcoord_1_1, texcoord_2_1,
+ !bTranspose );
+ }
+ else
+ {
+ if ( !BezierCurveTree_isLeaf( BX->left ) ) {
+ TesselateSubMatrix( BX->left, BY,
+ offStartX, offStartY,
+ BX->index, offEndY,
+ newFlagsX, newFlagsY,
+ left, vertex_1_0, tmp,
+ texLeft, texcoord_1_0, texTmp,
+ bTranspose );
+ }
+
+ if ( !BezierCurveTree_isLeaf( BX->right ) ) {
+ TesselateSubMatrix( BX->right, BY,
+ BX->index, offStartY,
+ offEndX, offEndY,
+ newFlagsX, newFlagsY,
+ tmp, vertex_1_1, right,
+ texTmp, texcoord_1_1, texRight,
+ bTranspose );
+ }
+ }
+
+}
+
+void Patch::BuildTesselationCurves( EMatrixMajor major ){
+ std::size_t nArrayStride, length, cross, strideU, strideV;
+ switch ( major )
+ {
+ case ROW:
+ nArrayStride = 1;
+ length = ( m_width - 1 ) >> 1;
+ cross = m_height;
+ strideU = 1;
+ strideV = m_width;
+
+ if ( !m_patchDef3 ) {
+ BezierCurveTreeArray_deleteAll( m_tess.m_curveTreeU );
+ }
+
+ break;
+ case COL:
+ nArrayStride = m_tess.m_nArrayWidth;
+ length = ( m_height - 1 ) >> 1;
+ cross = m_width;
+ strideU = m_width;
+ strideV = 1;
+
+ if ( !m_patchDef3 ) {
+ BezierCurveTreeArray_deleteAll( m_tess.m_curveTreeV );
+ }
+
+ break;
+ default:
+ ERROR_MESSAGE( "neither row-major nor column-major" );
+ return;
+ }
+
+ Array<std::size_t> arrayLength( length );
+ Array<BezierCurveTree*> pCurveTree( length );
+
+ std::size_t nArrayLength = 1;
+
+ if ( m_patchDef3 ) {
+ for ( Array<std::size_t>::iterator i = arrayLength.begin(); i != arrayLength.end(); ++i )
+ {
+ *i = Array<std::size_t>::value_type( ( major == ROW ) ? m_subdivisions_x : m_subdivisions_y );
+ nArrayLength += *i;
+ }
+ }
+ else
+ {
+ // create a list of the horizontal control curves in each column of sub-patches
+ // adaptively tesselate each horizontal control curve in the list
+ // create a binary tree representing the combined tesselation of the list
+ for ( std::size_t i = 0; i != length; ++i )
+ {
+ PatchControl* p1 = m_ctrlTransformed.data() + ( i * 2 * strideU );
+ GSList* pCurveList = 0;
+ for ( std::size_t j = 0; j < cross; j += 2 )
+ {
+ PatchControl* p2 = p1 + strideV;
+ PatchControl* p3 = p2 + strideV;
+
+ // directly taken from one row of control points
+ {
+ BezierCurve* pCurve = new BezierCurve;
+ pCurve->crd = ( p1 + strideU )->m_vertex;
+ pCurve->left = p1->m_vertex;
+ pCurve->right = ( p1 + ( strideU << 1 ) )->m_vertex;
+ pCurveList = g_slist_prepend( pCurveList, pCurve );
+ }
+
+ if ( j + 2 >= cross ) {
+ break;
+ }
+
+ // interpolated from three columns of control points
+ {
+ BezierCurve* pCurve = new BezierCurve;
+ pCurve->crd = vector3_mid( ( p1 + strideU )->m_vertex, ( p3 + strideU )->m_vertex );
+ pCurve->left = vector3_mid( p1->m_vertex, p3->m_vertex );
+ pCurve->right = vector3_mid( ( p1 + ( strideU << 1 ) )->m_vertex, ( p3 + ( strideU << 1 ) )->m_vertex );
+
+ pCurve->crd = vector3_mid( pCurve->crd, ( p2 + strideU )->m_vertex );
+ pCurve->left = vector3_mid( pCurve->left, p2->m_vertex );
+ pCurve->right = vector3_mid( pCurve->right, ( p2 + ( strideU << 1 ) )->m_vertex );
+ pCurveList = g_slist_prepend( pCurveList, pCurve );
+ }
+
+ p1 = p3;
+ }
+
+ pCurveTree[i] = new BezierCurveTree;
+ BezierCurveTree_FromCurveList( pCurveTree[i], pCurveList );
+ for ( GSList* l = pCurveList; l != 0; l = g_slist_next( l ) )
+ {
+ delete static_cast<BezierCurve*>( ( *l ).data );
+ }
+ g_slist_free( pCurveList );
+
+ // set up array indices for binary tree
+ // accumulate subarray width
+ arrayLength[i] = Array<std::size_t>::value_type( BezierCurveTree_Setup( pCurveTree[i], nArrayLength, nArrayStride ) - ( nArrayLength - 1 ) );
+ // accumulate total array width
+ nArrayLength += arrayLength[i];
+ }
+ }
+
+ switch ( major )
+ {
+ case ROW:
+ m_tess.m_nArrayWidth = nArrayLength;
+ std::swap( m_tess.m_arrayWidth, arrayLength );
+
+ if ( !m_patchDef3 ) {
+ std::swap( m_tess.m_curveTreeU, pCurveTree );
+ }
+ break;
+ case COL:
+ m_tess.m_nArrayHeight = nArrayLength;
+ std::swap( m_tess.m_arrayHeight, arrayLength );
+
+ if ( !m_patchDef3 ) {
+ std::swap( m_tess.m_curveTreeV, pCurveTree );
+ }
+ break;
+ }
+}
+
+inline void vertex_assign_ctrl( ArbitraryMeshVertex& vertex, const PatchControl& ctrl ){
+ vertex.vertex = vertex3f_for_vector3( ctrl.m_vertex );
+ vertex.texcoord = texcoord2f_for_vector2( ctrl.m_texcoord );
+}
+
+inline void vertex_clear_normal( ArbitraryMeshVertex& vertex ){
+ vertex.normal = Normal3f( 0, 0, 0 );
+ vertex.tangent = Normal3f( 0, 0, 0 );
+ vertex.bitangent = Normal3f( 0, 0, 0 );
+}
+
+inline void tangents_remove_degenerate( Vector3 tangents[6], Vector2 textureTangents[6], unsigned int flags ){
+ if ( flags & DEGEN_0a ) {
+ const std::size_t i =
+ ( flags & DEGEN_0b )
+ ? ( flags & DEGEN_1a )
+ ? ( flags & DEGEN_1b )
+ ? ( flags & DEGEN_2a )
+ ? 5
+ : 4
+ : 3
+ : 2
+ : 1;
+ tangents[0] = tangents[i];
+ textureTangents[0] = textureTangents[i];
+ }
+ if ( flags & DEGEN_0b ) {
+ const std::size_t i =
+ ( flags & DEGEN_0a )
+ ? ( flags & DEGEN_1b )
+ ? ( flags & DEGEN_1a )
+ ? ( flags & DEGEN_2b )
+ ? 4
+ : 5
+ : 2
+ : 3
+ : 0;
+ tangents[1] = tangents[i];
+ textureTangents[1] = textureTangents[i];
+ }
+ if ( flags & DEGEN_2a ) {
+ const std::size_t i =
+ ( flags & DEGEN_2b )
+ ? ( flags & DEGEN_1a )
+ ? ( flags & DEGEN_1b )
+ ? ( flags & DEGEN_0a )
+ ? 1
+ : 0
+ : 3
+ : 2
+ : 5;
+ tangents[4] = tangents[i];
+ textureTangents[4] = textureTangents[i];
+ }
+ if ( flags & DEGEN_2b ) {
+ const std::size_t i =
+ ( flags & DEGEN_2a )
+ ? ( flags & DEGEN_1b )
+ ? ( flags & DEGEN_1a )
+ ? ( flags & DEGEN_0b )
+ ? 0
+ : 1
+ : 2
+ : 3
+ : 4;
+ tangents[5] = tangents[i];
+ textureTangents[5] = textureTangents[i];
+ }
+}
+
+void bestTangents00( unsigned int degenerateFlags, double dot, double length, std::size_t& index0, std::size_t& index1 ){
+ if ( fabs( dot + length ) < 0.001 ) { // opposing direction = degenerate
+ if ( !( degenerateFlags & DEGEN_1a ) ) { // if this tangent is degenerate we cannot use it
+ index0 = 2;
+ index1 = 0;
+ }
+ else if ( !( degenerateFlags & DEGEN_0b ) ) {
+ index0 = 0;
+ index1 = 1;
+ }
+ else
+ {
+ index0 = 1;
+ index1 = 0;
+ }
+ }
+ else if ( fabs( dot - length ) < 0.001 ) { // same direction = degenerate
+ if ( degenerateFlags & DEGEN_0b ) {
+ index0 = 0;
+ index1 = 1;
+ }
+ else
+ {
+ index0 = 1;
+ index1 = 0;
+ }
+ }
+}
+
+void bestTangents01( unsigned int degenerateFlags, double dot, double length, std::size_t& index0, std::size_t& index1 ){
+ if ( fabs( dot - length ) < 0.001 ) { // same direction = degenerate
+ if ( !( degenerateFlags & DEGEN_1a ) ) { // if this tangent is degenerate we cannot use it
+ index0 = 2;
+ index1 = 1;
+ }
+ else if ( !( degenerateFlags & DEGEN_2b ) ) {
+ index0 = 4;
+ index1 = 0;
+ }
+ else
+ {
+ index0 = 5;
+ index1 = 1;
+ }
+ }
+ else if ( fabs( dot + length ) < 0.001 ) { // opposing direction = degenerate
+ if ( degenerateFlags & DEGEN_2b ) {
+ index0 = 4;
+ index1 = 0;
+ }
+ else
+ {
+ index0 = 5;
+ index1 = 1;
+ }
+ }
+}
+
+void bestTangents10( unsigned int degenerateFlags, double dot, double length, std::size_t& index0, std::size_t& index1 ){
+ if ( fabs( dot - length ) < 0.001 ) { // same direction = degenerate
+ if ( !( degenerateFlags & DEGEN_1b ) ) { // if this tangent is degenerate we cannot use it
+ index0 = 3;
+ index1 = 4;
+ }
+ else if ( !( degenerateFlags & DEGEN_0a ) ) {
+ index0 = 1;
+ index1 = 5;
+ }
+ else
+ {
+ index0 = 0;
+ index1 = 4;
+ }
+ }
+ else if ( fabs( dot + length ) < 0.001 ) { // opposing direction = degenerate
+ if ( degenerateFlags & DEGEN_0a ) {
+ index0 = 1;
+ index1 = 5;
+ }
+ else
+ {
+ index0 = 0;
+ index1 = 4;
+ }
+ }
+}
+
+void bestTangents11( unsigned int degenerateFlags, double dot, double length, std::size_t& index0, std::size_t& index1 ){
+ if ( fabs( dot + length ) < 0.001 ) { // opposing direction = degenerate
+ if ( !( degenerateFlags & DEGEN_1b ) ) { // if this tangent is degenerate we cannot use it
+ index0 = 3;
+ index1 = 5;
+ }
+ else if ( !( degenerateFlags & DEGEN_2a ) ) {
+ index0 = 5;
+ index1 = 4;
+ }
+ else
+ {
+ index0 = 4;
+ index1 = 5;
+ }
+ }
+ else if ( fabs( dot - length ) < 0.001 ) { // same direction = degenerate
+ if ( degenerateFlags & DEGEN_2a ) {
+ index0 = 5;
+ index1 = 4;
+ }
+ else
+ {
+ index0 = 4;
+ index1 = 5;
+ }
+ }
+}
+
+void Patch::accumulateVertexTangentSpace( std::size_t index, Vector3 tangentX[6], Vector3 tangentY[6], Vector2 tangentS[6], Vector2 tangentT[6], std::size_t index0, std::size_t index1 ){
+ {
+ Vector3 normal( vector3_cross( tangentX[index0], tangentY[index1] ) );
+ if ( !vector3_equal( normal, g_vector3_identity ) ) {
+ vector3_add( normal_for_index( m_tess.m_vertices, index ), vector3_normalised( normal ) );
+ }
+ }
+
+ {
+ ArbitraryMeshVertex a, b, c;
+ a.vertex = Vertex3f( 0, 0, 0 );
+ a.texcoord = TexCoord2f( 0, 0 );
+ b.vertex = vertex3f_for_vector3( tangentX[index0] );
+ b.texcoord = texcoord2f_for_vector2( tangentS[index0] );
+ c.vertex = vertex3f_for_vector3( tangentY[index1] );
+ c.texcoord = texcoord2f_for_vector2( tangentT[index1] );
+
+ Vector3 s, t;
+ ArbitraryMeshTriangle_calcTangents( a, b, c, s, t );
+ if ( !vector3_equal( s, g_vector3_identity ) ) {
+ vector3_add( tangent_for_index( m_tess.m_vertices, index ), vector3_normalised( s ) );
+ }
+ if ( !vector3_equal( t, g_vector3_identity ) ) {
+ vector3_add( bitangent_for_index( m_tess.m_vertices, index ), vector3_normalised( t ) );
+ }
+ }