/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "patch.h" #include #include "preferences.h" #include "brush_primit.h" #include "signal/signal.h" Signal0 g_patchTextureChangedCallbacks; void Patch_addTextureChangedCallback( const SignalHandler& handler ){ g_patchTextureChangedCallbacks.connectLast( handler ); } void Patch_textureChanged(){ g_patchTextureChangedCallbacks(); } Shader* PatchInstance::m_state_selpoint; Shader* Patch::m_state_ctrl; Shader* Patch::m_state_lattice; EPatchType Patch::m_type; std::size_t MAX_PATCH_WIDTH = 0; std::size_t MAX_PATCH_HEIGHT = 0; int g_PatchSubdivideThreshold = 4; void BezierCurveTree_Delete( BezierCurveTree *pCurve ){ if ( pCurve ) { BezierCurveTree_Delete( pCurve->left ); BezierCurveTree_Delete( pCurve->right ); delete pCurve; } } std::size_t BezierCurveTree_Setup( BezierCurveTree *pCurve, std::size_t index, std::size_t stride ){ if ( pCurve ) { if ( pCurve->left && pCurve->right ) { index = BezierCurveTree_Setup( pCurve->left, index, stride ); pCurve->index = index * stride; index++; index = BezierCurveTree_Setup( pCurve->right, index, stride ); } else { pCurve->index = BEZIERCURVETREE_MAX_INDEX; } } return index; } bool BezierCurve_IsCurved( BezierCurve *pCurve ){ Vector3 vTemp( vector3_subtracted( pCurve->right, pCurve->left ) ); Vector3 v1( vector3_subtracted( pCurve->crd, pCurve->left ) ); Vector3 v2( vector3_subtracted( pCurve->right, pCurve->crd ) ); if ( vector3_equal( v1, g_vector3_identity ) || vector3_equal( vTemp, v1 ) ) { // return 0 if 1->2 == 0 or 1->2 == 1->3 return false; } vector3_normalise( v1 ); vector3_normalise( v2 ); if ( vector3_equal( v1, v2 ) ) { return false; } Vector3 v3( vTemp ); const double width = vector3_length( v3 ); vector3_scale( v3, 1.0 / width ); if ( vector3_equal( v1, v3 ) && vector3_equal( v2, v3 ) ) { return false; } const double angle = acos( vector3_dot( v1, v2 ) ) / c_pi; const double index = width * angle; if ( index > static_cast( g_PatchSubdivideThreshold ) ) { return true; } return false; } void BezierInterpolate( BezierCurve *pCurve ){ pCurve->left = vector3_mid( pCurve->left, pCurve->crd ); pCurve->right = vector3_mid( pCurve->crd, pCurve->right ); pCurve->crd = vector3_mid( pCurve->left, pCurve->right ); } const std::size_t PATCH_MAX_SUBDIVISION_DEPTH = 16; void BezierCurveTree_FromCurveList( BezierCurveTree *pTree, GSList *pCurveList, std::size_t depth = 0 ){ GSList *pLeftList = 0; GSList *pRightList = 0; BezierCurve *pCurve, *pLeftCurve, *pRightCurve; bool bSplit = false; for ( GSList *l = pCurveList; l; l = l->next ) { pCurve = (BezierCurve *)( l->data ); if ( bSplit || BezierCurve_IsCurved( pCurve ) ) { bSplit = true; pLeftCurve = new BezierCurve; pRightCurve = new BezierCurve; pLeftCurve->left = pCurve->left; pRightCurve->right = pCurve->right; BezierInterpolate( pCurve ); pLeftCurve->crd = pCurve->left; pRightCurve->crd = pCurve->right; pLeftCurve->right = pCurve->crd; pRightCurve->left = pCurve->crd; pLeftList = g_slist_prepend( pLeftList, pLeftCurve ); pRightList = g_slist_prepend( pRightList, pRightCurve ); } } if ( pLeftList != 0 && pRightList != 0 && depth != PATCH_MAX_SUBDIVISION_DEPTH ) { pTree->left = new BezierCurveTree; pTree->right = new BezierCurveTree; BezierCurveTree_FromCurveList( pTree->left, pLeftList, depth + 1 ); BezierCurveTree_FromCurveList( pTree->right, pRightList, depth + 1 ); for ( GSList* l = pLeftList; l != 0; l = g_slist_next( l ) ) { delete (BezierCurve*)l->data; } for ( GSList* l = pRightList; l != 0; l = g_slist_next( l ) ) { delete (BezierCurve*)l->data; } g_slist_free( pLeftList ); g_slist_free( pRightList ); } else { pTree->left = 0; pTree->right = 0; } } int Patch::m_CycleCapIndex = 0; void Patch::setDims( std::size_t w, std::size_t h ){ if ( ( w % 2 ) == 0 ) { w -= 1; } ASSERT_MESSAGE( w <= MAX_PATCH_WIDTH, "patch too wide" ); if ( w > MAX_PATCH_WIDTH ) { w = MAX_PATCH_WIDTH; } else if ( w < MIN_PATCH_WIDTH ) { w = MIN_PATCH_WIDTH; } if ( ( h % 2 ) == 0 ) { m_height -= 1; } ASSERT_MESSAGE( h <= MAX_PATCH_HEIGHT, "patch too tall" ); if ( h > MAX_PATCH_HEIGHT ) { h = MAX_PATCH_HEIGHT; } else if ( h < MIN_PATCH_HEIGHT ) { h = MIN_PATCH_HEIGHT; } m_width = w; m_height = h; if ( m_width * m_height != m_ctrl.size() ) { m_ctrl.resize( m_width * m_height ); onAllocate( m_ctrl.size() ); } } inline const Colour4b& colour_for_index( std::size_t i, std::size_t width ){ return ( i % 2 || ( i / width ) % 2 ) ? colour_inside : colour_corner; } inline bool float_valid( float f ){ return f == f; } bool Patch::isValid() const { if ( !m_width || !m_height ) { return false; } for ( const_iterator i = m_ctrl.begin(); i != m_ctrl.end(); ++i ) { if ( !float_valid( ( *i ).m_vertex.x() ) || !float_valid( ( *i ).m_vertex.y() ) || !float_valid( ( *i ).m_vertex.z() ) || !float_valid( ( *i ).m_texcoord.x() ) || !float_valid( ( *i ).m_texcoord.y() ) ) { globalErrorStream() << "patch has invalid control points\n"; return false; } } return true; } void Patch::UpdateCachedData(){ m_ctrl_vertices.clear(); m_lattice_indices.clear(); if ( !isValid() ) { m_tess.m_numStrips = 0; m_tess.m_lenStrips = 0; m_tess.m_nArrayHeight = 0; m_tess.m_nArrayWidth = 0; m_tess.m_curveTreeU.resize( 0 ); m_tess.m_curveTreeV.resize( 0 ); m_tess.m_indices.resize( 0 ); m_tess.m_vertices.resize( 0 ); m_tess.m_arrayHeight.resize( 0 ); m_tess.m_arrayWidth.resize( 0 ); m_aabb_local = AABB(); return; } BuildTesselationCurves( ROW ); BuildTesselationCurves( COL ); BuildVertexArray(); AccumulateBBox(); IndexBuffer ctrl_indices; m_lattice_indices.reserve( ( ( m_width * ( m_height - 1 ) ) + ( m_height * ( m_width - 1 ) ) ) << 1 ); ctrl_indices.reserve( m_ctrlTransformed.size() ); { UniqueVertexBuffer inserter( m_ctrl_vertices ); for ( iterator i = m_ctrlTransformed.begin(); i != m_ctrlTransformed.end(); ++i ) { ctrl_indices.insert( inserter.insert( pointvertex_quantised( PointVertex( reinterpret_cast( ( *i ).m_vertex ), colour_for_index( i - m_ctrlTransformed.begin(), m_width ) ) ) ) ); } } { for ( IndexBuffer::iterator i = ctrl_indices.begin(); i != ctrl_indices.end(); ++i ) { if ( std::size_t( i - ctrl_indices.begin() ) % m_width ) { m_lattice_indices.insert( *( i - 1 ) ); m_lattice_indices.insert( *i ); } if ( std::size_t( i - ctrl_indices.begin() ) >= m_width ) { m_lattice_indices.insert( *( i - m_width ) ); m_lattice_indices.insert( *i ); } } } #if 0 { Array::iterator first = m_tess.m_indices.begin(); for ( std::size_t s = 0; s < m_tess.m_numStrips; s++ ) { Array::iterator last = first + m_tess.m_lenStrips; for ( Array::iterator i( first ); i + 2 != last; i += 2 ) { ArbitraryMeshTriangle_sumTangents( m_tess.m_vertices[*( i + 0 )], m_tess.m_vertices[*( i + 1 )], m_tess.m_vertices[*( i + 2 )] ); ArbitraryMeshTriangle_sumTangents( m_tess.m_vertices[*( i + 2 )], m_tess.m_vertices[*( i + 1 )], m_tess.m_vertices[*( i + 3 )] ); } first = last; } for ( Array::iterator i = m_tess.m_vertices.begin(); i != m_tess.m_vertices.end(); ++i ) { vector3_normalise( reinterpret_cast( ( *i ).tangent ) ); vector3_normalise( reinterpret_cast( ( *i ).bitangent ) ); } } #endif SceneChangeNotify(); } void Patch::InvertMatrix(){ undoSave(); PatchControlArray_invert( m_ctrl, m_width, m_height ); controlPointsChanged(); } void Patch::TransposeMatrix(){ undoSave(); { Array tmp( m_width * m_height ); copy_ctrl( tmp.data(), m_ctrl.data(), m_ctrl.data() + m_width * m_height ); PatchControlIter from = tmp.data(); for ( std::size_t h = 0; h != m_height; ++h ) { PatchControlIter to = m_ctrl.data() + h; for ( std::size_t w = 0; w != m_width; ++w, ++from, to += m_height ) { *to = *from; } } } { std::size_t tmp = m_width; m_width = m_height; m_height = tmp; } controlPointsChanged(); } void Patch::Redisperse( EMatrixMajor mt ){ std::size_t w, h, width, height, row_stride, col_stride; PatchControl* p1, * p2, * p3; undoSave(); switch ( mt ) { case COL: width = ( m_width - 1 ) >> 1; height = m_height; col_stride = 1; row_stride = m_width; break; case ROW: width = ( m_height - 1 ) >> 1; height = m_width; col_stride = m_width; row_stride = 1; break; default: ERROR_MESSAGE( "neither row-major nor column-major" ); return; } for ( h = 0; h < height; h++ ) { p1 = m_ctrl.data() + ( h * row_stride ); for ( w = 0; w < width; w++ ) { p2 = p1 + col_stride; p3 = p2 + col_stride; p2->m_vertex = vector3_mid( p1->m_vertex, p3->m_vertex ); p1 = p3; } } controlPointsChanged(); } void Patch::Smooth( EMatrixMajor mt ){ std::size_t w, h, width, height, row_stride, col_stride; bool wrap; PatchControl* p1, * p2, * p3, * p2b; undoSave(); switch ( mt ) { case COL: width = ( m_width - 1 ) >> 1; height = m_height; col_stride = 1; row_stride = m_width; break; case ROW: width = ( m_height - 1 ) >> 1; height = m_width; col_stride = m_width; row_stride = 1; break; default: ERROR_MESSAGE( "neither row-major nor column-major" ); return; } wrap = true; for ( h = 0; h < height; h++ ) { p1 = m_ctrl.data() + ( h * row_stride ); p2 = p1 + ( 2 * width ) * col_stride; //globalErrorStream() << "compare " << p1->m_vertex << " and " << p2->m_vertex << "\n"; if ( vector3_length_squared( vector3_subtracted( p1->m_vertex, p2->m_vertex ) ) > 1.0 ) { //globalErrorStream() << "too far\n"; wrap = false; break; } } for ( h = 0; h < height; h++ ) { p1 = m_ctrl.data() + ( h * row_stride ) + col_stride; for ( w = 0; w < width - 1; w++ ) { p2 = p1 + col_stride; p3 = p2 + col_stride; p2->m_vertex = vector3_mid( p1->m_vertex, p3->m_vertex ); p1 = p3; } if ( wrap ) { p1 = m_ctrl.data() + ( h * row_stride ) + ( 2 * width - 1 ) * col_stride; p2 = m_ctrl.data() + ( h * row_stride ); p2b = m_ctrl.data() + ( h * row_stride ) + ( 2 * width ) * col_stride; p3 = m_ctrl.data() + ( h * row_stride ) + col_stride; p2->m_vertex = p2b->m_vertex = vector3_mid( p1->m_vertex, p3->m_vertex ); } } controlPointsChanged(); } void Patch::InsertRemove( bool bInsert, bool bColumn, bool bFirst ){ undoSave(); if ( bInsert ) { if ( bColumn && ( m_width + 2 <= MAX_PATCH_WIDTH ) ) { InsertPoints( COL, bFirst ); } else if ( m_height + 2 <= MAX_PATCH_HEIGHT ) { InsertPoints( ROW, bFirst ); } } else { if ( bColumn && ( m_width - 2 >= MIN_PATCH_WIDTH ) ) { RemovePoints( COL, bFirst ); } else if ( m_height - 2 >= MIN_PATCH_HEIGHT ) { RemovePoints( ROW, bFirst ); } } controlPointsChanged(); } Patch* Patch::MakeCap( Patch* patch, EPatchCap eType, EMatrixMajor mt, bool bFirst ){ std::size_t i, width, height; switch ( mt ) { case ROW: width = m_width; height = m_height; break; case COL: width = m_height; height = m_width; break; default: ERROR_MESSAGE( "neither row-major nor column-major" ); return 0; } Array p( width ); std::size_t nIndex = ( bFirst ) ? 0 : height - 1; if ( mt == ROW ) { for ( i = 0; i < width; i++ ) { p[( bFirst ) ? i : ( width - 1 ) - i] = ctrlAt( nIndex, i ).m_vertex; } } else { for ( i = 0; i < width; i++ ) { p[( bFirst ) ? i : ( width - 1 ) - i] = ctrlAt( i, nIndex ).m_vertex; } } patch->ConstructSeam( eType, p.data(), width ); return patch; } void Patch::FlipTexture( int nAxis ){ undoSave(); for ( PatchControlIter i = m_ctrl.data(); i != m_ctrl.data() + m_ctrl.size(); ++i ) { ( *i ).m_texcoord[nAxis] = -( *i ).m_texcoord[nAxis]; } controlPointsChanged(); } void Patch::TranslateTexture( float s, float t ){ undoSave(); s = -1 * s / m_state->getTexture().width; t = t / m_state->getTexture().height; for ( PatchControlIter i = m_ctrl.data(); i != m_ctrl.data() + m_ctrl.size(); ++i ) { ( *i ).m_texcoord[0] += s; ( *i ).m_texcoord[1] += t; } controlPointsChanged(); } void Patch::ScaleTexture( float s, float t ){ undoSave(); for ( PatchControlIter i = m_ctrl.data(); i != m_ctrl.data() + m_ctrl.size(); ++i ) { ( *i ).m_texcoord[0] *= s; ( *i ).m_texcoord[1] *= t; } controlPointsChanged(); } void Patch::RotateTexture( float angle ){ undoSave(); const float s = static_cast( sin( degrees_to_radians( angle ) ) ); const float c = static_cast( cos( degrees_to_radians( angle ) ) ); for ( PatchControlIter i = m_ctrl.data(); i != m_ctrl.data() + m_ctrl.size(); ++i ) { const float x = ( *i ).m_texcoord[0]; const float y = ( *i ).m_texcoord[1]; ( *i ).m_texcoord[0] = ( x * c ) - ( y * s ); ( *i ).m_texcoord[1] = ( y * c ) + ( x * s ); } controlPointsChanged(); } void Patch::SetTextureRepeat( float s, float t ){ std::size_t w, h; float si, ti, sc, tc; PatchControl *pDest; undoSave(); si = s / (float)( m_width - 1 ); ti = t / (float)( m_height - 1 ); pDest = m_ctrl.data(); for ( h = 0, tc = 0.0f; h < m_height; h++, tc += ti ) { for ( w = 0, sc = 0.0f; w < m_width; w++, sc += si ) { pDest->m_texcoord[0] = sc; pDest->m_texcoord[1] = tc; pDest++; } } controlPointsChanged(); } /* void Patch::SetTextureInfo(texdef_t *pt) { if(pt->getShift()[0] || pt->getShift()[1]) TranslateTexture (pt->getShift()[0], pt->getShift()[1]); else if(pt->getScale()[0] || pt->getScale()[1]) { if(pt->getScale()[0] == 0.0f) pt->setScale(0, 1.0f); if(pt->getScale()[1] == 0.0f) pt->setScale(1, 1.0f); ScaleTexture (pt->getScale()[0], pt->getScale()[1]); } else if(pt->rotate) RotateTexture (pt->rotate); } */ inline int texture_axis( const Vector3& normal ){ // axis dominance order: Z, X, Y return ( normal.x() >= normal.y() ) ? ( normal.x() > normal.z() ) ? 0 : 2 : ( normal.y() > normal.z() ) ? 1 : 2; } void Patch::CapTexture(){ const PatchControl& p1 = m_ctrl[m_width]; const PatchControl& p2 = m_ctrl[m_width * ( m_height - 1 )]; const PatchControl& p3 = m_ctrl[( m_width * m_height ) - 1]; Vector3 normal( g_vector3_identity ); { Vector3 tmp( vector3_cross( vector3_subtracted( p2.m_vertex, m_ctrl[0].m_vertex ), vector3_subtracted( p3.m_vertex, m_ctrl[0].m_vertex ) ) ); if ( !vector3_equal( tmp, g_vector3_identity ) ) { vector3_add( normal, tmp ); } } { Vector3 tmp( vector3_cross( vector3_subtracted( p1.m_vertex, p3.m_vertex ), vector3_subtracted( m_ctrl[0].m_vertex, p3.m_vertex ) ) ); if ( !vector3_equal( tmp, g_vector3_identity ) ) { vector3_add( normal, tmp ); } } ProjectTexture( texture_axis( normal ) ); } // uses longest parallel chord to calculate texture coords for each row/col void Patch::NaturalTexture(){ undoSave(); { float fSize = (float)m_state->getTexture().width * Texdef_getDefaultTextureScale(); double texBest = 0; double tex = 0; PatchControl* pWidth = m_ctrl.data(); for ( std::size_t w = 0; w < m_width; w++, pWidth++ ) { { PatchControl* pHeight = pWidth; for ( std::size_t h = 0; h < m_height; h++, pHeight += m_width ) pHeight->m_texcoord[0] = static_cast( tex ); } if ( w + 1 == m_width ) { break; } { PatchControl* pHeight = pWidth; for ( std::size_t h = 0; h < m_height; h++, pHeight += m_width ) { Vector3 v( vector3_subtracted( pHeight->m_vertex, ( pHeight + 1 )->m_vertex ) ); double length = tex + ( vector3_length( v ) / fSize ); if ( fabs( length ) > texBest ) { texBest = length; } } } tex = texBest; } } { float fSize = -(float)m_state->getTexture().height * Texdef_getDefaultTextureScale(); double texBest = 0; double tex = 0; PatchControl* pHeight = m_ctrl.data(); for ( std::size_t h = 0; h < m_height; h++, pHeight += m_width ) { { PatchControl* pWidth = pHeight; for ( std::size_t w = 0; w < m_width; w++, pWidth++ ) pWidth->m_texcoord[1] = static_cast( tex ); } if ( h + 1 == m_height ) { break; } { PatchControl* pWidth = pHeight; for ( std::size_t w = 0; w < m_width; w++, pWidth++ ) { Vector3 v( vector3_subtracted( pWidth->m_vertex, ( pWidth + m_width )->m_vertex ) ); double length = tex + ( vector3_length( v ) / fSize ); if ( fabs( length ) > texBest ) { texBest = length; } } } tex = texBest; } } controlPointsChanged(); } // private: void Patch::AccumulateBBox(){ m_aabb_local = AABB(); for ( PatchControlArray::iterator i = m_ctrlTransformed.begin(); i != m_ctrlTransformed.end(); ++i ) { aabb_extend_by_point_safe( m_aabb_local, ( *i ).m_vertex ); } m_boundsChanged(); m_lightsChanged(); } void Patch::InsertPoints( EMatrixMajor mt, bool bFirst ){ std::size_t width, height, row_stride, col_stride; switch ( mt ) { case ROW: col_stride = 1; row_stride = m_width; width = m_width; height = m_height; break; case COL: col_stride = m_width; row_stride = 1; width = m_height; height = m_width; break; default: ERROR_MESSAGE( "neither row-major nor column-major" ); return; } std::size_t pos = 0; { PatchControl* p1 = m_ctrl.data(); /* if(GlobalSelectionSystem().countSelected() != 0) { scene::Instance& instance = GlobalSelectionSystem().ultimateSelected(); PatchInstance* patch = Instance_getPatch(instance); patch->m_selectable.isSelected(); } */ for ( std::size_t w = 0; w != width; ++w, p1 += col_stride ) { { PatchControl* p2 = p1; for ( std::size_t h = 1; h < height; h += 2, p2 += 2 * row_stride ) { if ( 0 ) { //p2->m_selectable.isSelected()) pos = h; break; } } if ( pos != 0 ) { break; } } { PatchControl* p2 = p1; for ( std::size_t h = 0; h < height; h += 2, p2 += 2 * row_stride ) { if ( 0 ) { //p2->m_selectable.isSelected()) pos = h; break; } } if ( pos != 0 ) { break; } } } } Array tmp( m_ctrl ); std::size_t row_stride2, col_stride2; switch ( mt ) { case ROW: setDims( m_width, m_height + 2 ); col_stride2 = 1; row_stride2 = m_width; break; case COL: setDims( m_width + 2, m_height ); col_stride2 = m_width; row_stride2 = 1; break; default: ERROR_MESSAGE( "neither row-major nor column-major" ); return; } if ( bFirst ) { pos = height - 1; } else { pos = 2; } if ( pos >= height ) { if ( bFirst ) { pos = height - 1; } else { pos = 2; } } else if ( pos == 0 ) { pos = 2; } else if ( pos % 2 ) { ++pos; } for ( std::size_t w = 0; w != width; ++w ) { PatchControl* p1 = tmp.data() + ( w * col_stride ); PatchControl* p2 = m_ctrl.data() + ( w * col_stride2 ); for ( std::size_t h = 0; h != height; ++h, p2 += row_stride2, p1 += row_stride ) { if ( h == pos ) { p2 += 2 * row_stride2; } *p2 = *p1; } p1 = tmp.data() + ( w * col_stride + pos * row_stride ); p2 = m_ctrl.data() + ( w * col_stride2 + pos * row_stride2 ); PatchControl* r2a = ( p2 + row_stride2 ); PatchControl* r2b = ( p2 - row_stride2 ); PatchControl* c2a = ( p1 - 2 * row_stride ); PatchControl* c2b = ( p1 - row_stride ); // set two new row points *( p2 + 2 * row_stride2 ) = *p1; *r2a = *c2b; for ( std::size_t i = 0; i != 3; ++i ) { r2a->m_vertex[i] = float_mid( c2b->m_vertex[i], p1->m_vertex[i] ); r2b->m_vertex[i] = float_mid( c2a->m_vertex[i], c2b->m_vertex[i] ); p2->m_vertex[i] = float_mid( r2a->m_vertex[i], r2b->m_vertex[i] ); } for ( std::size_t i = 0; i != 2; ++i ) { r2a->m_texcoord[i] = float_mid( c2b->m_texcoord[i], p1->m_texcoord[i] ); r2b->m_texcoord[i] = float_mid( c2a->m_texcoord[i], c2b->m_texcoord[i] ); p2->m_texcoord[i] = float_mid( r2a->m_texcoord[i], r2b->m_texcoord[i] ); } } } void Patch::RemovePoints( EMatrixMajor mt, bool bFirst ){ std::size_t width, height, row_stride, col_stride; switch ( mt ) { case ROW: col_stride = 1; row_stride = m_width; width = m_width; height = m_height; break; case COL: col_stride = m_width; row_stride = 1; width = m_height; height = m_width; break; default: ERROR_MESSAGE( "neither row-major nor column-major" ); return; } std::size_t pos = 0; { PatchControl* p1 = m_ctrl.data(); for ( std::size_t w = 0; w != width; ++w, p1 += col_stride ) { { PatchControl* p2 = p1; for ( std::size_t h = 1; h < height; h += 2, p2 += 2 * row_stride ) { if ( 0 ) { //p2->m_selectable.isSelected()) pos = h; break; } } if ( pos != 0 ) { break; } } { PatchControl* p2 = p1; for ( std::size_t h = 0; h < height; h += 2, p2 += 2 * row_stride ) { if ( 0 ) { //p2->m_selectable.isSelected()) pos = h; break; } } if ( pos != 0 ) { break; } } } } Array tmp( m_ctrl ); std::size_t row_stride2, col_stride2; switch ( mt ) { case ROW: setDims( m_width, m_height - 2 ); col_stride2 = 1; row_stride2 = m_width; break; case COL: setDims( m_width - 2, m_height ); col_stride2 = m_width; row_stride2 = 1; break; default: ERROR_MESSAGE( "neither row-major nor column-major" ); return; } if ( bFirst ) { pos = height - 3; } else { pos = 2; } if ( pos >= height ) { if ( bFirst ) { pos = height - 3; } else { pos = 2; } } else if ( pos == 0 ) { pos = 2; } else if ( pos > height - 3 ) { pos = height - 3; } else if ( pos % 2 ) { ++pos; } for ( std::size_t w = 0; w != width; w++ ) { PatchControl* p1 = tmp.data() + ( w * col_stride ); PatchControl* p2 = m_ctrl.data() + ( w * col_stride2 ); for ( std::size_t h = 0; h != height; ++h, p2 += row_stride2, p1 += row_stride ) { if ( h == pos ) { p1 += 2 * row_stride2; h += 2; } *p2 = *p1; } p1 = tmp.data() + ( w * col_stride + pos * row_stride ); p2 = m_ctrl.data() + ( w * col_stride2 + pos * row_stride2 ); for ( std::size_t i = 0; i < 3; i++ ) { ( p2 - row_stride2 )->m_vertex[i] = ( ( p1 + 2 * row_stride )->m_vertex[i] + ( p1 - 2 * row_stride )->m_vertex[i] ) * 0.5f; ( p2 - row_stride2 )->m_vertex[i] = ( p2 - row_stride2 )->m_vertex[i] + ( 2.0f * ( ( p1 )->m_vertex[i] - ( p2 - row_stride2 )->m_vertex[i] ) ); } for ( std::size_t i = 0; i < 2; i++ ) { ( p2 - row_stride2 )->m_texcoord[i] = ( ( p1 + 2 * row_stride )->m_texcoord[i] + ( p1 - 2 * row_stride )->m_texcoord[i] ) * 0.5f; ( p2 - row_stride2 )->m_texcoord[i] = ( p2 - row_stride2 )->m_texcoord[i] + ( 2.0f * ( ( p1 )->m_texcoord[i] - ( p2 - row_stride2 )->m_texcoord[i] ) ); } } } void Patch::ConstructSeam( EPatchCap eType, Vector3* p, std::size_t width ){ switch ( eType ) { case eCapIBevel: { setDims( 3, 3 ); m_ctrl[0].m_vertex = p[0]; m_ctrl[1].m_vertex = p[1]; m_ctrl[2].m_vertex = p[1]; m_ctrl[3].m_vertex = p[1]; m_ctrl[4].m_vertex = p[1]; m_ctrl[5].m_vertex = p[1]; m_ctrl[6].m_vertex = p[2]; m_ctrl[7].m_vertex = p[1]; m_ctrl[8].m_vertex = p[1]; } break; case eCapBevel: { setDims( 3, 3 ); Vector3 p3( vector3_added( p[2], vector3_subtracted( p[0], p[1] ) ) ); m_ctrl[0].m_vertex = p3; m_ctrl[1].m_vertex = p3; m_ctrl[2].m_vertex = p[2]; m_ctrl[3].m_vertex = p3; m_ctrl[4].m_vertex = p3; m_ctrl[5].m_vertex = p[1]; m_ctrl[6].m_vertex = p3; m_ctrl[7].m_vertex = p3; m_ctrl[8].m_vertex = p[0]; } break; case eCapEndCap: { Vector3 p5( vector3_mid( p[0], p[4] ) ); setDims( 3, 3 ); m_ctrl[0].m_vertex = p[0]; m_ctrl[1].m_vertex = p5; m_ctrl[2].m_vertex = p[4]; m_ctrl[3].m_vertex = p[1]; m_ctrl[4].m_vertex = p[2]; m_ctrl[5].m_vertex = p[3]; m_ctrl[6].m_vertex = p[2]; m_ctrl[7].m_vertex = p[2]; m_ctrl[8].m_vertex = p[2]; } break; case eCapIEndCap: { setDims( 5, 3 ); m_ctrl[0].m_vertex = p[4]; m_ctrl[1].m_vertex = p[3]; m_ctrl[2].m_vertex = p[2]; m_ctrl[3].m_vertex = p[1]; m_ctrl[4].m_vertex = p[0]; m_ctrl[5].m_vertex = p[3]; m_ctrl[6].m_vertex = p[3]; m_ctrl[7].m_vertex = p[2]; m_ctrl[8].m_vertex = p[1]; m_ctrl[9].m_vertex = p[1]; m_ctrl[10].m_vertex = p[3]; m_ctrl[11].m_vertex = p[3]; m_ctrl[12].m_vertex = p[2]; m_ctrl[13].m_vertex = p[1]; m_ctrl[14].m_vertex = p[1]; } break; case eCapCylinder: { std::size_t mid = ( width - 1 ) >> 1; bool degenerate = ( mid % 2 ) != 0; std::size_t newHeight = mid + ( degenerate ? 2 : 1 ); setDims( 3, newHeight ); if ( degenerate ) { ++mid; for ( std::size_t i = width; i != width + 2; ++i ) { p[i] = p[width - 1]; } } { PatchControl* pCtrl = m_ctrl.data(); for ( std::size_t i = 0; i != m_height; ++i, pCtrl += m_width ) { pCtrl->m_vertex = p[i]; } } { PatchControl* pCtrl = m_ctrl.data() + 2; std::size_t h = m_height - 1; for ( std::size_t i = 0; i != m_height; ++i, pCtrl += m_width ) { pCtrl->m_vertex = p[h + ( h - i )]; } } Redisperse( COL ); } break; default: ERROR_MESSAGE( "invalid patch-cap type" ); return; } CapTexture(); controlPointsChanged(); } void Patch::ProjectTexture( int nAxis ){ undoSave(); int s, t; switch ( nAxis ) { case 2: s = 0; t = 1; break; case 0: s = 1; t = 2; break; case 1: s = 0; t = 2; break; default: ERROR_MESSAGE( "invalid axis" ); return; } float fWidth = 1 / ( m_state->getTexture().width * Texdef_getDefaultTextureScale() ); float fHeight = 1 / ( m_state->getTexture().height * -Texdef_getDefaultTextureScale() ); for ( PatchControlIter i = m_ctrl.data(); i != m_ctrl.data() + m_ctrl.size(); ++i ) { ( *i ).m_texcoord[0] = ( *i ).m_vertex[s] * fWidth; ( *i ).m_texcoord[1] = ( *i ).m_vertex[t] * fHeight; } controlPointsChanged(); } 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); int i, j; float f = 1 / cos( M_PI / n ); for ( 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 ( 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); int i, j; float f = 1 / cos( M_PI / n ); for ( i = 0; i < width; ++i ) { float angle = ( M_PI * i ) / n; for ( 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); int i, j; float f = 1 / cos( M_PI / n ); float g = 1 / cos( M_PI / ( 2 * m ) ); for ( i = 0; i < width; ++i ) { float angle = ( M_PI * i ) / n; for ( 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(); } #define DEGEN_0a 0x01 #define DEGEN_1a 0x02 #define DEGEN_2a 0x04 #define DEGEN_0b 0x08 #define DEGEN_1b 0x10 #define DEGEN_2b 0x20 #define SPLIT 0x40 #define 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& vertices, std::size_t index ){ return reinterpret_cast( vertices[index].texcoord ); } inline Vector3& vertex_for_index( Array& vertices, std::size_t index ){ return reinterpret_cast( vertices[index].vertex ); } inline Vector3& normal_for_index( Array& vertices, std::size_t index ){ return reinterpret_cast( vertices[index].normal ); } inline Vector3& tangent_for_index( Array& vertices, std::size_t index ){ return reinterpret_cast( vertices[index].tangent ); } inline Vector3& bitangent_for_index( Array& vertices, std::size_t index ){ return reinterpret_cast( vertices[index].bitangent ); } inline const Vector2& texcoord_for_index( const Array& vertices, std::size_t index ){ return reinterpret_cast( vertices[index].texcoord ); } inline const Vector3& vertex_for_index( const Array& vertices, std::size_t index ){ return reinterpret_cast( vertices[index].vertex ); } inline const Vector3& normal_for_index( const Array& vertices, std::size_t index ){ return reinterpret_cast( vertices[index].normal ); } inline const Vector3& tangent_for_index( const Array& vertices, std::size_t index ){ return reinterpret_cast( vertices[index].tangent ); } inline const Vector3& bitangent_for_index( const Array& vertices, std::size_t index ){ return reinterpret_cast( 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::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::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::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 arrayLength( length ); Array pCurveTree( length ); std::size_t nArrayLength = 1; if ( m_patchDef3 ) { for ( Array::iterator i = arrayLength.begin(); i != arrayLength.end(); ++i ) { *i = Array::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( ( *l ).data ); } g_slist_free( pCurveList ); // set up array indices for binary tree // accumulate subarray width arrayLength[i] = Array::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 ) ); } } } const std::size_t PATCH_MAX_VERTEX_ARRAY = 1048576; void Patch::BuildVertexArray(){ const std::size_t strideU = 1; const std::size_t strideV = m_width; const std::size_t numElems = m_tess.m_nArrayWidth * m_tess.m_nArrayHeight; // total number of elements in vertex array const bool bWidthStrips = ( m_tess.m_nArrayWidth >= m_tess.m_nArrayHeight ); // decide if horizontal strips are longer than vertical // allocate vertex, normal, texcoord and primitive-index arrays m_tess.m_vertices.resize( numElems ); m_tess.m_indices.resize( m_tess.m_nArrayWidth * 2 * ( m_tess.m_nArrayHeight - 1 ) ); // set up strip indices if ( bWidthStrips ) { m_tess.m_numStrips = m_tess.m_nArrayHeight - 1; m_tess.m_lenStrips = m_tess.m_nArrayWidth * 2; for ( std::size_t i = 0; i < m_tess.m_nArrayWidth; i++ ) { for ( std::size_t j = 0; j < m_tess.m_numStrips; j++ ) { m_tess.m_indices[( j * m_tess.m_lenStrips ) + i * 2] = RenderIndex( j * m_tess.m_nArrayWidth + i ); m_tess.m_indices[( j * m_tess.m_lenStrips ) + i * 2 + 1] = RenderIndex( ( j + 1 ) * m_tess.m_nArrayWidth + i ); // reverse because radiant uses CULL_FRONT //m_tess.m_indices[(j*m_tess.m_lenStrips)+i*2+1] = RenderIndex(j*m_tess.m_nArrayWidth+i); //m_tess.m_indices[(j*m_tess.m_lenStrips)+i*2] = RenderIndex((j+1)*m_tess.m_nArrayWidth+i); } } } else { m_tess.m_numStrips = m_tess.m_nArrayWidth - 1; m_tess.m_lenStrips = m_tess.m_nArrayHeight * 2; for ( std::size_t i = 0; i < m_tess.m_nArrayHeight; i++ ) { for ( std::size_t j = 0; j < m_tess.m_numStrips; j++ ) { m_tess.m_indices[( j * m_tess.m_lenStrips ) + i * 2] = RenderIndex( ( ( m_tess.m_nArrayHeight - 1 ) - i ) * m_tess.m_nArrayWidth + j ); m_tess.m_indices[( j * m_tess.m_lenStrips ) + i * 2 + 1] = RenderIndex( ( ( m_tess.m_nArrayHeight - 1 ) - i ) * m_tess.m_nArrayWidth + j + 1 ); // reverse because radiant uses CULL_FRONT //m_tess.m_indices[(j*m_tess.m_lenStrips)+i*2+1] = RenderIndex(((m_tess.m_nArrayHeight-1)-i)*m_tess.m_nArrayWidth+j); //m_tess.m_indices[(j*m_tess.m_lenStrips)+i*2] = RenderIndex(((m_tess.m_nArrayHeight-1)-i)*m_tess.m_nArrayWidth+j+1); } } } { PatchControlIter pCtrl = m_ctrlTransformed.data(); for ( std::size_t j = 0, offStartY = 0; j + 1 < m_height; j += 2, pCtrl += ( strideU + strideV ) ) { // set up array offsets for this sub-patch const bool leafY = ( m_patchDef3 ) ? false : BezierCurveTree_isLeaf( m_tess.m_curveTreeV[j >> 1] ); const std::size_t offMidY = ( m_patchDef3 ) ? 0 : m_tess.m_curveTreeV[j >> 1]->index; const std::size_t widthY = m_tess.m_arrayHeight[j >> 1] * m_tess.m_nArrayWidth; const std::size_t offEndY = offStartY + widthY; for ( std::size_t i = 0, offStartX = 0; i + 1 < m_width; i += 2, pCtrl += ( strideU << 1 ) ) { const bool leafX = ( m_patchDef3 ) ? false : BezierCurveTree_isLeaf( m_tess.m_curveTreeU[i >> 1] ); const std::size_t offMidX = ( m_patchDef3 ) ? 0 : m_tess.m_curveTreeU[i >> 1]->index; const std::size_t widthX = m_tess.m_arrayWidth[i >> 1]; const std::size_t offEndX = offStartX + widthX; PatchControl *subMatrix[3][3]; subMatrix[0][0] = pCtrl; subMatrix[0][1] = subMatrix[0][0] + strideU; subMatrix[0][2] = subMatrix[0][1] + strideU; subMatrix[1][0] = subMatrix[0][0] + strideV; subMatrix[1][1] = subMatrix[1][0] + strideU; subMatrix[1][2] = subMatrix[1][1] + strideU; subMatrix[2][0] = subMatrix[1][0] + strideV; subMatrix[2][1] = subMatrix[2][0] + strideU; subMatrix[2][2] = subMatrix[2][1] + strideU; // assign on-patch control points to vertex array if ( i == 0 && j == 0 ) { vertex_clear_normal( m_tess.m_vertices[offStartX + offStartY] ); } vertex_assign_ctrl( m_tess.m_vertices[offStartX + offStartY], *subMatrix[0][0] ); if ( j == 0 ) { vertex_clear_normal( m_tess.m_vertices[offEndX + offStartY] ); } vertex_assign_ctrl( m_tess.m_vertices[offEndX + offStartY], *subMatrix[0][2] ); if ( i == 0 ) { vertex_clear_normal( m_tess.m_vertices[offStartX + offEndY] ); } vertex_assign_ctrl( m_tess.m_vertices[offStartX + offEndY], *subMatrix[2][0] ); vertex_clear_normal( m_tess.m_vertices[offEndX + offEndY] ); vertex_assign_ctrl( m_tess.m_vertices[offEndX + offEndY], *subMatrix[2][2] ); if ( !m_patchDef3 ) { // assign remaining control points to vertex array if ( !leafX ) { vertex_assign_ctrl( m_tess.m_vertices[offMidX + offStartY], *subMatrix[0][1] ); vertex_assign_ctrl( m_tess.m_vertices[offMidX + offEndY], *subMatrix[2][1] ); } if ( !leafY ) { vertex_assign_ctrl( m_tess.m_vertices[offStartX + offMidY], *subMatrix[1][0] ); vertex_assign_ctrl( m_tess.m_vertices[offEndX + offMidY], *subMatrix[1][2] ); if ( !leafX ) { vertex_assign_ctrl( m_tess.m_vertices[offMidX + offMidY], *subMatrix[1][1] ); } } } // test all 12 edges for degeneracy unsigned int nFlagsX = subarray_get_degen( pCtrl, strideU, strideV ); unsigned int nFlagsY = subarray_get_degen( pCtrl, strideV, strideU ); Vector3 tangentX[6], tangentY[6]; Vector2 tangentS[6], tangentT[6]; // set up tangents for each of the 12 edges if they were not degenerate if ( !( nFlagsX & DEGEN_0a ) ) { tangentX[0] = vector3_subtracted( subMatrix[0][1]->m_vertex, subMatrix[0][0]->m_vertex ); tangentS[0] = vector2_subtracted( subMatrix[0][1]->m_texcoord, subMatrix[0][0]->m_texcoord ); } if ( !( nFlagsX & DEGEN_0b ) ) { tangentX[1] = vector3_subtracted( subMatrix[0][2]->m_vertex, subMatrix[0][1]->m_vertex ); tangentS[1] = vector2_subtracted( subMatrix[0][2]->m_texcoord, subMatrix[0][1]->m_texcoord ); } if ( !( nFlagsX & DEGEN_1a ) ) { tangentX[2] = vector3_subtracted( subMatrix[1][1]->m_vertex, subMatrix[1][0]->m_vertex ); tangentS[2] = vector2_subtracted( subMatrix[1][1]->m_texcoord, subMatrix[1][0]->m_texcoord ); } if ( !( nFlagsX & DEGEN_1b ) ) { tangentX[3] = vector3_subtracted( subMatrix[1][2]->m_vertex, subMatrix[1][1]->m_vertex ); tangentS[3] = vector2_subtracted( subMatrix[1][2]->m_texcoord, subMatrix[1][1]->m_texcoord ); } if ( !( nFlagsX & DEGEN_2a ) ) { tangentX[4] = vector3_subtracted( subMatrix[2][1]->m_vertex, subMatrix[2][0]->m_vertex ); tangentS[4] = vector2_subtracted( subMatrix[2][1]->m_texcoord, subMatrix[2][0]->m_texcoord ); } if ( !( nFlagsX & DEGEN_2b ) ) { tangentX[5] = vector3_subtracted( subMatrix[2][2]->m_vertex, subMatrix[2][1]->m_vertex ); tangentS[5] = vector2_subtracted( subMatrix[2][2]->m_texcoord, subMatrix[2][1]->m_texcoord ); } if ( !( nFlagsY & DEGEN_0a ) ) { tangentY[0] = vector3_subtracted( subMatrix[1][0]->m_vertex, subMatrix[0][0]->m_vertex ); tangentT[0] = vector2_subtracted( subMatrix[1][0]->m_texcoord, subMatrix[0][0]->m_texcoord ); } if ( !( nFlagsY & DEGEN_0b ) ) { tangentY[1] = vector3_subtracted( subMatrix[2][0]->m_vertex, subMatrix[1][0]->m_vertex ); tangentT[1] = vector2_subtracted( subMatrix[2][0]->m_texcoord, subMatrix[1][0]->m_texcoord ); } if ( !( nFlagsY & DEGEN_1a ) ) { tangentY[2] = vector3_subtracted( subMatrix[1][1]->m_vertex, subMatrix[0][1]->m_vertex ); tangentT[2] = vector2_subtracted( subMatrix[1][1]->m_texcoord, subMatrix[0][1]->m_texcoord ); } if ( !( nFlagsY & DEGEN_1b ) ) { tangentY[3] = vector3_subtracted( subMatrix[2][1]->m_vertex, subMatrix[1][1]->m_vertex ); tangentT[3] = vector2_subtracted( subMatrix[2][1]->m_texcoord, subMatrix[1][1]->m_texcoord ); } if ( !( nFlagsY & DEGEN_2a ) ) { tangentY[4] = vector3_subtracted( subMatrix[1][2]->m_vertex, subMatrix[0][2]->m_vertex ); tangentT[4] = vector2_subtracted( subMatrix[1][2]->m_texcoord, subMatrix[0][2]->m_texcoord ); } if ( !( nFlagsY & DEGEN_2b ) ) { tangentY[5] = vector3_subtracted( subMatrix[2][2]->m_vertex, subMatrix[1][2]->m_vertex ); tangentT[5] = vector2_subtracted( subMatrix[2][2]->m_texcoord, subMatrix[1][2]->m_texcoord ); } // set up remaining edge tangents by borrowing the tangent from the closest parallel non-degenerate edge tangents_remove_degenerate( tangentX, tangentS, nFlagsX ); tangents_remove_degenerate( tangentY, tangentT, nFlagsY ); { // x=0, y=0 std::size_t index = offStartX + offStartY; std::size_t index0 = 0; std::size_t index1 = 0; double dot = vector3_dot( tangentX[index0], tangentY[index1] ); double length = vector3_length( tangentX[index0] ) * vector3_length( tangentY[index1] ); bestTangents00( nFlagsX, dot, length, index0, index1 ); accumulateVertexTangentSpace( index, tangentX, tangentY, tangentS, tangentT, index0, index1 ); } { // x=1, y=0 std::size_t index = offEndX + offStartY; std::size_t index0 = 1; std::size_t index1 = 4; double dot = vector3_dot( tangentX[index0],tangentY[index1] ); double length = vector3_length( tangentX[index0] ) * vector3_length( tangentY[index1] ); bestTangents10( nFlagsX, dot, length, index0, index1 ); accumulateVertexTangentSpace( index, tangentX, tangentY, tangentS, tangentT, index0, index1 ); } { // x=0, y=1 std::size_t index = offStartX + offEndY; std::size_t index0 = 4; std::size_t index1 = 1; double dot = vector3_dot( tangentX[index0], tangentY[index1] ); double length = vector3_length( tangentX[index1] ) * vector3_length( tangentY[index1] ); bestTangents01( nFlagsX, dot, length, index0, index1 ); accumulateVertexTangentSpace( index, tangentX, tangentY, tangentS, tangentT, index0, index1 ); } { // x=1, y=1 std::size_t index = offEndX + offEndY; std::size_t index0 = 5; std::size_t index1 = 5; double dot = vector3_dot( tangentX[index0],tangentY[index1] ); double length = vector3_length( tangentX[index0] ) * vector3_length( tangentY[index1] ); bestTangents11( nFlagsX, dot, length, index0, index1 ); accumulateVertexTangentSpace( index, tangentX, tangentY, tangentS, tangentT, index0, index1 ); } //normalise normals that won't be accumulated again if ( i != 0 || j != 0 ) { normalise_safe( normal_for_index( m_tess.m_vertices, offStartX + offStartY ) ); normalise_safe( tangent_for_index( m_tess.m_vertices, offStartX + offStartY ) ); normalise_safe( bitangent_for_index( m_tess.m_vertices, offStartX + offStartY ) ); } if ( i + 3 == m_width ) { normalise_safe( normal_for_index( m_tess.m_vertices, offEndX + offStartY ) ); normalise_safe( tangent_for_index( m_tess.m_vertices, offEndX + offStartY ) ); normalise_safe( bitangent_for_index( m_tess.m_vertices, offEndX + offStartY ) ); } if ( j + 3 == m_height ) { normalise_safe( normal_for_index( m_tess.m_vertices, offStartX + offEndY ) ); normalise_safe( tangent_for_index( m_tess.m_vertices, offStartX + offEndY ) ); normalise_safe( bitangent_for_index( m_tess.m_vertices, offStartX + offEndY ) ); } if ( i + 3 == m_width && j + 3 == m_height ) { normalise_safe( normal_for_index( m_tess.m_vertices, offEndX + offEndY ) ); normalise_safe( tangent_for_index( m_tess.m_vertices, offEndX + offEndY ) ); normalise_safe( bitangent_for_index( m_tess.m_vertices, offEndX + offEndY ) ); } // set flags to average normals between shared edges if ( j != 0 ) { nFlagsX |= AVERAGE; } if ( i != 0 ) { nFlagsY |= AVERAGE; } // set flags to save evaluating shared edges twice nFlagsX |= SPLIT; nFlagsY |= SPLIT; // if the patch is curved.. tesselate recursively // use the relevant control curves for this sub-patch if ( m_patchDef3 ) { TesselateSubMatrixFixed( m_tess.m_vertices.data() + offStartX + offStartY, 1, m_tess.m_nArrayWidth, nFlagsX, nFlagsY, subMatrix ); } else { if ( !leafX ) { TesselateSubMatrix( m_tess.m_curveTreeU[i >> 1], m_tess.m_curveTreeV[j >> 1], offStartX, offStartY, offEndX, offEndY, // array offsets nFlagsX, nFlagsY, subMatrix[1][0]->m_vertex, subMatrix[1][1]->m_vertex, subMatrix[1][2]->m_vertex, subMatrix[1][0]->m_texcoord, subMatrix[1][1]->m_texcoord, subMatrix[1][2]->m_texcoord, false ); } else if ( !leafY ) { TesselateSubMatrix( m_tess.m_curveTreeV[j >> 1], m_tess.m_curveTreeU[i >> 1], offStartY, offStartX, offEndY, offEndX, // array offsets nFlagsY, nFlagsX, subMatrix[0][1]->m_vertex, subMatrix[1][1]->m_vertex, subMatrix[2][1]->m_vertex, subMatrix[0][1]->m_texcoord, subMatrix[1][1]->m_texcoord, subMatrix[2][1]->m_texcoord, true ); } } offStartX = offEndX; } offStartY = offEndY; } } } class PatchFilterWrapper : public Filter { bool m_active; bool m_invert; PatchFilter& m_filter; public: PatchFilterWrapper( PatchFilter& filter, bool invert ) : m_invert( invert ), m_filter( filter ){ } void setActive( bool active ){ m_active = active; } bool active(){ return m_active; } bool filter( const Patch& patch ){ return m_invert ^ m_filter.filter( patch ); } }; typedef std::list PatchFilters; PatchFilters g_patchFilters; void add_patch_filter( PatchFilter& filter, int mask, bool invert ){ g_patchFilters.push_back( PatchFilterWrapper( filter, invert ) ); GlobalFilterSystem().addFilter( g_patchFilters.back(), mask ); } bool patch_filtered( Patch& patch ){ for ( PatchFilters::iterator i = g_patchFilters.begin(); i != g_patchFilters.end(); ++i ) { if ( ( *i ).active() && ( *i ).filter( patch ) ) { return true; } } return false; }