From 2426697f65fe8b51e01ef68261328d43dc4ae45e Mon Sep 17 00:00:00 2001 From: Garux Date: Mon, 10 Jun 2019 15:17:57 +0300 Subject: [PATCH] UV Tool: add patch support patch controls move modifiers: shift = only move along the axis with the biggest move, ctrl = snap hard to grid and povit lines fix a couple of Patch::Calculate_AvgNormal() and Patch::NaturalTexture() problems --- libs/generic/vector.h | 6 + radiant/brush_primit.cpp | 20 +- radiant/brush_primit.h | 1 + radiant/patch.cpp | 98 ++-- radiant/patch.h | 3 +- radiant/selection.cpp | 970 ++++++++++++++++++++++++++++----------- 6 files changed, 784 insertions(+), 314 deletions(-) diff --git a/libs/generic/vector.h b/libs/generic/vector.h index 1a5863df..85258f5b 100644 --- a/libs/generic/vector.h +++ b/libs/generic/vector.h @@ -59,6 +59,12 @@ BasicVector3( const BasicVector3& other ){ y() = static_cast( other.y() ); z() = static_cast( other.z() ); } +template +explicit BasicVector3( const BasicVector2& vec2 ){ + x() = static_cast( vec2.x() ); + y() = static_cast( vec2.y() ); + z() = 0; +} BasicVector3( const Element& x_, const Element& y_, const Element& z_ ){ x() = x_; y() = y_; diff --git a/radiant/brush_primit.cpp b/radiant/brush_primit.cpp index 9052810b..2f3237fd 100644 --- a/radiant/brush_primit.cpp +++ b/radiant/brush_primit.cpp @@ -1581,7 +1581,7 @@ double Det3x3( double a00, double a01, double a02, + a02 * ( a10 * a21 - a11 * a20 ); } -void BP_from_ST( brushprimit_texdef_t& bp, const DoubleVector3 points[3], const DoubleVector3 st[3], const DoubleVector3& normal ){ +void BP_from_ST( brushprimit_texdef_t& bp, const DoubleVector3 points[3], const DoubleVector3 st[3], const DoubleVector3& normal, const bool normalize = true ){ double xyI[2], xyJ[2], xyK[2]; double stI[2], stJ[2], stK[2]; double D, D0, D1, D2; @@ -1627,7 +1627,7 @@ void BP_from_ST( brushprimit_texdef_t& bp, const DoubleVector3 points[3], const ); bp.coords[i][0] = D0 / D; bp.coords[i][1] = D1 / D; - bp.coords[i][2] = fmod( D2 / D, 1.0 ); + bp.coords[i][2] = normalize? fmod( D2 / D, 1.0 ) : ( D2 / D ); } // globalOutputStream() << "BP out: ( " << bp.coords[0][0] << " " << bp.coords[0][1] << " " << bp.coords[0][2] << " ) ( " << bp.coords[1][0] << " " << bp.coords[1][1] << " " << bp.coords[1][2] << " )\n"; } @@ -2172,6 +2172,22 @@ void Texdef_from_ST( TextureProjection& projection, const DoubleVector3 points[3 } } +void Texdef_Construct_local2tex_from_ST( const DoubleVector3 points[3], const DoubleVector3 st[3], Matrix4& local2tex ){ + const Plane3 plane( plane3_for_points( points ) ); + brushprimit_texdef_t bp; + BP_from_ST( bp, points, st, plane.normal(), false ); + + BPTexdef_toTransform( bp, local2tex ); + { + // Texdef_basisForNormal + Matrix4 basis = g_matrix4_identity; + ComputeAxisBase( plane.normal(), vector4_to_vector3( basis.x() ), vector4_to_vector3( basis.y() ) ); + vector4_to_vector3( basis.z() ) = plane.normal(); + matrix4_transpose( basis ); + matrix4_multiply_by_matrix4( local2tex, basis ); + } +} + #if 0 void Texdef_getTexAxes( const TextureProjection& projection, const Plane3& plane, std::size_t width, std::size_t height, Matrix4& local2tex, Matrix4& tex2local, Matrix4& basis ){ Texdef_Construct_local2tex( projection, width, height, plane.normal(), local2tex ); diff --git a/radiant/brush_primit.h b/radiant/brush_primit.h index 6b8861f9..7c95bfb5 100644 --- a/radiant/brush_primit.h +++ b/radiant/brush_primit.h @@ -106,6 +106,7 @@ void Texdef_ProjectTexture( TextureProjection& projection, std::size_t width, st void Texdef_FitTexture( TextureProjection& projection, std::size_t width, std::size_t height, const Vector3& normal, const Winding& w, float s_repeat, float t_repeat, bool only_dimension ); void Texdef_Construct_local2tex( const TextureProjection& projection, std::size_t width, std::size_t height, const Vector3& normal, Matrix4& local2tex ); void Texdef_Construct_local2tex4projection( const texdef_t& texdef, std::size_t width, std::size_t height, const Vector3& normal, const Vector3* direction, Matrix4& local2tex ); +void Texdef_Construct_local2tex_from_ST( const DoubleVector3 points[3], const DoubleVector3 st[3], Matrix4& local2tex ); void Texdef_EmitTextureCoordinates( const TextureProjection& projection, std::size_t width, std::size_t height, Winding& w, const Vector3& normal, const Matrix4& localToWorld ); void ShiftScaleRotate_fromFace( texdef_t& shiftScaleRotate, const TextureProjection& projection ); diff --git a/radiant/patch.cpp b/radiant/patch.cpp index 6aabaf00..c2eb9b35 100644 --- a/radiant/patch.cpp +++ b/radiant/patch.cpp @@ -604,42 +604,52 @@ void Patch::SetTextureRepeat( float s, float t ){ } */ -Vector3 Patch::Calculate_AvgNormal(){ - Vector3 wDir( 0, 0, 0 ), hDir( 0, 0, 0 ); - for ( std::size_t i = 0; i < m_height; ++i ){ - wDir += ctrlAt( i, m_width - 1 ).m_vertex - ctrlAt( i, 0 ).m_vertex; +void Patch::Calculate_AvgAxes( Vector3& wDir, Vector3& hDir ) const { + wDir = hDir = g_vector3_identity; + for ( std::size_t r = 0; r < m_height; ++r ){ + wDir += ctrlAt( r, m_width - 1 ).m_vertex - ctrlAt( r, 0 ).m_vertex; } - for ( std::size_t i = 0; i < m_width; ++i ){ - hDir += ctrlAt( m_height - 1, i ).m_vertex - ctrlAt( 0, i ).m_vertex; + for ( std::size_t c = 0; c < m_width; ++c ){ + hDir += ctrlAt( m_height - 1, c ).m_vertex - ctrlAt( 0, c ).m_vertex; } - /* fallback */ - if ( vector3_equal_epsilon( wDir, g_vector3_identity, 1e-3f ) || vector3_equal_epsilon( hDir, g_vector3_identity, 1e-3f ) ) { - for ( std::size_t i = 0; i < m_height; ++i ){ - for ( std::size_t j = 0; j < m_width - 1; ++j ){ - wDir = ctrlAt( i, j + 1 ).m_vertex - ctrlAt( i, j ).m_vertex; - if ( !vector3_equal_epsilon( wDir, g_vector3_identity, 1e-3f ) ) - goto break1; - } - } -break1: - for ( std::size_t i = 0; i < m_width; ++i ){ - for ( std::size_t j = 0; j < m_height; ++j ){ - hDir = ctrlAt( j + 1, i ).m_vertex - ctrlAt( j, i ).m_vertex; - if ( !vector3_equal_epsilon( hDir, g_vector3_identity, 1e-3f ) ) - goto break2; + /* fallback to longest hord */ + if ( vector3_equal_epsilon( wDir, g_vector3_identity, 1.f ) ){ + float bestLength = 0; + for ( std::size_t r = 0; r < m_height; ++r ){ + for ( std::size_t c = 0; c < m_width - 1; ++c ){ + const Vector3 dir = ctrlAt( r, c + 1 ).m_vertex - ctrlAt( r, c ).m_vertex; + const float length = vector3_length( dir ); + if ( length - bestLength > 1 ){ + bestLength = length; + wDir = dir; + } + } + } + } + if( vector3_equal_epsilon( hDir, g_vector3_identity, 1.f ) ){ + float bestLength = 0; + for ( std::size_t c = 0; c < m_width; ++c ){ + for ( std::size_t r = 0; r < m_height - 1; ++r ){ + const Vector3 dir = ctrlAt( r + 1, c ).m_vertex - ctrlAt( r, c ).m_vertex; + const float length = vector3_length( dir ); + if ( length - bestLength > 1 ){ + bestLength = length; + hDir = dir; + } } } } -break2: - Vector3 normal( vector3_cross( wDir, hDir ) ); - if ( vector3_equal( normal, g_vector3_identity ) ) { - normal = Vector3( 0, 0, 1 ); + if ( vector3_equal_epsilon( vector3_cross( wDir, hDir ), g_vector3_identity, 0.1f ) ) { + wDir = g_vector3_axis_x; + hDir = g_vector3_axis_y; } - else{ - vector3_normalise( normal ); - } - return normal; +} + +Vector3 Patch::Calculate_AvgNormal() const { + Vector3 wDir, hDir; + Calculate_AvgAxes( wDir, hDir ); + return vector3_normalised( vector3_cross( wDir, hDir ) ); } inline int texture_axis( const Vector3& normal ){ @@ -693,16 +703,16 @@ void Patch::NaturalTexture(){ undoSave(); { - float fSize = (float)m_state->getTexture().width * Texdef_getDefaultTextureScale(); + const double texSize = (double)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++ ) + 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 ) + for ( std::size_t h = 0; h < m_height; ++h, pHeight += m_width ) pHeight->m_texcoord[0] = static_cast( tex ); } @@ -711,12 +721,11 @@ void Patch::NaturalTexture(){ } { - PatchControl* pHeight = pWidth; - for ( std::size_t h = 0; h < m_height; h++, pHeight += m_width ) + const 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 ) { + const double length = tex + ( vector3_length( pHeight->m_vertex - ( pHeight + 1 )->m_vertex ) / texSize ); + if ( fabs( length ) > fabs( texBest ) ) { // comparing abs values supports possible negative Texdef_getDefaultTextureScale() texBest = length; } } @@ -727,16 +736,16 @@ void Patch::NaturalTexture(){ } { - float fSize = -(float)m_state->getTexture().height * Texdef_getDefaultTextureScale(); + const double texSize = -(double)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 ) + 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++ ) + for ( std::size_t w = 0; w < m_width; ++w, ++pWidth ) pWidth->m_texcoord[1] = static_cast( tex ); } @@ -745,12 +754,11 @@ void Patch::NaturalTexture(){ } { - PatchControl* pWidth = pHeight; - for ( std::size_t w = 0; w < m_width; w++, pWidth++ ) + const 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 ) { + const double length = tex + ( vector3_length( pWidth->m_vertex - ( pWidth + m_width )->m_vertex ) / texSize ); + if ( fabs( length ) > fabs( texBest ) ) { texBest = length; } } diff --git a/radiant/patch.h b/radiant/patch.h index b0b1cdb8..aae960cb 100644 --- a/radiant/patch.h +++ b/radiant/patch.h @@ -930,7 +930,8 @@ void RotateTexture( float angle ); void SetTextureRepeat( float s, float t ); // call with s=1 t=1 for FIT void CapTexture(); void NaturalTexture(); -Vector3 Calculate_AvgNormal(); +Vector3 Calculate_AvgNormal() const; +void Calculate_AvgAxes( Vector3& wDir, Vector3& hDir ) const; void ProjectTexture( TextureProjection projection, const Vector3& normal ); void ProjectTexture( const texdef_t& texdef, const Vector3* direction ); void createThickenedOpposite(const Patch& sourcePatch, const float thickness, const int axis, bool& no12, bool& no34 ); diff --git a/radiant/selection.cpp b/radiant/selection.cpp index d80ae172..177e997b 100644 --- a/radiant/selection.cpp +++ b/radiant/selection.cpp @@ -203,12 +203,18 @@ const View* Manipulatable::m_view = 0; float Manipulatable::m_device_point[2]; float Manipulatable::m_device_epsilon[2]; -void transform_local2object( Matrix4& object, const Matrix4& local, const Matrix4& local2object ){ - object = matrix4_multiplied_by_matrix4( +inline Matrix4 transform_local2object( const Matrix4& local, const Matrix4& local2object ){ + return matrix4_multiplied_by_matrix4( matrix4_multiplied_by_matrix4( local2object, local ), matrix4_full_inverse( local2object ) ); } +inline Matrix4 transform_local2object( const Matrix4& localTransform, const Matrix4& local2parent, const Matrix4& parent2local ){ + return matrix4_multiplied_by_matrix4( + matrix4_multiplied_by_matrix4( local2parent, localTransform ), + parent2local + ); +} class Rotatable { @@ -319,14 +325,22 @@ void aabb_snap_translation( Vector3& move, const AABB& bounds ){ } } -void translation_local2object( Vector3& object, const Vector3& local, const Matrix4& local2object ){ - object = matrix4_get_translation_vec3( +inline Vector3 translation_local2object( const Vector3& local, const Matrix4& local2object ){ + return matrix4_get_translation_vec3( matrix4_multiplied_by_matrix4( matrix4_translated_by_vec3( local2object, local ), matrix4_full_inverse( local2object ) ) ); } +inline Vector3 translation_local2object( const Vector3& localTranslation, const Matrix4& local2parent, const Matrix4& parent2local ){ + return matrix4_get_translation_vec3( + matrix4_multiplied_by_matrix4( + matrix4_translated_by_vec3( local2parent, localTranslation ), + parent2local + ) + ); +} class Translatable { @@ -352,7 +366,7 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const Vector3 current = point_on_axis( m_axis, device2manip, x, y ); current = vector3_scaled( m_axis, distance_for_axis( m_start, current, m_axis ) ); - translation_local2object( current, current, manip2object ); + current = translation_local2object( current, manip2object ); if( snapbbox ) aabb_snap_translation( current, m_bounds ); else @@ -424,7 +438,7 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const if( snap ) current *= g_vector3_axes[vector3_max_abs_component_index( current )]; - translation_local2object( current, current, manip2object ); + current = translation_local2object( current, manip2object ); if( snapbbox ) aabb_snap_translation( current, m_bounds ); @@ -529,7 +543,7 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const Vector3 current = point_on_axis( m_axis, device2manip, x, y ); Vector3 delta = vector3_subtracted( current, m_start ); - translation_local2object( delta, delta, manip2object ); + delta = translation_local2object( delta, manip2object ); vector3_snap( delta, GetSnapGridSize() ); vector3_scale( delta, m_axis ); @@ -601,7 +615,7 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const Vector3 current = point_on_plane( device2manip, x, y ); Vector3 delta = vector3_subtracted( current, m_start ); - translation_local2object( delta, delta, manip2object ); + delta = translation_local2object( delta, manip2object ); vector3_snap( delta, GetSnapGridSize() ); if( m_axis != g_vector3_identity ) delta = vector3_scaled( delta, m_axis ) + vector3_scaled( delta, m_axis2 ); @@ -3193,7 +3207,7 @@ void translation_for_pivoted_matrix_transform( Vector3& parent_translation, cons ) ); - translation_local2object( parent_translation, local_translation, localToParent ); + parent_translation = translation_local2object( local_translation, localToParent ); /* // verify it! @@ -4391,6 +4405,9 @@ Shader* BuildManipulator::m_state_line; +#include "patch.h" +#include "iglrender.h" + class UVManipulator : public Manipulator, public Manipulatable { struct RenderablePoint : public OpenGLRenderable @@ -4408,6 +4425,17 @@ class UVManipulator : public Manipulator, public Manipulatable m_point.colour = colour; } }; + struct RenderablePoints : public OpenGLRenderable + { + std::vector m_points; + RenderablePoints(){ + } + void render( RenderStateFlags state ) const { + glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_points[0].colour ); + glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_points[0].vertex ); + glDrawArrays( GL_POINTS, 0, m_points.size() ); + } + }; struct RenderableLines : public OpenGLRenderable { std::vector m_lines; @@ -4439,43 +4467,42 @@ class UVManipulator : public Manipulator, public Manipulatable } } }; - class UVSelector : public Selector { - SelectionIntersection m_bestIntersection; - public: - PointVertex* m_pointVertex; - UVSelector() : m_bestIntersection( SelectionIntersection() ) { + typedef Array PatchControlArray; + struct RenderablePatchTexture : public OpenGLRenderable + { + std::vector m_trianglesIndices; + const PatchControlArray* m_patchControlArray; + RenderablePatchTexture(){ } - void pushSelectable( Selectable& selectable ) { - } - void popSelectable() { - m_bestIntersection = SelectionIntersection(); - } - void addIntersection( const SelectionIntersection& intersection ) { - if( SelectionIntersection_closer( intersection, m_bestIntersection ) ) { - m_bestIntersection = intersection; + void render( RenderStateFlags state ) const { + if( state & RENDER_FILL ){ + const std::vector normals( m_patchControlArray->size(), g_vector3_axis_z ); + glNormalPointer( GL_FLOAT, sizeof( Vector3 ), normals.data() ); + glVertexPointer( 2, GL_FLOAT, sizeof( PatchControl ), &m_patchControlArray->data()->m_texcoord ); + glTexCoordPointer( 2, GL_FLOAT, sizeof( PatchControl ), &m_patchControlArray->data()->m_texcoord ); + glDrawElements( GL_TRIANGLES, GLsizei( m_trianglesIndices.size() ), RenderIndexTypeID, m_trianglesIndices.data() ); } } - void addIntersection( const SelectionIntersection& intersection, PointVertex* pointVertex ) { - if( SelectionIntersection_closer( intersection, m_bestIntersection ) ) { - m_bestIntersection = intersection; - m_pointVertex = pointVertex; - } - } - bool isSelected() { - return m_bestIntersection.valid(); - } }; const Colour4b m_cWhite{ 255, 255, 255, 255 }; const Colour4b m_cGray{ 255, 255, 255, 125 }; const Colour4b m_cGrayer{ 100, 100, 100, 150 }; const Colour4b m_cRed{ 255, 0, 0, 255 }; const Colour4b m_cGreen{ 0, 255, 0, 255 }; + const Colour4b m_cGree{ 0, 150, 0, 255 }; + const Colour4b m_cPink{ 255, 0, 255, 255 }; + const Colour4b m_cPin{ 150, 0, 150, 255 }; + const Colour4b m_cOrange{ 255, 125, 0, 255 }; + const Colour4b m_cOrang{ 255, 125, 0, 125 }; enum EUVSelection{ eNone, ePivot, eGridU, eGridV, + ePatchPoint, + ePatchRow, + ePatchColumn, eCircle, ePivotU, ePivotV, @@ -4488,8 +4515,44 @@ class UVManipulator : public Manipulator, public Manipulatable } m_selection; PointVertex* m_selectedU = 0; // must nullify this on m_Ulines, m_Vlines change PointVertex* m_selectedV = 0; + int m_selectedPatchIndex = -1; bool m_isSelected = false; + class UVSelector : public Selector { + SelectionIntersection m_bestIntersection; + public: + EUVSelection m_selection = eNone; + int m_index = -1; + UVSelector() : m_bestIntersection( SelectionIntersection() ) { + } + void pushSelectable( Selectable& selectable ) { + } + void popSelectable() { + m_bestIntersection = SelectionIntersection(); + } + void addIntersection( const SelectionIntersection& intersection ) { + if( SelectionIntersection_closer( intersection, m_bestIntersection ) ) { + m_bestIntersection = intersection; + } + } + void addIntersection( const SelectionIntersection& intersection, EUVSelection selection, int index ) { + if( SelectionIntersection_closer( intersection, m_bestIntersection ) ) { + m_bestIntersection = intersection; + m_selection = selection; + m_index = index; + } + } + void addIntersection( const SelectionIntersection& intersection, EUVSelection selection ) { + if( SelectionIntersection_closer( intersection, m_bestIntersection ) ) { + m_bestIntersection = intersection; + m_selection = selection; + } + } + bool isSelected() { + return m_bestIntersection.valid(); + } + }; + Face* m_face = 0; Plane3 m_plane; std::size_t m_width, m_height; @@ -4521,6 +4584,17 @@ class UVManipulator : public Manipulator, public Manipulatable RenderableCircle m_circle; Matrix4 m_circle2world; + Patch* m_patch = 0; //tracking face/patch mode by only nonzero pointer + std::size_t m_patchWidth; + std::size_t m_patchHeight; + PatchControlArray m_patchCtrl; + RenderablePoints m_patchRenderPoints; + RenderableLines m_patchRenderLattice; + RenderablePatchTexture m_patchRenderTex; + const Shader* m_state_patch_raw = 0; // original patch texture shader + Shader* m_state_patch = 0; // local patch texture overlay + const char* m_state_patch_name = "$uvtool/patchtexture"; + public: static Shader* m_state_line; static Shader* m_state_point; @@ -4532,37 +4606,202 @@ public: m_gridPointV.setColour( m_cWhite ); m_pivotLines.m_lines.resize( 4, PointVertex( vertex3f_identity, m_cWhite ) ); } + ~UVManipulator() { + patchShaderDestroy(); + } private: + void patchShaderConstruct(){ + patchShaderDestroy(); - bool UpdateFaceData( bool updateOrigin, bool updateLines = true ) { + OpenGLState state; + GlobalOpenGLStateLibrary().getDefaultState( state ); + state.m_state = RENDER_FILL /*| RENDER_CULLFACE*/ | RENDER_TEXTURE | RENDER_COLOURWRITE | RENDER_LIGHTING | RENDER_SMOOTH; + state.m_sort = OpenGLState::eSortOverlayLast; + state.m_texture = m_patch->getShader()->getTexture().texture_number; + + GlobalOpenGLStateLibrary().insert( m_state_patch_name, state ); + m_state_patch = GlobalShaderCache().capture( m_state_patch_name ); + } + + void patchShaderDestroy(){ + if( m_state_patch ){ + m_state_patch = 0; + GlobalShaderCache().release( m_state_patch_name ); + GlobalOpenGLStateLibrary().erase( m_state_patch_name ); + } + } + bool patchCtrl_isInside( std::size_t i ) const { + return ( i % 2 || ( i / m_patchWidth ) % 2 ); + } + template + void forEachEdge( const Functor& functor ) const { + if( m_face ){ + const Winding& winding = m_face->getWinding(); + for( Winding::const_iterator next = winding.begin(), i = winding.end() - 1; next != winding.end(); i = next, ++next ) + functor( ( *i ).vertex, ( *next ).vertex ); + } + else if( m_patch ){ + for( std::vector::const_iterator i = m_patchRenderLattice.m_lines.begin(); i != m_patchRenderLattice.m_lines.end(); ++++i ){ + const Vector3 p0( matrix4_transformed_point( m_faceTex2local, ( *i ).vertex ) ); + const Vector3 p1( matrix4_transformed_point( m_faceTex2local, ( *( i + 1 ) ).vertex ) ); + if( vector3_length_squared( p1 - p0 ) > 0.1 ) + functor( p0, p1 ); + } + } + } + template + void forEachPoint( const Functor& functor ) const { + if( m_face ){ + const Winding& winding = m_face->getWinding(); + for( const auto& v : winding ) + functor( v.vertex ); + } + else if( m_patch ){ + for( const auto& v : m_patchCtrl ) + functor( matrix4_transformed_point( m_faceTex2local, Vector3( v.m_texcoord ) ) ); + } + } + template + void forEachUVPoint( const Functor& functor ) const { + if( m_face ){ + const Winding& winding = m_face->getWinding(); + for( const auto& v : winding ) + functor( matrix4_transformed_point( m_faceLocal2tex, v.vertex ) ); + } + else if( m_patch ){ + for( const auto& v : m_patchCtrl ) + functor( Vector3( v.m_texcoord ) ); + } + } + bool projection_valid() const { + return !( !std::isfinite( m_local2tex[0] ) //nan + || fabs( vector3_dot( m_plane.normal(), vector4_to_vector3( m_tex2local.z() ) ) ) < 1e-6 //projected along face + || vector3_length_squared( vector4_to_vector3( m_tex2local.x() ) ) < .01 //srsly scaled down, limit at max 10 textures per world unit + || vector3_length_squared( vector4_to_vector3( m_tex2local.y() ) ) < .01 ); + } + void UpdateFaceData( bool updateOrigin, bool updateLines = true ) { //!? todo fewer outer quads for large textures //!? todo auto subdivisions num, based on tex size and world scale //! todo update on undo/redo, when face stays the same, but transformed //! todo update on nudgeSelectedLeft and the rest, qe tool move w/o projection change or with tex lock off //+ todo put default origin to winding's UV aabb corner //+ todo disable 3d workzone in this manipulator mode - m_plane = m_face->getPlane().plane3(); - m_width = m_face->getShader().width(); - m_height = m_face->getShader().height(); -// m_face->GetTexdef( m_projection ); - m_projection = m_face->getTexdef().m_projection; + if( m_face ){ + m_plane = m_face->getPlane().plane3(); + m_width = m_face->getShader().width(); + m_height = m_face->getShader().height(); +// m_face->GetTexdef( m_projection ); + m_projection = m_face->getTexdef().m_projection; - const Winding& winding = m_face->getWinding(); + Texdef_Construct_local2tex( m_projection, m_width, m_height, m_plane.normal(), m_local2tex ); + m_tex2local = matrix4_affine_inverse( m_local2tex ); + } + else if( m_patch ){ + m_plane.normal() = m_patch->Calculate_AvgNormal(); + m_plane.dist() = vector3_dot( m_plane.normal(), m_patch->localAABB().origin ); + m_patchWidth = m_patch->getWidth(); + m_patchHeight = m_patch->getHeight(); + m_patchCtrl = m_patch->getControlPoints(); + m_state_patch_raw = m_patch->getShader(); + patchShaderConstruct(); + { //! todo force or deduce orthogonal uv axes for convenience + Vector3 wDir, hDir; + m_patch->Calculate_AvgAxes( wDir, hDir ); + vector3_normalise( wDir ); + vector3_normalise( hDir ); +// globalOutputStream() << wDir << " wDir\n"; +// globalOutputStream() << hDir << " hDir\n"; +// globalOutputStream() << m_plane.normal() << " m_plane.normal()\n"; + + /* find longest row and column */ + float wLength = 0, hLength = 0; //!? todo break, if some of these is 0 + std::size_t row = 0, col = 0; + for ( std::size_t r = 0; r < m_patchHeight; ++r ){ + float length = 0; + for ( std::size_t c = 0; c < m_patchWidth - 1; ++c ){ + length += vector3_length( m_patch->ctrlAt( r, c + 1 ).m_vertex - m_patch->ctrlAt( r, c ).m_vertex ); + } + if( length - wLength > .1f || ( ( r == 0 || r == m_patchHeight - 1 ) && float_equal_epsilon( length, wLength, .1f ) ) ){ // prioritize first and last rows + wLength = length; + row = r; + } + } + for ( std::size_t c = 0; c < m_patchWidth; ++c ){ + float length = 0; + for ( std::size_t r = 0; r < m_patchHeight - 1; ++r ){ + length += vector3_length( m_patch->ctrlAt( r + 1, c ).m_vertex - m_patch->ctrlAt( r, c ).m_vertex ); + } + if( length - hLength > .1f || ( ( c == 0 || c == m_patchWidth - 1 ) && float_equal_epsilon( length, hLength, .1f ) ) ){ + hLength = length; + col = c; + } + } + //! todo handle case, when uv start = end, like projection to cylinder + //! todo consider max uv length to have manipulator size according to patch size + /* pick 3 points at the found row and column */ + const PatchControl* p0, *p1, *p2; + Vector3 v0, v1, v2; + { + float distW0 = 0, distW1 = 0; + for ( std::size_t c = 0; c < col; ++c ){ + distW0 += vector3_length( m_patch->ctrlAt( row, c + 1 ).m_vertex - m_patch->ctrlAt( row, c ).m_vertex ); + } + for ( std::size_t c = col; c < m_patchWidth - 1; ++c ){ + distW1 += vector3_length( m_patch->ctrlAt( row, c + 1 ).m_vertex - m_patch->ctrlAt( row, c ).m_vertex ); + } + float distH0 = 0, distH1 = 0; + for ( std::size_t r = 0; r < row; ++r ){ + distH0 += vector3_length( m_patch->ctrlAt( r + 1, col ).m_vertex - m_patch->ctrlAt( r, col ).m_vertex ); + } + for ( std::size_t r = row; r < m_patchHeight - 1; ++r ){ + distH1 += vector3_length( m_patch->ctrlAt( r + 1, col ).m_vertex - m_patch->ctrlAt( r, col ).m_vertex ); + } + + if( ( distW0 > distH0 && distW0 > distH1 ) || ( distW1 > distH0 && distW1 > distH1 ) ){ + p0 = &m_patch->ctrlAt( 0, col ); + p1 = &m_patch->ctrlAt( m_patchHeight - 1, col ); + p2 = distW0 > distW1? &m_patch->ctrlAt( row, 0 ) : &m_patch->ctrlAt( row, m_patchWidth - 1 ); + v0 = m_patch->localAABB().origin + + hDir * vector3_dot( m_patch->localAABB().extents, Vector3( fabs( hDir.x() ), fabs( hDir.y() ), fabs( hDir.z() ) ) ) * 1.1 + + wDir * ( distW0 - wLength / 2 ); + v1 = v0 + hDir * hLength; + v2 = v0 + hDir * distH0 + ( distW0 > distW1? ( wDir * -distW0 ) : ( wDir * distW1 ) ); + } + else{ + p0 = &m_patch->ctrlAt( row, 0 ); + p1 = &m_patch->ctrlAt( row, m_patchWidth - 1 ); + p2 = distH0 > distH1? &m_patch->ctrlAt( 0, col ) : &m_patch->ctrlAt( m_patchHeight - 1, col ); + v0 = m_patch->localAABB().origin + + wDir * vector3_dot( m_patch->localAABB().extents, Vector3( fabs( wDir.x() ), fabs( wDir.y() ), fabs( wDir.z() ) ) ) * 1.1 + + hDir * ( distH0 - hLength / 2 ); + v1 = v0 + wDir * wLength; + v2 = v0 + wDir * distW0 + ( distH0 > distH1? ( hDir * -distH0 ) : ( hDir * distH1 ) ); + } + + if( vector3_dot( plane3_for_points( v0, v1, v2 ).normal(), m_plane.normal() ) < 0 ){ + std::swap( p0, p1 ); + std::swap( v0, v1 ); + } + } + const DoubleVector3 vertices[3]{ v0, v1, v2 }; + const DoubleVector3 sts[3]{ DoubleVector3( p0->m_texcoord ), + DoubleVector3( p1->m_texcoord ), + DoubleVector3( p2->m_texcoord ) }; + Texdef_Construct_local2tex_from_ST( vertices, sts, m_local2tex ); + m_tex2local = matrix4_affine_inverse( m_local2tex ); + } + } - Texdef_Construct_local2tex( m_projection, m_width, m_height, m_plane.normal(), m_local2tex ); - m_tex2local = matrix4_affine_inverse( m_local2tex ); // globalOutputStream() << m_local2tex << " m_local2tex\n"; // globalOutputStream() << m_tex2local << " m_tex2local\n"; /* error checking */ - if( !std::isfinite( m_local2tex[0] ) //nan - || fabs( vector3_dot( m_plane.normal(), vector4_to_vector3( m_tex2local.z() ) ) ) < 1e-6 //projected along face - || vector3_length_squared( vector4_to_vector3( m_tex2local.x() ) ) < .01 //srsly scaled down, limit at max 10 textures per world unit - || vector3_length_squared( vector4_to_vector3( m_tex2local.y() ) ) < .01 ){ + if( !projection_valid() ){ m_selectedU = m_selectedV = 0; m_Ulines.m_lines.clear(); m_Vlines.m_lines.clear(); - return false; + m_selectedPatchIndex = -1; + return; } m_faceTex2local = m_tex2local; @@ -4576,18 +4815,106 @@ private: m_faceTex2local ); m_faceLocal2tex = matrix4_affine_inverse( m_faceTex2local ); - Vector2 min( FLT_MAX, FLT_MAX ); - Vector2 max( -FLT_MAX, -FLT_MAX ); - for( const auto& v : winding ){ - const Vector3 p = matrix4_transformed_point( m_faceLocal2tex, v.vertex ); - min.x() = std::min( min.x(), p.x() ); - max.x() = std::max( max.x(), p.x() ); - min.y() = std::min( min.y(), p.y() ); - max.y() = std::max( max.y(), p.y() ); + if( m_patch ){ + m_patchRenderPoints.m_points.clear(); + m_patchRenderPoints.m_points.reserve( m_patchWidth * m_patchHeight ); + for( std::size_t i = 0; i < m_patchCtrl.size(); ++i ){ + m_patchRenderPoints.m_points.emplace_back( vertex3f_for_vector3( Vector3( m_patchCtrl[i].m_texcoord ) ), patchCtrl_isInside( i )? m_cPin : m_cGree ); + } + + m_patchRenderLattice.m_lines.clear(); + m_patchRenderLattice.m_lines.reserve( ( ( m_patchWidth - 1 ) * m_patchHeight + ( m_patchHeight - 1 ) * m_patchWidth ) * 2 ); + for ( std::size_t r = 0; r < m_patchHeight; ++r ){ + for ( std::size_t c = 0; c < m_patchWidth - 1; ++c ){ + const Vector2& a = m_patch->ctrlAt( r, c ).m_texcoord; + const Vector2& b = m_patch->ctrlAt( r, c + 1 ).m_texcoord; + m_patchRenderLattice.m_lines.emplace_back( vertex3f_for_vector3( Vector3( a ) ), m_cOrang ); + m_patchRenderLattice.m_lines.emplace_back( vertex3f_for_vector3( Vector3( b ) ), m_cOrang ); + } + } + for ( std::size_t c = 0; c < m_patchWidth; ++c ){ + for ( std::size_t r = 0; r < m_patchHeight - 1; ++r ){ + const Vector2& a = m_patch->ctrlAt( r, c ).m_texcoord; + const Vector2& b = m_patch->ctrlAt( r + 1, c ).m_texcoord; + m_patchRenderLattice.m_lines.emplace_back( vertex3f_for_vector3( Vector3( a ) ), m_cOrang ); + m_patchRenderLattice.m_lines.emplace_back( vertex3f_for_vector3( Vector3( b ) ), m_cOrang ); + } + } + + m_patchRenderTex.m_trianglesIndices.clear(); + m_patchRenderTex.m_trianglesIndices.reserve( ( m_patchHeight - 1 ) * ( m_patchWidth - 1 ) * 2 * 3 ); + const PatchControlArray& pc = m_patch->getControlPointsTransformed(); + m_patchRenderTex.m_patchControlArray = &pc; + const double degenerate_epsilon = 1e-5; + for ( std::size_t r = 0; r < m_patchHeight - 1; ++r ){ + for ( std::size_t c = 0; c < m_patchWidth - 1; ++c ){ + const RenderIndex i0 = m_patchWidth * r + c; + const RenderIndex i1 = m_patchWidth * ( r + 1 ) + c; + const RenderIndex i2 = m_patchWidth * ( r + 1 ) + c + 1; + const RenderIndex i3 = m_patchWidth * r + c + 1; + double cross = vector2_cross( pc[i2].m_texcoord - pc[i0].m_texcoord, pc[i1].m_texcoord - pc[i0].m_texcoord ); + if( !float_equal_epsilon( cross, 0, degenerate_epsilon ) ){ + m_patchRenderTex.m_trianglesIndices.push_back( i0 ); + m_patchRenderTex.m_trianglesIndices.push_back( i1 ); + m_patchRenderTex.m_trianglesIndices.push_back( i2 ); + if( cross < 0 ) + std::swap( *( m_patchRenderTex.m_trianglesIndices.end() - 1 ), *( m_patchRenderTex.m_trianglesIndices.end() - 2 ) ); + } + cross = vector2_cross( pc[i3].m_texcoord - pc[i0].m_texcoord, pc[i2].m_texcoord - pc[i0].m_texcoord ); + if( !float_equal_epsilon( cross, 0, degenerate_epsilon ) ){ + m_patchRenderTex.m_trianglesIndices.push_back( i0 ); + m_patchRenderTex.m_trianglesIndices.push_back( i2 ); + m_patchRenderTex.m_trianglesIndices.push_back( i3 ); + if( cross < 0 ) + std::swap( *( m_patchRenderTex.m_trianglesIndices.end() - 1 ), *( m_patchRenderTex.m_trianglesIndices.end() - 2 ) ); + } + } + } + if( m_patchRenderTex.m_trianglesIndices.size() == 0 ){ // try to make at least one triangle or more + RenderIndex i0 = 0, i1 = 1, i2; + for( ; i1 < pc.size(); ++i1 ){ + if( vector2_length( pc[i1].m_texcoord - pc[i0].m_texcoord ) > degenerate_epsilon ){ + i2 = i1 + 1; + for( ; i2 < pc.size(); ++i2 ){ + const double cross = vector2_cross( pc[i2].m_texcoord - pc[i0].m_texcoord, pc[i1].m_texcoord - pc[i0].m_texcoord ); + if( !float_equal_epsilon( cross, 0, degenerate_epsilon ) ){ + m_patchRenderTex.m_trianglesIndices.push_back( i0 ); + m_patchRenderTex.m_trianglesIndices.push_back( i1 ); + m_patchRenderTex.m_trianglesIndices.push_back( i2 ); + if( cross < 0 ) + std::swap( *( m_patchRenderTex.m_trianglesIndices.end() - 1 ), *( m_patchRenderTex.m_trianglesIndices.end() - 2 ) ); + break; + } + } + } + } + } } + Vector2 min( FLT_MAX, FLT_MAX ); + Vector2 max( -FLT_MAX, -FLT_MAX ); + forEachUVPoint( [&]( const Vector3& point ){ + min.x() = std::min( min.x(), point.x() ); + max.x() = std::max( max.x(), point.x() ); + min.y() = std::min( min.y(), point.y() ); + max.y() = std::max( max.y(), point.y() ); + } ); + if( updateOrigin ) - m_origin = matrix4_transformed_point( m_faceTex2local, Vector3( min.x(), min.y(), 0 ) ); //winding.points[0].vertex; + m_origin = matrix4_transformed_point( m_faceTex2local, Vector3( min ) ); + + const Vector3 uv_origin = matrix4_transformed_point( m_faceLocal2tex, m_origin ); + + { // grid grain controls, on the polygon side of origin + m_gridSign.x() = max.y() - uv_origin.y() >= uv_origin.y() - min.y()? 1 : -1; + m_gridSign.y() = max.x() - uv_origin.x() >= uv_origin.x() - min.x()? 1 : -1; + m_gridPointU.m_point.vertex = Vertex3f( uv_origin.x(), + float_to_integer( uv_origin.y() + m_gridSign.x() * .25 ) + m_gridSign.x() * ( 1 - 1.0 / std::max( float( m_gridU ), 1.8f ) ), + 0 ); + m_gridPointV.m_point.vertex = Vertex3f( float_to_integer( uv_origin.x() + m_gridSign.y() * .25 ) + m_gridSign.y() * ( 1 - 1.0 / std::max( float( m_gridV ), 1.8f ) ), + uv_origin.y(), + 0 ); + } m_pivot2world = m_tex2local; vector3_normalise( vector4_to_vector3( m_pivot2world.x() ) ); @@ -4597,16 +4924,15 @@ private: { float bestDist = 0; - for( const auto& v : winding ){ - const float dist = vector3_length_squared( v.vertex - m_origin ); + forEachPoint( [&]( const Vector3& point ){ + const float dist = vector3_length_squared( point - m_origin ); if( dist > bestDist ){ bestDist = dist; } - } + } ); bestDist = sqrt( bestDist ); m_circle2world = g_matrix4_identity; - //! todo gcc 7 declines this template use w/o cast, wth - ComputeAxisBase( static_cast( m_plane.normal() ), vector4_to_vector3( m_circle2world.x() ), vector4_to_vector3( m_circle2world.y() ) ); + ComputeAxisBase( m_plane.normal(), vector4_to_vector3( m_circle2world.x() ), vector4_to_vector3( m_circle2world.y() ) ); vector4_to_vector3( m_circle2world.x() ) *= bestDist; vector4_to_vector3( m_circle2world.y() ) *= bestDist; vector4_to_vector3( m_circle2world.z() ) = m_plane.normal(); @@ -4621,6 +4947,7 @@ private: max.y() = float_to_integer( max.y() ); m_selectedU = m_selectedV = 0; + m_selectedPatchIndex = -1; m_lines2world = m_faceTex2local; m_pivotLines2world = m_faceTex2local; if( updateLines ){ @@ -4654,7 +4981,6 @@ private: } } { - const Vector3 uv_origin = matrix4_transformed_point( m_faceLocal2tex, m_origin ); { // u pivot line m_pivotLines.m_lines[0].vertex = Vertex3f( min.x(), uv_origin.y(), 0 ); m_pivotLines.m_lines[1].vertex = Vertex3f( max.x(), uv_origin.y(), 0 ); @@ -4663,27 +4989,39 @@ private: m_pivotLines.m_lines[2].vertex = Vertex3f( uv_origin.x(), min.y(), 0 ); m_pivotLines.m_lines[3].vertex = Vertex3f( uv_origin.x(), max.y(), 0 ); } - // grid grain controls, on the polygon side of origin - m_gridSign.x() = max.y() - uv_origin.y() >= uv_origin.y() - min.y()? 1 : -1; - m_gridSign.y() = max.x() - uv_origin.x() >= uv_origin.x() - min.x()? 1 : -1; - m_gridPointU.m_point.vertex = Vertex3f( uv_origin.x(), float_to_integer( uv_origin.y() + m_gridSign.x() * .25 ) + m_gridSign.x() * ( 1 - 1.0 / std::max( float( m_gridU ), 1.8f ) ), 0 ); - m_gridPointV.m_point.vertex = Vertex3f( float_to_integer( uv_origin.x() + m_gridSign.y() * .25 ) + m_gridSign.y() * ( 1 - 1.0 / std::max( float( m_gridV ), 1.8f ) ), uv_origin.y(), 0 ); } - return true; } bool UpdateData() { if( !g_SelectedFaceInstances.empty() ){ Face* face = &g_SelectedFaceInstances.last().getFace(); if( m_face != face ){ m_face = face; - return UpdateFaceData( true ); + m_patch = 0; + UpdateFaceData( true ); } else if( memcmp( &m_projection, &m_face->getTexdef().m_projection, sizeof( TextureProjection ) ) != 0 || m_width != m_face->getShader().width() || m_height != m_face->getShader().height() ) { - return UpdateFaceData( false ); + UpdateFaceData( false ); + } + return projection_valid(); + } + else if( GlobalSelectionSystem().countSelected() != 0 ){ + Patch* patch = Node_getPatch( GlobalSelectionSystem().ultimateSelected().path().top() ); + if( patch ){ + if( m_patch != patch ){ + m_patch = patch; + m_face = 0; + UpdateFaceData( true ); + } + else if( m_patchWidth != m_patch->getWidth() + || m_patchHeight != m_patch->getHeight() + || memcmp( m_patchCtrl.data(), m_patch->getControlPoints().data(), sizeof( *m_patchCtrl.data() ) * m_patchCtrl.size() ) != 0 + || m_state_patch_raw != m_patch->getShader() ){ + UpdateFaceData( false ); + } + return projection_valid(); } - return true; } return false; @@ -4691,16 +5029,25 @@ private: public: void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ) { if( volume.fill() && UpdateData() ){ + if( m_patch ){ + renderer.SetState( const_cast( m_state_patch ), Renderer::eFullMaterials ); + renderer.addRenderable( m_patchRenderTex, m_lines2world ); + } renderer.SetState( m_state_line, Renderer::eFullMaterials ); renderer.addRenderable( m_Ulines, m_lines2world ); renderer.addRenderable( m_Vlines, m_lines2world ); renderer.addRenderable( m_pivotLines, m_pivotLines2world ); + if( m_patch ) + renderer.addRenderable( m_patchRenderLattice, m_faceTex2local ); + //fix pivot position for better visibility m_pivot.render( renderer, volume, matrix4_multiplied_by_matrix4( matrix4_translation_for_vec3( vector3_normalised( volume.getViewer() - m_origin ) ), m_pivot2world ) ); renderer.addRenderable( m_circle, m_circle2world ); renderer.SetState( m_state_point, Renderer::eFullMaterials ); + if( m_patch ) + renderer.addRenderable( m_patchRenderPoints, m_faceTex2local ); renderer.addRenderable( m_pivotPoint, m_pivot2world ); renderer.addRenderable( m_gridPointU, m_pivotLines2world ); renderer.addRenderable( m_gridPointV, m_pivotLines2world ); @@ -4713,32 +5060,44 @@ public: return; } - EUVSelection selection = eNone; - UVSelector selector; { // try pivot point const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_pivot2world ) ); SelectionIntersection best; Point_BestPoint( local2view, m_pivotPoint.m_point.vertex, best ); - selector.addIntersection( best ); - if( selector.isSelected() ){ - selection = ePivot; - } + selector.addIntersection( best, ePivot ); } - if( !selector.isSelected() ){ // try grid points + if( !selector.isSelected() ){ // try grid control points const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_faceTex2local ) ); SelectionIntersection best; Point_BestPoint( local2view, m_gridPointU.m_point.vertex, best ); - selector.addIntersection( best ); - if( selector.isSelected() ){ - selection = eGridU; + selector.addIntersection( best, eGridU ); + Point_BestPoint( local2view, m_gridPointV.m_point.vertex, best ); + selector.addIntersection( best, eGridV ); + } + + if( !selector.isSelected() && m_patch ){ // try patch points + const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_faceTex2local ) ); + SelectionIntersection best; + for( std::size_t i = 0; i < m_patchRenderPoints.m_points.size(); ++i ){ + Point_BestPoint( local2view, m_patchRenderPoints.m_points[i], best ); + selector.addIntersection( best, ePatchPoint, i ); } - else{ - Point_BestPoint( local2view, m_gridPointV.m_point.vertex, best ); - selector.addIntersection( best ); - if( selector.isSelected() ){ - selection = eGridV; + } + if( !selector.isSelected() && m_patch ){ // try patch rows, columns + const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_faceTex2local ) ); + SelectionIntersection best; + for ( std::size_t r = 0; r < m_patchHeight; ++r ){ + for ( std::size_t c = 0; c < m_patchWidth - 1; ++c ){ + Line_BestPoint( local2view, &m_patchRenderLattice.m_lines[( r * ( m_patchWidth - 1 ) + c ) * 2], best ); + selector.addIntersection( best, ePatchRow, r ); + } + } + for ( std::size_t c = 0; c < m_patchWidth; ++c ){ + for ( std::size_t r = 0; r < m_patchHeight - 1; ++r ){ + Line_BestPoint( local2view, &m_patchRenderLattice.m_lines[( m_patchWidth - 1 ) * m_patchHeight * 2 + ( c * ( m_patchHeight - 1 ) + r ) * 2], best ); + selector.addIntersection( best, ePatchColumn, c ); } } } @@ -4747,33 +5106,22 @@ public: const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_circle2world ) ); SelectionIntersection best; LineLoop_BestPoint( local2view, m_circle.m_vertices.data(), m_circle.m_vertices.size(), best ); - selector.addIntersection( best ); - if( selector.isSelected() ){ - selection = eCircle; - } + selector.addIntersection( best, eCircle ); } if( !selector.isSelected() ){ // try pivot lines const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_faceTex2local ) ); SelectionIntersection best; Line_BestPoint( local2view, &m_pivotLines.m_lines[0], best ); - selector.addIntersection( best ); - if( selector.isSelected() ){ - selection = ePivotU; - } - else{ - Line_BestPoint( local2view, &m_pivotLines.m_lines[2], best ); - selector.addIntersection( best ); - if( selector.isSelected() ){ - selection = ePivotV; - } - } + selector.addIntersection( best, ePivotU ); + Line_BestPoint( local2view, &m_pivotLines.m_lines[2], best ); + selector.addIntersection( best, ePivotV ); } PointVertex* selectedU = 0; PointVertex* selectedV = 0; + EUVSelection& selection = selector.m_selection; if( !selector.isSelected() ){ // try UV lines -#if 1 /* -|------ | @@ -4877,48 +5225,16 @@ V line center| - - tex U center - - } } } -#else - const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_faceTex2local ) ); - { - SelectionIntersection best; - for( std::vector::iterator i = m_Ulines.m_lines.begin(); i != m_Ulines.m_lines.end(); ++++i ){ - Line_BestPoint( local2view, &( *i ), best ); - selector.addIntersection( best, &( *i ) ); - } - if( selector.isSelected() ){ - selection = g_bAltResize_AltSelect? eSkewU : eU; - selectedU = selector.m_pointVertex; - } - } - { - selector.popSelectable(); - SelectionIntersection best; - for( std::vector::iterator i = m_Vlines.m_lines.begin(); i != m_Vlines.m_lines.end(); ++++i ){ - Line_BestPoint( local2view, &( *i ), best ); - selector.addIntersection( best, &( *i ) ); - } - if( selector.isSelected() ){ - selection = g_bAltResize_AltSelect? eSkewV : eV; - selectedV = selector.m_pointVertex; - } - } - if( selectedU && selectedV ){ - selection = eUV; - if( g_bAltResize_AltSelect ){ - selection = eSkewU; - selectedV = 0; - } - } -#endif } - applySelection( selection, selectedU, selectedV ); + applySelection( selector.m_selection, selectedU, selectedV, selector.m_index ); } private: - void applySelection( EUVSelection selection, PointVertex* selectedU, PointVertex* selectedV ){ + void applySelection( EUVSelection selection, PointVertex* selectedU, PointVertex* selectedV, int selectedPatchIndex ){ if( m_selection != selection || m_selectedU != selectedU - || m_selectedV != selectedV ){ + || m_selectedV != selectedV + || m_selectedPatchIndex != selectedPatchIndex ){ if( m_selection != selection ){ switch ( m_selection ) { @@ -4990,13 +5306,97 @@ private: ( selectedV + 1 )->colour = colour_selected; } + if( m_selectedPatchIndex != selectedPatchIndex || m_selection != selection ){ + if( m_selectedPatchIndex >= 0 ){ + switch ( m_selection ) + { + case ePatchPoint: + m_patchRenderPoints.m_points[m_selectedPatchIndex].colour = patchCtrl_isInside( m_selectedPatchIndex )? m_cPin : m_cGree; + break; + case ePatchRow: + for ( std::size_t c = 0; c < m_patchWidth - 1; ++c ){ + const std::size_t i = ( m_selectedPatchIndex * ( m_patchWidth - 1 ) + c ) * 2; + m_patchRenderLattice.m_lines[i].colour = + m_patchRenderLattice.m_lines[i + 1].colour = m_cOrang; + } + for ( std::size_t c = 0; c < m_patchWidth; ++c ){ + const std::size_t i = m_selectedPatchIndex * m_patchWidth + c; + m_patchRenderPoints.m_points[i].colour = patchCtrl_isInside( i )? m_cPin : m_cGree; + } + break; + case ePatchColumn: + for ( std::size_t r = 0; r < m_patchHeight - 1; ++r ){ + const std::size_t i = ( m_patchWidth - 1 ) * m_patchHeight * 2 + ( m_selectedPatchIndex * ( m_patchHeight - 1 ) + r ) * 2; + m_patchRenderLattice.m_lines[i].colour = + m_patchRenderLattice.m_lines[i + 1].colour = m_cOrang; + } + for ( std::size_t r = 0; r < m_patchHeight; ++r ){ + const std::size_t i = r * m_patchWidth + m_selectedPatchIndex; + m_patchRenderPoints.m_points[i].colour = patchCtrl_isInside( i )? m_cPin : m_cGree; + } + break; + default: + break; + } + } + if( selectedPatchIndex >= 0 ){ + switch ( selection ) + { + case ePatchPoint: + m_patchRenderPoints.m_points[selectedPatchIndex].colour = patchCtrl_isInside( selectedPatchIndex )? m_cPink : m_cGreen; + break; + case ePatchRow: + for ( std::size_t c = 0; c < m_patchWidth - 1; ++c ){ + const std::size_t i = ( selectedPatchIndex * ( m_patchWidth - 1 ) + c ) * 2; + m_patchRenderLattice.m_lines[i].colour = + m_patchRenderLattice.m_lines[i + 1].colour = m_cOrange; + } + for ( std::size_t c = 0; c < m_patchWidth; ++c ){ + const std::size_t i = selectedPatchIndex * m_patchWidth + c; + m_patchRenderPoints.m_points[i].colour = patchCtrl_isInside( i )? m_cPink : m_cGreen; + } + break; + case ePatchColumn: + for ( std::size_t r = 0; r < m_patchHeight - 1; ++r ){ + const std::size_t i = ( m_patchWidth - 1 ) * m_patchHeight * 2 + ( selectedPatchIndex * ( m_patchHeight - 1 ) + r ) * 2; + m_patchRenderLattice.m_lines[i].colour = + m_patchRenderLattice.m_lines[i + 1].colour = m_cOrange; + } + for ( std::size_t r = 0; r < m_patchHeight; ++r ){ + const std::size_t i = r * m_patchWidth + selectedPatchIndex; + m_patchRenderPoints.m_points[i].colour = patchCtrl_isInside( i )? m_cPink : m_cGreen; + } + break; + default: + break; + } + } + } + m_selection = selection; m_selectedU = selectedU; m_selectedV = selectedV; + m_selectedPatchIndex = selectedPatchIndex; SceneChangeNotify(); } m_isSelected = ( selection != eNone ); } + void commitTransform( const Matrix4& transform ) const { + if( m_face ){ + m_face->transform_texdef( transform, m_origin ); //! todo make SI update after Brush_textureChanged(); same problem after brush moved with tex lock + } + else if( m_patch ){ + const Matrix4 uvTransform = transform_local2object( matrix4_affine_inverse( transform ), m_faceLocal2tex, m_faceTex2local ); + for( std::size_t i = 0; i < m_patchCtrl.size(); ++i ){ + const Vector3 uv = matrix4_transformed_point( uvTransform, Vector3( m_patchCtrl[i].m_texcoord ) ); + m_patch->getControlPointsTransformed()[i].m_texcoord.x() = uv.x(); + m_patch->getControlPointsTransformed()[i].m_texcoord.y() = uv.y(); + } +// m_patch->controlPointsChanged(); + m_patch->UpdateCachedData(); + } + SceneChangeNotify(); + } /* Manipulatable */ Vector3 m_start; public: @@ -5004,9 +5404,10 @@ public: m_start = point_on_plane( m_plane, m_view->GetViewMatrix(), x, y ); } //!? todo change snap dist measurement from world to screenspace - //! fix meaningless undo on grid/origin change, then click tex or lines + //!? fix meaningless undo on grid/origin change, then click tex or lines + //!? todo no snap mode with alt modifier void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap, const bool snapHard, const bool alt ){ - Vector3 current = point_on_plane( m_plane, m_view->GetViewMatrix(), x, y ); + const Vector3 current = point_on_plane( m_plane, m_view->GetViewMatrix(), x, y ); switch ( m_selection ) { case ePivot: @@ -5031,19 +5432,18 @@ public: snapToV = ( *i ).vertex.x(); } } - for( const auto& v : m_face->getWinding() ){ - const Vector3 p = matrix4_transformed_point( m_faceLocal2tex, v.vertex ); - const float distU = fabs( p.y() - uv_origin.y() ); + forEachUVPoint( [&]( const Vector3& point ){ + const float distU = fabs( point.y() - uv_origin.y() ); if( distU < bestDistU ){ bestDistU = distU; - snapToU = p.y(); + snapToU = point.y(); } - const float distV = fabs( p.x() - uv_origin.x() ); + const float distV = fabs( point.x() - uv_origin.x() ); if( distV < bestDistV ){ bestDistV = distV; - snapToV = p.x(); + snapToV = point.x(); } - } + } ); Vector3 result( uv_origin_start ); if( bestDistU * vector3_length( vector4_to_vector3( m_faceTex2local.y() ) ) < 3 || snapHard ){ result.y() = snapToU; @@ -5075,14 +5475,13 @@ public: snapTo = ( *i ).vertex.y(); } } - for( const auto& v : m_face->getWinding() ){ - const Vector3 p = matrix4_transformed_point( m_faceLocal2tex, v.vertex ); - const float dist = fabs( p.y() - uv_origin.y() ); + forEachUVPoint( [&]( const Vector3& point ){ + const float dist = fabs( point.y() - uv_origin.y() ); if( dist < bestDist ){ bestDist = dist; - snapTo = p.y(); + snapTo = point.y(); } - } + } ); Vector3 result( uv_origin_start ); if( bestDist * vector3_length( vector4_to_vector3( m_faceTex2local.y() ) ) < 3 || snapHard ){ result.y() = snapTo; @@ -5108,14 +5507,13 @@ public: snapTo = ( *i ).vertex.x(); } } - for( const auto& v : m_face->getWinding() ){ - const Vector3 p = matrix4_transformed_point( m_faceLocal2tex, v.vertex ); - const float dist = fabs( p.x() - uv_origin.x() ); + forEachUVPoint( [&]( const Vector3& point ){ + const float dist = fabs( point.x() - uv_origin.x() ); if( dist < bestDist ){ bestDist = dist; - snapTo = p.x(); + snapTo = point.x(); } - } + } ); Vector3 result( uv_origin_start ); if( bestDist * vector3_length( vector4_to_vector3( m_faceTex2local.x() ) ) < 3 || snapHard ){ result.x() = snapTo; @@ -5202,14 +5600,13 @@ public: m_origin ); } { // snap - const Winding& winding = m_face->getWinding(); const Vector3 uvec = vector3_normalised( matrix4_transformed_direction( rot, vector4_to_vector3( m_tex2local.x() ) ) ); const Vector3 vvec = vector3_normalised( matrix4_transformed_direction( rot, vector4_to_vector3( m_tex2local.y() ) ) ); float bestDot = 0; Vector3 bestTo; bool V = false; - for( Winding::const_iterator next = winding.begin(), i = winding.end() - 1; next != winding.end(); i = next, ++next ){ - Vector3 vec( ( *next ).vertex - ( *i ).vertex ); + forEachEdge( [&]( const Vector3& point0, const Vector3& point1 ){ + Vector3 vec( point1 - point0 ); constrain_to_axis( vec, vector4_to_vector3( m_tex2local.z() ) ); const float dotU = fabs( vector3_dot( uvec, vec ) ); if( dotU > bestDot ){ @@ -5223,7 +5620,7 @@ public: bestTo = vector3_dot( vvec, vec ) > 0? vec : -vec; V = true; } - } + } ); if( bestDot > 0.9994f || snapHard ){ const Vector3 bestFrom = vector3_normalised( vector4_to_vector3( V? m_tex2local.y() : m_tex2local.x() ) ); rot = g_matrix4_identity; @@ -5247,25 +5644,23 @@ public: m_pivot2world = matrix4_multiplied_by_matrix4( rot, m_pivot2world0 ); - m_face->transform_texdef( rot, m_origin ); //! todo make SI update after Brush_textureChanged(); same problem after brush moved with tex lock - SceneChangeNotify(); + commitTransform( rot ); } break; - case eU: + case eU: //!? todo modifier or default snap to set scale u = scale v { const Vector3 uv_origin = matrix4_transformed_point( m_local2tex, m_origin ); const Vector3 uv_start = m_selectedU->vertex; const Vector3 uv_current = m_selectedU->vertex + matrix4_transformed_point( m_local2tex, current ) - matrix4_transformed_point( m_local2tex, m_start ); float bestDist = FLT_MAX; float snapTo = 0; - for( const auto& v : m_face->getWinding() ){ - const Vector3 p = matrix4_transformed_point( m_local2tex, v.vertex ); - const float dist = fabs( p.y() - uv_current.y() ); + forEachUVPoint( [&]( const Vector3& point ){ + const float dist = fabs( point.y() - uv_current.y() ); if( dist < bestDist ){ bestDist = dist; - snapTo = p.y(); + snapTo = point.y(); } - } + } ); Vector3 result( 1, uv_current.y(), 1 ); if( bestDist * vector3_length( vector4_to_vector3( m_faceTex2local.y() ) ) < 3 || snapHard ){ result.y() = snapTo; @@ -5281,11 +5676,7 @@ public: Matrix4 scale = g_matrix4_identity; matrix4_pivoted_scale_by_vec3( scale, result, uv_origin ); - // transform_local2object - scale = matrix4_multiplied_by_matrix4( - matrix4_multiplied_by_matrix4( m_tex2local, scale ), - m_local2tex - ); + scale = transform_local2object( scale, m_tex2local, m_local2tex ); { Matrix4 linescale = g_matrix4_identity; matrix4_pivoted_scale_by_vec3( linescale, result, matrix4_transformed_point( m_faceLocal2tex, m_origin ) ); @@ -5293,8 +5684,7 @@ public: m_pivot2world = matrix4_multiplied_by_matrix4( m_pivot2world0, matrix4_scale_for_vec3( result ) ); } - m_face->transform_texdef( scale, m_origin ); - SceneChangeNotify(); + commitTransform( scale ); } break; case eV: @@ -5304,14 +5694,13 @@ public: const Vector3 uv_current = m_selectedV->vertex + matrix4_transformed_point( m_local2tex, current ) - matrix4_transformed_point( m_local2tex, m_start ); float bestDist = FLT_MAX; float snapTo = 0; - for( const auto& v : m_face->getWinding() ){ - const Vector3 p = matrix4_transformed_point( m_local2tex, v.vertex ); - const float dist = fabs( p.x() - uv_current.x() ); + forEachUVPoint( [&]( const Vector3& point ){ + const float dist = fabs( point.x() - uv_current.x() ); if( dist < bestDist ){ bestDist = dist; - snapTo = p.x(); + snapTo = point.x(); } - } + } ); Vector3 result( uv_current.x(), 1, 1 ); if( bestDist * vector3_length( vector4_to_vector3( m_faceTex2local.x() ) ) < 3 || snapHard ){ result.x() = snapTo; @@ -5327,11 +5716,7 @@ public: Matrix4 scale = g_matrix4_identity; matrix4_pivoted_scale_by_vec3( scale, result, uv_origin ); - // transform_local2object - scale = matrix4_multiplied_by_matrix4( - matrix4_multiplied_by_matrix4( m_tex2local, scale ), - m_local2tex - ); + scale = transform_local2object( scale, m_tex2local, m_local2tex ); { Matrix4 linescale = g_matrix4_identity; matrix4_pivoted_scale_by_vec3( linescale, result, matrix4_transformed_point( m_faceLocal2tex, m_origin ) ); @@ -5339,8 +5724,7 @@ public: m_pivot2world = matrix4_multiplied_by_matrix4( m_pivot2world0, matrix4_scale_for_vec3( result ) ); } - m_face->transform_texdef( scale, m_origin ); - SceneChangeNotify(); + commitTransform( scale ); } break; case eUV: @@ -5354,19 +5738,18 @@ public: float snapToU = 0; float bestDistV = FLT_MAX; float snapToV = 0; - for( const auto& v : m_face->getWinding() ){ - const Vector3 p = matrix4_transformed_point( m_local2tex, v.vertex ); - const float distU = fabs( p.y() - uv_current.y() ); + forEachUVPoint( [&]( const Vector3& point ){ + const float distU = fabs( point.y() - uv_current.y() ); if( distU < bestDistU ){ bestDistU = distU; - snapToU = p.y(); + snapToU = point.y(); } - const float distV = fabs( p.x() - uv_current.x() ); + const float distV = fabs( point.x() - uv_current.x() ); if( distV < bestDistV ){ bestDistV = distV; - snapToV = p.x(); + snapToV = point.x(); } - } + } ); Vector3 result( uv_current.x(), uv_current.y(), 1 ); if( bestDistU * vector3_length( vector4_to_vector3( m_faceTex2local.y() ) ) < 3 || snapHard ){ @@ -5391,11 +5774,7 @@ public: Matrix4 scale = g_matrix4_identity; matrix4_pivoted_scale_by_vec3( scale, result, uv_origin ); - // transform_local2object - scale = matrix4_multiplied_by_matrix4( - matrix4_multiplied_by_matrix4( m_tex2local, scale ), - m_local2tex - ); + scale = transform_local2object( scale, m_tex2local, m_local2tex ); { Matrix4 linescale = g_matrix4_identity; matrix4_pivoted_scale_by_vec3( linescale, result, matrix4_transformed_point( m_faceLocal2tex, m_origin ) ); @@ -5403,8 +5782,7 @@ public: m_pivot2world = matrix4_multiplied_by_matrix4( m_pivot2world0, matrix4_scale_for_vec3( result ) ); } - m_face->transform_texdef( scale, m_origin ); - SceneChangeNotify(); + commitTransform( scale ); } break; case eSkewU: @@ -5421,20 +5799,19 @@ public: matrix4_multiply_by_matrix4( scale, m_faceLocal2tex ); float bestDot = 0; Vector3 bestTo; - const Winding& winding = m_face->getWinding(); - for( Winding::const_iterator next = winding.begin(), i = winding.end() - 1; next != winding.end(); i = next, ++next ){ - const Vector3 vec( vector3_normalised( matrix4_transformed_point( scale, ( *next ).vertex ) - - matrix4_transformed_point( scale, ( *i ).vertex ) ) ); + forEachEdge( [&]( const Vector3& point0, const Vector3& point1 ){ + const Vector3 vec( vector3_normalised( matrix4_transformed_point( scale, point1 ) - + matrix4_transformed_point( scale, point0 ) ) ); const float dot = fabs( vector3_dot( skewed, vec ) ); if( dot > bestDot && fabs( vector3_dot( vec, g_vector3_axis_x ) ) < 0.99999 ){ // don't snap so, that one axis = the other bestDot = dot; - const Vector3 vecTo( vector3_normalised( matrix4_transformed_point( m_faceLocal2tex, ( *next ).vertex ) - - matrix4_transformed_point( m_faceLocal2tex, ( *i ).vertex ) ) ); + const Vector3 vecTo( vector3_normalised( matrix4_transformed_point( m_faceLocal2tex, point1 ) - + matrix4_transformed_point( m_faceLocal2tex, point0 ) ) ); bestTo = vector3_dot( skewed, vec ) > 0? vecTo : -vecTo; } - } - if( bestDot > 0.9994f || snapHard ){ + } ); + if( bestDot > 0.9994f || snapHard ){ //!? todo add snap: make manipulated axis orthogonal to the other skew[4] = bestTo.x() / bestTo.y(); } @@ -5447,20 +5824,11 @@ public: } m_lines2world = m_pivotLines2world = matrix4_multiplied_by_matrix4( m_faceTex2local, skew ); - // transform_local2object - m_pivot2world = matrix4_multiplied_by_matrix4( - matrix4_multiplied_by_matrix4( m_tex2local, skew ), - m_local2tex - ); + m_pivot2world = transform_local2object( skew, m_tex2local, m_local2tex ); matrix4_multiply_by_matrix4( m_pivot2world, m_pivot2world0 ); - // transform_local2object - skew = matrix4_multiplied_by_matrix4( - matrix4_multiplied_by_matrix4( m_faceTex2local, skew ), - m_faceLocal2tex - ); - m_face->transform_texdef( skew, m_origin ); - SceneChangeNotify(); + skew = transform_local2object( skew, m_faceTex2local, m_faceLocal2tex ); + commitTransform( skew ); } break; case eSkewV: @@ -5477,19 +5845,18 @@ public: matrix4_multiply_by_matrix4( scale, m_faceLocal2tex ); float bestDot = 0; Vector3 bestTo; - const Winding& winding = m_face->getWinding(); - for( Winding::const_iterator next = winding.begin(), i = winding.end() - 1; next != winding.end(); i = next, ++next ){ - const Vector3 vec( vector3_normalised( matrix4_transformed_point( scale, ( *next ).vertex ) - - matrix4_transformed_point( scale, ( *i ).vertex ) ) ); + forEachEdge( [&]( const Vector3& point0, const Vector3& point1 ){ + const Vector3 vec( vector3_normalised( matrix4_transformed_point( scale, point1 ) - + matrix4_transformed_point( scale, point0 ) ) ); const float dot = fabs( vector3_dot( skewed, vec ) ); if( dot > bestDot && fabs( vector3_dot( vec, g_vector3_axis_y ) ) < 0.99999 ){ // don't snap so, that one axis = the other bestDot = dot; - const Vector3 vecTo( vector3_normalised( matrix4_transformed_point( m_faceLocal2tex, ( *next ).vertex ) - - matrix4_transformed_point( m_faceLocal2tex, ( *i ).vertex ) ) ); + const Vector3 vecTo( vector3_normalised( matrix4_transformed_point( m_faceLocal2tex, point1 ) - + matrix4_transformed_point( m_faceLocal2tex, point0 ) ) ); bestTo = vector3_dot( skewed, vec ) > 0? vecTo : -vecTo; } - } + } ); if( bestDot > 0.9994f || snapHard ){ skew[1] = bestTo.y() / bestTo.x(); } @@ -5503,20 +5870,11 @@ public: } m_lines2world = m_pivotLines2world = matrix4_multiplied_by_matrix4( m_faceTex2local, skew ); - // transform_local2object - m_pivot2world = matrix4_multiplied_by_matrix4( - matrix4_multiplied_by_matrix4( m_tex2local, skew ), - m_local2tex - ); + m_pivot2world = transform_local2object( skew, m_tex2local, m_local2tex ); matrix4_multiply_by_matrix4( m_pivot2world, m_pivot2world0 ); - // transform_local2object - skew = matrix4_multiplied_by_matrix4( - matrix4_multiplied_by_matrix4( m_faceTex2local, skew ), - m_faceLocal2tex - ); - m_face->transform_texdef( skew, m_origin ); - SceneChangeNotify(); + skew = transform_local2object( skew, m_faceTex2local, m_faceLocal2tex ); + commitTransform( skew ); } break; case eTex: @@ -5528,39 +5886,99 @@ public: float bestDistV = FLT_MAX; float snapMoveU = 0; float snapMoveV = 0; - const Winding& winding = m_face->getWinding(); // snap uvmove - for( const auto& v : winding ){ - const Vector3 p = matrix4_transformed_point( m_faceLocal2tex, v.vertex ); + const auto functor = [&]( const Vector3& point ){ for( std::vector::const_iterator i = m_Ulines.m_lines.begin(); i != m_Ulines.m_lines.end(); ++++i ){ - const float dist = p.y() - ( ( *i ).vertex.y() + uvmove.y() ); + const float dist = point.y() - ( ( *i ).vertex.y() + uvmove.y() ); if( fabs( dist ) < bestDistU ){ bestDistU = fabs( dist ); snapMoveU = uvmove.y() + dist; } } for( std::vector::const_iterator i = m_Vlines.m_lines.begin(); i != m_Vlines.m_lines.end(); ++++i ){ - const float dist = p.x() - ( ( *i ).vertex.x() + uvmove.x() ); + const float dist = point.x() - ( ( *i ).vertex.x() + uvmove.x() ); if( fabs( dist ) < bestDistV ){ bestDistV = fabs( dist ); snapMoveV = uvmove.x() + dist; } } + }; + forEachUVPoint( functor ); + functor( matrix4_transformed_point( m_faceLocal2tex, m_origin ) ); + + Vector3 result( uvmove ); + if( bestDistU * vector3_length( vector4_to_vector3( m_faceTex2local.y() ) ) < 3 || snapHard ){ + result.y() = snapMoveU; } - { - const Vector3 p = matrix4_transformed_point( m_faceLocal2tex, m_origin ); // uvorigin + if( bestDistV * vector3_length( vector4_to_vector3( m_faceTex2local.x() ) ) < 3 || snapHard ){ + result.x() = snapMoveV; + } + + if( snap ){ + auto& smaller = fabs( uvmove.x() * vector3_length( vector4_to_vector3( m_faceTex2local.x() ) ) ) < + fabs( uvmove.y() * vector3_length( vector4_to_vector3( m_faceTex2local.y() ) ) )? result.x() : result.y(); + smaller = 0; + } + + result = translation_local2object( result, m_faceTex2local, m_faceLocal2tex ); + + const Matrix4 translation = matrix4_translation_for_vec3( result ); + + m_lines2world = matrix4_multiplied_by_matrix4( translation, m_faceTex2local ); + + commitTransform( translation ); + } + break; + case ePatchPoint: + case ePatchRow: + case ePatchColumn: + { + std::vector indices; + if( m_selection == ePatchPoint ) + indices.push_back( m_selectedPatchIndex ); + else if( m_selection == ePatchRow ) + for ( std::size_t c = 0; c < m_patchWidth; ++c ) + indices.push_back( m_selectedPatchIndex * m_patchWidth + c ); + else if( m_selection == ePatchColumn ) + for ( std::size_t r = 0; r < m_patchHeight; ++r ) + indices.push_back( r * m_patchWidth + m_selectedPatchIndex ); + + const Vector3 uvstart = matrix4_transformed_point( m_faceLocal2tex, m_start ); + const Vector3 uvcurrent = matrix4_transformed_point( m_faceLocal2tex, current ); + const Vector3 uvmove = uvcurrent - uvstart; + float bestDistU = FLT_MAX; + float bestDistV = FLT_MAX; + float snapMoveU = 0; + float snapMoveV = 0; + // snap uvmove + for( std::size_t index : indices ){ for( std::vector::const_iterator i = m_Ulines.m_lines.begin(); i != m_Ulines.m_lines.end(); ++++i ){ - const float dist = p.y() - ( ( *i ).vertex.y() + uvmove.y() ); + const float dist = m_patchCtrl[index].m_texcoord.y() + uvmove.y() - ( *i ).vertex.y(); if( fabs( dist ) < bestDistU ){ bestDistU = fabs( dist ); - snapMoveU = uvmove.y() + dist; + snapMoveU = uvmove.y() - dist; } } for( std::vector::const_iterator i = m_Vlines.m_lines.begin(); i != m_Vlines.m_lines.end(); ++++i ){ - const float dist = p.x() - ( ( *i ).vertex.x() + uvmove.x() ); + const float dist = m_patchCtrl[index].m_texcoord.x() + uvmove.x() - ( *i ).vertex.x(); if( fabs( dist ) < bestDistV ){ bestDistV = fabs( dist ); - snapMoveV = uvmove.x() + dist; + snapMoveV = uvmove.x() - dist; + } + } + const Vector3 origin = matrix4_transformed_point( m_faceLocal2tex, m_origin ); + { + const float dist = m_patchCtrl[index].m_texcoord.y() + uvmove.y() - origin.y(); + if( fabs( dist ) < bestDistU ){ + bestDistU = fabs( dist ); + snapMoveU = uvmove.y() - dist; + } + } + { + const float dist = m_patchCtrl[index].m_texcoord.x() + uvmove.x() - origin.x(); + if( fabs( dist ) < bestDistV ){ + bestDistV = fabs( dist ); + snapMoveV = uvmove.x() - dist; } } } @@ -5579,37 +5997,57 @@ public: smaller = 0; } - // translation_local2object - result = matrix4_get_translation_vec3( - matrix4_multiplied_by_matrix4( - matrix4_translated_by_vec3( m_faceTex2local, result ), - m_faceLocal2tex - ) - ); - const Matrix4 translation = matrix4_translation_for_vec3( result ); + for( std::size_t i : indices ){ + const Vector3 uv = matrix4_transformed_point( translation, Vector3( m_patchCtrl[i].m_texcoord ) ); + m_patch->getControlPointsTransformed()[i].m_texcoord.x() = uv.x(); + m_patch->getControlPointsTransformed()[i].m_texcoord.y() = uv.y(); + m_patchRenderPoints.m_points[i].vertex = vertex3f_for_vector3( uv ); + } - m_lines2world = matrix4_multiplied_by_matrix4( translation, m_faceTex2local ); + // update lattice renderable entirely + for ( std::size_t r = 0; r < m_patchHeight; ++r ){ + for ( std::size_t c = 0; c < m_patchWidth - 1; ++c ){ + const Vector2& a = m_patch->getControlPointsTransformed()[r * m_patchWidth + c].m_texcoord; + const Vector2& b = m_patch->getControlPointsTransformed()[r * m_patchWidth + c + 1].m_texcoord; + m_patchRenderLattice.m_lines[( r * ( m_patchWidth - 1 ) + c ) * 2].vertex = vertex3f_for_vector3( Vector3( a ) ); + m_patchRenderLattice.m_lines[( r * ( m_patchWidth - 1 ) + c ) * 2 + 1].vertex = vertex3f_for_vector3( Vector3( b ) ); + } + } + for ( std::size_t c = 0; c < m_patchWidth; ++c ){ + for ( std::size_t r = 0; r < m_patchHeight - 1; ++r ){ + const Vector2& a = m_patch->getControlPointsTransformed()[r * m_patchWidth + c].m_texcoord; + const Vector2& b = m_patch->getControlPointsTransformed()[( r + 1 ) * m_patchWidth + c].m_texcoord; + m_patchRenderLattice.m_lines[( m_patchWidth - 1 ) * m_patchHeight * 2 + ( c * ( m_patchHeight - 1 ) + r ) * 2].vertex = vertex3f_for_vector3( Vector3( a ) ); + m_patchRenderLattice.m_lines[( m_patchWidth - 1 ) * m_patchHeight * 2 + ( c * ( m_patchHeight - 1 ) + r ) * 2 + 1].vertex = vertex3f_for_vector3( Vector3( b ) ); + } + } - m_face->transform_texdef( translation ); + m_patch->UpdateCachedData(); SceneChangeNotify(); } - break; default: break; } } void freezeTransform(){ - if( m_face && - ( m_selection == eCircle + if( m_selection == eCircle || m_selection == eU || m_selection == eV || m_selection == eUV || m_selection == eSkewU || m_selection == eSkewV - || m_selection == eTex ) ) - m_face->freezeTransform(); + || m_selection == eTex + || m_selection == ePatchPoint + || m_selection == ePatchRow + || m_selection == ePatchColumn ) + { + if( m_face ) + m_face->freezeTransform(); + else if( m_patch ) + m_patch->freezeTransform(); + } } Manipulatable* GetManipulatable() { @@ -5670,7 +6108,7 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const } } - translation_local2object( current, current, manip2object ); + current = translation_local2object( current, manip2object ); m_transformOriginTranslatable.transformOriginTranslate( current, set ); }