From da5a4686a8c06d68b272631f2e33b0835a17ca96 Mon Sep 17 00:00:00 2001 From: hmedina <1H7Wae6oEiFL> Date: Tue, 26 Oct 2021 17:49:34 -0400 Subject: [PATCH 01/10] Fix: cubic key interpolation uses the derivatives The interpolation request declares a type "T", which will be used in the getter methods. When "value" is a Vector3, the getters will get Vector3s; when "value" is a float, the getters will get floats. This procedure therefore works with 3D interpolation (e.g. translocation) as well as 1D interpolation (e.g. scale). --- src/gl/glcontroller.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/gl/glcontroller.cpp b/src/gl/glcontroller.cpp index 2c84ef2ff..3a7960700 100644 --- a/src/gl/glcontroller.cpp +++ b/src/gl/glcontroller.cpp @@ -384,15 +384,24 @@ template bool interpolate( T & value, const QModelIndex & array, fl { // Quadratic /* - In general, for keyframe values v1 = 0, v2 = 1 it appears that - setting v1's corresponding "Backward" value to 1 and v2's - corresponding "Forward" to 1 results in a linear interpolation. - */ + Interpolate X, Y, and Z values independently, along a segment between + two points, at an arbitrary time "x", using an Hermite Cubic spline. + * The "v1" keyframe is the one previous in time to "x", + * The "v2" keyframe is the next one, + * The segment's backward derivative, "t1" is stored as part of "v1" + * The segment's forward derivative, "t2" is stored as part of "v2" + N.B. v2's backward derivate does not "belong" to the segment with v1, + but rather to the segment with v3. Otherwise said, the key at t=0.0 + can have a forward derivative, but it will not be used; the final key's + backward derivative will also not be used. + + Setting the derivatives to zero results in linear interpolation. + */ // Tangent 1 - float t1 = nif->get( frames.child( last, 0 ), "Backward" ); + T t1 = nif->get( frames.child( last, 0 ), "Backward" ); // Tangent 2 - float t2 = nif->get( frames.child( next, 0 ), "Forward" ); + T t2 = nif->get( frames.child( next, 0 ), "Forward" ); float x2 = x * x; float x3 = x2 * x; @@ -400,7 +409,7 @@ template bool interpolate( T & value, const QModelIndex & array, fl // Cubic Hermite spline // x(t) = (2t^3 - 3t^2 + 1)P1 + (-2t^3 + 3t^2)P2 + (t^3 - 2t^2 + t)T1 + (t^3 - t^2)T2 - value = v1 * (2.0f * x3 - 3.0f * x2 + 1.0f) + v2 * (-2.0f * x3 + 3.0f * x2) + t1 * (x3 - 2.0f * x2 + x) + t2 * (x3 - x2); + value = v1 * (2.0f * x3 - 3.0f * x2 + 1.0f) + v2 * (-2.0f * x3 + 3.0f * x2) + t1 * (x3 - 2.0f * x2 + x) + t2 * (x3 - x2); } return true; From 3b158f2c32c8edb5ab9a9d47f06d7ba1f9e716c3 Mon Sep 17 00:00:00 2001 From: DonkeyKong Date: Sat, 13 Nov 2021 16:06:45 -0500 Subject: [PATCH 02/10] Fix macOS build --- NifSkope.pro | 3 +++ src/xml/kfmxml.cpp | 3 +++ src/xml/nifxml.cpp | 3 +++ 3 files changed, 9 insertions(+) diff --git a/NifSkope.pro b/NifSkope.pro index 1c0bc5a69..1a0f7eee5 100644 --- a/NifSkope.pro +++ b/NifSkope.pro @@ -346,6 +346,9 @@ gli { } zlib { + macx { + DEFINES += Z_HAVE_UNISTD_H + } !*msvc*:QMAKE_CFLAGS += -isystem ../nifskope/lib/zlib !*msvc*:QMAKE_CXXFLAGS += -isystem ../nifskope/lib/zlib else:INCLUDEPATH += lib/zlib diff --git a/src/xml/kfmxml.cpp b/src/xml/kfmxml.cpp index 878fd0e0e..aa89b0a5e 100644 --- a/src/xml/kfmxml.cpp +++ b/src/xml/kfmxml.cpp @@ -266,6 +266,9 @@ bool KfmModel::loadXML() << "kfm.xml" #ifdef Q_OS_LINUX << "/usr/share/nifskope/kfm.xml" +#endif +#ifdef Q_OS_MACX + << "../../../kfm.xml" #endif ); for ( const QString& str : xmlList ) { diff --git a/src/xml/nifxml.cpp b/src/xml/nifxml.cpp index 182e0629c..936c16feb 100644 --- a/src/xml/nifxml.cpp +++ b/src/xml/nifxml.cpp @@ -606,6 +606,9 @@ bool NifModel::loadXML() << "nif.xml" #ifdef Q_OS_LINUX << "/usr/share/nifskope/nif.xml" +#endif +#ifdef Q_OS_MACX + << "../../../nif.xml" #endif ); for ( const QString& str : xmlList ) { From 535f4c360e478d848cad182a239cd22bff4eeee3 Mon Sep 17 00:00:00 2001 From: DonkeyKong Date: Sun, 14 Nov 2021 01:54:10 -0500 Subject: [PATCH 03/10] Fix vertex selection click test --- src/gl/bsshape.cpp | 162 ++++++++++++++++++++------------------- src/gl/glmesh.cpp | 186 +++++++++++++++++++++++---------------------- 2 files changed, 177 insertions(+), 171 deletions(-) diff --git a/src/gl/bsshape.cpp b/src/gl/bsshape.cpp index 7a1a43943..af42a82c5 100644 --- a/src/gl/bsshape.cpp +++ b/src/gl/bsshape.cpp @@ -357,13 +357,13 @@ void BSShape::drawShapes( NodeList * secondPass, bool presort ) return; auto nif = static_cast(iBlock.model()); - + bool drawPolygons = true; if ( Node::SELECTING ) { if ( scene->selMode & Scene::SelObject ) { int s_nodeId = ID2COLORKEY( nodeId ); glColor4ubv( (GLubyte *)&s_nodeId ); - } else { - glColor4f( 0, 0, 0, 1 ); + } else { + drawPolygons = false; } } @@ -384,83 +384,85 @@ void BSShape::drawShapes( NodeList * secondPass, bool presort ) glMultMatrix( viewTrans() ); } - // Render polygon fill slightly behind alpha transparency and wireframe - if ( !drawSecond ) { - glEnable( GL_POLYGON_OFFSET_FILL ); - glPolygonOffset( 1.0f, 2.0f ); - } - - glEnableClientState( GL_VERTEX_ARRAY ); - glVertexPointer( 3, GL_FLOAT, 0, transVerts.constData() ); - - if ( !Node::SELECTING ) { - glEnableClientState( GL_NORMAL_ARRAY ); - glNormalPointer( GL_FLOAT, 0, transNorms.constData() ); - - bool doVCs = (bssp && (bssp->getFlags2() & ShaderFlags::SLSF2_Vertex_Colors)); - // Always do vertex colors for FO4 if colors present - if ( nifVersion == 130 && hasVertexColors && colors.count() ) - doVCs = true; - - if ( transColors.count() && (scene->options & Scene::DoVertexColors) && doVCs ) { - glEnableClientState( GL_COLOR_ARRAY ); - glColorPointer( 4, GL_FLOAT, 0, transColors.constData() ); - } else if ( !hasVertexColors && (bslsp && bslsp->hasVertexColors) ) { - // Correctly blacken the mesh if SLSF2_Vertex_Colors is still on - // yet "Has Vertex Colors" is not. - glColor( Color3( 0.0f, 0.0f, 0.0f ) ); - } else { - glColor( Color3( 1.0f, 1.0f, 1.0f ) ); - } - } - - - if ( !Node::SELECTING ) - shader = scene->renderer->setupProgram( this, shader ); - - if ( isDoubleSided ) { - glCullFace( GL_FRONT ); - glDrawElements( GL_TRIANGLES, triangles.count() * 3, GL_UNSIGNED_SHORT, triangles.constData() ); - glCullFace( GL_BACK ); - } - - if ( !isLOD ) { - glDrawElements( GL_TRIANGLES, triangles.count() * 3, GL_UNSIGNED_SHORT, triangles.constData() ); - } else if ( triangles.count() ) { - auto lod0 = nif->get( iBlock, "LOD0 Size" ); - auto lod1 = nif->get( iBlock, "LOD1 Size" ); - auto lod2 = nif->get( iBlock, "LOD2 Size" ); - - auto lod0tris = triangles.mid( 0, lod0 ); - auto lod1tris = triangles.mid( lod0, lod1 ); - auto lod2tris = triangles.mid( lod0 + lod1, lod2 ); - - // If Level2, render all - // If Level1, also render Level0 - switch ( scene->lodLevel ) { - case Scene::Level2: - if ( lod2tris.count() ) - glDrawElements( GL_TRIANGLES, lod2tris.count() * 3, GL_UNSIGNED_SHORT, lod2tris.constData() ); - case Scene::Level1: - if ( lod1tris.count() ) - glDrawElements( GL_TRIANGLES, lod1tris.count() * 3, GL_UNSIGNED_SHORT, lod1tris.constData() ); - case Scene::Level0: - default: - if ( lod0tris.count() ) - glDrawElements( GL_TRIANGLES, lod0tris.count() * 3, GL_UNSIGNED_SHORT, lod0tris.constData() ); - break; - } - } - - if ( !Node::SELECTING ) - scene->renderer->stopProgram(); - - glDisableClientState( GL_VERTEX_ARRAY ); - glDisableClientState( GL_NORMAL_ARRAY ); - glDisableClientState( GL_COLOR_ARRAY ); - - glDisable( GL_POLYGON_OFFSET_FILL ); - + if (drawPolygons) { + + // Render polygon fill slightly behind alpha transparency and wireframe + if ( !drawSecond ) { + glEnable( GL_POLYGON_OFFSET_FILL ); + glPolygonOffset( 1.0f, 2.0f ); + } + + glEnableClientState( GL_VERTEX_ARRAY ); + glVertexPointer( 3, GL_FLOAT, 0, transVerts.constData() ); + + if ( !Node::SELECTING ) { + glEnableClientState( GL_NORMAL_ARRAY ); + glNormalPointer( GL_FLOAT, 0, transNorms.constData() ); + + bool doVCs = (bssp && (bssp->getFlags2() & ShaderFlags::SLSF2_Vertex_Colors)); + // Always do vertex colors for FO4 if colors present + if ( nifVersion == 130 && hasVertexColors && colors.count() ) + doVCs = true; + + if ( transColors.count() && (scene->options & Scene::DoVertexColors) && doVCs ) { + glEnableClientState( GL_COLOR_ARRAY ); + glColorPointer( 4, GL_FLOAT, 0, transColors.constData() ); + } else if ( !hasVertexColors && (bslsp && bslsp->hasVertexColors) ) { + // Correctly blacken the mesh if SLSF2_Vertex_Colors is still on + // yet "Has Vertex Colors" is not. + glColor( Color3( 0.0f, 0.0f, 0.0f ) ); + } else { + glColor( Color3( 1.0f, 1.0f, 1.0f ) ); + } + } + + + if ( !Node::SELECTING ) + shader = scene->renderer->setupProgram( this, shader ); + + if ( isDoubleSided ) { + glCullFace( GL_FRONT ); + glDrawElements( GL_TRIANGLES, triangles.count() * 3, GL_UNSIGNED_SHORT, triangles.constData() ); + glCullFace( GL_BACK ); + } + + if ( !isLOD ) { + glDrawElements( GL_TRIANGLES, triangles.count() * 3, GL_UNSIGNED_SHORT, triangles.constData() ); + } else if ( triangles.count() ) { + auto lod0 = nif->get( iBlock, "LOD0 Size" ); + auto lod1 = nif->get( iBlock, "LOD1 Size" ); + auto lod2 = nif->get( iBlock, "LOD2 Size" ); + + auto lod0tris = triangles.mid( 0, lod0 ); + auto lod1tris = triangles.mid( lod0, lod1 ); + auto lod2tris = triangles.mid( lod0 + lod1, lod2 ); + + // If Level2, render all + // If Level1, also render Level0 + switch ( scene->lodLevel ) { + case Scene::Level2: + if ( lod2tris.count() ) + glDrawElements( GL_TRIANGLES, lod2tris.count() * 3, GL_UNSIGNED_SHORT, lod2tris.constData() ); + case Scene::Level1: + if ( lod1tris.count() ) + glDrawElements( GL_TRIANGLES, lod1tris.count() * 3, GL_UNSIGNED_SHORT, lod1tris.constData() ); + case Scene::Level0: + default: + if ( lod0tris.count() ) + glDrawElements( GL_TRIANGLES, lod0tris.count() * 3, GL_UNSIGNED_SHORT, lod0tris.constData() ); + break; + } + } + + if ( !Node::SELECTING ) + scene->renderer->stopProgram(); + + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_NORMAL_ARRAY ); + glDisableClientState( GL_COLOR_ARRAY ); + + glDisable( GL_POLYGON_OFFSET_FILL ); + } if ( scene->selMode & Scene::SelVertex ) { drawVerts(); diff --git a/src/gl/glmesh.cpp b/src/gl/glmesh.cpp index 794cb7359..3283ca787 100644 --- a/src/gl/glmesh.cpp +++ b/src/gl/glmesh.cpp @@ -992,13 +992,14 @@ void Mesh::drawShapes( NodeList * secondPass, bool presort ) return; auto nif = static_cast(iBlock.model()); - + bool drawPolygons = true; + if ( Node::SELECTING ) { if ( scene->selMode & Scene::SelObject ) { int s_nodeId = ID2COLORKEY( nodeId ); glColor4ubv( (GLubyte *)&s_nodeId ); } else { - glColor4f( 0, 0, 0, 1 ); + drawPolygons = false; } } @@ -1035,94 +1036,97 @@ void Mesh::drawShapes( NodeList * secondPass, bool presort ) // setup array pointers - // Render polygon fill slightly behind alpha transparency and wireframe - glEnable( GL_POLYGON_OFFSET_FILL ); - glPolygonOffset( 1.0f, 2.0f ); - - glEnableClientState( GL_VERTEX_ARRAY ); - glVertexPointer( 3, GL_FLOAT, 0, transVerts.constData() ); - - if ( !Node::SELECTING ) { - if ( transNorms.count() ) { - glEnableClientState( GL_NORMAL_ARRAY ); - glNormalPointer( GL_FLOAT, 0, transNorms.constData() ); - } - - // Do VCs if legacy or if either bslsp or bsesp is set - bool doVCs = (!bssp) || (bssp && (bssp->getFlags2() & ShaderFlags::SLSF2_Vertex_Colors)); - - if ( transColors.count() - && ( scene->options & Scene::DoVertexColors ) - && doVCs ) - { - glEnableClientState( GL_COLOR_ARRAY ); - glColorPointer( 4, GL_FLOAT, 0, transColors.constData() ); - } else { - if ( !hasVertexColors && (bslsp && bslsp->hasVertexColors) ) { - // Correctly blacken the mesh if SLSF2_Vertex_Colors is still on - // yet "Has Vertex Colors" is not. - glColor( Color3( 0.0f, 0.0f, 0.0f ) ); - } else { - glColor( Color3( 1.0f, 1.0f, 1.0f ) ); - } - } - } - - // TODO: Hotspot. See about optimizing this. - if ( !Node::SELECTING ) - shader = scene->renderer->setupProgram( this, shader ); - - if ( isDoubleSided ) { - glDisable( GL_CULL_FACE ); - } - - if ( !isLOD ) { - // render the triangles - if ( sortedTriangles.count() ) - glDrawElements( GL_TRIANGLES, sortedTriangles.count() * 3, GL_UNSIGNED_SHORT, sortedTriangles.constData() ); - - } else if ( sortedTriangles.count() ) { - auto lod0 = nif->get( iBlock, "LOD0 Size" ); - auto lod1 = nif->get( iBlock, "LOD1 Size" ); - auto lod2 = nif->get( iBlock, "LOD2 Size" ); - - auto lod0tris = sortedTriangles.mid( 0, lod0 ); - auto lod1tris = sortedTriangles.mid( lod0, lod1 ); - auto lod2tris = sortedTriangles.mid( lod0 + lod1, lod2 ); - - // If Level2, render all - // If Level1, also render Level0 - switch ( scene->lodLevel ) { - case Scene::Level2: - if ( lod2tris.count() ) - glDrawElements( GL_TRIANGLES, lod2tris.count() * 3, GL_UNSIGNED_SHORT, lod2tris.constData() ); - case Scene::Level1: - if ( lod1tris.count() ) - glDrawElements( GL_TRIANGLES, lod1tris.count() * 3, GL_UNSIGNED_SHORT, lod1tris.constData() ); - case Scene::Level0: - default: - if ( lod0tris.count() ) - glDrawElements( GL_TRIANGLES, lod0tris.count() * 3, GL_UNSIGNED_SHORT, lod0tris.constData() ); - break; - } - } - - // render the tristrips - for ( auto & s : tristrips ) - glDrawElements( GL_TRIANGLE_STRIP, s.count(), GL_UNSIGNED_SHORT, s.constData() ); - - if ( isDoubleSided ) { - glEnable( GL_CULL_FACE ); - } - - if ( !Node::SELECTING ) - scene->renderer->stopProgram(); - - glDisableClientState( GL_VERTEX_ARRAY ); - glDisableClientState( GL_NORMAL_ARRAY ); - glDisableClientState( GL_COLOR_ARRAY ); - - glDisable( GL_POLYGON_OFFSET_FILL ); + if (drawPolygons) { + + // Render polygon fill slightly behind alpha transparency and wireframe + glEnable( GL_POLYGON_OFFSET_FILL ); + glPolygonOffset( 1.0f, 2.0f ); + + glEnableClientState( GL_VERTEX_ARRAY ); + glVertexPointer( 3, GL_FLOAT, 0, transVerts.constData() ); + + if ( !Node::SELECTING ) { + if ( transNorms.count() ) { + glEnableClientState( GL_NORMAL_ARRAY ); + glNormalPointer( GL_FLOAT, 0, transNorms.constData() ); + } + + // Do VCs if legacy or if either bslsp or bsesp is set + bool doVCs = (!bssp) || (bssp && (bssp->getFlags2() & ShaderFlags::SLSF2_Vertex_Colors)); + + if ( transColors.count() + && ( scene->options & Scene::DoVertexColors ) + && doVCs ) + { + glEnableClientState( GL_COLOR_ARRAY ); + glColorPointer( 4, GL_FLOAT, 0, transColors.constData() ); + } else { + if ( !hasVertexColors && (bslsp && bslsp->hasVertexColors) ) { + // Correctly blacken the mesh if SLSF2_Vertex_Colors is still on + // yet "Has Vertex Colors" is not. + glColor( Color3( 0.0f, 0.0f, 0.0f ) ); + } else { + glColor( Color3( 1.0f, 1.0f, 1.0f ) ); + } + } + } + + // TODO: Hotspot. See about optimizing this. + if ( !Node::SELECTING ) + shader = scene->renderer->setupProgram( this, shader ); + + if ( isDoubleSided ) { + glDisable( GL_CULL_FACE ); + } + + if ( !isLOD ) { + // render the triangles + if ( sortedTriangles.count() ) + glDrawElements( GL_TRIANGLES, sortedTriangles.count() * 3, GL_UNSIGNED_SHORT, sortedTriangles.constData() ); + + } else if ( sortedTriangles.count() ) { + auto lod0 = nif->get( iBlock, "LOD0 Size" ); + auto lod1 = nif->get( iBlock, "LOD1 Size" ); + auto lod2 = nif->get( iBlock, "LOD2 Size" ); + + auto lod0tris = sortedTriangles.mid( 0, lod0 ); + auto lod1tris = sortedTriangles.mid( lod0, lod1 ); + auto lod2tris = sortedTriangles.mid( lod0 + lod1, lod2 ); + + // If Level2, render all + // If Level1, also render Level0 + switch ( scene->lodLevel ) { + case Scene::Level2: + if ( lod2tris.count() ) + glDrawElements( GL_TRIANGLES, lod2tris.count() * 3, GL_UNSIGNED_SHORT, lod2tris.constData() ); + case Scene::Level1: + if ( lod1tris.count() ) + glDrawElements( GL_TRIANGLES, lod1tris.count() * 3, GL_UNSIGNED_SHORT, lod1tris.constData() ); + case Scene::Level0: + default: + if ( lod0tris.count() ) + glDrawElements( GL_TRIANGLES, lod0tris.count() * 3, GL_UNSIGNED_SHORT, lod0tris.constData() ); + break; + } + } + + // render the tristrips + for ( auto & s : tristrips ) + glDrawElements( GL_TRIANGLE_STRIP, s.count(), GL_UNSIGNED_SHORT, s.constData() ); + + if ( isDoubleSided ) { + glEnable( GL_CULL_FACE ); + } + + if ( !Node::SELECTING ) + scene->renderer->stopProgram(); + + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_NORMAL_ARRAY ); + glDisableClientState( GL_COLOR_ARRAY ); + + glDisable( GL_POLYGON_OFFSET_FILL ); + } glPointSize( 8.5 ); if ( scene->selMode & Scene::SelVertex ) { @@ -1138,7 +1142,7 @@ void Mesh::drawVerts() const glDisable( GL_LIGHTING ); glNormalColor(); - glBegin( GL_POINTS ); + glBegin( GL_POINTS ); for ( int i = 0; i < transVerts.count(); i++ ) { if ( Node::SELECTING ) { From c108c3247b6f49a50f4076dbc6611a691d171bf7 Mon Sep 17 00:00:00 2001 From: DonkeyKong Date: Tue, 16 Nov 2021 03:54:13 -0500 Subject: [PATCH 04/10] First pass at vertex color painting feature --- res/icon/paint-verts.png | Bin 0 -> 2535 bytes res/nifskope.qrc | 1 + src/data/niftypes.h | 20 ++ src/gl/bsshape.cpp | 6 +- src/gl/glmesh.cpp | 6 +- src/gl/glnode.cpp | 12 +- src/gl/glscene.cpp | 6 +- src/gl/glscene.h | 20 +- src/glview.cpp | 385 +++++++++++++++++++++++++-------------- src/glview.h | 28 ++- src/nifskope.h | 3 + src/nifskope_ui.cpp | 67 +++++-- src/ui/nifskope.ui | 368 ++++++++++++++++++++++++++++++++++++- 13 files changed, 748 insertions(+), 174 deletions(-) create mode 100644 res/icon/paint-verts.png diff --git a/res/icon/paint-verts.png b/res/icon/paint-verts.png new file mode 100644 index 0000000000000000000000000000000000000000..0274ebc6881a71cd8dd93f616a11774893e36f5f GIT binary patch literal 2535 zcmaJ@c|26>8=sJ?DJ5$WoraWd%wiTZnHaNJvW$s}x-!eb#4KixX3$28NCt%>DOXX^ zP8->h6iP}&s#~|Il&xrS^*bv4et-14=kqz|eV_MzzR&Z0w$JD6Vtc#lB2AG91VY!{ zjlqFetBF@f8~#2;A5O5c9LqQJ66+}ng1RW6w&2XXL z8fcA|CxtH&*>NXy?35xYj7A_Fos?28KOBSr9vCVVQ&Ig_uAu;-fQnj8@WOaW>0p@9 zEk*{ejPds4$At6A0+iEoz)?wo4TwO93n)bqVmU=gMSZhNf%g-~XcX{G1q!F4zLQ$x z#RljS83>T+<5!g3vIm7|~OXHW? za=&OPh~|KDNtBEa7bL`T5)3Er-yKaT!fYr$G9g?QZUjTZj}n1m$elq&!8djSp@4#Q zVK8Yd8i7E-5Se%^md3=ouyAAs1D0IKO#4Y2zrxaRE=-&Q#=)L}bHHMmbO$Ech0dbk zaU?v}g+yRXV%^1Zh%4rUlYWJ;-yc}gzhWtL8OVhsGCzqVVzLF;VG>9p50gj%IvuV! zmIN$bEffnR3c1ZhiM~h70A<3BpnxTlh=6Z7rU-w*0m~%0knHjHL@a>_^Cyw9L;?*5 zca;l?OrjBC{@<~JU*+vP7XN>-XqXIoqCx(rVJ45@E}Yo@?0;DJIYFQp9vc}vA6evt z_XvcxzB_~Fr|i0&Gu?CL>Nz*AJ>uO!%uZ;t!7b4vXm-wE4ba%Xg)l?AH$^<-%RMxf zyw(X32R!3Hvy5!oq(_X5^sb4(7#h%sN%7v*Hu82I4bJcF%ZtyqJt#j<{nB#5f#n>i zHuf0`m-N4-zTDdS$>Qae84I+6jyA+)pVVE;_Gm(0FTW7(5qG9)Ui@pVQB7#x5YghX zv+o#pdLa?NwAv*gZH^umcaTb|A78gHNrv#Xlv(kLkIczdcAf)yDtV*Tb1Y|&q%6%eot{){cD@`&^{D@}75CZzZq3Yk>@mQr-LG(xs)_ z*uJ~>CAz*L{o%Qgco(~pot-x5G>A+5JmuD+B;A^2OMTJ}+zK=+%A98c5{EvpQu|<1 zo==x~!2JC39M9gl%-@_OCc)>fj1v?3x2fhf_Lr2DSXo+bpUH8)@yy}ld#;#L`S!A@XqcOH15yetqVYip@zIRCc4TELPC6*pa^XeSHy6WC3O;p@^H+89N;r zP|~gz*7#UYM#62IhU;GzbvN|B^IoH)JB`%V*5)Rc+aE4HeOlGB=e+B!Z7#-}X7z7T z&Ap$Izu@&{qoB4pO~rywNxgc_MMlaZ+GTkaf=zLC7RVtb^v4rCxA zDxMmYI;jtRjJI+=Qg`lUB2a&*i{;_bv~z51)4hT&8$m&<0e5z*1nih<9^(0k~+ z?;AL6?S|~kcdr;xH$)BfXts~F-eDiqeRa1Hoj7a0#TD5Lk1(Nd%07>&H8nNcR!mg~ z;IF1=r_qCFclLB7H>#`!doy`ig_Wvj&v-{xow)hpR<^rPXj4*FX8qyA2gYKral&!w4eAG*!>guztK@4kL8Qr@TRG*F)@^aW%S@+V0lu0DnZRv<>k!?EX}&CxUL$Z zd(~WsomCpZUn^sWt?2n+T#}d7;ZAz9xocjWvoq#N`n_X5qsp02hd)Jlgo#889r*Pf z9ohL?TRSTQ)>ou4-;c~`7b*Th$M)0>4-BwPjEv5Z;bQJ_v(nGlx9@s#a%w7=Hg{+G zBT>VvW?pZ5Y=tNUuSIvF4B+a zP_(LTLQj~#l7{aXKR)vyb-IaV5GPKny=mIK>wE)skx1{Nbe=jFs@`0?o_v{sM(`TxAtfBnGn`&1{Lg+QH#c7|q;l#Zy<40rO+p@MaH{I@WFxhLoaO~52Vp4zd;quF? zx{CTg8-J}xTV;M5bL*-}Hmx-XxGI+RZ(8IO8T$4t`EKA>ch8DerLx*=nrq*E?fy@< mv`zLtFN^JbQae(C&_rA^DeW^87n3G_z1*4Jj53$t`2PUD!zq0L literal 0 HcmV?d00001 diff --git a/res/nifskope.qrc b/res/nifskope.qrc index 5b60c8401..0347e3823 100644 --- a/res/nifskope.qrc +++ b/res/nifskope.qrc @@ -62,5 +62,6 @@ icon/skinned.png icon/collapse.png icon/expand.png + icon/paint-verts.png diff --git a/src/data/niftypes.h b/src/data/niftypes.h index eab32f167..9c099dcea 100644 --- a/src/data/niftypes.h +++ b/src/data/niftypes.h @@ -1352,6 +1352,16 @@ class Color4 return c; } + Color4 operator*( const Color4& o ) const + { + Color4 c( *this ); + c.rgba[0] *= o.rgba[0]; + c.rgba[1] *= o.rgba[1]; + c.rgba[2] *= o.rgba[2]; + c.rgba[3] *= o.rgba[3]; + return c; + } + //! Add-equals operator Color4 & operator+=( const Color4 & o ) { @@ -1452,6 +1462,7 @@ class Color4 friend class NifIStream; friend class NifOStream; + friend class ByteColor4; friend QDataStream & operator>>( QDataStream & ds, Color4 & c ); }; @@ -1461,6 +1472,15 @@ class ByteColor4 : public Color4 public: //! Default constructor ByteColor4() { rgba[0] = rgba[1] = rgba[2] = rgba[3] = 1.0; } + static ByteColor4 fromColor4(const Color4& c) + { + ByteColor4 o; + o.rgba[0] = c.rgba[0]; + o.rgba[1] = c.rgba[1]; + o.rgba[2] = c.rgba[2]; + o.rgba[3] = c.rgba[3]; + return o; + } }; diff --git a/src/gl/bsshape.cpp b/src/gl/bsshape.cpp index af42a82c5..3350d91a6 100644 --- a/src/gl/bsshape.cpp +++ b/src/gl/bsshape.cpp @@ -359,7 +359,7 @@ void BSShape::drawShapes( NodeList * secondPass, bool presort ) auto nif = static_cast(iBlock.model()); bool drawPolygons = true; if ( Node::SELECTING ) { - if ( scene->selMode & Scene::SelObject ) { + if ( scene->actionMode & Scene::Object ) { int s_nodeId = ID2COLORKEY( nodeId ); glColor4ubv( (GLubyte *)&s_nodeId ); } else { @@ -464,7 +464,7 @@ void BSShape::drawShapes( NodeList * secondPass, bool presort ) glDisable( GL_POLYGON_OFFSET_FILL ); } - if ( scene->selMode & Scene::SelVertex ) { + if ( scene->actionMode & Scene::Vertex ) { drawVerts(); } @@ -517,7 +517,7 @@ void BSShape::drawSelection() const if ( scene->options & Scene::ShowNodes ) Node::drawSelection(); - if ( isHidden() || !(scene->selMode & Scene::SelObject) ) + if ( isHidden() || !(scene->actionMode & Scene::Object) ) return; auto idx = scene->currentIndex; diff --git a/src/gl/glmesh.cpp b/src/gl/glmesh.cpp index 3283ca787..b3170a623 100644 --- a/src/gl/glmesh.cpp +++ b/src/gl/glmesh.cpp @@ -995,7 +995,7 @@ void Mesh::drawShapes( NodeList * secondPass, bool presort ) bool drawPolygons = true; if ( Node::SELECTING ) { - if ( scene->selMode & Scene::SelObject ) { + if ( scene->actionMode & Scene::Object ) { int s_nodeId = ID2COLORKEY( nodeId ); glColor4ubv( (GLubyte *)&s_nodeId ); } else { @@ -1129,7 +1129,7 @@ void Mesh::drawShapes( NodeList * secondPass, bool presort ) } glPointSize( 8.5 ); - if ( scene->selMode & Scene::SelVertex ) { + if ( scene->actionMode & Scene::Vertex ) { drawVerts(); } @@ -1169,7 +1169,7 @@ void Mesh::drawSelection() const if ( scene->options & Scene::ShowNodes ) Node::drawSelection(); - if ( isHidden() || !(scene->selMode & Scene::SelObject) ) + if ( isHidden() || !(scene->actionMode & Scene::Object) ) return; auto idx = scene->currentIndex; diff --git a/src/gl/glnode.cpp b/src/gl/glnode.cpp index d9cbb9da3..ca77fd671 100644 --- a/src/gl/glnode.cpp +++ b/src/gl/glnode.cpp @@ -530,7 +530,7 @@ void Node::draw() if ( isHidden() || iBlock == scene->currentBlock ) return; - if ( !(scene->selMode & Scene::SelObject) ) + if ( !(scene->actionMode & Scene::Object) ) return; if ( Node::SELECTING ) { @@ -587,7 +587,7 @@ void Node::drawSelection() const if ( !nif ) return; - if ( !(scene->selMode & Scene::SelObject) ) + if ( !(scene->actionMode & Scene::Object) ) return; bool extraData = false; @@ -776,7 +776,7 @@ void drawHvkShape( const NifModel * nif, const QModelIndex & iShape, QStackselMode & Scene::SelObject) ) + if ( !(scene->actionMode & Scene::Object) ) return; stack.push( iShape ); @@ -1079,7 +1079,7 @@ void drawHvkConstraint( const NifModel * nif, const QModelIndex & iConstraint, c if ( !( nif && iConstraint.isValid() && scene && (scene->options & Scene::ShowConstraints) ) ) return; - if ( !(scene->selMode & Scene::SelObject) ) + if ( !(scene->actionMode & Scene::Object) ) return; QList tBodies; @@ -1396,7 +1396,7 @@ void drawHvkConstraint( const NifModel * nif, const QModelIndex & iConstraint, c void Node::drawHavok() { - if ( !(scene->selMode & Scene::SelObject) ) + if ( !(scene->actionMode & Scene::Object) ) return; // TODO: Why are all these here - "drawNodes", "drawFurn", "drawHavok"? @@ -1810,7 +1810,7 @@ void Node::drawFurn() if ( !( iBlock.isValid() && nif ) ) return; - if ( !(scene->selMode & Scene::SelObject) ) + if ( !(scene->actionMode & Scene::Object) ) return; QModelIndex iExtraDataList = nif->getIndex( iBlock, "Extra Data List" ); diff --git a/src/gl/glscene.cpp b/src/gl/glscene.cpp index 265d07a1e..3fbcd0da8 100644 --- a/src/gl/glscene.cpp +++ b/src/gl/glscene.cpp @@ -66,9 +66,9 @@ Scene::Scene( TexCache * texcache, QOpenGLContext * context, QOpenGLFunctions * lodLevel = Level2; - visMode = VisNone; + visMode = VisNone; - selMode = SelObject; + actionMode = ( Select | Object ); // Startup Defaults @@ -197,7 +197,7 @@ void Scene::updateSelectMode( QAction * action ) if ( !action ) return; - selMode = SelMode( action->data().toInt() ); + actionMode = ActionMode( action->data().toInt() ); emit sceneUpdated(); } diff --git a/src/gl/glscene.h b/src/gl/glscene.h index 4adab9b3b..ea1462c91 100644 --- a/src/gl/glscene.h +++ b/src/gl/glscene.h @@ -129,16 +129,18 @@ class Scene final : public QObject VisMode visMode; - enum SelModes - { - SelNone = 0, - SelObject = 1, - SelVertex = 2 - }; + enum ActionModes + { + Deselect = 0x0, + Select = 0x1, + Paint = 0x2, + Object = 0x10000, + Vertex = 0x20000 + }; - Q_DECLARE_FLAGS( SelMode, SelModes ); + Q_DECLARE_FLAGS( ActionMode, ActionModes ); - SelMode selMode; + ActionMode actionMode; enum LodLevel { @@ -203,4 +205,6 @@ Q_DECLARE_OPERATORS_FOR_FLAGS( Scene::SceneOptions ) Q_DECLARE_OPERATORS_FOR_FLAGS( Scene::VisMode ) +Q_DECLARE_OPERATORS_FOR_FLAGS( Scene::ActionMode ) + #endif diff --git a/src/glview.cpp b/src/glview.cpp index d44d8c0d1..39ce473c0 100644 --- a/src/glview.cpp +++ b/src/glview.cpp @@ -764,150 +764,198 @@ void GLView::setVisMode( Scene::VisMode mode, bool checked ) typedef void (Scene::* DrawFunc)( void ); -int indexAt( /*GLuint *buffer,*/ NifModel * model, Scene * scene, QList drawFunc, int cycle, const QPoint & pos, int & furn ) -{ - Q_UNUSED( model ); Q_UNUSED( cycle ); - // Color Key O(1) selection - // Open GL 3.0 says glRenderMode is deprecated - // ATI OpenGL API implementation of GL_SELECT corrupts NifSkope memory - // - // Create FBO for sharp edges and no shading. - // Texturing, blending, dithering, lighting and smooth shading should be disabled. - // The FBO can be used for the drawing operations to keep the drawing operations invisible to the user. - - GLint viewport[4]; - glGetIntegerv( GL_VIEWPORT, viewport ); - - // Create new FBO with multisampling disabled - QOpenGLFramebufferObjectFormat fboFmt; - fboFmt.setTextureTarget( GL_TEXTURE_2D ); - fboFmt.setInternalTextureFormat( GL_RGB32F_ARB ); - fboFmt.setAttachment( QOpenGLFramebufferObject::Attachment::CombinedDepthStencil ); - - QOpenGLFramebufferObject fbo( viewport[2], viewport[3], fboFmt ); - fbo.bind(); - - glEnable( GL_LIGHTING ); - glDisable( GL_MULTISAMPLE ); - glDisable( GL_MULTISAMPLE_ARB ); - glDisable( GL_LINE_SMOOTH ); - glDisable( GL_POINT_SMOOTH ); - glDisable( GL_POLYGON_SMOOTH ); - glDisable( GL_TEXTURE_1D ); - glDisable( GL_TEXTURE_2D ); - glDisable( GL_TEXTURE_3D ); - glDisable( GL_BLEND ); - glDisable( GL_DITHER ); - glDisable( GL_FOG ); - glDisable( GL_LIGHTING ); - glShadeModel( GL_FLAT ); - glEnable( GL_DEPTH_TEST ); - glDepthFunc( GL_LEQUAL ); - glClearColor( 0, 0, 0, 1 ); - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - - // Rasterize the scene - Node::SELECTING = 1; - for ( DrawFunc df : drawFunc ) { - (scene->*df)(); - } - Node::SELECTING = 0; - - fbo.release(); - - QImage img( fbo.toImage() ); - QColor pixel = img.pixel( pos ); - -#ifndef QT_NO_DEBUG - img.save( "fbo.png" ); -#endif - - // Encode RGB to Int - int a = 0; - a |= pixel.red() << 0; - a |= pixel.green() << 8; - a |= pixel.blue() << 16; - - // Decode: - // R = (id & 0x000000FF) >> 0 - // G = (id & 0x0000FF00) >> 8 - // B = (id & 0x00FF0000) >> 16 - - int choose = COLORKEY2ID( a ); - - // Pick BSFurnitureMarker - if ( choose > 0 ) { - auto furnBlock = model->getBlock( model->index( 3, 0, model->getBlock( choose & 0x0ffff ) ), "BSFurnitureMarker" ); - - if ( furnBlock.isValid() ) { - furn = choose >> 16; - choose &= 0x0ffff; - } - } +QImage GLView::renderIndexImage() +{ + makeCurrent(); + + glPushAttrib( GL_ALL_ATTRIB_BITS ); + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + + glViewport( 0, 0, width(), height() ); + glProjection(); + + QList drawFuncs; + if ( scene->options & Scene::ShowCollision ) + drawFuncs << &Scene::drawHavok; + + if ( scene->options & Scene::ShowNodes ) + drawFuncs << &Scene::drawNodes; + + if ( scene->options & Scene::ShowMarkers ) + drawFuncs << &Scene::drawFurn; + + drawFuncs << &Scene::drawShapes; + // Color Key O(1) selection + // Open GL 3.0 says glRenderMode is deprecated + // ATI OpenGL API implementation of GL_SELECT corrupts NifSkope memory + // + // Create FBO for sharp edges and no shading. + // Texturing, blending, dithering, lighting and smooth shading should be disabled. + // The FBO can be used for the drawing operations to keep the drawing operations invisible to the user. + + GLint viewport[4]; + glGetIntegerv( GL_VIEWPORT, viewport ); + + // Create new FBO with multisampling disabled + QOpenGLFramebufferObjectFormat fboFmt; + fboFmt.setTextureTarget( GL_TEXTURE_2D ); + fboFmt.setInternalTextureFormat( GL_RGB32F_ARB ); + fboFmt.setAttachment( QOpenGLFramebufferObject::Attachment::CombinedDepthStencil ); + + QOpenGLFramebufferObject fbo( viewport[2], viewport[3], fboFmt ); + fbo.bind(); + + glEnable( GL_LIGHTING ); + glDisable( GL_MULTISAMPLE ); + glDisable( GL_MULTISAMPLE_ARB ); + glDisable( GL_LINE_SMOOTH ); + glDisable( GL_POINT_SMOOTH ); + glDisable( GL_POLYGON_SMOOTH ); + glDisable( GL_TEXTURE_1D ); + glDisable( GL_TEXTURE_2D ); + glDisable( GL_TEXTURE_3D ); + glDisable( GL_BLEND ); + glDisable( GL_DITHER ); + glDisable( GL_FOG ); + glDisable( GL_LIGHTING ); + glShadeModel( GL_FLAT ); + glEnable( GL_DEPTH_TEST ); + glDepthFunc( GL_LEQUAL ); + glClearColor( 0, 0, 0, 1 ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // Rasterize the scene + Node::SELECTING = 1; + for ( DrawFunc df : drawFuncs ) { + (scene->*df)(); + } + Node::SELECTING = 0; + + fbo.release(); + + glPopAttrib(); + glMatrixMode( GL_MODELVIEW ); + glPopMatrix(); + glMatrixMode( GL_PROJECTION ); + glPopMatrix(); + + return fbo.toImage(); +} + +QModelIndex GLView::sampleIndexImagePoint(const QImage& img, const QPoint& pos, NifModel* model) +{ + if (img.rect().contains(pos)) + { + QColor pixel = img.pixel( pos ); + return colorIndexToModelIndex(pixel, model); + } + return QModelIndex(); +} + +QVector GLView::sampleIndexImageCircle(const QImage& img, const QPoint& pos, float radius, NifModel* model) +{ + QVector indices; + if (img.rect().contains(pos)) + { + QPointF startf = (QPointF)pos - QPointF(radius, radius); + QPointF endf = (QPointF)pos + QPointF(radius, radius); + + QPoint start(std::round(startf.x()), std::round(startf.y())); + QPoint end(std::round(endf.x()), std::round(endf.y())); + + QVector2D origin(pos); + QVector colors; + for (int y=start.y(); y <= end.y(); y++) + { + for (int x=start.x(); x <= end.x(); x++) + { + if (img.rect().contains(x,y)) + { + float dist = (QVector2D(x,y) - origin).length(); + QColor pixel = img.pixel(x,y); + if (dist <= radius && !colors.contains(pixel)) + { + colors.push_back(pixel); + } + } + } + } + + for (const QColor& indColor : colors) + { + QModelIndex modelInd = colorIndexToModelIndex(indColor, model); + if (modelInd.isValid()) + indices.push_back(modelInd); + } + } + return indices; +} + +QModelIndex GLView::colorIndexToModelIndex(const QColor& color, NifModel* model) +{ + // Encode RGB to Int + int a = 0; + a |= color.red() << 0; + a |= color.green() << 8; + a |= color.blue() << 16; + + // Decode: + // R = (id & 0x000000FF) >> 0 + // G = (id & 0x0000FF00) >> 8 + // B = (id & 0x00FF0000) >> 16 + + int choose = COLORKEY2ID( a ); + int furn = -1; + + // Pick BSFurnitureMarker + if ( choose > 0 ) { + auto furnBlock = model->getBlock( model->index( 3, 0, model->getBlock( choose & 0x0ffff ) ), "BSFurnitureMarker" ); + + if ( furnBlock.isValid() ) { + furn = choose >> 16; + choose &= 0x0ffff; + } + } + + QModelIndex chooseIndex; + + if (scene->actionMode & Scene::Vertex ) { + // Vertex + int block = choose >> 16; + int vert = choose - (block << 16); + + auto shape = scene->shapes.value( block ); + if ( shape ) + chooseIndex = shape->vertexAt( vert ); + } else if ( choose != -1 ) { + // Block Index + chooseIndex = model->getBlock( choose ); - //qDebug() << "Key:" << a << " R" << pixel.red() << " G" << pixel.green() << " B" << pixel.blue(); - return choose; + if ( furn != -1 ) { + // Furniture Row @ Block Index + chooseIndex = model->index( furn, 0, model->index( 3, 0, chooseIndex ) ); + } + } + + return chooseIndex; } -QModelIndex GLView::indexAt( const QPoint & pos, int cycle ) -{ +QModelIndex GLView::indexAt(const QPoint & pos, int cycle) +{ + Q_UNUSED(cycle); + if ( !(model && isVisible() && height()) ) return QModelIndex(); - makeCurrent(); - - glPushAttrib( GL_ALL_ATTRIB_BITS ); - glMatrixMode( GL_PROJECTION ); - glPushMatrix(); - glMatrixMode( GL_MODELVIEW ); - glPushMatrix(); - - glViewport( 0, 0, width(), height() ); - glProjection( pos.x(), pos.y() ); - - QList df; - - if ( scene->options & Scene::ShowCollision ) - df << &Scene::drawHavok; - - if ( scene->options & Scene::ShowNodes ) - df << &Scene::drawNodes; - - if ( scene->options & Scene::ShowMarkers ) - df << &Scene::drawFurn; - - df << &Scene::drawShapes; - - int choose = -1, furn = -1; - choose = ::indexAt( model, scene, df, cycle, pos, /*out*/ furn ); - - glPopAttrib(); - glMatrixMode( GL_MODELVIEW ); - glPopMatrix(); - glMatrixMode( GL_PROJECTION ); - glPopMatrix(); - - QModelIndex chooseIndex; - - if ( scene->selMode & Scene::SelVertex ) { - // Vertex - int block = choose >> 16; - int vert = choose - (block << 16); - - auto shape = scene->shapes.value( block ); - if ( shape ) - chooseIndex = shape->vertexAt( vert ); - } else if ( choose != -1 ) { - // Block Index - chooseIndex = model->getBlock( choose ); - - if ( furn != -1 ) { - // Furniture Row @ Block Index - chooseIndex = model->index( furn, 0, model->index( 3, 0, chooseIndex ) ); - } - } + QImage img = renderIndexImage(); +#ifndef QT_NO_DEBUG + img.save( "fbo.png" ); +#endif - return chooseIndex; + return sampleIndexImagePoint(img, pos, model); } void GLView::center() @@ -943,6 +991,54 @@ void GLView::zoom( float z ) update(); } +void GLView::startVertexPaint(const QPoint& point) +{ + mousePaint = true; + mousePaintVerts.clear(); + mousePaintHitDetectImg = renderIndexImage(); + vertexPaint(point); +} + +void GLView::vertexPaint(const QPoint& point) +{ + if (mousePaint) + { + // Let's do an absolutely shit job and act like it's decent + auto inds = sampleIndexImageCircle(mousePaintHitDetectImg, point, cfg.brushSize, model); + + for (const auto& ind : inds) + { + if ( ind.isValid() && !mousePaintVerts.contains(ind)) + { + mousePaintVerts.push_back(ind); + Color4 base = model->get(ind.parent(), "Vertex Colors"); + Color4 blend; + + if (cfg.brushMode == PaintBlendMode::BlendNormal) + { + blend = (cfg.brushColor * cfg.brushOpacity) + (base * (Color4() - cfg.brushOpacity)); + } + else if (cfg.brushMode == PaintBlendMode::BlendMultiply) + { + blend = ((cfg.brushColor * cfg.brushOpacity) + (Color4() - cfg.brushOpacity)) * base; + } + else if (cfg.brushMode == PaintBlendMode::BlendAdd) + { + blend = (cfg.brushColor * cfg.brushOpacity) + base; + } + + model->set(ind.parent(), "Vertex Colors", ByteColor4::fromColor4(blend)); + } + } + } +} + +void GLView::endVertexPaint() +{ + mousePaint = false; + mousePaintVerts.clear(); +} + void GLView::setCenter() { Node * node = scene->getNode( model, scene->currentBlock ); @@ -1710,6 +1806,11 @@ void GLView::mouseDoubleClickEvent( QMouseEvent * ) void GLView::mouseMoveEvent( QMouseEvent * event ) { + if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { + vertexPaint(event->pos()); + return; + } + int dx = event->x() - lastPos.x(); int dy = event->y() - lastPos.y(); @@ -1732,6 +1833,11 @@ void GLView::mousePressEvent( QMouseEvent * event ) return; } + if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { + startVertexPaint(event->pos()); + return; + } + lastPos = event->pos(); if ( (pressPos - event->pos()).manhattanLength() <= 3 ) @@ -1744,6 +1850,11 @@ void GLView::mousePressEvent( QMouseEvent * event ) void GLView::mouseReleaseEvent( QMouseEvent * event ) { + if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { + endVertexPaint(); + return; + } + if ( !(model && (pressPos - event->pos()).manhattanLength() <= 3) ) return; diff --git a/src/glview.h b/src/glview.h index acfea7d74..05eabd452 100644 --- a/src/glview.h +++ b/src/glview.h @@ -34,6 +34,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define GLVIEW #include "gl/glscene.h" +#include "data/nifitem.h" #include // Inherited #include @@ -119,6 +120,13 @@ class GLView final : public QGLWidget ZAxis = 2 }; + enum PaintBlendMode + { + BlendNormal = 0, + BlendAdd = 1, + BlendMultiply = 2 + }; + void setNif( NifModel * ); Scene * getScene(); @@ -132,6 +140,10 @@ class GLView final : public QGLWidget void rotate( float, float, float ); void zoom( float ); + void startVertexPaint(const QPoint&); + void vertexPaint(const QPoint&); + void endVertexPaint(); + void setCenter(); void setDistance( float ); void setPosition( float, float, float ); @@ -147,8 +159,13 @@ class GLView final : public QGLWidget QColor clearColor() const; + QImage renderIndexImage(); + QModelIndex sampleIndexImagePoint(const QImage& img, const QPoint & p, NifModel* model); + QVector sampleIndexImageCircle(const QImage& img, const QPoint & p, float radius, NifModel* model); + + QModelIndex colorIndexToModelIndex(const QColor& color, NifModel* model); - QModelIndex indexAt( const QPoint & p, int cycle = 0 ); + QModelIndex indexAt( const QPoint & p, int cycle = 0 ); // UI @@ -243,6 +260,10 @@ protected slots: QPoint pressPos; Vector3 mouseMov; Vector3 mouseRot; + bool mousePaint; + QVector mousePaintVerts; + QImage mousePaintHitDetectImg; + int cycleSelect; QPersistentModelIndex iDragTarget; @@ -262,6 +283,11 @@ protected slots: float rotSpd = 45; UpAxis upAxis = ZAxis; + + float brushSize = 24.0f; + Color4 brushColor = {1.0f, 0.0f, 1.0f, 1.0f}; + Color4 brushOpacity = {1.0f, 1.0f, 1.0f, 0.0f}; + PaintBlendMode brushMode = PaintBlendMode::BlendNormal; } cfg; private slots: diff --git a/src/nifskope.h b/src/nifskope.h index e783f8831..72ea987da 100644 --- a/src/nifskope.h +++ b/src/nifskope.h @@ -175,6 +175,8 @@ public slots: void updateSettings(); + void updateVertexPaintSettings(); + //! Select a NIF index void select( const QModelIndex & ); @@ -379,6 +381,7 @@ protected slots: QDockWidget * dKfm; QDockWidget * dRefr; QDockWidget * dInsp; + QDockWidget * dPaint; QDockWidget * dBrowser; QToolBar * tool; diff --git a/src/nifskope_ui.cpp b/src/nifskope_ui.cpp index 91b655ccb..635dd09e1 100644 --- a/src/nifskope_ui.cpp +++ b/src/nifskope_ui.cpp @@ -225,8 +225,9 @@ void NifSkope::initActions() ui->aLighting->setData( Scene::DoLighting ); ui->aDisableShading->setData( Scene::DisableShaders ); - ui->aSelectObject->setData( Scene::SelObject ); - ui->aSelectVertex->setData( Scene::SelVertex ); + ui->aSelectObject->setData( (int)( Scene::Select | Scene::Object ) ); + ui->aSelectVertex->setData( (int)( Scene::Select | Scene::Vertex ) ); + ui->aPaintVertex->setData( (int)( Scene::Paint | Scene::Vertex ) ); auto agroup = [this]( QVector actions, bool exclusive ) { QActionGroup * ag = new QActionGroup( this ); @@ -239,8 +240,8 @@ void NifSkope::initActions() return ag; }; - selectActions = agroup( { ui->aSelectObject, ui->aSelectVertex }, true ); - connect( selectActions, &QActionGroup::triggered, ogl->getScene(), &Scene::updateSelectMode ); + selectActions = agroup( { ui->aSelectObject, ui->aSelectVertex, ui->aPaintVertex }, true ); + connect( selectActions, &QActionGroup::triggered, ogl->getScene(), &Scene::updateSelectMode ); showActions = agroup( { ui->aShowAxes, ui->aShowGrid, ui->aShowNodes, ui->aShowCollision, ui->aShowConstraints, ui->aShowMarkers, ui->aShowHidden, ui->aDoSkinning @@ -372,6 +373,7 @@ void NifSkope::initDockWidgets() dTree = ui->TreeDock; dHeader = ui->HeaderDock; dInsp = ui->InspectDock; + dPaint = ui->PaintDock; dKfm = ui->KfmDock; dBrowser = ui->BrowserDock; @@ -385,17 +387,40 @@ void NifSkope::initDockWidgets() // Hide certain docks by default dRefr->toggleViewAction()->setChecked( false ); dInsp->toggleViewAction()->setChecked( false ); + dPaint->toggleViewAction()->setChecked( false ); dKfm->toggleViewAction()->setChecked( false ); dRefr->setVisible( false ); dInsp->setVisible( false ); + dPaint->setVisible( false ); dKfm->setVisible( false ); // Set Inspect widget dInsp->setWidget( inspect ); - connect( dList->toggleViewAction(), &QAction::triggered, tree, &NifTreeView::clearRootIndex ); + // Push paint values to controls and hook up event + ui->paintBrushSize->setValue(ogl->cfg.brushSize); + ui->paintColorR->setValue(ogl->cfg.brushColor.red()); + ui->paintColorG->setValue(ogl->cfg.brushColor.green()); + ui->paintColorB->setValue(ogl->cfg.brushColor.blue()); + ui->paintColorA->setValue(ogl->cfg.brushColor.alpha()); + ui->paintOpacity->setValue(std::round(ogl->cfg.brushOpacity.red() * 255.0f)); + ui->paintOpacityMode->addItems({"Color", "Alpha", "Both"}); + ui->paintBlendMode->setCurrentIndex(0); + ui->paintBlendMode->addItems({"Normal","Add","Multiply"}); + ui->paintBlendMode->setCurrentIndex((int)ogl->cfg.brushMode); + + // Hookup change events to update the paint config + connect(ui->paintBrushSize, SIGNAL(valueChanged(double)), this, SLOT(updateVertexPaintSettings())); + connect(ui->paintColorR, SIGNAL(valueChanged(double)), this, SLOT(updateVertexPaintSettings())); + connect(ui->paintColorG, SIGNAL(valueChanged(double)), this, SLOT(updateVertexPaintSettings())); + connect(ui->paintColorB, SIGNAL(valueChanged(double)), this, SLOT(updateVertexPaintSettings())); + connect(ui->paintColorA, SIGNAL(valueChanged(double)), this, SLOT(updateVertexPaintSettings())); + connect(ui->paintOpacity, SIGNAL(valueChanged(int)), this, SLOT(updateVertexPaintSettings())); + connect(ui->paintOpacityMode, SIGNAL(currentIndexChanged(int)), this, SLOT(updateVertexPaintSettings())); + connect(ui->paintBlendMode, SIGNAL(currentIndexChanged(int)), this, SLOT(updateVertexPaintSettings())); + connect( dList->toggleViewAction(), &QAction::triggered, tree, &NifTreeView::clearRootIndex ); } void NifSkope::initMenu() @@ -511,7 +536,6 @@ void NifSkope::initMenu() } } - void NifSkope::initToolBars() { // Disable without NIF loaded @@ -645,7 +669,6 @@ void NifSkope::initConnections() connect( this, &NifSkope::completeSave, this, &NifSkope::onSaveComplete ); } - QMenu * NifSkope::lightingWidget() { QMenu * mLight = new QMenu( this ); @@ -665,7 +688,6 @@ QMenu * NifSkope::lightingWidget() return mLight; } - QWidget * NifSkope::filePathWidget( QWidget * parent ) { // Show Filepath of loaded NIF @@ -727,7 +749,6 @@ QWidget * NifSkope::filePathWidget( QWidget * parent ) return filepathWidget; } - void NifSkope::archiveDlg() { QString file = QFileDialog::getOpenFileName( this, tr( "Open Archive" ), "", "Archives (*.bsa *.ba2)" ); @@ -836,7 +857,6 @@ void NifSkope::onLoadComplete( bool success, QString & fname ) QTimer::singleShot( timeout, progress, SLOT( hide() ) ); } - void NifSkope::saveAsDlg() { QString filename = QFileDialog::getSaveFileName( this, tr( "Save File" ), nif->getFileInfo().absoluteFilePath(), @@ -935,7 +955,6 @@ void NifSkope::saveUi() const settings.setValue( "GLView/Perspective", ui->aViewPerspective->isChecked() ); } - void NifSkope::restoreUi() { QSettings settings; @@ -1219,7 +1238,6 @@ void NifSkope::resizeDone() ogl->resizeGL( centralWidget()->width(), centralWidget()->height() ); } - bool NifSkope::eventFilter( QObject * o, QEvent * e ) { // TODO: This doesn't seem to be doing anything extra @@ -1306,6 +1324,31 @@ bool NifSkope::eventFilter( QObject * o, QEvent * e ) * ********************** */ +void NifSkope::updateVertexPaintSettings() +{ + ogl->cfg.brushColor = Color4(ui->paintColorR->value(), + ui->paintColorG->value(), + ui->paintColorB->value(), + ui->paintColorA->value()); + + ogl->cfg.brushSize = ui->paintBrushSize->value(); + + float opacity = (float)ui->paintOpacity->value() / 255.0f; + if (ui->paintOpacityMode->currentIndex() == 0) // Color only + { + ogl->cfg.brushOpacity = Color4(opacity,opacity,opacity,0.0f); + } + else if (ui->paintOpacityMode->currentIndex() == 1) // Alpha only + { + ogl->cfg.brushOpacity = Color4(0,0,0,opacity); + } + else if (ui->paintOpacityMode->currentIndex() == 3) // Both + { + ogl->cfg.brushOpacity = Color4(opacity,opacity,opacity,opacity); + } + + ogl->cfg.brushMode = (GLView::PaintBlendMode)ui->paintBlendMode->currentIndex(); +} void NifSkope::contextMenu( const QPoint & pos ) { diff --git a/src/ui/nifskope.ui b/src/ui/nifskope.ui index 2ecad1d36..401b52dc6 100644 --- a/src/ui/nifskope.ui +++ b/src/ui/nifskope.ui @@ -10,7 +10,33 @@ 800 - + + + + + 0 + 0 + 114 + 24 + + + + + 0 + 0 + + + + true + + + + + + true + + + @@ -69,6 +95,7 @@ + @@ -278,6 +305,7 @@ + @@ -1021,6 +1049,222 @@ background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0 + + + Paint Vertex + + + 2 + + + + + + + + 1 + 0 + + + + Brush Size + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 4096.000000000000000 + + + + + + + + + + + 0 + 0 + + + + Color + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1.000000000000000 + + + 0.003906000000000 + + + 1.000000000000000 + + + + + + + 1.000000000000000 + + + 0.003906000000000 + + + + + + + 1.000000000000000 + + + 0.003906000000000 + + + 1.000000000000000 + + + + + + + 1.000000000000000 + + + 0.003906000000000 + + + + + + + + + + Opacity + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 255 + + + 255 + + + Qt::Horizontal + + + + + + + 255 + + + 255 + + + + + + + + + + Blend Mode + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + -1 + + + + + + + + + + + + true @@ -2093,6 +2337,32 @@ background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0 Error Color + + + true + + + + :/btn/paintVerts:/btn/paintVerts + + + Paint Vertex + + + Paint Vertex + + + + + true + + + Paint + + + Paint + + @@ -2654,6 +2924,102 @@ background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0 + + aTogglePaintSettings + toggled(bool) + PaintDock + setVisible(bool) + + + -1 + -1 + + + 1112 + 466 + + + + + PaintDock + visibilityChanged(bool) + aTogglePaintSettings + setChecked(bool) + + + 1112 + 466 + + + -1 + -1 + + + + + aTogglePaintSettings + triggered() + PaintDock + raise() + + + -1 + -1 + + + 1112 + 466 + + + + + aPaintVertex + toggled(bool) + aTogglePaintSettings + setChecked(bool) + + + -1 + -1 + + + -1 + -1 + + + + + paintOpacitySlider + valueChanged(int) + paintOpacity + setValue(int) + + + 1080 + 483 + + + 1160 + 483 + + + + + paintOpacity + valueChanged(int) + paintOpacitySlider + setValue(int) + + + 1160 + 483 + + + 1080 + 483 + + + openURL() From 6099356127c6ca0ba0dda3582e5ff554d21fc1f8 Mon Sep 17 00:00:00 2001 From: DonkeyKong Date: Wed, 17 Nov 2021 17:00:20 -0500 Subject: [PATCH 05/10] Add paint vertex brush cursor Refine paint vertex controls dock panel Fix several bugs with setting viewports to the wrong size on high DPI devices --- src/glview.cpp | 66 +++++++++++++++++++++++++---------- src/nifskope_ui.cpp | 15 +++++--- src/ui/nifskope.ui | 73 +++++++++++++++++++++++++++++++++++++-- src/ui/widgets/uvedit.cpp | 2 +- 4 files changed, 130 insertions(+), 26 deletions(-) diff --git a/src/glview.cpp b/src/glview.cpp index 39ce473c0..a983c338f 100644 --- a/src/glview.cpp +++ b/src/glview.cpp @@ -101,7 +101,7 @@ GLGraphicsView::GLGraphicsView( QWidget * parent ) : QGraphicsView() setContextMenuPolicy( Qt::CustomContextMenu ); setFocusPolicy( Qt::ClickFocus ); setAcceptDrops( true ); - + installEventFilter( parent ); } @@ -156,6 +156,7 @@ GLView::GLView( const QGLFormat & format, QWidget * p, const QGLWidget * shareWi //setAttribute( Qt::WA_NoSystemBackground ); setAutoFillBackground( false ); setAcceptDrops( true ); + setMouseTracking( true ); setContextMenuPolicy( Qt::CustomContextMenu ); // Manually handle the buffer swap @@ -622,7 +623,7 @@ void GLView::paintGL() if ( scene->options & Scene::ShowAxes ) { // Resize viewport to small corner of screen int axesSize = std::min( width() / 10, 125 ); - glViewport( 0, 0, axesSize, axesSize ); + glViewport( 0, 0, axesSize*devicePixelRatioF(), axesSize*devicePixelRatioF() ); // Reset matrices glMatrixMode( GL_PROJECTION ); @@ -662,11 +663,34 @@ void GLView::paintGL() glPopMatrix(); // Restore viewport size - glViewport( 0, 0, width(), height() ); + glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); + // Restore matrices glProjection(); } + // Draw mouse tool + if (scene->actionMode & Scene::Paint) + { + // Reset matrices + glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho(0, width(), height(), 0, -1, 1); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + glDisable( GL_LIGHTING ); + glDepthFunc( GL_ALWAYS ); + glLineWidth( 2.0f ); + glColor3f( 1.0, 0.0, 0.0 ); + //drawCircle(Vector3(0, 0, 0), Vector3(0,0,1), 0.2); + drawCircle(Vector3(lastPos.x(), lastPos.y(), 0), Vector3(0,0,1), cfg.brushSize + 2.0f); + + // Restore viewport and projection + glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); + glProjection(); + } + // Restore GL state glPopAttrib(); glMatrixMode( GL_MODELVIEW ); @@ -997,10 +1021,14 @@ void GLView::startVertexPaint(const QPoint& point) mousePaintVerts.clear(); mousePaintHitDetectImg = renderIndexImage(); vertexPaint(point); + QCursor cursor(Qt::BlankCursor); + QApplication::setOverrideCursor(cursor); + QApplication::changeOverrideCursor(cursor); } void GLView::vertexPaint(const QPoint& point) { + bool updated = false; if (mousePaint) { // Let's do an absolutely shit job and act like it's decent @@ -1028,15 +1056,22 @@ void GLView::vertexPaint(const QPoint& point) } model->set(ind.parent(), "Vertex Colors", ByteColor4::fromColor4(blend)); + updated = true; } } } + + if (!updated) + { + update(); + } } void GLView::endVertexPaint() { mousePaint = false; mousePaintVerts.clear(); + QApplication::restoreOverrideCursor(); } void GLView::setCenter() @@ -1806,15 +1841,12 @@ void GLView::mouseDoubleClickEvent( QMouseEvent * ) void GLView::mouseMoveEvent( QMouseEvent * event ) { + int dx = event->x() - lastPos.x(); + int dy = event->y() - lastPos.y(); + if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { vertexPaint(event->pos()); - return; - } - - int dx = event->x() - lastPos.x(); - int dy = event->y() - lastPos.y(); - - if ( event->buttons() & Qt::LeftButton && !kbd[Qt::Key_Space] ) { + } else if ( event->buttons() & Qt::LeftButton && !kbd[Qt::Key_Space] ) { mouseRot += Vector3( dy * .5, 0, dx * .5 ); } else if ( (event->buttons() & Qt::MidButton) || (event->buttons() & Qt::LeftButton && kbd[Qt::Key_Space]) ) { float d = axis / (qMax( width(), height() ) + 1); @@ -1835,16 +1867,14 @@ void GLView::mousePressEvent( QMouseEvent * event ) if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { startVertexPaint(event->pos()); - return; + } else { + if ( (pressPos - event->pos()).manhattanLength() <= 3 ) + cycleSelect++; + else + cycleSelect = 0; } - lastPos = event->pos(); - - if ( (pressPos - event->pos()).manhattanLength() <= 3 ) - cycleSelect++; - else - cycleSelect = 0; - + lastPos = event->pos(); pressPos = event->pos(); } diff --git a/src/nifskope_ui.cpp b/src/nifskope_ui.cpp index 635dd09e1..16384b209 100644 --- a/src/nifskope_ui.cpp +++ b/src/nifskope_ui.cpp @@ -128,7 +128,7 @@ NifSkope * NifSkope::createWindow( const QString & fname ) if ( !fname.isEmpty() ) { skope->loadFile( fname ); - } + } return skope; } @@ -405,13 +405,13 @@ void NifSkope::initDockWidgets() ui->paintColorB->setValue(ogl->cfg.brushColor.blue()); ui->paintColorA->setValue(ogl->cfg.brushColor.alpha()); ui->paintOpacity->setValue(std::round(ogl->cfg.brushOpacity.red() * 255.0f)); - ui->paintOpacityMode->addItems({"Color", "Alpha", "Both"}); + ui->paintOpacityMode->addItems({"Color", "Alpha", "Color + Alpha"}); ui->paintBlendMode->setCurrentIndex(0); ui->paintBlendMode->addItems({"Normal","Add","Multiply"}); ui->paintBlendMode->setCurrentIndex((int)ogl->cfg.brushMode); // Hookup change events to update the paint config - connect(ui->paintBrushSize, SIGNAL(valueChanged(double)), this, SLOT(updateVertexPaintSettings())); + connect(ui->paintBrushSize, SIGNAL(valueChanged(int)), this, SLOT(updateVertexPaintSettings())); connect(ui->paintColorR, SIGNAL(valueChanged(double)), this, SLOT(updateVertexPaintSettings())); connect(ui->paintColorG, SIGNAL(valueChanged(double)), this, SLOT(updateVertexPaintSettings())); connect(ui->paintColorB, SIGNAL(valueChanged(double)), this, SLOT(updateVertexPaintSettings())); @@ -850,6 +850,9 @@ void NifSkope::onLoadComplete( bool success, QString & fname ) nif->undoStack->clear(); indexStack->clear(); + // Update widget sizes to fixup ogl view + resizeDone(); + // Center the model on load ogl->center(); @@ -1331,7 +1334,8 @@ void NifSkope::updateVertexPaintSettings() ui->paintColorB->value(), ui->paintColorA->value()); - ogl->cfg.brushSize = ui->paintBrushSize->value(); + ogl->cfg.brushSize = (float)ui->paintBrushSize->value(); + ogl->update(); float opacity = (float)ui->paintOpacity->value() / 255.0f; if (ui->paintOpacityMode->currentIndex() == 0) // Color only @@ -1342,12 +1346,13 @@ void NifSkope::updateVertexPaintSettings() { ogl->cfg.brushOpacity = Color4(0,0,0,opacity); } - else if (ui->paintOpacityMode->currentIndex() == 3) // Both + else if (ui->paintOpacityMode->currentIndex() == 2) // Color+Alpha { ogl->cfg.brushOpacity = Color4(opacity,opacity,opacity,opacity); } ogl->cfg.brushMode = (GLView::PaintBlendMode)ui->paintBlendMode->currentIndex(); + ogl->update(); } void NifSkope::contextMenu( const QPoint & pos ) diff --git a/src/ui/nifskope.ui b/src/ui/nifskope.ui index 401b52dc6..c9386f8a6 100644 --- a/src/ui/nifskope.ui +++ b/src/ui/nifskope.ui @@ -1087,9 +1087,34 @@ background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0 0 - + - 4096.000000000000000 + 128 + + + 1 + + + 10 + + + 24 + + + Qt::Horizontal + + + + + + + 1 + + + 128 + + + 24 @@ -1129,6 +1154,9 @@ background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0 + + 3 + 1.000000000000000 @@ -1142,6 +1170,9 @@ background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0 + + 3 + 1.000000000000000 @@ -1152,6 +1183,9 @@ background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0 + + 3 + 1.000000000000000 @@ -1165,6 +1199,9 @@ background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0 + + 3 + 1.000000000000000 @@ -3020,6 +3057,38 @@ background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0 + + paintBrushSizeSlider + valueChanged(int) + paintBrushSize + setValue(int) + + + 1124 + 410 + + + 1246 + 410 + + + + + paintBrushSize + valueChanged(int) + paintBrushSizeSlider + setFocus() + + + 1246 + 410 + + + 1124 + 410 + + + openURL() diff --git a/src/ui/widgets/uvedit.cpp b/src/ui/widgets/uvedit.cpp index 1360e7a53..bb077ed7c 100644 --- a/src/ui/widgets/uvedit.cpp +++ b/src/ui/widgets/uvedit.cpp @@ -478,7 +478,7 @@ void UVWidget::setupViewport( int width, int height ) glMatrixMode( GL_PROJECTION ); glLoadIdentity(); - glViewport( 0, 0, width, height ); + glViewport( 0, 0, width*devicePixelRatioF(), height*devicePixelRatioF() ); glOrtho( glViewRect[0], glViewRect[1], glViewRect[2], glViewRect[3], -10.0, +10.0 ); } From a56e884b486ecb57d46f992c525817ab3957ed8a Mon Sep 17 00:00:00 2001 From: DonkeyKong Date: Wed, 17 Nov 2021 19:07:23 -0500 Subject: [PATCH 06/10] Refactor vertex paint settings into widget --- NifSkope.pro | 5 +- src/glview.cpp | 22 +- src/glview.h | 14 +- src/nifskope.h | 4 +- src/nifskope_ui.cpp | 55 +--- src/ui/nifskope.ui | 354 ++--------------------- src/ui/widgets/vertexpaintwidget.cpp | 109 +++++++ src/ui/widgets/vertexpaintwidget.h | 34 +++ src/ui/widgets/vertexpaintwidget.ui | 411 +++++++++++++++++++++++++++ 9 files changed, 611 insertions(+), 397 deletions(-) create mode 100644 src/ui/widgets/vertexpaintwidget.cpp create mode 100644 src/ui/widgets/vertexpaintwidget.h create mode 100644 src/ui/widgets/vertexpaintwidget.ui diff --git a/NifSkope.pro b/NifSkope.pro index 1a0f7eee5..a173dac0b 100644 --- a/NifSkope.pro +++ b/NifSkope.pro @@ -189,6 +189,7 @@ HEADERS += \ src/ui/widgets/refrbrowser.h \ src/ui/widgets/uvedit.h \ src/ui/widgets/valueedit.h \ + src/ui/widgets/vertexpaintwidget.h \ src/ui/widgets/xmlcheck.h \ src/ui/about_dialog.h \ src/ui/checkablemessagebox.h \ @@ -270,6 +271,7 @@ SOURCES += \ src/ui/widgets/refrbrowser.cpp \ src/ui/widgets/uvedit.cpp \ src/ui/widgets/valueedit.cpp \ + src/ui/widgets/vertexpaintwidget.cpp \ src/ui/widgets/xmlcheck.cpp \ src/ui/about_dialog.cpp \ src/ui/checkablemessagebox.cpp \ @@ -298,7 +300,8 @@ FORMS += \ src/ui/settingsgeneral.ui \ src/ui/settingsrender.ui \ src/ui/settingsresources.ui \ - src/ui/widgets/lightingwidget.ui + src/ui/widgets/lightingwidget.ui \ + src/ui/widgets/vertexpaintwidget.ui ############################### diff --git a/src/glview.cpp b/src/glview.cpp index a983c338f..6259c6caa 100644 --- a/src/glview.cpp +++ b/src/glview.cpp @@ -684,7 +684,7 @@ void GLView::paintGL() glLineWidth( 2.0f ); glColor3f( 1.0, 0.0, 0.0 ); //drawCircle(Vector3(0, 0, 0), Vector3(0,0,1), 0.2); - drawCircle(Vector3(lastPos.x(), lastPos.y(), 0), Vector3(0,0,1), cfg.brushSize + 2.0f); + drawCircle(Vector3(lastPos.x(), lastPos.y(), 0), Vector3(0,0,1), cfg.vertexPaintSettings.brushSize + 2.0f); // Restore viewport and projection glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); @@ -1015,6 +1015,12 @@ void GLView::zoom( float z ) update(); } +void GLView::setVertexPaintSettings(GLView::PaintSettings settings) +{ + cfg.vertexPaintSettings = settings; + update(); +} + void GLView::startVertexPaint(const QPoint& point) { mousePaint = true; @@ -1032,7 +1038,7 @@ void GLView::vertexPaint(const QPoint& point) if (mousePaint) { // Let's do an absolutely shit job and act like it's decent - auto inds = sampleIndexImageCircle(mousePaintHitDetectImg, point, cfg.brushSize, model); + auto inds = sampleIndexImageCircle(mousePaintHitDetectImg, point, cfg.vertexPaintSettings.brushSize, model); for (const auto& ind : inds) { @@ -1042,17 +1048,17 @@ void GLView::vertexPaint(const QPoint& point) Color4 base = model->get(ind.parent(), "Vertex Colors"); Color4 blend; - if (cfg.brushMode == PaintBlendMode::BlendNormal) + if (cfg.vertexPaintSettings.brushMode == PaintBlendMode::BlendNormal) { - blend = (cfg.brushColor * cfg.brushOpacity) + (base * (Color4() - cfg.brushOpacity)); + blend = (cfg.vertexPaintSettings.brushColor * cfg.vertexPaintSettings.brushOpacity) + (base * (Color4() - cfg.vertexPaintSettings.brushOpacity)); } - else if (cfg.brushMode == PaintBlendMode::BlendMultiply) + else if (cfg.vertexPaintSettings.brushMode == PaintBlendMode::BlendMultiply) { - blend = ((cfg.brushColor * cfg.brushOpacity) + (Color4() - cfg.brushOpacity)) * base; + blend = ((cfg.vertexPaintSettings.brushColor * cfg.vertexPaintSettings.brushOpacity) + (Color4() - cfg.vertexPaintSettings.brushOpacity)) * base; } - else if (cfg.brushMode == PaintBlendMode::BlendAdd) + else if (cfg.vertexPaintSettings.brushMode == PaintBlendMode::BlendAdd) { - blend = (cfg.brushColor * cfg.brushOpacity) + base; + blend = (cfg.vertexPaintSettings.brushColor * cfg.vertexPaintSettings.brushOpacity) + base; } model->set(ind.parent(), "Vertex Colors", ByteColor4::fromColor4(blend)); diff --git a/src/glview.h b/src/glview.h index 05eabd452..e834fc6db 100644 --- a/src/glview.h +++ b/src/glview.h @@ -127,6 +127,14 @@ class GLView final : public QGLWidget BlendMultiply = 2 }; + struct PaintSettings + { + float brushSize = 24.0f; + Color4 brushColor = {1.0f, 0.0f, 1.0f, 1.0f}; + Color4 brushOpacity = {1.0f, 1.0f, 1.0f, 0.0f}; + PaintBlendMode brushMode = PaintBlendMode::BlendNormal; + }; + void setNif( NifModel * ); Scene * getScene(); @@ -187,6 +195,7 @@ public slots: void updateAnimationState( bool checked ); void setVisMode( Scene::VisMode, bool checked = true ); void updateSettings(); + void setVertexPaintSettings(GLView::PaintSettings settings); signals: void clicked( const QModelIndex & ); @@ -284,10 +293,7 @@ protected slots: UpAxis upAxis = ZAxis; - float brushSize = 24.0f; - Color4 brushColor = {1.0f, 0.0f, 1.0f, 1.0f}; - Color4 brushOpacity = {1.0f, 1.0f, 1.0f, 0.0f}; - PaintBlendMode brushMode = PaintBlendMode::BlendNormal; + PaintSettings vertexPaintSettings; } cfg; private slots: diff --git a/src/nifskope.h b/src/nifskope.h index 72ea987da..d2b94c607 100644 --- a/src/nifskope.h +++ b/src/nifskope.h @@ -173,9 +173,7 @@ public slots: void enableUi(); - void updateSettings(); - - void updateVertexPaintSettings(); + void updateSettings(); //! Select a NIF index void select( const QModelIndex & ); diff --git a/src/nifskope_ui.cpp b/src/nifskope_ui.cpp index 16384b209..b6eb7d5bc 100644 --- a/src/nifskope_ui.cpp +++ b/src/nifskope_ui.cpp @@ -48,6 +48,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "ui/widgets/nifview.h" #include "ui/widgets/refrbrowser.h" #include "ui/widgets/inspect.h" +#include "ui/widgets/vertexpaintwidget.h" #include "ui/widgets/xmlcheck.h" #include "ui/about_dialog.h" #include "ui/settingsdialog.h" @@ -398,27 +399,11 @@ void NifSkope::initDockWidgets() // Set Inspect widget dInsp->setWidget( inspect ); - // Push paint values to controls and hook up event - ui->paintBrushSize->setValue(ogl->cfg.brushSize); - ui->paintColorR->setValue(ogl->cfg.brushColor.red()); - ui->paintColorG->setValue(ogl->cfg.brushColor.green()); - ui->paintColorB->setValue(ogl->cfg.brushColor.blue()); - ui->paintColorA->setValue(ogl->cfg.brushColor.alpha()); - ui->paintOpacity->setValue(std::round(ogl->cfg.brushOpacity.red() * 255.0f)); - ui->paintOpacityMode->addItems({"Color", "Alpha", "Color + Alpha"}); - ui->paintBlendMode->setCurrentIndex(0); - ui->paintBlendMode->addItems({"Normal","Add","Multiply"}); - ui->paintBlendMode->setCurrentIndex((int)ogl->cfg.brushMode); - - // Hookup change events to update the paint config - connect(ui->paintBrushSize, SIGNAL(valueChanged(int)), this, SLOT(updateVertexPaintSettings())); - connect(ui->paintColorR, SIGNAL(valueChanged(double)), this, SLOT(updateVertexPaintSettings())); - connect(ui->paintColorG, SIGNAL(valueChanged(double)), this, SLOT(updateVertexPaintSettings())); - connect(ui->paintColorB, SIGNAL(valueChanged(double)), this, SLOT(updateVertexPaintSettings())); - connect(ui->paintColorA, SIGNAL(valueChanged(double)), this, SLOT(updateVertexPaintSettings())); - connect(ui->paintOpacity, SIGNAL(valueChanged(int)), this, SLOT(updateVertexPaintSettings())); - connect(ui->paintOpacityMode, SIGNAL(currentIndexChanged(int)), this, SLOT(updateVertexPaintSettings())); - connect(ui->paintBlendMode, SIGNAL(currentIndexChanged(int)), this, SLOT(updateVertexPaintSettings())); + // Push the initial brush settings to the vertex paint widget + ui->vertexPaintSettings->setValue(ogl->cfg.vertexPaintSettings); + + // Connect vertex paint widget to glview + connect(ui->vertexPaintSettings, &PaintSettingsWidget::valueChanged, ogl, &GLView::setVertexPaintSettings); connect( dList->toggleViewAction(), &QAction::triggered, tree, &NifTreeView::clearRootIndex ); } @@ -1327,34 +1312,6 @@ bool NifSkope::eventFilter( QObject * o, QEvent * e ) * ********************** */ -void NifSkope::updateVertexPaintSettings() -{ - ogl->cfg.brushColor = Color4(ui->paintColorR->value(), - ui->paintColorG->value(), - ui->paintColorB->value(), - ui->paintColorA->value()); - - ogl->cfg.brushSize = (float)ui->paintBrushSize->value(); - ogl->update(); - - float opacity = (float)ui->paintOpacity->value() / 255.0f; - if (ui->paintOpacityMode->currentIndex() == 0) // Color only - { - ogl->cfg.brushOpacity = Color4(opacity,opacity,opacity,0.0f); - } - else if (ui->paintOpacityMode->currentIndex() == 1) // Alpha only - { - ogl->cfg.brushOpacity = Color4(0,0,0,opacity); - } - else if (ui->paintOpacityMode->currentIndex() == 2) // Color+Alpha - { - ogl->cfg.brushOpacity = Color4(opacity,opacity,opacity,opacity); - } - - ogl->cfg.brushMode = (GLView::PaintBlendMode)ui->paintBlendMode->currentIndex(); - ogl->update(); -} - void NifSkope::contextMenu( const QPoint & pos ) { QModelIndex idx; diff --git a/src/ui/nifskope.ui b/src/ui/nifskope.ui index c9386f8a6..6001bf1ed 100644 --- a/src/ui/nifskope.ui +++ b/src/ui/nifskope.ui @@ -10,33 +10,7 @@ 800 - - - - - 0 - 0 - 114 - 24 - - - - - 0 - 0 - - - - true - - - - - - true - - - + @@ -1057,247 +1031,21 @@ background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0 2 - - - - - - 1 - 0 - - - - Brush Size - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 128 - - - 1 - - - 10 - - - 24 - - - Qt::Horizontal - - - - - - - 1 - - - 128 - - - 24 - - - - - - - - - - - 0 - 0 - - - - Color - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 3 - - - 1.000000000000000 - - - 0.003906000000000 - - - 1.000000000000000 - - - - - - - 3 - - - 1.000000000000000 - - - 0.003906000000000 - - - - - - - 3 - - - 1.000000000000000 - - - 0.003906000000000 - - - 1.000000000000000 - - - - - - - 3 - - - 1.000000000000000 - - - 0.003906000000000 - - - - - - - - - - Opacity - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 255 - - - Qt::Horizontal - - - - - - - 255 - - - 255 - - - - - - - - - - Blend Mode - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - -1 - - - - - - - - + + + 0 + + + 0 + + + 0 + + + 0 + + + @@ -2412,6 +2160,12 @@ background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0 QTextBrowser
ui/widgets/refrbrowser.h
+ + PaintSettingsWidget + QWidget +
ui/widgets/vertexpaintwidget.h
+ 1 +
@@ -3025,70 +2779,6 @@ background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0 - - paintOpacitySlider - valueChanged(int) - paintOpacity - setValue(int) - - - 1080 - 483 - - - 1160 - 483 - - - - - paintOpacity - valueChanged(int) - paintOpacitySlider - setValue(int) - - - 1160 - 483 - - - 1080 - 483 - - - - - paintBrushSizeSlider - valueChanged(int) - paintBrushSize - setValue(int) - - - 1124 - 410 - - - 1246 - 410 - - - - - paintBrushSize - valueChanged(int) - paintBrushSizeSlider - setFocus() - - - 1246 - 410 - - - 1124 - 410 - - - openURL() diff --git a/src/ui/widgets/vertexpaintwidget.cpp b/src/ui/widgets/vertexpaintwidget.cpp new file mode 100644 index 000000000..140119555 --- /dev/null +++ b/src/ui/widgets/vertexpaintwidget.cpp @@ -0,0 +1,109 @@ +#include "vertexpaintwidget.h" +#include "ui_vertexpaintwidget.h" +#include + +PaintSettingsWidget::PaintSettingsWidget(QWidget *parent) : + QWidget(parent), + ui(new Ui::PaintSettingsWidget) +{ + ui->setupUi(this); + supressUpdate_ = false; + + ui->opacityMode->addItems({"Color", "Alpha", "Color + Alpha"}); + ui->blendMode->setCurrentIndex(0); + ui->blendMode->addItems({"Normal","Add","Multiply"}); + ui->blendMode->setCurrentIndex(0); + + // Hookup change events to update the paint config + connect(ui->brushSize, SIGNAL(valueChanged(int)), this, SLOT(updateValue())); + connect(ui->colorR, SIGNAL(valueChanged(double)), this, SLOT(updateValue())); + connect(ui->colorG, SIGNAL(valueChanged(double)), this, SLOT(updateValue())); + connect(ui->colorB, SIGNAL(valueChanged(double)), this, SLOT(updateValue())); + connect(ui->colorA, SIGNAL(valueChanged(double)), this, SLOT(updateValue())); + connect(ui->opacity, SIGNAL(valueChanged(int)), this, SLOT(updateValue())); + connect(ui->opacityMode, SIGNAL(currentIndexChanged(int)), this, SLOT(updateValue())); + connect(ui->blendMode, SIGNAL(currentIndexChanged(int)), this, SLOT(updateValue())); +} + +void PaintSettingsWidget::setValue(const GLView::PaintSettings& value) +{ + supressUpdate_ = true; + + // Read all the settings that we can directly into controls + ui->brushSize->setValue(value.brushSize); + ui->colorR->setValue(value.brushColor.red()); + ui->colorG->setValue(value.brushColor.green()); + ui->colorB->setValue(value.brushColor.blue()); + ui->colorA->setValue(value.brushColor.alpha()); + ui->blendMode->setCurrentIndex((int)value.brushMode); + + // Infer opacity from brushOpacity + int opacity = qBound(std::round( + (value.brushOpacity.red() + + value.brushOpacity.green() + + value.brushOpacity.blue() + + value.brushOpacity.alpha() ) / 4.0f * 255.0f), 0, 255); + + ui->opacity->setValue(opacity); + + // Infer the paintOpacityMode from the provided brushOpacity + if (value.brushOpacity.red() != 0 && + value.brushOpacity.green() != 0 && + value.brushOpacity.blue() != 0 && + value.brushOpacity.alpha() == 0) + { + ui->opacityMode->setCurrentIndex(0); + } + else if (value.brushOpacity.red() == 0 && + value.brushOpacity.green() == 0 && + value.brushOpacity.blue() == 0 && + value.brushOpacity.alpha() != 0) + { + ui->opacityMode->setCurrentIndex(1); + } + else + { + ui->opacityMode->setCurrentIndex(2); + } + + supressUpdate_ = false; + + // With all controls populated, issue a manual updateValue + updateValue(); +} + +void PaintSettingsWidget::updateValue() +{ + if (supressUpdate_) + return; + + value_.brushColor = Color4(ui->colorR->value(), + ui->colorG->value(), + ui->colorB->value(), + ui->colorA->value()); + + value_.brushSize = (float)ui->brushSize->value(); + + float opacity = (float)ui->opacity->value() / 255.0f; + if (ui->opacityMode->currentIndex() == 0) // Color only + { + value_.brushOpacity = Color4(opacity,opacity,opacity,0.0f); + } + else if (ui->opacityMode->currentIndex() == 1) // Alpha only + { + value_.brushOpacity = Color4(0,0,0,opacity); + } + else if (ui->opacityMode->currentIndex() == 2) // Color+Alpha + { + value_.brushOpacity = Color4(opacity,opacity,opacity,opacity); + } + + value_.brushMode = (GLView::PaintBlendMode)ui->blendMode->currentIndex(); + + emit valueChanged(value_); +} + +PaintSettingsWidget::~PaintSettingsWidget() +{ + delete ui; +} diff --git a/src/ui/widgets/vertexpaintwidget.h b/src/ui/widgets/vertexpaintwidget.h new file mode 100644 index 000000000..deb6250bf --- /dev/null +++ b/src/ui/widgets/vertexpaintwidget.h @@ -0,0 +1,34 @@ +#ifndef VERTEXPAINTWIDGET_H +#define VERTEXPAINTWIDGET_H + +#include +#include + +namespace Ui { +class PaintSettingsWidget; +} + +class PaintSettingsWidget final : public QWidget +{ + Q_OBJECT + +public: + explicit PaintSettingsWidget(QWidget *parent = nullptr); + ~PaintSettingsWidget(); + +public slots: + void setValue( const GLView::PaintSettings& value ); + +signals: + void valueChanged( const GLView::PaintSettings& value ); + +protected slots: + void updateValue(); + +private: + Ui::PaintSettingsWidget *ui; + GLView::PaintSettings value_; + bool supressUpdate_; +}; + +#endif // VERTEXPAINTWIDGET_H diff --git a/src/ui/widgets/vertexpaintwidget.ui b/src/ui/widgets/vertexpaintwidget.ui new file mode 100644 index 000000000..b9cae39cc --- /dev/null +++ b/src/ui/widgets/vertexpaintwidget.ui @@ -0,0 +1,411 @@ + + + PaintSettingsWidget + + + + 0 + 0 + 366 + 165 + + + + Form + + + + + + + 1 + 0 + + + + Brush Size + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 128 + + + 1 + + + 10 + + + 24 + + + Qt::Horizontal + + + + + + + 1 + + + 128 + + + 24 + + + + + + + + + + + 0 + 0 + + + + Color + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 3 + + + 1.000000000000000 + + + 0.003906000000000 + + + 1.000000000000000 + + + + + + + 3 + + + 1.000000000000000 + + + 0.003906000000000 + + + + + + + 3 + + + 1.000000000000000 + + + 0.003906000000000 + + + 1.000000000000000 + + + + + + + 3 + + + 1.000000000000000 + + + 0.003906000000000 + + + 1.000000000000000 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 1 + 0 + + + + + + + + + 2 + 0 + + + + + 16777215 + 16777215 + + + + HHHHHHHH + + + FF00FFFF + + + 8 + + + + + + + + 1 + 0 + + + + + 0 + 0 + + + + true + + + QFrame::Box + + + QFrame::Plain + + + 2 + + + + + + + + + + Opacity + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 255 + + + 255 + + + Qt::Horizontal + + + + + + + 255 + + + 255 + + + + + + + + + + Blend Mode + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + -1 + + + + + + + + + + + + + + + brushSizeSlider + valueChanged(int) + brushSize + setValue(int) + + + 194 + 21 + + + 332 + 21 + + + + + brushSize + valueChanged(int) + brushSizeSlider + setValue(int) + + + 332 + 21 + + + 194 + 21 + + + + + opacity + valueChanged(int) + opacitySlider + setValue(int) + + + 331 + 113 + + + 193 + 113 + + + + + opacitySlider + valueChanged(int) + opacity + setValue(int) + + + 193 + 113 + + + 331 + 113 + + + + + From a22cff95ee7dda937a0b4ba98c58cacc68f4e9a6 Mon Sep 17 00:00:00 2001 From: DonkeyKong Date: Wed, 17 Nov 2021 21:29:17 -0500 Subject: [PATCH 07/10] Add color wheel to paint settings --- src/ui/widgets/vertexpaintwidget.cpp | 174 +++++++++++++++++++++++++-- src/ui/widgets/vertexpaintwidget.h | 8 ++ src/ui/widgets/vertexpaintwidget.ui | 89 +++++++++++--- 3 files changed, 245 insertions(+), 26 deletions(-) diff --git a/src/ui/widgets/vertexpaintwidget.cpp b/src/ui/widgets/vertexpaintwidget.cpp index 140119555..3d14ef195 100644 --- a/src/ui/widgets/vertexpaintwidget.cpp +++ b/src/ui/widgets/vertexpaintwidget.cpp @@ -8,11 +8,28 @@ PaintSettingsWidget::PaintSettingsWidget(QWidget *parent) : { ui->setupUi(this); supressUpdate_ = false; + supressHexUpdate_ = false; + supressWheelUpdate_ = false; ui->opacityMode->addItems({"Color", "Alpha", "Color + Alpha"}); ui->blendMode->setCurrentIndex(0); ui->blendMode->addItems({"Normal","Add","Multiply"}); ui->blendMode->setCurrentIndex(0); + setHexFromColor(); + setPreviewFromValue(); + setWheelFromColor(); + + // Hookup change events to handle sync of the hex and double color reps + connect(ui->colorR, SIGNAL(valueChanged(double)), this, SLOT(setHexFromColor())); + connect(ui->colorG, SIGNAL(valueChanged(double)), this, SLOT(setHexFromColor())); + connect(ui->colorB, SIGNAL(valueChanged(double)), this, SLOT(setHexFromColor())); + connect(ui->colorA, SIGNAL(valueChanged(double)), this, SLOT(setHexFromColor())); + connect(ui->colorR, SIGNAL(valueChanged(double)), this, SLOT(setWheelFromColor())); + connect(ui->colorG, SIGNAL(valueChanged(double)), this, SLOT(setWheelFromColor())); + connect(ui->colorB, SIGNAL(valueChanged(double)), this, SLOT(setWheelFromColor())); + connect(ui->colorA, SIGNAL(valueChanged(double)), this, SLOT(setWheelFromColor())); + connect(ui->colorHex, SIGNAL(textChanged(QString)), this, SLOT(setColorFromHex())); + connect(ui->colorWheel, SIGNAL(sigColor(QColor)), this, SLOT(setColorFromWheel())); // Hookup change events to update the paint config connect(ui->brushSize, SIGNAL(valueChanged(int)), this, SLOT(updateValue())); @@ -25,6 +42,16 @@ PaintSettingsWidget::PaintSettingsWidget(QWidget *parent) : connect(ui->blendMode, SIGNAL(currentIndexChanged(int)), this, SLOT(updateValue())); } +static int colorDoubleToInt(double value) +{ + return qBound((int)std::round(value*255.0), 0, 255); +} + +static double colorIntToDouble(int value) +{ + return qBound((double)value/255.0, 0.0, 1.0); +} + void PaintSettingsWidget::setValue(const GLView::PaintSettings& value) { supressUpdate_ = true; @@ -35,24 +62,18 @@ void PaintSettingsWidget::setValue(const GLView::PaintSettings& value) ui->colorG->setValue(value.brushColor.green()); ui->colorB->setValue(value.brushColor.blue()); ui->colorA->setValue(value.brushColor.alpha()); + setHexFromColor(); + setWheelFromColor(); ui->blendMode->setCurrentIndex((int)value.brushMode); - // Infer opacity from brushOpacity - int opacity = qBound(std::round( - (value.brushOpacity.red() + - value.brushOpacity.green() + - value.brushOpacity.blue() + - value.brushOpacity.alpha() ) / 4.0f * 255.0f), 0, 255); - - ui->opacity->setValue(opacity); - - // Infer the paintOpacityMode from the provided brushOpacity + // Infer the opacity from the provided brushOpacity if (value.brushOpacity.red() != 0 && value.brushOpacity.green() != 0 && value.brushOpacity.blue() != 0 && value.brushOpacity.alpha() == 0) { ui->opacityMode->setCurrentIndex(0); + ui->opacity->setValue(colorDoubleToInt(value.brushOpacity.red())); } else if (value.brushOpacity.red() == 0 && value.brushOpacity.green() == 0 && @@ -60,10 +81,12 @@ void PaintSettingsWidget::setValue(const GLView::PaintSettings& value) value.brushOpacity.alpha() != 0) { ui->opacityMode->setCurrentIndex(1); + ui->opacity->setValue(colorDoubleToInt(value.brushOpacity.alpha())); } else { ui->opacityMode->setCurrentIndex(2); + ui->opacity->setValue(colorDoubleToInt(value.brushOpacity.red())); } supressUpdate_ = false; @@ -100,9 +123,140 @@ void PaintSettingsWidget::updateValue() value_.brushMode = (GLView::PaintBlendMode)ui->blendMode->currentIndex(); + // Update some internal stuff too... + setPreviewFromValue(); + emit valueChanged(value_); } +void PaintSettingsWidget::setColorFromHex() +{ + QString hex = ui->colorHex->text(); + bool ok; + + supressHexUpdate_ = true; + + // Red + if (hex.length() >= 2) + { + int i = hex.midRef(0,2).toInt(&ok, 16); + if (ok) + ui->colorR->setValue(colorIntToDouble(i)); + } + else + { + ui->colorR->setValue(0.0); + ui->colorG->setValue(0.0); + ui->colorB->setValue(0.0); + ui->colorA->setValue(1.0); + } + + // Green + if (hex.length() >= 4) + { + int i = hex.midRef(2,2).toInt(&ok, 16); + if (ok) + ui->colorG->setValue(colorIntToDouble(i)); + } + else + { + ui->colorG->setValue(0.0); + ui->colorB->setValue(0.0); + ui->colorA->setValue(1.0); + } + + // Blue + if (hex.length() >= 6) + { + int i = hex.midRef(4,2).toInt(&ok, 16); + if (ok) + ui->colorB->setValue(colorIntToDouble(i)); + } + else + { + ui->colorB->setValue(0.0); + ui->colorA->setValue(1.0); + } + + // Alpha + if (hex.length() >= 8) + { + int i = hex.midRef(6,2).toInt(&ok, 16); + if (ok) + ui->colorA->setValue(colorIntToDouble(i)); + } + else + { + ui->colorA->setValue(1.0); + } + + supressHexUpdate_ = false; +} + +void PaintSettingsWidget::setHexFromColor() +{ + if (supressHexUpdate_) + return; + + int r = colorDoubleToInt(ui->colorR->value()); + int g = colorDoubleToInt(ui->colorG->value()); + int b = colorDoubleToInt(ui->colorB->value()); + int a = colorDoubleToInt(ui->colorA->value()); + + QString result = QString("%1").arg(r, 2, 16, QLatin1Char('0')).toUpper() + + QString("%1").arg(g, 2, 16, QLatin1Char('0')).toUpper() + + QString("%1").arg(b, 2, 16, QLatin1Char('0')).toUpper() + + QString("%1").arg(a, 2, 16, QLatin1Char('0')).toUpper(); + ui->colorHex->setText(result); +} + +void PaintSettingsWidget::setColorFromWheel() +{ + supressWheelUpdate_ = true; + + QColor color = ui->colorWheel->getColor(); + ui->colorR->setValue(color.redF()); + ui->colorG->setValue(color.greenF()); + ui->colorB->setValue(color.blueF()); + + supressWheelUpdate_ = false; +} + +void PaintSettingsWidget::setWheelFromColor() +{ + if (supressWheelUpdate_) + return; + + int r = colorDoubleToInt(ui->colorR->value()); + int g = colorDoubleToInt(ui->colorG->value()); + int b = colorDoubleToInt(ui->colorB->value()); + int a = colorDoubleToInt(ui->colorA->value()); + + ui->colorWheel->setColor(QColor::fromRgb(r,g,b,a)); +} + +void PaintSettingsWidget::setPreviewFromValue() +{ + QPalette pal = QPalette(); + pal.setColor(QPalette::Button, QColor::fromRgbF(value_.brushColor.red(), + value_.brushColor.green(), + value_.brushColor.blue(), + 1.0f)); + ui->colorPreview->setPalette(pal); + + QPalette palA = QPalette(); + palA.setColor(QPalette::Button, QColor::fromRgbF(value_.brushColor.red(), + value_.brushColor.green(), + value_.brushColor.blue(), + value_.brushColor.alpha())); + ui->colorPreviewAlpha->setPalette(palA); +} + +void PaintSettingsWidget::showColorPicker() +{ + +} + PaintSettingsWidget::~PaintSettingsWidget() { delete ui; diff --git a/src/ui/widgets/vertexpaintwidget.h b/src/ui/widgets/vertexpaintwidget.h index deb6250bf..1f380515e 100644 --- a/src/ui/widgets/vertexpaintwidget.h +++ b/src/ui/widgets/vertexpaintwidget.h @@ -18,17 +18,25 @@ class PaintSettingsWidget final : public QWidget public slots: void setValue( const GLView::PaintSettings& value ); + void showColorPicker(); signals: void valueChanged( const GLView::PaintSettings& value ); protected slots: + void setColorFromHex(); + void setColorFromWheel(); + void setPreviewFromValue(); + void setHexFromColor(); + void setWheelFromColor(); void updateValue(); private: Ui::PaintSettingsWidget *ui; GLView::PaintSettings value_; bool supressUpdate_; + bool supressHexUpdate_; + bool supressWheelUpdate_; }; #endif // VERTEXPAINTWIDGET_H diff --git a/src/ui/widgets/vertexpaintwidget.ui b/src/ui/widgets/vertexpaintwidget.ui index b9cae39cc..8a0f4bf42 100644 --- a/src/ui/widgets/vertexpaintwidget.ui +++ b/src/ui/widgets/vertexpaintwidget.ui @@ -7,7 +7,7 @@ 0 0 366 - 165 + 183 @@ -223,44 +223,90 @@ - + 1 0 - - - 0 - 0 - - true - QFrame::Box - - - QFrame::Plain + QFrame::StyledPanel 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + true + + + + + + true + + + + + + + + 0 + 0 + + + + true + + + + + + true + + + + - + Opacity - + @@ -301,14 +347,14 @@ - + Blend Mode - + @@ -339,8 +385,19 @@ + + + + + + ColorWheel + QWidget +
ui/widgets/colorwheel.h
+ 1 +
+
From 4903deaf2e545a5eefdd5c913428333695f2ef97 Mon Sep 17 00:00:00 2001 From: megapatato <60947952+megapatato@users.noreply.github.com> Date: Thu, 27 Jan 2022 17:02:53 -0500 Subject: [PATCH 08/10] Update glcontroller.cpp More accurate documentation of the tangents in the Hermite cubic interpolation. --- src/gl/glcontroller.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/gl/glcontroller.cpp b/src/gl/glcontroller.cpp index 3a7960700..c68baea9d 100644 --- a/src/gl/glcontroller.cpp +++ b/src/gl/glcontroller.cpp @@ -388,14 +388,19 @@ template bool interpolate( T & value, const QModelIndex & array, fl two points, at an arbitrary time "x", using an Hermite Cubic spline. * The "v1" keyframe is the one previous in time to "x", * The "v2" keyframe is the next one, - * The segment's backward derivative, "t1" is stored as part of "v1" - * The segment's forward derivative, "t2" is stored as part of "v2" + * The segment's backward derivative, "tangent 1" is stored as part of "v1" + * The segment's forward derivative, "tangent 2" is stored as part of "v2" N.B. v2's backward derivate does not "belong" to the segment with v1, but rather to the segment with v3. Otherwise said, the key at t=0.0 can have a forward derivative, but it will not be used; the final key's - backward derivative will also not be used. - - Setting the derivatives to zero results in linear interpolation. + backward derivative will also not be used. The segment between time 1 and 2 + uses time 1's backward derivative and 2's forward derivative, not the 1's + forward derivative and 2's backward derivative. + + The XYZ vectors used in the derivatives, if directed from V1 to V2, can appear + as linear interpolation, however if their magnitude is not correct, then + temporally there will be a speed up/down, even if the spacial trajectory is + a straight line. */ // Tangent 1 From d3d96136995b8926d7c5f6080e17343420b23743 Mon Sep 17 00:00:00 2001 From: DonkeyKong Date: Fri, 4 Feb 2022 02:34:32 -0500 Subject: [PATCH 09/10] Normalize whitespace on touched files Address minor PR comments --- src/data/niftypes.h | 218 +++++------ src/gl/bsshape.cpp | 194 +++++----- src/gl/glcontroller.cpp | 42 +-- src/gl/glmesh.cpp | 212 +++++------ src/gl/glnode.cpp | 40 +- src/gl/glscene.cpp | 6 +- src/gl/glscene.h | 22 +- src/glview.cpp | 538 +++++++++++++-------------- src/glview.h | 56 +-- src/nifskope.h | 10 +- src/nifskope_ui.cpp | 54 +-- src/ui/widgets/uvedit.cpp | 6 +- src/ui/widgets/vertexpaintwidget.cpp | 424 ++++++++++----------- src/ui/widgets/vertexpaintwidget.h | 33 +- src/xml/kfmxml.cpp | 16 +- src/xml/nifxml.cpp | 18 +- 16 files changed, 946 insertions(+), 943 deletions(-) diff --git a/src/data/niftypes.h b/src/data/niftypes.h index 9c099dcea..d5cf0588d 100644 --- a/src/data/niftypes.h +++ b/src/data/niftypes.h @@ -432,11 +432,11 @@ class Vector3 QString toHtml() const { return tr( "X %1 Y %2 Z %3\nlength %4" ).arg( - NumOrMinMax( xyz[0] ), - NumOrMinMax( xyz[1] ), - NumOrMinMax( xyz[2] ), - QString::number( length() ) - ); + NumOrMinMax( xyz[0] ), + NumOrMinMax( xyz[1] ), + NumOrMinMax( xyz[2] ), + QString::number( length() ) + ); } protected: @@ -687,12 +687,12 @@ class Vector4 QString toHtml() const { return tr( "X %1 Y %2 Z %3 W %4\nlength %5" ).arg( - NumOrMinMax( xyzw[0] ), - NumOrMinMax( xyzw[1] ), - NumOrMinMax( xyzw[2] ), - NumOrMinMax( xyzw[3] ), - QString::number( length() ) - ); + NumOrMinMax( xyzw[0] ), + NumOrMinMax( xyzw[1] ), + NumOrMinMax( xyzw[2] ), + NumOrMinMax( xyzw[3] ), + QString::number( length() ) + ); } protected: @@ -748,10 +748,10 @@ class Quat void normalize () { float mag = ( - (wxyz[0] * wxyz[0]) - + (wxyz[1] * wxyz[1]) - + (wxyz[2] * wxyz[2]) - + (wxyz[3] * wxyz[3]) + (wxyz[0] * wxyz[0]) + + (wxyz[1] * wxyz[1]) + + (wxyz[2] * wxyz[2]) + + (wxyz[3] * wxyz[3]) ); wxyz[0] /= mag; wxyz[1] /= mag; @@ -833,11 +833,11 @@ class Quat QString toHtml() const { return tr( "W %1\nX %2\nY %3\nZ %4" ).arg( - NumOrMinMax( wxyz[0] ), - NumOrMinMax( wxyz[1] ), - NumOrMinMax( wxyz[2] ), - NumOrMinMax( wxyz[3] ) - ); + NumOrMinMax( wxyz[0] ), + NumOrMinMax( wxyz[1] ), + NumOrMinMax( wxyz[2] ), + NumOrMinMax( wxyz[3] ) + ); } protected: @@ -874,8 +874,8 @@ class Matrix for ( int r = 0; r < 3; r++ ) { for ( int c = 0; c < 3; c++ ) { m3.m[r][c] = m[r][0] * m2.m[0][c] - + m[r][1] * m2.m[1][c] - + m[r][2] * m2.m[2][c]; + + m[r][1] * m2.m[1][c] + + m[r][2] * m2.m[2][c]; } } @@ -968,9 +968,9 @@ class Matrix4 for ( int r = 0; r < 4; r++ ) { for ( int c = 0; c < 4; c++ ) { m3.m[r][c] = m[r][0] * m2.m[0][c] - + m[r][1] * m2.m[1][c] - + m[r][2] * m2.m[2][c] - + m[r][3] * m2.m[3][c]; + + m[r][1] * m2.m[1][c] + + m[r][2] * m2.m[2][c] + + m[r][3] * m2.m[3][c]; } } @@ -1352,15 +1352,15 @@ class Color4 return c; } - Color4 operator*( const Color4& o ) const - { - Color4 c( *this ); - c.rgba[0] *= o.rgba[0]; - c.rgba[1] *= o.rgba[1]; - c.rgba[2] *= o.rgba[2]; - c.rgba[3] *= o.rgba[3]; - return c; - } + Color4 operator*( const Color4& o ) const + { + Color4 c( *this ); + c.rgba[0] *= o.rgba[0]; + c.rgba[1] *= o.rgba[1]; + c.rgba[2] *= o.rgba[2]; + c.rgba[3] *= o.rgba[3]; + return c; + } //! Add-equals operator Color4 & operator+=( const Color4 & o ) @@ -1462,7 +1462,7 @@ class Color4 friend class NifIStream; friend class NifOStream; - friend class ByteColor4; + friend class ByteColor4; friend QDataStream & operator>>( QDataStream & ds, Color4 & c ); }; @@ -1472,15 +1472,15 @@ class ByteColor4 : public Color4 public: //! Default constructor ByteColor4() { rgba[0] = rgba[1] = rgba[2] = rgba[3] = 1.0; } - static ByteColor4 fromColor4(const Color4& c) - { - ByteColor4 o; - o.rgba[0] = c.rgba[0]; - o.rgba[1] = c.rgba[1]; - o.rgba[2] = c.rgba[2]; - o.rgba[3] = c.rgba[3]; - return o; - } + static ByteColor4 fromColor4(const Color4& c) + { + ByteColor4 o; + o.rgba[0] = c.rgba[0]; + o.rgba[1] = c.rgba[1]; + o.rgba[2] = c.rgba[2]; + o.rgba[3] = c.rgba[3]; + return o; + } }; @@ -1956,71 +1956,71 @@ struct DataStreamMetadata }; typedef enum { - F_UNKNOWN = 0x00000000, - F_INT8_1 = 0x00010101, - F_INT8_2 = 0x00020102, - F_INT8_3 = 0x00030103, - F_INT8_4 = 0x00040104, - F_UINT8_1 = 0x00010105, - F_UINT8_2 = 0x00020106, - F_UINT8_3 = 0x00030107, - F_UINT8_4 = 0x00040108, - F_NORMINT8_1 = 0x00010109, - F_NORMINT8_2 = 0x0002010A, - F_NORMINT8_3 = 0x0003010B, - F_NORMINT8_4 = 0x0004010C, - F_NORMUINT8_1 = 0x0001010D, - F_NORMUINT8_2 = 0x0002010E, - F_NORMUINT8_3 = 0x0003010F, - F_NORMUINT8_4 = 0x00040110, - F_INT16_1 = 0x00010211, - F_INT16_2 = 0x00020212, - F_INT16_3 = 0x00030213, - F_INT16_4 = 0x00040214, - F_UINT16_1 = 0x00010215, - F_UINT16_2 = 0x00020216, - F_UINT16_3 = 0x00030217, - F_UINT16_4 = 0x00040218, - F_NORMINT16_1 = 0x00010219, - F_NORMINT16_2 = 0x0002021A, - F_NORMINT16_3 = 0x0003021B, - F_NORMINT16_4 = 0x0004021C, - F_NORMUINT16_1 = 0x0001021D, - F_NORMUINT16_2 = 0x0002021E, - F_NORMUINT16_3 = 0x0003021F, - F_NORMUINT16_4 = 0x00040220, - F_INT32_1 = 0x00010421, - F_INT32_2 = 0x00020422, - F_INT32_3 = 0x00030423, - F_INT32_4 = 0x00040424, - F_UINT32_1 = 0x00010425, - F_UINT32_2 = 0x00020426, - F_UINT32_3 = 0x00030427, - F_UINT32_4 = 0x00040428, - F_NORMINT32_1 = 0x00010429, - F_NORMINT32_2 = 0x0002042A, - F_NORMINT32_3 = 0x0003042B, - F_NORMINT32_4 = 0x0004042C, - F_NORMUINT32_1 = 0x0001042D, - F_NORMUINT32_2 = 0x0002042E, - F_NORMUINT32_3 = 0x0003042F, - F_NORMUINT32_4 = 0x00040430, - F_FLOAT16_1 = 0x00010231, - F_FLOAT16_2 = 0x00020232, - F_FLOAT16_3 = 0x00030233, - F_FLOAT16_4 = 0x00040234, - F_FLOAT32_1 = 0x00010435, - F_FLOAT32_2 = 0x00020436, - F_FLOAT32_3 = 0x00030437, - F_FLOAT32_4 = 0x00040438, - F_UINT_10_10_10_L1 = 0x00010439, - F_NORMINT_10_10_10_L1 = 0x0001043A, - F_NORMINT_11_11_10 = 0x0001043B, - F_NORMUINT8_4_BGRA = 0x0004013C, - F_NORMINT_10_10_10_2 = 0x0001043D, - F_UINT_10_10_10_2 = 0x0001043E, - F_TYPE_COUNT = 63, - F_MAX_COMP_SIZE = 16 + F_UNKNOWN = 0x00000000, + F_INT8_1 = 0x00010101, + F_INT8_2 = 0x00020102, + F_INT8_3 = 0x00030103, + F_INT8_4 = 0x00040104, + F_UINT8_1 = 0x00010105, + F_UINT8_2 = 0x00020106, + F_UINT8_3 = 0x00030107, + F_UINT8_4 = 0x00040108, + F_NORMINT8_1 = 0x00010109, + F_NORMINT8_2 = 0x0002010A, + F_NORMINT8_3 = 0x0003010B, + F_NORMINT8_4 = 0x0004010C, + F_NORMUINT8_1 = 0x0001010D, + F_NORMUINT8_2 = 0x0002010E, + F_NORMUINT8_3 = 0x0003010F, + F_NORMUINT8_4 = 0x00040110, + F_INT16_1 = 0x00010211, + F_INT16_2 = 0x00020212, + F_INT16_3 = 0x00030213, + F_INT16_4 = 0x00040214, + F_UINT16_1 = 0x00010215, + F_UINT16_2 = 0x00020216, + F_UINT16_3 = 0x00030217, + F_UINT16_4 = 0x00040218, + F_NORMINT16_1 = 0x00010219, + F_NORMINT16_2 = 0x0002021A, + F_NORMINT16_3 = 0x0003021B, + F_NORMINT16_4 = 0x0004021C, + F_NORMUINT16_1 = 0x0001021D, + F_NORMUINT16_2 = 0x0002021E, + F_NORMUINT16_3 = 0x0003021F, + F_NORMUINT16_4 = 0x00040220, + F_INT32_1 = 0x00010421, + F_INT32_2 = 0x00020422, + F_INT32_3 = 0x00030423, + F_INT32_4 = 0x00040424, + F_UINT32_1 = 0x00010425, + F_UINT32_2 = 0x00020426, + F_UINT32_3 = 0x00030427, + F_UINT32_4 = 0x00040428, + F_NORMINT32_1 = 0x00010429, + F_NORMINT32_2 = 0x0002042A, + F_NORMINT32_3 = 0x0003042B, + F_NORMINT32_4 = 0x0004042C, + F_NORMUINT32_1 = 0x0001042D, + F_NORMUINT32_2 = 0x0002042E, + F_NORMUINT32_3 = 0x0003042F, + F_NORMUINT32_4 = 0x00040430, + F_FLOAT16_1 = 0x00010231, + F_FLOAT16_2 = 0x00020232, + F_FLOAT16_3 = 0x00030233, + F_FLOAT16_4 = 0x00040234, + F_FLOAT32_1 = 0x00010435, + F_FLOAT32_2 = 0x00020436, + F_FLOAT32_3 = 0x00030437, + F_FLOAT32_4 = 0x00040438, + F_UINT_10_10_10_L1 = 0x00010439, + F_NORMINT_10_10_10_L1 = 0x0001043A, + F_NORMINT_11_11_10 = 0x0001043B, + F_NORMUINT8_4_BGRA = 0x0004013C, + F_NORMINT_10_10_10_2 = 0x0001043D, + F_UINT_10_10_10_2 = 0x0001043E, + F_TYPE_COUNT = 63, + F_MAX_COMP_SIZE = 16 } DataStreamFormat; typedef enum diff --git a/src/gl/bsshape.cpp b/src/gl/bsshape.cpp index 3350d91a6..c31bc806c 100644 --- a/src/gl/bsshape.cpp +++ b/src/gl/bsshape.cpp @@ -9,7 +9,7 @@ BSShape::BSShape( Scene * s, const QModelIndex & b ) : Shape( s, b ) { - + } void BSShape::clear() @@ -222,8 +222,8 @@ void BSShape::transform() if ( !nif || !iBlock.isValid() ) { clear(); return; - } - + } + if ( updateData ) { updateData = false; } @@ -357,13 +357,13 @@ void BSShape::drawShapes( NodeList * secondPass, bool presort ) return; auto nif = static_cast(iBlock.model()); - bool drawPolygons = true; + bool drawPolygons = true; if ( Node::SELECTING ) { - if ( scene->actionMode & Scene::Object ) { + if ( scene->actionMode & Scene::Object ) { int s_nodeId = ID2COLORKEY( nodeId ); glColor4ubv( (GLubyte *)&s_nodeId ); - } else { - drawPolygons = false; + } else { + drawPolygons = false; } } @@ -384,87 +384,87 @@ void BSShape::drawShapes( NodeList * secondPass, bool presort ) glMultMatrix( viewTrans() ); } - if (drawPolygons) { - - // Render polygon fill slightly behind alpha transparency and wireframe - if ( !drawSecond ) { - glEnable( GL_POLYGON_OFFSET_FILL ); - glPolygonOffset( 1.0f, 2.0f ); - } - - glEnableClientState( GL_VERTEX_ARRAY ); - glVertexPointer( 3, GL_FLOAT, 0, transVerts.constData() ); - - if ( !Node::SELECTING ) { - glEnableClientState( GL_NORMAL_ARRAY ); - glNormalPointer( GL_FLOAT, 0, transNorms.constData() ); - - bool doVCs = (bssp && (bssp->getFlags2() & ShaderFlags::SLSF2_Vertex_Colors)); - // Always do vertex colors for FO4 if colors present - if ( nifVersion == 130 && hasVertexColors && colors.count() ) - doVCs = true; - - if ( transColors.count() && (scene->options & Scene::DoVertexColors) && doVCs ) { - glEnableClientState( GL_COLOR_ARRAY ); - glColorPointer( 4, GL_FLOAT, 0, transColors.constData() ); - } else if ( !hasVertexColors && (bslsp && bslsp->hasVertexColors) ) { - // Correctly blacken the mesh if SLSF2_Vertex_Colors is still on - // yet "Has Vertex Colors" is not. - glColor( Color3( 0.0f, 0.0f, 0.0f ) ); - } else { - glColor( Color3( 1.0f, 1.0f, 1.0f ) ); - } - } - - - if ( !Node::SELECTING ) - shader = scene->renderer->setupProgram( this, shader ); - - if ( isDoubleSided ) { - glCullFace( GL_FRONT ); - glDrawElements( GL_TRIANGLES, triangles.count() * 3, GL_UNSIGNED_SHORT, triangles.constData() ); - glCullFace( GL_BACK ); - } - - if ( !isLOD ) { - glDrawElements( GL_TRIANGLES, triangles.count() * 3, GL_UNSIGNED_SHORT, triangles.constData() ); - } else if ( triangles.count() ) { - auto lod0 = nif->get( iBlock, "LOD0 Size" ); - auto lod1 = nif->get( iBlock, "LOD1 Size" ); - auto lod2 = nif->get( iBlock, "LOD2 Size" ); - - auto lod0tris = triangles.mid( 0, lod0 ); - auto lod1tris = triangles.mid( lod0, lod1 ); - auto lod2tris = triangles.mid( lod0 + lod1, lod2 ); - - // If Level2, render all - // If Level1, also render Level0 - switch ( scene->lodLevel ) { - case Scene::Level2: - if ( lod2tris.count() ) - glDrawElements( GL_TRIANGLES, lod2tris.count() * 3, GL_UNSIGNED_SHORT, lod2tris.constData() ); - case Scene::Level1: - if ( lod1tris.count() ) - glDrawElements( GL_TRIANGLES, lod1tris.count() * 3, GL_UNSIGNED_SHORT, lod1tris.constData() ); - case Scene::Level0: - default: - if ( lod0tris.count() ) - glDrawElements( GL_TRIANGLES, lod0tris.count() * 3, GL_UNSIGNED_SHORT, lod0tris.constData() ); - break; - } - } - - if ( !Node::SELECTING ) - scene->renderer->stopProgram(); - - glDisableClientState( GL_VERTEX_ARRAY ); - glDisableClientState( GL_NORMAL_ARRAY ); - glDisableClientState( GL_COLOR_ARRAY ); - - glDisable( GL_POLYGON_OFFSET_FILL ); - } - - if ( scene->actionMode & Scene::Vertex ) { + if (drawPolygons) { + + // Render polygon fill slightly behind alpha transparency and wireframe + if ( !drawSecond ) { + glEnable( GL_POLYGON_OFFSET_FILL ); + glPolygonOffset( 1.0f, 2.0f ); + } + + glEnableClientState( GL_VERTEX_ARRAY ); + glVertexPointer( 3, GL_FLOAT, 0, transVerts.constData() ); + + if ( !Node::SELECTING ) { + glEnableClientState( GL_NORMAL_ARRAY ); + glNormalPointer( GL_FLOAT, 0, transNorms.constData() ); + + bool doVCs = (bssp && (bssp->getFlags2() & ShaderFlags::SLSF2_Vertex_Colors)); + // Always do vertex colors for FO4 if colors present + if ( nifVersion == 130 && hasVertexColors && colors.count() ) + doVCs = true; + + if ( transColors.count() && (scene->options & Scene::DoVertexColors) && doVCs ) { + glEnableClientState( GL_COLOR_ARRAY ); + glColorPointer( 4, GL_FLOAT, 0, transColors.constData() ); + } else if ( !hasVertexColors && (bslsp && bslsp->hasVertexColors) ) { + // Correctly blacken the mesh if SLSF2_Vertex_Colors is still on + // yet "Has Vertex Colors" is not. + glColor( Color3( 0.0f, 0.0f, 0.0f ) ); + } else { + glColor( Color3( 1.0f, 1.0f, 1.0f ) ); + } + } + + + if ( !Node::SELECTING ) + shader = scene->renderer->setupProgram( this, shader ); + + if ( isDoubleSided ) { + glCullFace( GL_FRONT ); + glDrawElements( GL_TRIANGLES, triangles.count() * 3, GL_UNSIGNED_SHORT, triangles.constData() ); + glCullFace( GL_BACK ); + } + + if ( !isLOD ) { + glDrawElements( GL_TRIANGLES, triangles.count() * 3, GL_UNSIGNED_SHORT, triangles.constData() ); + } else if ( triangles.count() ) { + auto lod0 = nif->get( iBlock, "LOD0 Size" ); + auto lod1 = nif->get( iBlock, "LOD1 Size" ); + auto lod2 = nif->get( iBlock, "LOD2 Size" ); + + auto lod0tris = triangles.mid( 0, lod0 ); + auto lod1tris = triangles.mid( lod0, lod1 ); + auto lod2tris = triangles.mid( lod0 + lod1, lod2 ); + + // If Level2, render all + // If Level1, also render Level0 + switch ( scene->lodLevel ) { + case Scene::Level2: + if ( lod2tris.count() ) + glDrawElements( GL_TRIANGLES, lod2tris.count() * 3, GL_UNSIGNED_SHORT, lod2tris.constData() ); + case Scene::Level1: + if ( lod1tris.count() ) + glDrawElements( GL_TRIANGLES, lod1tris.count() * 3, GL_UNSIGNED_SHORT, lod1tris.constData() ); + case Scene::Level0: + default: + if ( lod0tris.count() ) + glDrawElements( GL_TRIANGLES, lod0tris.count() * 3, GL_UNSIGNED_SHORT, lod0tris.constData() ); + break; + } + } + + if ( !Node::SELECTING ) + scene->renderer->stopProgram(); + + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_NORMAL_ARRAY ); + glDisableClientState( GL_COLOR_ARRAY ); + + glDisable( GL_POLYGON_OFFSET_FILL ); + } + + if ( scene->actionMode & Scene::Vertex ) { drawVerts(); } @@ -517,7 +517,7 @@ void BSShape::drawSelection() const if ( scene->options & Scene::ShowNodes ) Node::drawSelection(); - if ( isHidden() || !(scene->actionMode & Scene::Object) ) + if ( isHidden() || !(scene->actionMode & Scene::Object) ) return; auto idx = scene->currentIndex; @@ -547,7 +547,7 @@ void BSShape::drawSelection() const // Parent index auto pBlock = nif->getBlock( nif->getParent( blk ) ); - auto push = [this] ( const Transform & t ) { + auto push = [this] ( const Transform & t ) { if ( transformRigid ) { glPushMatrix(); glMultMatrix( t ); @@ -593,7 +593,7 @@ void BSShape::drawSelection() const if ( normalScale < 0.1f ) normalScale = 0.1f; - + // Draw All Verts lambda auto allv = [this]( float size ) { @@ -613,7 +613,7 @@ void BSShape::drawSelection() const drawSphereSimple( sph.center, sph.radius, 72 ); } } - + if ( blockName.startsWith( "BSPackedCombined" ) && pBlock == iBlock ) { QVector idxs; if ( n == "Bounding Sphere" ) { @@ -701,8 +701,8 @@ void BSShape::drawSelection() const glVertex( transVerts.value( s ) ); glEnd(); } - } - + } + glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); // Draw Lines lambda @@ -734,7 +734,7 @@ void BSShape::drawSelection() const glLineWidth( lineWidth ); } }; - + // Draw Normals if ( n.contains( "Normal" ) ) { lines( transNorms ); @@ -768,7 +768,7 @@ void BSShape::drawSelection() const int s; QVector cols = { { 255, 0, 0, 128 }, { 0, 255, 0, 128 }, { 0, 0, 255, 128 }, { 255, 255, 0, 128 }, - { 0, 255, 255, 128 }, { 255, 0, 255, 128 }, { 255, 255, 255, 128 } + { 0, 255, 255, 128 }, { 255, 0, 255, 128 }, { 255, 255, 255, 128 } }; glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); @@ -840,7 +840,7 @@ void BSShape::drawSelection() const for ( int i = off; i < cnt + off; i++ ) { if ( i >= maxTris ) continue; - + Triangle tri = triangles[i]; glBegin( GL_TRIANGLES ); glVertex( transVerts.value( tri.v1() ) ); diff --git a/src/gl/glcontroller.cpp b/src/gl/glcontroller.cpp index c68baea9d..44dc07460 100644 --- a/src/gl/glcontroller.cpp +++ b/src/gl/glcontroller.cpp @@ -344,7 +344,7 @@ bool Controller::timeIndex( float time, const NifModel * nif, const QModelIndex // Previously, this branch was causing x to decrement from 1.0. // (This works fine for linear interpolation apparently) x = 1.0 - x; - + // Swap I and J // With x inverted, we must swap I and J or the animation will reverse. auto tmpI = i; @@ -379,34 +379,34 @@ template bool interpolate( T & value, const QModelIndex & array, fl T v2 = nif->get( frames.child( next, 0 ), "Value" ); switch ( nif->get( array, "Interpolation" ) ) { - + case 2: { // Quadratic /* - Interpolate X, Y, and Z values independently, along a segment between - two points, at an arbitrary time "x", using an Hermite Cubic spline. - * The "v1" keyframe is the one previous in time to "x", - * The "v2" keyframe is the next one, - * The segment's backward derivative, "tangent 1" is stored as part of "v1" - * The segment's forward derivative, "tangent 2" is stored as part of "v2" - N.B. v2's backward derivate does not "belong" to the segment with v1, - but rather to the segment with v3. Otherwise said, the key at t=0.0 - can have a forward derivative, but it will not be used; the final key's - backward derivative will also not be used. The segment between time 1 and 2 + Interpolate X, Y, and Z values independently, along a segment between + two points, at an arbitrary time "x", using an Hermite Cubic spline. + * The "v1" keyframe is the one previous in time to "x", + * The "v2" keyframe is the next one, + * The segment's backward derivative, "tangent 1" is stored as part of "v1" + * The segment's forward derivative, "tangent 2" is stored as part of "v2" + N.B. v2's backward derivate does not "belong" to the segment with v1, + but rather to the segment with v3. Otherwise said, the key at t=0.0 + can have a forward derivative, but it will not be used; the final key's + backward derivative will also not be used. The segment between time 1 and 2 uses time 1's backward derivative and 2's forward derivative, not the 1's forward derivative and 2's backward derivative. - The XYZ vectors used in the derivatives, if directed from V1 to V2, can appear + The XYZ vectors used in the derivatives, if directed from V1 to V2, can appear as linear interpolation, however if their magnitude is not correct, then temporally there will be a speed up/down, even if the spacial trajectory is a straight line. - */ + */ // Tangent 1 - T t1 = nif->get( frames.child( last, 0 ), "Backward" ); + T t1 = nif->get( frames.child( last, 0 ), "Backward" ); // Tangent 2 - T t2 = nif->get( frames.child( next, 0 ), "Forward" ); + T t2 = nif->get( frames.child( next, 0 ), "Forward" ); float x2 = x * x; float x3 = x2 * x; @@ -414,10 +414,10 @@ template bool interpolate( T & value, const QModelIndex & array, fl // Cubic Hermite spline // x(t) = (2t^3 - 3t^2 + 1)P1 + (-2t^3 + 3t^2)P2 + (t^3 - 2t^2 + t)T1 + (t^3 - t^2)T2 - value = v1 * (2.0f * x3 - 3.0f * x2 + 1.0f) + v2 * (-2.0f * x3 + 3.0f * x2) + t1 * (x3 - 2.0f * x2 + x) + t2 * (x3 - x2); + value = v1 * (2.0f * x3 - 3.0f * x2 + 1.0f) + v2 * (-2.0f * x3 + 3.0f * x2) + t1 * (x3 - 2.0f * x2 + x) + t2 * (x3 - x2); } return true; - + case 5: // Constant if ( x < 0.5 ) @@ -518,8 +518,8 @@ template <> bool Controller::interpolate( Matrix & value, const QModelIndex & ar float a = acos( Quat::dotproduct( v1, v2 ) ); if ( fabs( a ) >= 0.00005 ) { - float i = 1.0 / sin( a ); - v4 = v1 * sin( ( 1.0 - x ) * a ) * i + v2 * sin( x * a ) * i; + float i = 1.0 / sin( a ); + v4 = v1 * sin( ( 1.0 - x ) * a ) * i + v2 * sin( x * a ) * i; } */ value.fromQuat( v3 ); @@ -647,7 +647,7 @@ static float blend( int k, int t, int * u, float v ) value = (v - u[k]) / (u[k + t - 1] - u[k]) * blend( k, t - 1, u, v ); else value = ( v - u[k] ) / ( u[k + t - 1] - u[k] ) * blend( k, t - 1, u, v ) - + ( u[k + t] - v ) / ( u[k + t] - u[k + 1] ) * blend( k + 1, t - 1, u, v ); + + ( u[k + t] - v ) / ( u[k + t] - u[k + 1] ) * blend( k + 1, t - 1, u, v ); } return value; diff --git a/src/gl/glmesh.cpp b/src/gl/glmesh.cpp index b3170a623..a99573c6f 100644 --- a/src/gl/glmesh.cpp +++ b/src/gl/glmesh.cpp @@ -113,7 +113,7 @@ void Shape::update( const NifModel * nif, const QModelIndex & index ) Node::update( nif, index ); // If shaders are reenabled, reset - if ( !(scene->options & Scene::DisableShaders) && shader.isNull() + if ( !(scene->options & Scene::DisableShaders) && shader.isNull() && nif->checkVersion( 0x14020007, 0x14020007 ) ) { updateShaderProperties( nif ); @@ -289,10 +289,10 @@ QModelIndex Mesh::vertexAt( int idx ) const bool Mesh::isHidden() const { return ( Node::isHidden() - || ( !(scene->options & Scene::ShowHidden) /*&& Options::onlyTextured()*/ - && !properties.get() - && !properties.get() - ) + || ( !(scene->options & Scene::ShowHidden) /*&& Options::onlyTextured()*/ + && !properties.get() + && !properties.get() + ) ); } @@ -327,7 +327,7 @@ void Mesh::transform() weights.clear(); triangles.clear(); - + // All the semantics used by this mesh NiMesh::SemanticFlags semFlags = NiMesh::HAS_NONE; @@ -391,7 +391,7 @@ void Mesh::transform() // The Nth component after ignoring DataStreamUsage > 1 int compIdx = 0; for ( int i = 0; i < nif->rowCount( iData ); i++ ) { - // TODO: For now, submeshes are not actually used and the regions are + // TODO: For now, submeshes are not actually used and the regions are // filled in order for each data stream. // Submeshes may be required if total index values exceed USHRT_MAX QMap submeshMap; @@ -992,14 +992,14 @@ void Mesh::drawShapes( NodeList * secondPass, bool presort ) return; auto nif = static_cast(iBlock.model()); - bool drawPolygons = true; + bool drawPolygons = true; if ( Node::SELECTING ) { - if ( scene->actionMode & Scene::Object ) { + if ( scene->actionMode & Scene::Object ) { int s_nodeId = ID2COLORKEY( nodeId ); glColor4ubv( (GLubyte *)&s_nodeId ); } else { - drawPolygons = false; + drawPolygons = false; } } @@ -1036,100 +1036,100 @@ void Mesh::drawShapes( NodeList * secondPass, bool presort ) // setup array pointers - if (drawPolygons) { - - // Render polygon fill slightly behind alpha transparency and wireframe - glEnable( GL_POLYGON_OFFSET_FILL ); - glPolygonOffset( 1.0f, 2.0f ); - - glEnableClientState( GL_VERTEX_ARRAY ); - glVertexPointer( 3, GL_FLOAT, 0, transVerts.constData() ); - - if ( !Node::SELECTING ) { - if ( transNorms.count() ) { - glEnableClientState( GL_NORMAL_ARRAY ); - glNormalPointer( GL_FLOAT, 0, transNorms.constData() ); - } - - // Do VCs if legacy or if either bslsp or bsesp is set - bool doVCs = (!bssp) || (bssp && (bssp->getFlags2() & ShaderFlags::SLSF2_Vertex_Colors)); - - if ( transColors.count() - && ( scene->options & Scene::DoVertexColors ) - && doVCs ) - { - glEnableClientState( GL_COLOR_ARRAY ); - glColorPointer( 4, GL_FLOAT, 0, transColors.constData() ); - } else { - if ( !hasVertexColors && (bslsp && bslsp->hasVertexColors) ) { - // Correctly blacken the mesh if SLSF2_Vertex_Colors is still on - // yet "Has Vertex Colors" is not. - glColor( Color3( 0.0f, 0.0f, 0.0f ) ); - } else { - glColor( Color3( 1.0f, 1.0f, 1.0f ) ); - } - } - } - - // TODO: Hotspot. See about optimizing this. - if ( !Node::SELECTING ) - shader = scene->renderer->setupProgram( this, shader ); - - if ( isDoubleSided ) { - glDisable( GL_CULL_FACE ); - } - - if ( !isLOD ) { - // render the triangles - if ( sortedTriangles.count() ) - glDrawElements( GL_TRIANGLES, sortedTriangles.count() * 3, GL_UNSIGNED_SHORT, sortedTriangles.constData() ); - - } else if ( sortedTriangles.count() ) { - auto lod0 = nif->get( iBlock, "LOD0 Size" ); - auto lod1 = nif->get( iBlock, "LOD1 Size" ); - auto lod2 = nif->get( iBlock, "LOD2 Size" ); - - auto lod0tris = sortedTriangles.mid( 0, lod0 ); - auto lod1tris = sortedTriangles.mid( lod0, lod1 ); - auto lod2tris = sortedTriangles.mid( lod0 + lod1, lod2 ); - - // If Level2, render all - // If Level1, also render Level0 - switch ( scene->lodLevel ) { - case Scene::Level2: - if ( lod2tris.count() ) - glDrawElements( GL_TRIANGLES, lod2tris.count() * 3, GL_UNSIGNED_SHORT, lod2tris.constData() ); - case Scene::Level1: - if ( lod1tris.count() ) - glDrawElements( GL_TRIANGLES, lod1tris.count() * 3, GL_UNSIGNED_SHORT, lod1tris.constData() ); - case Scene::Level0: - default: - if ( lod0tris.count() ) - glDrawElements( GL_TRIANGLES, lod0tris.count() * 3, GL_UNSIGNED_SHORT, lod0tris.constData() ); - break; - } - } - - // render the tristrips - for ( auto & s : tristrips ) - glDrawElements( GL_TRIANGLE_STRIP, s.count(), GL_UNSIGNED_SHORT, s.constData() ); - - if ( isDoubleSided ) { - glEnable( GL_CULL_FACE ); - } - - if ( !Node::SELECTING ) - scene->renderer->stopProgram(); - - glDisableClientState( GL_VERTEX_ARRAY ); - glDisableClientState( GL_NORMAL_ARRAY ); - glDisableClientState( GL_COLOR_ARRAY ); - - glDisable( GL_POLYGON_OFFSET_FILL ); - } + if (drawPolygons) { + + // Render polygon fill slightly behind alpha transparency and wireframe + glEnable( GL_POLYGON_OFFSET_FILL ); + glPolygonOffset( 1.0f, 2.0f ); + + glEnableClientState( GL_VERTEX_ARRAY ); + glVertexPointer( 3, GL_FLOAT, 0, transVerts.constData() ); + + if ( !Node::SELECTING ) { + if ( transNorms.count() ) { + glEnableClientState( GL_NORMAL_ARRAY ); + glNormalPointer( GL_FLOAT, 0, transNorms.constData() ); + } + + // Do VCs if legacy or if either bslsp or bsesp is set + bool doVCs = (!bssp) || (bssp && (bssp->getFlags2() & ShaderFlags::SLSF2_Vertex_Colors)); + + if ( transColors.count() + && ( scene->options & Scene::DoVertexColors ) + && doVCs ) + { + glEnableClientState( GL_COLOR_ARRAY ); + glColorPointer( 4, GL_FLOAT, 0, transColors.constData() ); + } else { + if ( !hasVertexColors && (bslsp && bslsp->hasVertexColors) ) { + // Correctly blacken the mesh if SLSF2_Vertex_Colors is still on + // yet "Has Vertex Colors" is not. + glColor( Color3( 0.0f, 0.0f, 0.0f ) ); + } else { + glColor( Color3( 1.0f, 1.0f, 1.0f ) ); + } + } + } + + // TODO: Hotspot. See about optimizing this. + if ( !Node::SELECTING ) + shader = scene->renderer->setupProgram( this, shader ); + + if ( isDoubleSided ) { + glDisable( GL_CULL_FACE ); + } + + if ( !isLOD ) { + // render the triangles + if ( sortedTriangles.count() ) + glDrawElements( GL_TRIANGLES, sortedTriangles.count() * 3, GL_UNSIGNED_SHORT, sortedTriangles.constData() ); + + } else if ( sortedTriangles.count() ) { + auto lod0 = nif->get( iBlock, "LOD0 Size" ); + auto lod1 = nif->get( iBlock, "LOD1 Size" ); + auto lod2 = nif->get( iBlock, "LOD2 Size" ); + + auto lod0tris = sortedTriangles.mid( 0, lod0 ); + auto lod1tris = sortedTriangles.mid( lod0, lod1 ); + auto lod2tris = sortedTriangles.mid( lod0 + lod1, lod2 ); + + // If Level2, render all + // If Level1, also render Level0 + switch ( scene->lodLevel ) { + case Scene::Level2: + if ( lod2tris.count() ) + glDrawElements( GL_TRIANGLES, lod2tris.count() * 3, GL_UNSIGNED_SHORT, lod2tris.constData() ); + case Scene::Level1: + if ( lod1tris.count() ) + glDrawElements( GL_TRIANGLES, lod1tris.count() * 3, GL_UNSIGNED_SHORT, lod1tris.constData() ); + case Scene::Level0: + default: + if ( lod0tris.count() ) + glDrawElements( GL_TRIANGLES, lod0tris.count() * 3, GL_UNSIGNED_SHORT, lod0tris.constData() ); + break; + } + } + + // render the tristrips + for ( auto & s : tristrips ) + glDrawElements( GL_TRIANGLE_STRIP, s.count(), GL_UNSIGNED_SHORT, s.constData() ); + + if ( isDoubleSided ) { + glEnable( GL_CULL_FACE ); + } + + if ( !Node::SELECTING ) + scene->renderer->stopProgram(); + + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_NORMAL_ARRAY ); + glDisableClientState( GL_COLOR_ARRAY ); + + glDisable( GL_POLYGON_OFFSET_FILL ); + } glPointSize( 8.5 ); - if ( scene->actionMode & Scene::Vertex ) { + if ( scene->actionMode & Scene::Vertex ) { drawVerts(); } @@ -1142,7 +1142,7 @@ void Mesh::drawVerts() const glDisable( GL_LIGHTING ); glNormalColor(); - glBegin( GL_POINTS ); + glBegin( GL_POINTS ); for ( int i = 0; i < transVerts.count(); i++ ) { if ( Node::SELECTING ) { @@ -1169,7 +1169,7 @@ void Mesh::drawSelection() const if ( scene->options & Scene::ShowNodes ) Node::drawSelection(); - if ( isHidden() || !(scene->actionMode & Scene::Object) ) + if ( isHidden() || !(scene->actionMode & Scene::Object) ) return; auto idx = scene->currentIndex; @@ -1180,7 +1180,7 @@ void Mesh::drawSelection() const return; if ( blk != iBlock && blk != iData && blk != iSkinPart && blk != iSkinData - && ( !iTangentData.isValid() || blk != iTangentData ) ) + && ( !iTangentData.isValid() || blk != iTangentData ) ) { return; } @@ -1230,7 +1230,7 @@ void Mesh::drawSelection() const glPolygonMode( GL_FRONT_AND_BACK, GL_POINT ); if ( n == "Vertices" || n == "Normals" || n == "Vertex Colors" - || n == "UV Sets" || n == "Tangents" || n == "Bitangents" ) + || n == "UV Sets" || n == "Tangents" || n == "Bitangents" ) { glBegin( GL_POINTS ); diff --git a/src/gl/glnode.cpp b/src/gl/glnode.cpp index ca77fd671..5cb22ed03 100644 --- a/src/gl/glnode.cpp +++ b/src/gl/glnode.cpp @@ -530,7 +530,7 @@ void Node::draw() if ( isHidden() || iBlock == scene->currentBlock ) return; - if ( !(scene->actionMode & Scene::Object) ) + if ( !(scene->actionMode & Scene::Object) ) return; if ( Node::SELECTING ) { @@ -587,7 +587,7 @@ void Node::drawSelection() const if ( !nif ) return; - if ( !(scene->actionMode & Scene::Object) ) + if ( !(scene->actionMode & Scene::Object) ) return; bool extraData = false; @@ -776,7 +776,7 @@ void drawHvkShape( const NifModel * nif, const QModelIndex & iShape, QStackactionMode & Scene::Object) ) + if ( !(scene->actionMode & Scene::Object) ) return; stack.push( iShape ); @@ -1079,7 +1079,7 @@ void drawHvkConstraint( const NifModel * nif, const QModelIndex & iConstraint, c if ( !( nif && iConstraint.isValid() && scene && (scene->options & Scene::ShowConstraints) ) ) return; - if ( !(scene->actionMode & Scene::Object) ) + if ( !(scene->actionMode & Scene::Object) ) return; QList tBodies; @@ -1218,13 +1218,13 @@ void drawHvkConstraint( const NifModel * nif, const QModelIndex & iConstraint, c if ( nif->checkVersion( 0, 0x14000002 ) ) { - Vector3 axleB1temp( axleB[1], axleB[2], axleB[0] ); - Vector3 axleB2temp( Vector3::crossproduct( axleB, axleB1temp ) ); + Vector3 axleB1temp( axleB[1], axleB[2], axleB[0] ); + Vector3 axleB2temp( Vector3::crossproduct( axleB, axleB1temp ) ); } else if ( nif->checkVersion( 0x14020007, 0 ) ) { - Vector3 axleB1temp( nif->get( iHinge, "Perp2 Axle In B1" ) ); - Vector3 axleB2temp( nif->get( iHinge, "Perp2 Axle In B2" ) ); + Vector3 axleB1temp( nif->get( iHinge, "Perp2 Axle In B1" ) ); + Vector3 axleB2temp( nif->get( iHinge, "Perp2 Axle In B2" ) ); } const Vector3 axleB1( axleB1temp ); @@ -1396,7 +1396,7 @@ void drawHvkConstraint( const NifModel * nif, const QModelIndex & iConstraint, c void Node::drawHavok() { - if ( !(scene->actionMode & Scene::Object) ) + if ( !(scene->actionMode & Scene::Object) ) return; // TODO: Why are all these here - "drawNodes", "drawFurn", "drawHavok"? @@ -1482,7 +1482,7 @@ void Node::drawHavok() t.translation = center; glMultMatrix( t ); } - + if ( Node::SELECTING ) { int s_nodeId = ID2COLORKEY( nif->getBlockNumber( iBSMultiBoundData ) ); glColor4ubv( (GLubyte *)&s_nodeId ); @@ -1578,8 +1578,8 @@ void Node::drawHavok() if ( scene->currentBlock == nif->getBlock( nif->getLink( iBody, "Shape" ) ) ) { // fix: add selected visual to havok meshes glHighlightColor(); // TODO: idea: I do not recommend mimicking the Open GL API - // It confuses the one who reads the code. And the Open GL API is - // in constant development. + // It confuses the one who reads the code. And the Open GL API is + // in constant development. glLineWidth( 2.5 ); //glPointSize( 8.5 ); } @@ -1810,7 +1810,7 @@ void Node::drawFurn() if ( !( iBlock.isValid() && nif ) ) return; - if ( !(scene->actionMode & Scene::Object) ) + if ( !(scene->actionMode & Scene::Object) ) return; QModelIndex iExtraDataList = nif->getIndex( iBlock, "Extra Data List" ); @@ -1872,7 +1872,7 @@ void Node::drawShapes( NodeList * secondPass, bool presort ) return; const NifModel * nif = static_cast(iBlock.model()); - + // BSOrderedNode support // Only set if true (|=) so that it propagates to all children presort |= nif->getBlock( iBlock, "BSOrderedNode" ).isValid(); @@ -1893,11 +1893,11 @@ QString trans2string( Transform t ) float xr, yr, zr; t.rotation.toEuler( xr, yr, zr ); return QString( "translation X %1, Y %2, Z %3\n" ).Farg( t.translation[0] ).Farg( t.translation[1] ).Farg( t.translation[2] ) - + QString( "rotation Y %1, P %2, R %3 " ).Farg( xr * 180 / PI ).Farg( yr * 180 / PI ).Farg( zr * 180 / PI ) - + QString( "( (%1, %2, %3), " ).Farg( t.rotation( 0, 0 ) ).Farg( t.rotation( 0, 1 ) ).Farg( t.rotation( 0, 2 ) ) - + QString( "(%1, %2, %3), " ).Farg( t.rotation( 1, 0 ) ).Farg( t.rotation( 1, 1 ) ).Farg( t.rotation( 1, 2 ) ) - + QString( "(%1, %2, %3) )\n" ).Farg( t.rotation( 2, 0 ) ).Farg( t.rotation( 2, 1 ) ).Farg( t.rotation( 2, 2 ) ) - + QString( "scale %1\n" ).Farg( t.scale ); + + QString( "rotation Y %1, P %2, R %3 " ).Farg( xr * 180 / PI ).Farg( yr * 180 / PI ).Farg( zr * 180 / PI ) + + QString( "( (%1, %2, %3), " ).Farg( t.rotation( 0, 0 ) ).Farg( t.rotation( 0, 1 ) ).Farg( t.rotation( 0, 2 ) ) + + QString( "(%1, %2, %3), " ).Farg( t.rotation( 1, 0 ) ).Farg( t.rotation( 1, 1 ) ).Farg( t.rotation( 1, 2 ) ) + + QString( "(%1, %2, %3) )\n" ).Farg( t.rotation( 2, 0 ) ).Farg( t.rotation( 2, 1 ) ).Farg( t.rotation( 2, 2 ) ) + + QString( "scale %1\n" ).Farg( t.scale ); } QString Node::textStats() const @@ -1989,7 +1989,7 @@ void LODNode::update( const NifModel * nif, const QModelIndex & index ) if ( iLevels.isValid() ) { for ( int r = 0; r < nif->rowCount( iLevels ); r++ ) { ranges.append( { nif->get( iLevels.child( r, 0 ), "Near Extent" ), - nif->get( iLevels.child( r, 0 ), "Far Extent" ) } + nif->get( iLevels.child( r, 0 ), "Far Extent" ) } ); } } diff --git a/src/gl/glscene.cpp b/src/gl/glscene.cpp index 3fbcd0da8..82c8539b8 100644 --- a/src/gl/glscene.cpp +++ b/src/gl/glscene.cpp @@ -66,9 +66,9 @@ Scene::Scene( TexCache * texcache, QOpenGLContext * context, QOpenGLFunctions * lodLevel = Level2; - visMode = VisNone; + visMode = VisNone; - actionMode = ( Select | Object ); + actionMode = ( Select | Object ); // Startup Defaults @@ -197,7 +197,7 @@ void Scene::updateSelectMode( QAction * action ) if ( !action ) return; - actionMode = ActionMode( action->data().toInt() ); + actionMode = ActionMode( action->data().toInt() ); emit sceneUpdated(); } diff --git a/src/gl/glscene.h b/src/gl/glscene.h index ea1462c91..da27eb45b 100644 --- a/src/gl/glscene.h +++ b/src/gl/glscene.h @@ -129,18 +129,18 @@ class Scene final : public QObject VisMode visMode; - enum ActionModes - { - Deselect = 0x0, - Select = 0x1, - Paint = 0x2, - Object = 0x10000, - Vertex = 0x20000 - }; + enum ActionModes + { + Deselect = 0x0, + Select = 0x1, + Paint = 0x2, + Object = 0x10000, + Vertex = 0x20000 + }; - Q_DECLARE_FLAGS( ActionMode, ActionModes ); + Q_DECLARE_FLAGS( ActionMode, ActionModes ); - ActionMode actionMode; + ActionMode actionMode; enum LodLevel { @@ -151,7 +151,7 @@ class Scene final : public QObject LodLevel lodLevel; - + Renderer * renderer; NodeList nodes; diff --git a/src/glview.cpp b/src/glview.cpp index 6259c6caa..7c1576fbe 100644 --- a/src/glview.cpp +++ b/src/glview.cpp @@ -80,7 +80,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // NOTE: The FPS define is a frame limiter, // NOT the guaranteed FPS in the viewport. -// Also the QTimer is integer milliseconds +// Also the QTimer is integer milliseconds // so 60 will give you 1000/60 = 16, not 16.666 // therefore it's really 62.5FPS #define FPS 144 @@ -128,7 +128,7 @@ GLView * GLView::create( NifSkope * window ) } else { fmt.setSampleBuffers( aa > 0 ); } - + // OpenGL version fmt.setVersion( 2, 1 ); // Ignored if version < 3.2 @@ -156,7 +156,7 @@ GLView::GLView( const QGLFormat & format, QWidget * p, const QGLWidget * shareWi //setAttribute( Qt::WA_NoSystemBackground ); setAutoFillBackground( false ); setAcceptDrops( true ); - setMouseTracking( true ); + setMouseTracking( true ); setContextMenuPolicy( Qt::CustomContextMenu ); // Manually handle the buffer swap @@ -249,7 +249,7 @@ QColor GLView::clearColor() const } -/* +/* * Scene */ @@ -290,7 +290,7 @@ void GLView::updateAnimationState( bool checked ) void GLView::initializeGL() { GLenum err; - + if ( scene->options & Scene::DoMultisampling ) { if ( !glContext->hasExtension( "GL_EXT_framebuffer_multisample" ) ) { scene->options &= ~Scene::DoMultisampling; @@ -390,7 +390,7 @@ void GLView::paintEvent( QPaintEvent * event ) void GLView::paintGL() { #endif - + // Save GL state glPushAttrib( GL_ALL_ATTRIB_BITS ); @@ -405,8 +405,8 @@ void GLView::paintGL() } //glViewport( 0, 0, width(), height() ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); - - + + // Compile the model if ( doCompile ) { textures->setNifFolder( model->getFolder() ); @@ -549,10 +549,10 @@ void GLView::paintGL() { amb = 0.1f; } - + GLfloat mat_amb[] = { amb, amb, amb, 1.0f }; GLfloat mat_diff[] = { brightness, brightness, brightness, 1.0f }; - + glShadeModel( GL_SMOOTH ); //glEnable( GL_LIGHTING ); @@ -569,7 +569,7 @@ void GLView::paintGL() GLfloat mat_amb[] = { amb, amb, amb, 1.0f }; GLfloat mat_diff[] = { 1.0f, 1.0f, 1.0f, 1.0f }; - + glShadeModel( GL_SMOOTH ); //glEnable( GL_LIGHTING ); @@ -623,7 +623,7 @@ void GLView::paintGL() if ( scene->options & Scene::ShowAxes ) { // Resize viewport to small corner of screen int axesSize = std::min( width() / 10, 125 ); - glViewport( 0, 0, axesSize*devicePixelRatioF(), axesSize*devicePixelRatioF() ); + glViewport( 0, 0, axesSize * devicePixelRatioF(), axesSize * devicePixelRatioF() ); // Reset matrices glMatrixMode( GL_PROJECTION ); @@ -663,33 +663,32 @@ void GLView::paintGL() glPopMatrix(); // Restore viewport size - glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); + glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); // Restore matrices glProjection(); } - // Draw mouse tool - if (scene->actionMode & Scene::Paint) - { - // Reset matrices - glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); - glMatrixMode( GL_PROJECTION ); - glLoadIdentity(); - glOrtho(0, width(), height(), 0, -1, 1); - glMatrixMode( GL_MODELVIEW ); - glLoadIdentity(); - glDisable( GL_LIGHTING ); - glDepthFunc( GL_ALWAYS ); - glLineWidth( 2.0f ); - glColor3f( 1.0, 0.0, 0.0 ); - //drawCircle(Vector3(0, 0, 0), Vector3(0,0,1), 0.2); - drawCircle(Vector3(lastPos.x(), lastPos.y(), 0), Vector3(0,0,1), cfg.vertexPaintSettings.brushSize + 2.0f); - - // Restore viewport and projection - glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); - glProjection(); - } + // Draw mouse tool + if (scene->actionMode & Scene::Paint) + { + // Reset matrices + glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho(0, width(), height(), 0, -1, 1); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + glDisable( GL_LIGHTING ); + glDepthFunc( GL_ALWAYS ); + glLineWidth( 2.0f ); + glColor3f( 1.0, 0.0, 0.0 ); + drawCircle(Vector3(lastPos.x(), lastPos.y(), 0), Vector3(0,0,1), cfg.vertexPaintSettings.brushSize + 2.0f); + + // Restore viewport and projection + glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); + glProjection(); + } // Restore GL state glPopAttrib(); @@ -790,196 +789,196 @@ typedef void (Scene::* DrawFunc)( void ); QImage GLView::renderIndexImage() { - makeCurrent(); - - glPushAttrib( GL_ALL_ATTRIB_BITS ); - glMatrixMode( GL_PROJECTION ); - glPushMatrix(); - glMatrixMode( GL_MODELVIEW ); - glPushMatrix(); - - glViewport( 0, 0, width(), height() ); - glProjection(); - - QList drawFuncs; - if ( scene->options & Scene::ShowCollision ) - drawFuncs << &Scene::drawHavok; - - if ( scene->options & Scene::ShowNodes ) - drawFuncs << &Scene::drawNodes; - - if ( scene->options & Scene::ShowMarkers ) - drawFuncs << &Scene::drawFurn; - - drawFuncs << &Scene::drawShapes; - // Color Key O(1) selection - // Open GL 3.0 says glRenderMode is deprecated - // ATI OpenGL API implementation of GL_SELECT corrupts NifSkope memory - // - // Create FBO for sharp edges and no shading. - // Texturing, blending, dithering, lighting and smooth shading should be disabled. - // The FBO can be used for the drawing operations to keep the drawing operations invisible to the user. - - GLint viewport[4]; - glGetIntegerv( GL_VIEWPORT, viewport ); - - // Create new FBO with multisampling disabled - QOpenGLFramebufferObjectFormat fboFmt; - fboFmt.setTextureTarget( GL_TEXTURE_2D ); - fboFmt.setInternalTextureFormat( GL_RGB32F_ARB ); - fboFmt.setAttachment( QOpenGLFramebufferObject::Attachment::CombinedDepthStencil ); - - QOpenGLFramebufferObject fbo( viewport[2], viewport[3], fboFmt ); - fbo.bind(); - - glEnable( GL_LIGHTING ); - glDisable( GL_MULTISAMPLE ); - glDisable( GL_MULTISAMPLE_ARB ); - glDisable( GL_LINE_SMOOTH ); - glDisable( GL_POINT_SMOOTH ); - glDisable( GL_POLYGON_SMOOTH ); - glDisable( GL_TEXTURE_1D ); - glDisable( GL_TEXTURE_2D ); - glDisable( GL_TEXTURE_3D ); - glDisable( GL_BLEND ); - glDisable( GL_DITHER ); - glDisable( GL_FOG ); - glDisable( GL_LIGHTING ); - glShadeModel( GL_FLAT ); - glEnable( GL_DEPTH_TEST ); - glDepthFunc( GL_LEQUAL ); - glClearColor( 0, 0, 0, 1 ); - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - - // Rasterize the scene - Node::SELECTING = 1; - for ( DrawFunc df : drawFuncs ) { - (scene->*df)(); - } - Node::SELECTING = 0; - - fbo.release(); - - glPopAttrib(); - glMatrixMode( GL_MODELVIEW ); - glPopMatrix(); - glMatrixMode( GL_PROJECTION ); - glPopMatrix(); - - return fbo.toImage(); + makeCurrent(); + + glPushAttrib( GL_ALL_ATTRIB_BITS ); + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + + glViewport( 0, 0, width(), height() ); + glProjection(); + + QList drawFuncs; + if ( scene->options & Scene::ShowCollision ) + drawFuncs << &Scene::drawHavok; + + if ( scene->options & Scene::ShowNodes ) + drawFuncs << &Scene::drawNodes; + + if ( scene->options & Scene::ShowMarkers ) + drawFuncs << &Scene::drawFurn; + + drawFuncs << &Scene::drawShapes; + // Color Key O(1) selection + // Open GL 3.0 says glRenderMode is deprecated + // ATI OpenGL API implementation of GL_SELECT corrupts NifSkope memory + // + // Create FBO for sharp edges and no shading. + // Texturing, blending, dithering, lighting and smooth shading should be disabled. + // The FBO can be used for the drawing operations to keep the drawing operations invisible to the user. + + GLint viewport[4]; + glGetIntegerv( GL_VIEWPORT, viewport ); + + // Create new FBO with multisampling disabled + QOpenGLFramebufferObjectFormat fboFmt; + fboFmt.setTextureTarget( GL_TEXTURE_2D ); + fboFmt.setInternalTextureFormat( GL_RGB32F_ARB ); + fboFmt.setAttachment( QOpenGLFramebufferObject::Attachment::CombinedDepthStencil ); + + QOpenGLFramebufferObject fbo( viewport[2], viewport[3], fboFmt ); + fbo.bind(); + + glEnable( GL_LIGHTING ); + glDisable( GL_MULTISAMPLE ); + glDisable( GL_MULTISAMPLE_ARB ); + glDisable( GL_LINE_SMOOTH ); + glDisable( GL_POINT_SMOOTH ); + glDisable( GL_POLYGON_SMOOTH ); + glDisable( GL_TEXTURE_1D ); + glDisable( GL_TEXTURE_2D ); + glDisable( GL_TEXTURE_3D ); + glDisable( GL_BLEND ); + glDisable( GL_DITHER ); + glDisable( GL_FOG ); + glDisable( GL_LIGHTING ); + glShadeModel( GL_FLAT ); + glEnable( GL_DEPTH_TEST ); + glDepthFunc( GL_LEQUAL ); + glClearColor( 0, 0, 0, 1 ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // Rasterize the scene + Node::SELECTING = 1; + for ( DrawFunc df : drawFuncs ) { + (scene->*df)(); + } + Node::SELECTING = 0; + + fbo.release(); + + glPopAttrib(); + glMatrixMode( GL_MODELVIEW ); + glPopMatrix(); + glMatrixMode( GL_PROJECTION ); + glPopMatrix(); + + return fbo.toImage(); } QModelIndex GLView::sampleIndexImagePoint(const QImage& img, const QPoint& pos, NifModel* model) { - if (img.rect().contains(pos)) - { - QColor pixel = img.pixel( pos ); - return colorIndexToModelIndex(pixel, model); - } - return QModelIndex(); + if (img.rect().contains(pos)) + { + QColor pixel = img.pixel( pos ); + return colorIndexToModelIndex(pixel, model); + } + return QModelIndex(); } QVector GLView::sampleIndexImageCircle(const QImage& img, const QPoint& pos, float radius, NifModel* model) { - QVector indices; - if (img.rect().contains(pos)) - { - QPointF startf = (QPointF)pos - QPointF(radius, radius); - QPointF endf = (QPointF)pos + QPointF(radius, radius); - - QPoint start(std::round(startf.x()), std::round(startf.y())); - QPoint end(std::round(endf.x()), std::round(endf.y())); - - QVector2D origin(pos); - QVector colors; - for (int y=start.y(); y <= end.y(); y++) - { - for (int x=start.x(); x <= end.x(); x++) - { - if (img.rect().contains(x,y)) - { - float dist = (QVector2D(x,y) - origin).length(); - QColor pixel = img.pixel(x,y); - if (dist <= radius && !colors.contains(pixel)) - { - colors.push_back(pixel); - } - } - } - } - - for (const QColor& indColor : colors) - { - QModelIndex modelInd = colorIndexToModelIndex(indColor, model); - if (modelInd.isValid()) - indices.push_back(modelInd); - } - } - return indices; + QVector indices; + if (img.rect().contains(pos)) + { + QPointF startf = (QPointF)pos - QPointF(radius, radius); + QPointF endf = (QPointF)pos + QPointF(radius, radius); + + QPoint start(std::round(startf.x()), std::round(startf.y())); + QPoint end(std::round(endf.x()), std::round(endf.y())); + + QVector2D origin(pos); + QVector colors; + for (int y=start.y(); y <= end.y(); y++) + { + for (int x=start.x(); x <= end.x(); x++) + { + if (img.rect().contains(x,y)) + { + float dist = (QVector2D(x,y) - origin).length(); + QColor pixel = img.pixel(x,y); + if (dist <= radius && !colors.contains(pixel)) + { + colors.push_back(pixel); + } + } + } + } + + for (const QColor& indColor : colors) + { + QModelIndex modelInd = colorIndexToModelIndex(indColor, model); + if (modelInd.isValid()) + indices.push_back(modelInd); + } + } + return indices; } QModelIndex GLView::colorIndexToModelIndex(const QColor& color, NifModel* model) { - // Encode RGB to Int - int a = 0; - a |= color.red() << 0; - a |= color.green() << 8; - a |= color.blue() << 16; + // Encode RGB to Int + int a = 0; + a |= color.red() << 0; + a |= color.green() << 8; + a |= color.blue() << 16; - // Decode: - // R = (id & 0x000000FF) >> 0 - // G = (id & 0x0000FF00) >> 8 - // B = (id & 0x00FF0000) >> 16 + // Decode: + // R = (id & 0x000000FF) >> 0 + // G = (id & 0x0000FF00) >> 8 + // B = (id & 0x00FF0000) >> 16 - int choose = COLORKEY2ID( a ); - int furn = -1; + int choose = COLORKEY2ID( a ); + int furn = -1; - // Pick BSFurnitureMarker - if ( choose > 0 ) { - auto furnBlock = model->getBlock( model->index( 3, 0, model->getBlock( choose & 0x0ffff ) ), "BSFurnitureMarker" ); + // Pick BSFurnitureMarker + if ( choose > 0 ) { + auto furnBlock = model->getBlock( model->index( 3, 0, model->getBlock( choose & 0x0ffff ) ), "BSFurnitureMarker" ); - if ( furnBlock.isValid() ) { - furn = choose >> 16; - choose &= 0x0ffff; - } - } + if ( furnBlock.isValid() ) { + furn = choose >> 16; + choose &= 0x0ffff; + } + } - QModelIndex chooseIndex; + QModelIndex chooseIndex; - if (scene->actionMode & Scene::Vertex ) { - // Vertex - int block = choose >> 16; - int vert = choose - (block << 16); + if (scene->actionMode & Scene::Vertex ) { + // Vertex + int block = choose >> 16; + int vert = choose - (block << 16); - auto shape = scene->shapes.value( block ); - if ( shape ) - chooseIndex = shape->vertexAt( vert ); - } else if ( choose != -1 ) { - // Block Index - chooseIndex = model->getBlock( choose ); + auto shape = scene->shapes.value( block ); + if ( shape ) + chooseIndex = shape->vertexAt( vert ); + } else if ( choose != -1 ) { + // Block Index + chooseIndex = model->getBlock( choose ); - if ( furn != -1 ) { - // Furniture Row @ Block Index - chooseIndex = model->index( furn, 0, model->index( 3, 0, chooseIndex ) ); - } - } + if ( furn != -1 ) { + // Furniture Row @ Block Index + chooseIndex = model->index( furn, 0, model->index( 3, 0, chooseIndex ) ); + } + } - return chooseIndex; + return chooseIndex; } QModelIndex GLView::indexAt(const QPoint & pos, int cycle) { - Q_UNUSED(cycle); + Q_UNUSED(cycle); if ( !(model && isVisible() && height()) ) return QModelIndex(); - QImage img = renderIndexImage(); + QImage img = renderIndexImage(); #ifndef QT_NO_DEBUG - img.save( "fbo.png" ); + img.save( "fbo.png" ); #endif - return sampleIndexImagePoint(img, pos, model); + return sampleIndexImagePoint(img, pos, model); } void GLView::center() @@ -1017,67 +1016,68 @@ void GLView::zoom( float z ) void GLView::setVertexPaintSettings(GLView::PaintSettings settings) { - cfg.vertexPaintSettings = settings; - update(); + cfg.vertexPaintSettings = settings; + update(); } void GLView::startVertexPaint(const QPoint& point) { - mousePaint = true; - mousePaintVerts.clear(); - mousePaintHitDetectImg = renderIndexImage(); - vertexPaint(point); - QCursor cursor(Qt::BlankCursor); - QApplication::setOverrideCursor(cursor); - QApplication::changeOverrideCursor(cursor); + mousePaint = true; + mousePaintVerts.clear(); + mousePaintHitDetectImg = renderIndexImage(); + vertexPaint(point); + QCursor cursor(Qt::BlankCursor); + QApplication::setOverrideCursor(cursor); + QApplication::changeOverrideCursor(cursor); } void GLView::vertexPaint(const QPoint& point) { - bool updated = false; - if (mousePaint) - { - // Let's do an absolutely shit job and act like it's decent - auto inds = sampleIndexImageCircle(mousePaintHitDetectImg, point, cfg.vertexPaintSettings.brushSize, model); - - for (const auto& ind : inds) - { - if ( ind.isValid() && !mousePaintVerts.contains(ind)) - { - mousePaintVerts.push_back(ind); - Color4 base = model->get(ind.parent(), "Vertex Colors"); - Color4 blend; - - if (cfg.vertexPaintSettings.brushMode == PaintBlendMode::BlendNormal) - { - blend = (cfg.vertexPaintSettings.brushColor * cfg.vertexPaintSettings.brushOpacity) + (base * (Color4() - cfg.vertexPaintSettings.brushOpacity)); - } - else if (cfg.vertexPaintSettings.brushMode == PaintBlendMode::BlendMultiply) - { - blend = ((cfg.vertexPaintSettings.brushColor * cfg.vertexPaintSettings.brushOpacity) + (Color4() - cfg.vertexPaintSettings.brushOpacity)) * base; - } - else if (cfg.vertexPaintSettings.brushMode == PaintBlendMode::BlendAdd) - { - blend = (cfg.vertexPaintSettings.brushColor * cfg.vertexPaintSettings.brushOpacity) + base; - } - - model->set(ind.parent(), "Vertex Colors", ByteColor4::fromColor4(blend)); - updated = true; - } - } - } - - if (!updated) - { - update(); - } + bool updated = false; + if (mousePaint) + { + // Let's do an absolutely shit job and act like it's decent + auto inds = sampleIndexImageCircle(mousePaintHitDetectImg, point, cfg.vertexPaintSettings.brushSize, model); + Color4 brushColorAndOpacity = cfg.vertexPaintSettings.brushColor * cfg.vertexPaintSettings.brushOpacity; + + for (const auto& ind : inds) + { + if ( ind.isValid() && !mousePaintVerts.contains(ind)) + { + mousePaintVerts.push_back(ind); + Color4 base = model->get(ind.parent(), "Vertex Colors"); + Color4 blend; + + if (cfg.vertexPaintSettings.brushMode == PaintBlendMode::BlendNormal) + { + blend = brushColorAndOpacity + (base * (Color4() - cfg.vertexPaintSettings.brushOpacity)); + } + else if (cfg.vertexPaintSettings.brushMode == PaintBlendMode::BlendMultiply) + { + blend = (brushColorAndOpacity + (Color4() - cfg.vertexPaintSettings.brushOpacity)) * base; + } + else if (cfg.vertexPaintSettings.brushMode == PaintBlendMode::BlendAdd) + { + blend = brushColorAndOpacity + base; + } + + model->set(ind.parent(), "Vertex Colors", ByteColor4::fromColor4(blend)); + updated = true; + } + } + } + + if (!updated) + { + update(); + } } void GLView::endVertexPaint() { - mousePaint = false; - mousePaintVerts.clear(); - QApplication::restoreOverrideCursor(); + mousePaint = false; + mousePaintVerts.clear(); + QApplication::restoreOverrideCursor(); } void GLView::setCenter() @@ -1362,7 +1362,7 @@ void GLView::setSceneSequence( const QString & seqname ) // Called from self and not UI emit sequenceChanged( seqname ); } - + scene->setSequence( seqname ); time = scene->timeMin(); emit sceneTimeChanged( time, scene->timeMin(), scene->timeMax() ); @@ -1417,10 +1417,10 @@ void GLView::advanceGears() if ( time > scene->timeMax() ) { if ( ( animState & AnimSwitch ) && !scene->animGroups.isEmpty() ) { int ix = scene->animGroups.indexOf( scene->animGroup ); - + if ( ++ix >= scene->animGroups.count() ) ix -= scene->animGroups.count(); - + setSceneSequence( scene->animGroups.value( ix ) ); } else if ( animState & AnimLoop ) { time = scene->timeMin(); @@ -1507,7 +1507,7 @@ void GLView::saveImage() lay->addWidget( file, 0, 0, 1, -1 ); auto grpDir = new QButtonGroup( dlg ); - + QRadioButton * nifskopeDir = new QRadioButton( tr( "NifSkope Directory" ), dlg ); nifskopeDir->setChecked( true ); nifskopeDir->setToolTip( tr( "Save to NifSkope screenshots directory" ) ); @@ -1546,7 +1546,7 @@ void GLView::saveImage() auto btnSize = [dlg]( const QString & name ) { auto btn = new QRadioButton( name, dlg ); btn->setCheckable( true ); - + return btn; }; @@ -1584,7 +1584,7 @@ void GLView::saveImage() grpSize->addButton( btnEightX, 8 ); grpSize->setExclusive( true ); - + lay->addWidget( grpBox, 2, 0, 1, -1 ); @@ -1611,7 +1611,7 @@ void GLView::saveImage() ); // Validate on OK - connect( btnOk, &QPushButton::clicked, [&]() + connect( btnOk, &QPushButton::clicked, [&]() { // Save JPEG Quality QSettings settings; @@ -1638,7 +1638,7 @@ void GLView::saveImage() resizeGL( w, h ); } - + QOpenGLFramebufferObjectFormat fboFmt; fboFmt.setTextureTarget( GL_TEXTURE_2D ); fboFmt.setInternalTextureFormat( GL_RGB ); @@ -1694,8 +1694,8 @@ void GLView::saveImage() } -/* - * QWidget Event Handlers +/* + * QWidget Event Handlers */ void GLView::dragEnterEvent( QDragEnterEvent * e ) @@ -1847,12 +1847,12 @@ void GLView::mouseDoubleClickEvent( QMouseEvent * ) void GLView::mouseMoveEvent( QMouseEvent * event ) { - int dx = event->x() - lastPos.x(); - int dy = event->y() - lastPos.y(); + int dx = event->x() - lastPos.x(); + int dy = event->y() - lastPos.y(); - if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { - vertexPaint(event->pos()); - } else if ( event->buttons() & Qt::LeftButton && !kbd[Qt::Key_Space] ) { + if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { + vertexPaint(event->pos()); + } else if ( event->buttons() & Qt::LeftButton && !kbd[Qt::Key_Space] ) { mouseRot += Vector3( dy * .5, 0, dx * .5 ); } else if ( (event->buttons() & Qt::MidButton) || (event->buttons() & Qt::LeftButton && kbd[Qt::Key_Space]) ) { float d = axis / (qMax( width(), height() ) + 1); @@ -1871,25 +1871,25 @@ void GLView::mousePressEvent( QMouseEvent * event ) return; } - if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { - startVertexPaint(event->pos()); - } else { - if ( (pressPos - event->pos()).manhattanLength() <= 3 ) - cycleSelect++; - else - cycleSelect = 0; - } + if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { + startVertexPaint(event->pos()); + } else { + if ( (pressPos - event->pos()).manhattanLength() <= 3 ) + cycleSelect++; + else + cycleSelect = 0; + } - lastPos = event->pos(); + lastPos = event->pos(); pressPos = event->pos(); } void GLView::mouseReleaseEvent( QMouseEvent * event ) { - if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { - endVertexPaint(); - return; - } + if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { + endVertexPaint(); + return; + } if ( !(model && (pressPos - event->pos()).manhattanLength() <= 3) ) return; diff --git a/src/glview.h b/src/glview.h index e834fc6db..c4eb5da5f 100644 --- a/src/glview.h +++ b/src/glview.h @@ -120,20 +120,20 @@ class GLView final : public QGLWidget ZAxis = 2 }; - enum PaintBlendMode - { - BlendNormal = 0, - BlendAdd = 1, - BlendMultiply = 2 - }; - - struct PaintSettings - { - float brushSize = 24.0f; - Color4 brushColor = {1.0f, 0.0f, 1.0f, 1.0f}; - Color4 brushOpacity = {1.0f, 1.0f, 1.0f, 0.0f}; - PaintBlendMode brushMode = PaintBlendMode::BlendNormal; - }; + enum PaintBlendMode + { + BlendNormal = 0, + BlendAdd = 1, + BlendMultiply = 2 + }; + + struct PaintSettings + { + float brushSize = 24.0f; + Color4 brushColor = {1.0f, 0.0f, 1.0f, 1.0f}; + Color4 brushOpacity = {1.0f, 1.0f, 1.0f, 0.0f}; + PaintBlendMode brushMode = PaintBlendMode::BlendNormal; + }; void setNif( NifModel * ); @@ -148,9 +148,9 @@ class GLView final : public QGLWidget void rotate( float, float, float ); void zoom( float ); - void startVertexPaint(const QPoint&); - void vertexPaint(const QPoint&); - void endVertexPaint(); + void startVertexPaint(const QPoint&); + void vertexPaint(const QPoint&); + void endVertexPaint(); void setCenter(); void setDistance( float ); @@ -167,13 +167,13 @@ class GLView final : public QGLWidget QColor clearColor() const; - QImage renderIndexImage(); - QModelIndex sampleIndexImagePoint(const QImage& img, const QPoint & p, NifModel* model); - QVector sampleIndexImageCircle(const QImage& img, const QPoint & p, float radius, NifModel* model); + QImage renderIndexImage(); + QModelIndex sampleIndexImagePoint(const QImage& img, const QPoint & p, NifModel* model); + QVector sampleIndexImageCircle(const QImage& img, const QPoint & p, float radius, NifModel* model); - QModelIndex colorIndexToModelIndex(const QColor& color, NifModel* model); + QModelIndex colorIndexToModelIndex(const QColor& color, NifModel* model); - QModelIndex indexAt( const QPoint & p, int cycle = 0 ); + QModelIndex indexAt( const QPoint & p, int cycle = 0 ); // UI @@ -195,7 +195,7 @@ public slots: void updateAnimationState( bool checked ); void setVisMode( Scene::VisMode, bool checked = true ); void updateSettings(); - void setVertexPaintSettings(GLView::PaintSettings settings); + void setVertexPaintSettings(GLView::PaintSettings settings); signals: void clicked( const QModelIndex & ); @@ -263,15 +263,15 @@ protected slots: Transform viewTrans; GLdouble aspect; - + QHash kbd; QPoint lastPos; QPoint pressPos; Vector3 mouseMov; Vector3 mouseRot; - bool mousePaint; - QVector mousePaintVerts; - QImage mousePaintHitDetectImg; + bool mousePaint; + QVector mousePaintVerts; + QImage mousePaintHitDetectImg; int cycleSelect; @@ -293,7 +293,7 @@ protected slots: UpAxis upAxis = ZAxis; - PaintSettings vertexPaintSettings; + PaintSettings vertexPaintSettings; } cfg; private slots: diff --git a/src/nifskope.h b/src/nifskope.h index d2b94c607..f6b1f13a9 100644 --- a/src/nifskope.h +++ b/src/nifskope.h @@ -173,7 +173,7 @@ public slots: void enableUi(); - void updateSettings(); + void updateSettings(); //! Select a NIF index void select( const QModelIndex & ); @@ -203,12 +203,12 @@ public slots: void on_aViewTop_triggered( bool ); void on_aViewFront_triggered( bool ); void on_aViewLeft_triggered( bool ); - + void on_aViewCenter_triggered(); void on_aViewFlip_triggered( bool ); void on_aViewPerspective_toggled( bool ); void on_aViewWalk_triggered( bool ); - + void on_aViewUser_toggled( bool ); void on_aViewUserSave_triggered( bool ); @@ -370,7 +370,7 @@ protected slots: bool selecting = false; bool initialShowEvent = true; - + QProgressBar * progress = nullptr; QDockWidget * dList; @@ -379,7 +379,7 @@ protected slots: QDockWidget * dKfm; QDockWidget * dRefr; QDockWidget * dInsp; - QDockWidget * dPaint; + QDockWidget * dPaint; QDockWidget * dBrowser; QToolBar * tool; diff --git a/src/nifskope_ui.cpp b/src/nifskope_ui.cpp index b6eb7d5bc..c4e785add 100644 --- a/src/nifskope_ui.cpp +++ b/src/nifskope_ui.cpp @@ -129,7 +129,7 @@ NifSkope * NifSkope::createWindow( const QString & fname ) if ( !fname.isEmpty() ) { skope->loadFile( fname ); - } + } return skope; } @@ -144,8 +144,8 @@ void NifSkope::initActions() aSelectFont = ui->aSelectFont; // Build all actions list - allActions = QSet::fromList( - ui->tFile->actions() + allActions = QSet::fromList( + ui->tFile->actions() << ui->mRender->actions() << ui->tRender->actions() << ui->tAnim->actions() @@ -184,12 +184,12 @@ void NifSkope::initActions() connect( ui->aBrowseArchive, &QAction::triggered, this, &NifSkope::archiveDlg ); connect( ui->aOpen, &QAction::triggered, this, &NifSkope::openDlg ); - connect( ui->aSave, &QAction::triggered, this, &NifSkope::save ); + connect( ui->aSave, &QAction::triggered, this, &NifSkope::save ); connect( ui->aSaveAs, &QAction::triggered, this, &NifSkope::saveAsDlg ); // TODO: Assure Actions and Scene state are synced // Set Data for Actions to pass onto Scene when clicking - /* + /* ShowAxes = 0x1, ShowGrid = 0x2, ShowNodes = 0x4, @@ -226,9 +226,9 @@ void NifSkope::initActions() ui->aLighting->setData( Scene::DoLighting ); ui->aDisableShading->setData( Scene::DisableShaders ); - ui->aSelectObject->setData( (int)( Scene::Select | Scene::Object ) ); - ui->aSelectVertex->setData( (int)( Scene::Select | Scene::Vertex ) ); - ui->aPaintVertex->setData( (int)( Scene::Paint | Scene::Vertex ) ); + ui->aSelectObject->setData( (int)( Scene::Select | Scene::Object ) ); + ui->aSelectVertex->setData( (int)( Scene::Select | Scene::Vertex ) ); + ui->aPaintVertex->setData( (int)( Scene::Paint | Scene::Vertex ) ); auto agroup = [this]( QVector actions, bool exclusive ) { QActionGroup * ag = new QActionGroup( this ); @@ -241,8 +241,8 @@ void NifSkope::initActions() return ag; }; - selectActions = agroup( { ui->aSelectObject, ui->aSelectVertex, ui->aPaintVertex }, true ); - connect( selectActions, &QActionGroup::triggered, ogl->getScene(), &Scene::updateSelectMode ); + selectActions = agroup( { ui->aSelectObject, ui->aSelectVertex, ui->aPaintVertex }, true ); + connect( selectActions, &QActionGroup::triggered, ogl->getScene(), &Scene::updateSelectMode ); showActions = agroup( { ui->aShowAxes, ui->aShowGrid, ui->aShowNodes, ui->aShowCollision, ui->aShowConstraints, ui->aShowMarkers, ui->aShowHidden, ui->aDoSkinning @@ -317,7 +317,7 @@ void NifSkope::initActions() ogl->setDebugMode( GLView::DbgColorPicker ); else ogl->setDebugMode( GLView::DbgNone ); - + ogl->update(); } ); @@ -374,7 +374,7 @@ void NifSkope::initDockWidgets() dTree = ui->TreeDock; dHeader = ui->HeaderDock; dInsp = ui->InspectDock; - dPaint = ui->PaintDock; + dPaint = ui->PaintDock; dKfm = ui->KfmDock; dBrowser = ui->BrowserDock; @@ -388,22 +388,22 @@ void NifSkope::initDockWidgets() // Hide certain docks by default dRefr->toggleViewAction()->setChecked( false ); dInsp->toggleViewAction()->setChecked( false ); - dPaint->toggleViewAction()->setChecked( false ); + dPaint->toggleViewAction()->setChecked( false ); dKfm->toggleViewAction()->setChecked( false ); dRefr->setVisible( false ); dInsp->setVisible( false ); - dPaint->setVisible( false ); + dPaint->setVisible( false ); dKfm->setVisible( false ); // Set Inspect widget dInsp->setWidget( inspect ); - // Push the initial brush settings to the vertex paint widget - ui->vertexPaintSettings->setValue(ogl->cfg.vertexPaintSettings); + // Push the initial brush settings to the vertex paint widget + ui->vertexPaintSettings->setValue(ogl->cfg.vertexPaintSettings); - // Connect vertex paint widget to glview - connect(ui->vertexPaintSettings, &PaintSettingsWidget::valueChanged, ogl, &GLView::setVertexPaintSettings); + // Connect vertex paint widget to glview + connect(ui->vertexPaintSettings, &PaintSettingsWidget::valueChanged, ogl, &GLView::setVertexPaintSettings); connect( dList->toggleViewAction(), &QAction::triggered, tree, &NifTreeView::clearRootIndex ); } @@ -511,7 +511,7 @@ void NifSkope::initMenu() QActionGroup * grpTheme = new QActionGroup( this ); - // Fill the action data with the integer correlating to + // Fill the action data with the integer correlating to // their position in WindowTheme and add to the action group. int i = 0; auto themes = ui->mTheme->actions(); @@ -530,7 +530,7 @@ void NifSkope::initToolBars() // Status Bar ui->statusbar->setContentsMargins( 0, 0, 0, 0 ); ui->statusbar->addPermanentWidget( progress ); - + // TODO: Split off into own widget ui->statusbar->addPermanentWidget( filePathWidget( this ) ); @@ -584,7 +584,7 @@ void NifSkope::initToolBars() connect( animSlider, &FloatSlider::valueChanged, animSliderEdit, &FloatEdit::setValue ); connect( animSliderEdit, static_cast(&FloatEdit::sigEdited), ogl, &GLView::setSceneTime ); connect( animSliderEdit, static_cast(&FloatEdit::sigEdited), animSlider, &FloatSlider::setValue ); - + // Animations animGroups = new QComboBox( ui->tAnim ); animGroups->setMinimumWidth( 60 ); @@ -658,7 +658,7 @@ QMenu * NifSkope::lightingWidget() { QMenu * mLight = new QMenu( this ); mLight->setObjectName( "mLight" ); - + auto lightingWidget = new LightingWidget( ogl, mLight ); lightingWidget->setActions( {ui->aLighting, ui->aTextures, ui->aVertexColors, @@ -814,7 +814,7 @@ void NifSkope::onLoadComplete( bool success, QString & fname ) } else { // File failed to load - Message::append( this, NifModel::tr( readFail ), + Message::append( this, NifModel::tr( readFail ), NifModel::tr( readFailFinal ).arg( fname ), QMessageBox::Critical ); nif->clear(); @@ -835,8 +835,8 @@ void NifSkope::onLoadComplete( bool success, QString & fname ) nif->undoStack->clear(); indexStack->clear(); - // Update widget sizes to fixup ogl view - resizeDone(); + // Update widget sizes to fixup ogl view + resizeDone(); // Center the model on load ogl->center(); @@ -1103,7 +1103,7 @@ void NifSkope::loadTheme() pal.setColor( QPalette::ColorGroup::Disabled, QPalette::HighlightedText, baseCTxtHighlightDark ); // Set Palette and Stylesheet - + QDir qssDir( QApplication::applicationDirPath() ); QStringList qssList( QStringList() << "style.qss" @@ -1456,7 +1456,7 @@ void NifSkope::on_aViewWalk_triggered( bool checked ) void NifSkope::on_aViewUserSave_triggered( bool checked ) -{ +{ Q_UNUSED( checked ); ogl->saveUserView(); ui->aViewUser->setChecked( true ); diff --git a/src/ui/widgets/uvedit.cpp b/src/ui/widgets/uvedit.cpp index bb077ed7c..f7f8331a1 100644 --- a/src/ui/widgets/uvedit.cpp +++ b/src/ui/widgets/uvedit.cpp @@ -478,7 +478,7 @@ void UVWidget::setupViewport( int width, int height ) glMatrixMode( GL_PROJECTION ); glLoadIdentity(); - glViewport( 0, 0, width*devicePixelRatioF(), height*devicePixelRatioF() ); + glViewport( 0, 0, width * devicePixelRatioF(), height * devicePixelRatioF() ); glOrtho( glViewRect[0], glViewRect[1], glViewRect[2], glViewRect[3], -10.0, +10.0 ); } @@ -979,7 +979,7 @@ bool UVWidget::setTexCoords() tris << nif->getArray( nif->index( i, 0, partIdx ), "Triangles" ); } } - + } if ( tris.isEmpty() ) @@ -1022,7 +1022,7 @@ void UVWidget::updateNif() nif->dataChanged( iShape, iShape ); } - + nif->restoreState(); connect( nif, &NifModel::dataChanged, this, &UVWidget::nifDataChanged ); } diff --git a/src/ui/widgets/vertexpaintwidget.cpp b/src/ui/widgets/vertexpaintwidget.cpp index 3d14ef195..cdc653a99 100644 --- a/src/ui/widgets/vertexpaintwidget.cpp +++ b/src/ui/widgets/vertexpaintwidget.cpp @@ -2,44 +2,51 @@ #include "ui_vertexpaintwidget.h" #include +enum OpacityMode : int +{ + Color = 0, + Alpha = 1, + ColorAndAlpha = 2 +}; + PaintSettingsWidget::PaintSettingsWidget(QWidget *parent) : - QWidget(parent), - ui(new Ui::PaintSettingsWidget) + QWidget(parent), + ui(new Ui::PaintSettingsWidget) { - ui->setupUi(this); - supressUpdate_ = false; - supressHexUpdate_ = false; - supressWheelUpdate_ = false; - - ui->opacityMode->addItems({"Color", "Alpha", "Color + Alpha"}); - ui->blendMode->setCurrentIndex(0); - ui->blendMode->addItems({"Normal","Add","Multiply"}); - ui->blendMode->setCurrentIndex(0); - setHexFromColor(); - setPreviewFromValue(); - setWheelFromColor(); - - // Hookup change events to handle sync of the hex and double color reps - connect(ui->colorR, SIGNAL(valueChanged(double)), this, SLOT(setHexFromColor())); - connect(ui->colorG, SIGNAL(valueChanged(double)), this, SLOT(setHexFromColor())); - connect(ui->colorB, SIGNAL(valueChanged(double)), this, SLOT(setHexFromColor())); - connect(ui->colorA, SIGNAL(valueChanged(double)), this, SLOT(setHexFromColor())); - connect(ui->colorR, SIGNAL(valueChanged(double)), this, SLOT(setWheelFromColor())); - connect(ui->colorG, SIGNAL(valueChanged(double)), this, SLOT(setWheelFromColor())); - connect(ui->colorB, SIGNAL(valueChanged(double)), this, SLOT(setWheelFromColor())); - connect(ui->colorA, SIGNAL(valueChanged(double)), this, SLOT(setWheelFromColor())); - connect(ui->colorHex, SIGNAL(textChanged(QString)), this, SLOT(setColorFromHex())); - connect(ui->colorWheel, SIGNAL(sigColor(QColor)), this, SLOT(setColorFromWheel())); - - // Hookup change events to update the paint config - connect(ui->brushSize, SIGNAL(valueChanged(int)), this, SLOT(updateValue())); - connect(ui->colorR, SIGNAL(valueChanged(double)), this, SLOT(updateValue())); - connect(ui->colorG, SIGNAL(valueChanged(double)), this, SLOT(updateValue())); - connect(ui->colorB, SIGNAL(valueChanged(double)), this, SLOT(updateValue())); - connect(ui->colorA, SIGNAL(valueChanged(double)), this, SLOT(updateValue())); - connect(ui->opacity, SIGNAL(valueChanged(int)), this, SLOT(updateValue())); - connect(ui->opacityMode, SIGNAL(currentIndexChanged(int)), this, SLOT(updateValue())); - connect(ui->blendMode, SIGNAL(currentIndexChanged(int)), this, SLOT(updateValue())); + ui->setupUi(this); + supressUpdate_ = false; + supressHexUpdate_ = false; + supressWheelUpdate_ = false; + + ui->opacityMode->addItems({"Color", "Alpha", "Color + Alpha"}); + ui->blendMode->setCurrentIndex(0); + ui->blendMode->addItems({"Normal","Add","Multiply"}); + ui->blendMode->setCurrentIndex(OpacityMode::Color); + setHexFromColor(); + setPreviewFromValue(); + setWheelFromColor(); + + // Hookup change events to handle sync of the hex and double color reps + connect(ui->colorR, SIGNAL(valueChanged(double)), this, SLOT(setHexFromColor())); + connect(ui->colorG, SIGNAL(valueChanged(double)), this, SLOT(setHexFromColor())); + connect(ui->colorB, SIGNAL(valueChanged(double)), this, SLOT(setHexFromColor())); + connect(ui->colorA, SIGNAL(valueChanged(double)), this, SLOT(setHexFromColor())); + connect(ui->colorR, SIGNAL(valueChanged(double)), this, SLOT(setWheelFromColor())); + connect(ui->colorG, SIGNAL(valueChanged(double)), this, SLOT(setWheelFromColor())); + connect(ui->colorB, SIGNAL(valueChanged(double)), this, SLOT(setWheelFromColor())); + connect(ui->colorA, SIGNAL(valueChanged(double)), this, SLOT(setWheelFromColor())); + connect(ui->colorHex, SIGNAL(textChanged(QString)), this, SLOT(setColorFromHex())); + connect(ui->colorWheel, SIGNAL(sigColor(QColor)), this, SLOT(setColorFromWheel())); + + // Hookup change events to update the paint config + connect(ui->brushSize, SIGNAL(valueChanged(int)), this, SLOT(updateValue())); + connect(ui->colorR, SIGNAL(valueChanged(double)), this, SLOT(updateValue())); + connect(ui->colorG, SIGNAL(valueChanged(double)), this, SLOT(updateValue())); + connect(ui->colorB, SIGNAL(valueChanged(double)), this, SLOT(updateValue())); + connect(ui->colorA, SIGNAL(valueChanged(double)), this, SLOT(updateValue())); + connect(ui->opacity, SIGNAL(valueChanged(int)), this, SLOT(updateValue())); + connect(ui->opacityMode, SIGNAL(currentIndexChanged(int)), this, SLOT(updateValue())); + connect(ui->blendMode, SIGNAL(currentIndexChanged(int)), this, SLOT(updateValue())); } static int colorDoubleToInt(double value) @@ -54,210 +61,207 @@ static double colorIntToDouble(int value) void PaintSettingsWidget::setValue(const GLView::PaintSettings& value) { - supressUpdate_ = true; - - // Read all the settings that we can directly into controls - ui->brushSize->setValue(value.brushSize); - ui->colorR->setValue(value.brushColor.red()); - ui->colorG->setValue(value.brushColor.green()); - ui->colorB->setValue(value.brushColor.blue()); - ui->colorA->setValue(value.brushColor.alpha()); - setHexFromColor(); - setWheelFromColor(); - ui->blendMode->setCurrentIndex((int)value.brushMode); - - // Infer the opacity from the provided brushOpacity - if (value.brushOpacity.red() != 0 && - value.brushOpacity.green() != 0 && - value.brushOpacity.blue() != 0 && - value.brushOpacity.alpha() == 0) - { - ui->opacityMode->setCurrentIndex(0); - ui->opacity->setValue(colorDoubleToInt(value.brushOpacity.red())); - } - else if (value.brushOpacity.red() == 0 && - value.brushOpacity.green() == 0 && - value.brushOpacity.blue() == 0 && - value.brushOpacity.alpha() != 0) - { - ui->opacityMode->setCurrentIndex(1); - ui->opacity->setValue(colorDoubleToInt(value.brushOpacity.alpha())); - } - else - { - ui->opacityMode->setCurrentIndex(2); - ui->opacity->setValue(colorDoubleToInt(value.brushOpacity.red())); - } - - supressUpdate_ = false; - - // With all controls populated, issue a manual updateValue - updateValue(); + supressUpdate_ = true; + + // Read all the settings that we can directly into controls + ui->brushSize->setValue(value.brushSize); + ui->colorR->setValue(value.brushColor.red()); + ui->colorG->setValue(value.brushColor.green()); + ui->colorB->setValue(value.brushColor.blue()); + ui->colorA->setValue(value.brushColor.alpha()); + setHexFromColor(); + setWheelFromColor(); + ui->blendMode->setCurrentIndex((int)value.brushMode); + + // Infer the opacity from the provided brushOpacity + if (value.brushOpacity.red() != 0 && + value.brushOpacity.green() != 0 && + value.brushOpacity.blue() != 0 && + value.brushOpacity.alpha() == 0) + { + ui->opacityMode->setCurrentIndex(OpacityMode::Color); + ui->opacity->setValue(colorDoubleToInt(value.brushOpacity.red())); + } + else if (value.brushOpacity.red() == 0 && + value.brushOpacity.green() == 0 && + value.brushOpacity.blue() == 0 && + value.brushOpacity.alpha() != 0) + { + ui->opacityMode->setCurrentIndex(OpacityMode::Alpha); + ui->opacity->setValue(colorDoubleToInt(value.brushOpacity.alpha())); + } + else + { + ui->opacityMode->setCurrentIndex(OpacityMode::ColorAndAlpha); + ui->opacity->setValue(colorDoubleToInt(value.brushOpacity.red())); + } + + supressUpdate_ = false; + + // With all controls populated, issue a manual updateValue + updateValue(); } void PaintSettingsWidget::updateValue() { - if (supressUpdate_) - return; - - value_.brushColor = Color4(ui->colorR->value(), - ui->colorG->value(), - ui->colorB->value(), - ui->colorA->value()); - - value_.brushSize = (float)ui->brushSize->value(); - - float opacity = (float)ui->opacity->value() / 255.0f; - if (ui->opacityMode->currentIndex() == 0) // Color only - { - value_.brushOpacity = Color4(opacity,opacity,opacity,0.0f); - } - else if (ui->opacityMode->currentIndex() == 1) // Alpha only - { - value_.brushOpacity = Color4(0,0,0,opacity); - } - else if (ui->opacityMode->currentIndex() == 2) // Color+Alpha - { - value_.brushOpacity = Color4(opacity,opacity,opacity,opacity); - } - - value_.brushMode = (GLView::PaintBlendMode)ui->blendMode->currentIndex(); - - // Update some internal stuff too... - setPreviewFromValue(); - - emit valueChanged(value_); + if (supressUpdate_) + return; + + value_.brushColor = Color4(ui->colorR->value(), + ui->colorG->value(), + ui->colorB->value(), + ui->colorA->value()); + + value_.brushSize = (float)ui->brushSize->value(); + + float opacity = (float)ui->opacity->value() / 255.0f; + if (ui->opacityMode->currentIndex() == OpacityMode::Color) // Color only + { + value_.brushOpacity = Color4(opacity,opacity,opacity,0.0f); + } + else if (ui->opacityMode->currentIndex() == OpacityMode::Alpha) // Alpha only + { + value_.brushOpacity = Color4(0,0,0,opacity); + } + else if (ui->opacityMode->currentIndex() == OpacityMode::ColorAndAlpha) // Color+Alpha + { + value_.brushOpacity = Color4(opacity,opacity,opacity,opacity); + } + + value_.brushMode = (GLView::PaintBlendMode)ui->blendMode->currentIndex(); + + // Update some internal stuff too... + setPreviewFromValue(); + + emit valueChanged(value_); } void PaintSettingsWidget::setColorFromHex() { - QString hex = ui->colorHex->text(); - bool ok; - - supressHexUpdate_ = true; - - // Red - if (hex.length() >= 2) - { - int i = hex.midRef(0,2).toInt(&ok, 16); - if (ok) - ui->colorR->setValue(colorIntToDouble(i)); - } - else - { - ui->colorR->setValue(0.0); - ui->colorG->setValue(0.0); - ui->colorB->setValue(0.0); - ui->colorA->setValue(1.0); - } - - // Green - if (hex.length() >= 4) - { - int i = hex.midRef(2,2).toInt(&ok, 16); - if (ok) - ui->colorG->setValue(colorIntToDouble(i)); - } - else - { - ui->colorG->setValue(0.0); - ui->colorB->setValue(0.0); - ui->colorA->setValue(1.0); - } - - // Blue - if (hex.length() >= 6) - { - int i = hex.midRef(4,2).toInt(&ok, 16); - if (ok) - ui->colorB->setValue(colorIntToDouble(i)); - } - else - { - ui->colorB->setValue(0.0); - ui->colorA->setValue(1.0); - } - - // Alpha - if (hex.length() >= 8) - { - int i = hex.midRef(6,2).toInt(&ok, 16); - if (ok) - ui->colorA->setValue(colorIntToDouble(i)); - } - else - { - ui->colorA->setValue(1.0); - } - - supressHexUpdate_ = false; + QString hex = ui->colorHex->text(); + bool ok; + + supressHexUpdate_ = true; + + // Red + if (hex.length() >= 2) + { + int i = hex.midRef(0,2).toInt(&ok, 16); + if (ok) + ui->colorR->setValue(colorIntToDouble(i)); + } + else + { + ui->colorR->setValue(0.0); + ui->colorG->setValue(0.0); + ui->colorB->setValue(0.0); + ui->colorA->setValue(1.0); + } + + // Green + if (hex.length() >= 4) + { + int i = hex.midRef(2,2).toInt(&ok, 16); + if (ok) + ui->colorG->setValue(colorIntToDouble(i)); + } + else + { + ui->colorG->setValue(0.0); + ui->colorB->setValue(0.0); + ui->colorA->setValue(1.0); + } + + // Blue + if (hex.length() >= 6) + { + int i = hex.midRef(4,2).toInt(&ok, 16); + if (ok) + ui->colorB->setValue(colorIntToDouble(i)); + } + else + { + ui->colorB->setValue(0.0); + ui->colorA->setValue(1.0); + } + + // Alpha + if (hex.length() >= 8) + { + int i = hex.midRef(6,2).toInt(&ok, 16); + if (ok) + ui->colorA->setValue(colorIntToDouble(i)); + } + else + { + ui->colorA->setValue(1.0); + } + + supressHexUpdate_ = false; } void PaintSettingsWidget::setHexFromColor() { - if (supressHexUpdate_) - return; - - int r = colorDoubleToInt(ui->colorR->value()); - int g = colorDoubleToInt(ui->colorG->value()); - int b = colorDoubleToInt(ui->colorB->value()); - int a = colorDoubleToInt(ui->colorA->value()); - - QString result = QString("%1").arg(r, 2, 16, QLatin1Char('0')).toUpper() + - QString("%1").arg(g, 2, 16, QLatin1Char('0')).toUpper() + - QString("%1").arg(b, 2, 16, QLatin1Char('0')).toUpper() + - QString("%1").arg(a, 2, 16, QLatin1Char('0')).toUpper(); - ui->colorHex->setText(result); + if (supressHexUpdate_) + return; + + // Convert the color components from doubles [0.0 , 1.0] to ints [0 , 255] + int r = colorDoubleToInt(ui->colorR->value()); + int g = colorDoubleToInt(ui->colorG->value()); + int b = colorDoubleToInt(ui->colorB->value()); + int a = colorDoubleToInt(ui->colorA->value()); + + // Format the rgba int components into an 8 character hex string like FFAA00FF + QString result = QString("%1").arg(r, 2, 16, QLatin1Char('0')).toUpper() + + QString("%1").arg(g, 2, 16, QLatin1Char('0')).toUpper() + + QString("%1").arg(b, 2, 16, QLatin1Char('0')).toUpper() + + QString("%1").arg(a, 2, 16, QLatin1Char('0')).toUpper(); + ui->colorHex->setText(result); } void PaintSettingsWidget::setColorFromWheel() { - supressWheelUpdate_ = true; + supressWheelUpdate_ = true; - QColor color = ui->colorWheel->getColor(); - ui->colorR->setValue(color.redF()); - ui->colorG->setValue(color.greenF()); - ui->colorB->setValue(color.blueF()); + QColor color = ui->colorWheel->getColor(); + ui->colorR->setValue(color.redF()); + ui->colorG->setValue(color.greenF()); + ui->colorB->setValue(color.blueF()); - supressWheelUpdate_ = false; + supressWheelUpdate_ = false; } void PaintSettingsWidget::setWheelFromColor() { - if (supressWheelUpdate_) - return; + if (supressWheelUpdate_) + return; - int r = colorDoubleToInt(ui->colorR->value()); - int g = colorDoubleToInt(ui->colorG->value()); - int b = colorDoubleToInt(ui->colorB->value()); - int a = colorDoubleToInt(ui->colorA->value()); + int r = colorDoubleToInt(ui->colorR->value()); + int g = colorDoubleToInt(ui->colorG->value()); + int b = colorDoubleToInt(ui->colorB->value()); + int a = colorDoubleToInt(ui->colorA->value()); - ui->colorWheel->setColor(QColor::fromRgb(r,g,b,a)); + ui->colorWheel->setColor(QColor::fromRgb(r,g,b,a)); } void PaintSettingsWidget::setPreviewFromValue() { - QPalette pal = QPalette(); - pal.setColor(QPalette::Button, QColor::fromRgbF(value_.brushColor.red(), - value_.brushColor.green(), - value_.brushColor.blue(), - 1.0f)); - ui->colorPreview->setPalette(pal); - - QPalette palA = QPalette(); - palA.setColor(QPalette::Button, QColor::fromRgbF(value_.brushColor.red(), - value_.brushColor.green(), - value_.brushColor.blue(), - value_.brushColor.alpha())); - ui->colorPreviewAlpha->setPalette(palA); -} - -void PaintSettingsWidget::showColorPicker() -{ - + QPalette pal = QPalette(); + pal.setColor(QPalette::Button, QColor::fromRgbF(value_.brushColor.red(), + value_.brushColor.green(), + value_.brushColor.blue(), + 1.0f)); + ui->colorPreview->setPalette(pal); + + QPalette palA = QPalette(); + palA.setColor(QPalette::Button, QColor::fromRgbF(value_.brushColor.red(), + value_.brushColor.green(), + value_.brushColor.blue(), + value_.brushColor.alpha())); + ui->colorPreviewAlpha->setPalette(palA); } PaintSettingsWidget::~PaintSettingsWidget() { - delete ui; + delete ui; } diff --git a/src/ui/widgets/vertexpaintwidget.h b/src/ui/widgets/vertexpaintwidget.h index 1f380515e..7017d62c1 100644 --- a/src/ui/widgets/vertexpaintwidget.h +++ b/src/ui/widgets/vertexpaintwidget.h @@ -10,33 +10,32 @@ class PaintSettingsWidget; class PaintSettingsWidget final : public QWidget { - Q_OBJECT + Q_OBJECT public: - explicit PaintSettingsWidget(QWidget *parent = nullptr); - ~PaintSettingsWidget(); + explicit PaintSettingsWidget(QWidget *parent = nullptr); + ~PaintSettingsWidget(); public slots: - void setValue( const GLView::PaintSettings& value ); - void showColorPicker(); + void setValue( const GLView::PaintSettings& value ); signals: - void valueChanged( const GLView::PaintSettings& value ); + void valueChanged( const GLView::PaintSettings& value ); protected slots: - void setColorFromHex(); - void setColorFromWheel(); - void setPreviewFromValue(); - void setHexFromColor(); - void setWheelFromColor(); - void updateValue(); + void setColorFromHex(); + void setColorFromWheel(); + void setPreviewFromValue(); + void setHexFromColor(); + void setWheelFromColor(); + void updateValue(); private: - Ui::PaintSettingsWidget *ui; - GLView::PaintSettings value_; - bool supressUpdate_; - bool supressHexUpdate_; - bool supressWheelUpdate_; + Ui::PaintSettingsWidget *ui; + GLView::PaintSettings value_; + bool supressUpdate_; + bool supressHexUpdate_; + bool supressWheelUpdate_; }; #endif // VERTEXPAINTWIDGET_H diff --git a/src/xml/kfmxml.cpp b/src/xml/kfmxml.cpp index aa89b0a5e..89a18de10 100644 --- a/src/xml/kfmxml.cpp +++ b/src/xml/kfmxml.cpp @@ -140,16 +140,16 @@ class KfmXmlHandler final : public QXmlDefaultHandler QString abs = list.value( "abstract" ); NifData data( - list.value( "name" ), + list.value( "name" ), type, tmpl, - NifValue( NifValue::type( type ) ), - list.value( "arg" ), + NifValue( NifValue::type( type ) ), + list.value( "arg" ), arr1, arr2, cond, - KfmModel::version2number( ver1 ), - KfmModel::version2number( ver2 ) + KfmModel::version2number( ver1 ), + KfmModel::version2number( ver2 ) ); bool isTemplated = (type == "TEMPLATE" || tmpl == "TEMPLATE"); @@ -263,12 +263,12 @@ bool KfmModel::loadXML() QDir dir( QCoreApplication::applicationDirPath() ); QString fname; QStringList xmlList( QStringList() - << "kfm.xml" + << "kfm.xml" #ifdef Q_OS_LINUX - << "/usr/share/nifskope/kfm.xml" + << "/usr/share/nifskope/kfm.xml" #endif #ifdef Q_OS_MACX - << "../../../kfm.xml" + << "../../../kfm.xml" #endif ); for ( const QString& str : xmlList ) { diff --git a/src/xml/nifxml.cpp b/src/xml/nifxml.cpp index 936c16feb..ded08178c 100644 --- a/src/xml/nifxml.cpp +++ b/src/xml/nifxml.cpp @@ -526,8 +526,8 @@ class NifXmlHandler final : public QXmlDefaultHandler bool checkType( const NifData & d ) { return ( NifModel::compounds.contains( d.type() ) - || NifValue::type( d.type() ) != NifValue::tNone - || d.type() == "TEMPLATE" + || NifValue::type( d.type() ) != NifValue::tNone + || d.type() == "TEMPLATE" ); } @@ -535,10 +535,10 @@ class NifXmlHandler final : public QXmlDefaultHandler bool checkTemp( const NifData & d ) { return ( d.temp().isEmpty() - || NifValue::type( d.temp() ) != NifValue::tNone - || d.temp() == "TEMPLATE" - || NifModel::blocks.contains( d.temp() ) - || NifModel::compounds.contains( d.temp() ) + || NifValue::type( d.temp() ) != NifValue::tNone + || d.temp() == "TEMPLATE" + || NifModel::blocks.contains( d.temp() ) + || NifModel::compounds.contains( d.temp() ) ); } @@ -603,12 +603,12 @@ bool NifModel::loadXML() QDir dir( QCoreApplication::applicationDirPath() ); QString fname; QStringList xmlList( QStringList() - << "nif.xml" + << "nif.xml" #ifdef Q_OS_LINUX - << "/usr/share/nifskope/nif.xml" + << "/usr/share/nifskope/nif.xml" #endif #ifdef Q_OS_MACX - << "../../../nif.xml" + << "../../../nif.xml" #endif ); for ( const QString& str : xmlList ) { From 1cd3d8b0f8e33120dcb6a656fa63c10103a0342b Mon Sep 17 00:00:00 2001 From: DonkeyKong Date: Thu, 9 Mar 2023 15:22:25 -0500 Subject: [PATCH 10/10] Fix vertex painting across split faces --- src/glview.cpp | 2983 ++++++++++++++++++++++++------------------------ 1 file changed, 1504 insertions(+), 1479 deletions(-) diff --git a/src/glview.cpp b/src/glview.cpp index 7c1576fbe..539a16eaf 100644 --- a/src/glview.cpp +++ b/src/glview.cpp @@ -72,9 +72,9 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Appears to be used solely for gluErrorString // There may be some Qt alternative #ifdef __APPLE__ - #include + #include #else - #include + #include #endif @@ -98,11 +98,11 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. GLGraphicsView::GLGraphicsView( QWidget * parent ) : QGraphicsView() { - setContextMenuPolicy( Qt::CustomContextMenu ); - setFocusPolicy( Qt::ClickFocus ); - setAcceptDrops( true ); + setContextMenuPolicy( Qt::CustomContextMenu ); + setFocusPolicy( Qt::ClickFocus ); + setAcceptDrops( true ); - installEventFilter( parent ); + installEventFilter( parent ); } GLGraphicsView::~GLGraphicsView() {} @@ -110,142 +110,142 @@ GLGraphicsView::~GLGraphicsView() {} GLView * GLView::create( NifSkope * window ) { - QGLFormat fmt; - static QList > views; + QGLFormat fmt; + static QList > views; - QGLWidget * share = nullptr; - for ( const QPointer& v : views ) { - if ( v ) - share = v; - } + QGLWidget * share = nullptr; + for ( const QPointer& v : views ) { + if ( v ) + share = v; + } - QSettings settings; - int aa = settings.value( "Settings/Render/General/Antialiasing", 4 ).toInt(); + QSettings settings; + int aa = settings.value( "Settings/Render/General/Antialiasing", 4 ).toInt(); - // All new windows after the first window will share a format - if ( share ) { - fmt = share->format(); - } else { - fmt.setSampleBuffers( aa > 0 ); - } + // All new windows after the first window will share a format + if ( share ) { + fmt = share->format(); + } else { + fmt.setSampleBuffers( aa > 0 ); + } - // OpenGL version - fmt.setVersion( 2, 1 ); - // Ignored if version < 3.2 - //fmt.setProfile(QGLFormat::CoreProfile); + // OpenGL version + fmt.setVersion( 2, 1 ); + // Ignored if version < 3.2 + //fmt.setProfile(QGLFormat::CoreProfile); - // V-Sync - fmt.setSwapInterval( 1 ); - fmt.setDoubleBuffer( true ); + // V-Sync + fmt.setSwapInterval( 1 ); + fmt.setDoubleBuffer( true ); - fmt.setSamples( std::pow( aa, 2 ) ); + fmt.setSamples( std::pow( aa, 2 ) ); - fmt.setDirectRendering( true ); - fmt.setRgba( true ); + fmt.setDirectRendering( true ); + fmt.setRgba( true ); - views.append( QPointer( new GLView( fmt, window, share ) ) ); + views.append( QPointer( new GLView( fmt, window, share ) ) ); - return views.last(); + return views.last(); } GLView::GLView( const QGLFormat & format, QWidget * p, const QGLWidget * shareWidget ) - : QGLWidget( format, p, shareWidget ) + : QGLWidget( format, p, shareWidget ) { - setFocusPolicy( Qt::ClickFocus ); - //setAttribute( Qt::WA_PaintOnScreen ); - //setAttribute( Qt::WA_NoSystemBackground ); - setAutoFillBackground( false ); - setAcceptDrops( true ); - setMouseTracking( true ); - setContextMenuPolicy( Qt::CustomContextMenu ); + setFocusPolicy( Qt::ClickFocus ); + //setAttribute( Qt::WA_PaintOnScreen ); + //setAttribute( Qt::WA_NoSystemBackground ); + setAutoFillBackground( false ); + setAcceptDrops( true ); + setMouseTracking( true ); + setContextMenuPolicy( Qt::CustomContextMenu ); - // Manually handle the buffer swap - // Fixes bug with QGraphicsView and double buffering - // Input becomes sluggish and CPU usage doubles when putting GLView - // inside a QGraphicsView. - setAutoBufferSwap( false ); + // Manually handle the buffer swap + // Fixes bug with QGraphicsView and double buffering + // Input becomes sluggish and CPU usage doubles when putting GLView + // inside a QGraphicsView. + setAutoBufferSwap( false ); - // Make the context current on this window - makeCurrent(); + // Make the context current on this window + makeCurrent(); - // Create an OpenGL context - glContext = context()->contextHandle(); + // Create an OpenGL context + glContext = context()->contextHandle(); - // Obtain a functions object and resolve all entry points - glFuncs = glContext->functions(); + // Obtain a functions object and resolve all entry points + glFuncs = glContext->functions(); - if ( !glFuncs ) { - Message::critical( this, tr( "Could not obtain OpenGL functions" ) ); - exit( 1 ); - } + if ( !glFuncs ) { + Message::critical( this, tr( "Could not obtain OpenGL functions" ) ); + exit( 1 ); + } - glFuncs->initializeOpenGLFunctions(); + glFuncs->initializeOpenGLFunctions(); - view = ViewDefault; - animState = AnimEnabled; - debugMode = DbgNone; + view = ViewDefault; + animState = AnimEnabled; + debugMode = DbgNone; - Zoom = 1.0; + Zoom = 1.0; - doCenter = false; - doCompile = false; + doCenter = false; + doCompile = false; - model = nullptr; + model = nullptr; - time = 0.0; - lastTime = QTime::currentTime(); + time = 0.0; + lastTime = QTime::currentTime(); - textures = new TexCache( this ); + textures = new TexCache( this ); - updateSettings(); + updateSettings(); - scene = new Scene( textures, glContext, glFuncs ); - connect( textures, &TexCache::sigRefresh, this, static_cast(&GLView::update) ); - connect( scene, &Scene::sceneUpdated, this, static_cast(&GLView::update) ); + scene = new Scene( textures, glContext, glFuncs ); + connect( textures, &TexCache::sigRefresh, this, static_cast(&GLView::update) ); + connect( scene, &Scene::sceneUpdated, this, static_cast(&GLView::update) ); - timer = new QTimer( this ); - timer->setInterval( 1000 / FPS ); - timer->start(); - connect( timer, &QTimer::timeout, this, &GLView::advanceGears ); + timer = new QTimer( this ); + timer->setInterval( 1000 / FPS ); + timer->start(); + connect( timer, &QTimer::timeout, this, &GLView::advanceGears ); - lightVisTimeout = 1500; - lightVisTimer = new QTimer( this ); - lightVisTimer->setSingleShot( true ); - connect( lightVisTimer, &QTimer::timeout, [this]() { setVisMode( Scene::VisLightPos, false ); update(); } ); + lightVisTimeout = 1500; + lightVisTimer = new QTimer( this ); + lightVisTimer->setSingleShot( true ); + connect( lightVisTimer, &QTimer::timeout, [this]() { setVisMode( Scene::VisLightPos, false ); update(); } ); - connect( NifSkope::getOptions(), &SettingsDialog::flush3D, textures, &TexCache::flush ); - connect( NifSkope::getOptions(), &SettingsDialog::update3D, [this]() { - updateSettings(); - qglClearColor( cfg.background ); - update(); - } ); + connect( NifSkope::getOptions(), &SettingsDialog::flush3D, textures, &TexCache::flush ); + connect( NifSkope::getOptions(), &SettingsDialog::update3D, [this]() { + updateSettings(); + qglClearColor( cfg.background ); + update(); + } ); } GLView::~GLView() { - flush(); + flush(); - delete textures; - delete scene; + delete textures; + delete scene; } void GLView::updateSettings() { - QSettings settings; - settings.beginGroup( "Settings/Render" ); + QSettings settings; + settings.beginGroup( "Settings/Render" ); - cfg.background = settings.value( "Colors/Background", QColor( 46, 46, 46 ) ).value(); - cfg.fov = settings.value( "General/Camera/Field Of View" ).toFloat(); - cfg.moveSpd = settings.value( "General/Camera/Movement Speed" ).toFloat(); - cfg.rotSpd = settings.value( "General/Camera/Rotation Speed" ).toFloat(); - cfg.upAxis = UpAxis(settings.value( "General/Up Axis", ZAxis ).toInt()); + cfg.background = settings.value( "Colors/Background", QColor( 46, 46, 46 ) ).value(); + cfg.fov = settings.value( "General/Camera/Field Of View" ).toFloat(); + cfg.moveSpd = settings.value( "General/Camera/Movement Speed" ).toFloat(); + cfg.rotSpd = settings.value( "General/Camera/Rotation Speed" ).toFloat(); + cfg.upAxis = UpAxis(settings.value( "General/Up Axis", ZAxis ).toInt()); - settings.endGroup(); + settings.endGroup(); } QColor GLView::clearColor() const { - return cfg.background; + return cfg.background; } @@ -255,31 +255,31 @@ QColor GLView::clearColor() const Scene * GLView::getScene() { - return scene; + return scene; } void GLView::updateScene() { - scene->update( model, QModelIndex() ); - update(); + scene->update( model, QModelIndex() ); + update(); } void GLView::updateAnimationState( bool checked ) { - QAction * action = qobject_cast(sender()); - if ( action ) { - auto opt = AnimationState( action->data().toInt() ); + QAction * action = qobject_cast(sender()); + if ( action ) { + auto opt = AnimationState( action->data().toInt() ); - if ( checked ) - animState |= opt; - else - animState &= ~opt; + if ( checked ) + animState |= opt; + else + animState &= ~opt; - scene->animate = (animState & AnimEnabled); - lastTime = QTime::currentTime(); + scene->animate = (animState & AnimEnabled); + lastTime = QTime::currentTime(); - update(); - } + update(); + } } @@ -289,953 +289,978 @@ void GLView::updateAnimationState( bool checked ) void GLView::initializeGL() { - GLenum err; + GLenum err; - if ( scene->options & Scene::DoMultisampling ) { - if ( !glContext->hasExtension( "GL_EXT_framebuffer_multisample" ) ) { - scene->options &= ~Scene::DoMultisampling; - //qDebug() << "System does not support multisampling"; - } /* else { - GLint maxSamples; - glGetIntegerv( GL_MAX_SAMPLES, &maxSamples ); - qDebug() << "Max samples:" << maxSamples; - }*/ - } + if ( scene->options & Scene::DoMultisampling ) { + if ( !glContext->hasExtension( "GL_EXT_framebuffer_multisample" ) ) { + scene->options &= ~Scene::DoMultisampling; + //qDebug() << "System does not support multisampling"; + } /* else { + GLint maxSamples; + glGetIntegerv( GL_MAX_SAMPLES, &maxSamples ); + qDebug() << "Max samples:" << maxSamples; + }*/ + } - initializeTextureUnits( glContext ); + initializeTextureUnits( glContext ); - if ( scene->renderer->initialize() ) - updateShaders(); + if ( scene->renderer->initialize() ) + updateShaders(); - // Initial viewport values - // Made viewport and aspect member variables. - // They were being updated every single frame instead of only when resizing. - //glGetIntegerv( GL_VIEWPORT, viewport ); - aspect = (GLdouble)width() / (GLdouble)height(); + // Initial viewport values + // Made viewport and aspect member variables. + // They were being updated every single frame instead of only when resizing. + //glGetIntegerv( GL_VIEWPORT, viewport ); + aspect = (GLdouble)width() / (GLdouble)height(); - // Check for errors - while ( ( err = glGetError() ) != GL_NO_ERROR ) - qDebug() << tr( "glview.cpp - GL ERROR (init) : " ) << (const char *)gluErrorString( err ); + // Check for errors + while ( ( err = glGetError() ) != GL_NO_ERROR ) + qDebug() << tr( "glview.cpp - GL ERROR (init) : " ) << (const char *)gluErrorString( err ); } void GLView::updateShaders() { - makeCurrent(); - scene->updateShaders(); - update(); + makeCurrent(); + scene->updateShaders(); + update(); } void GLView::glProjection( int x, int y ) { - Q_UNUSED( x ); Q_UNUSED( y ); + Q_UNUSED( x ); Q_UNUSED( y ); - glMatrixMode( GL_PROJECTION ); - glLoadIdentity(); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); - BoundSphere bs = scene->view * scene->bounds(); + BoundSphere bs = scene->view * scene->bounds(); - if ( scene->options & Scene::ShowAxes ) { - bs |= BoundSphere( scene->view * Vector3(), axis ); - } + if ( scene->options & Scene::ShowAxes ) { + bs |= BoundSphere( scene->view * Vector3(), axis ); + } - float bounds = (bs.radius > 1024.0) ? bs.radius : 1024.0; + float bounds = (bs.radius > 1024.0) ? bs.radius : 1024.0; - GLdouble nr = fabs( bs.center[2] ) - bounds * 1.5; - GLdouble fr = fabs( bs.center[2] ) + bounds * 1.5; + GLdouble nr = fabs( bs.center[2] ) - bounds * 1.5; + GLdouble fr = fabs( bs.center[2] ) + bounds * 1.5; - if ( perspectiveMode || (view == ViewWalk) ) { - // Perspective View - if ( nr < 1.0 ) - nr = 1.0; - if ( fr < 2.0 ) - fr = 2.0; + if ( perspectiveMode || (view == ViewWalk) ) { + // Perspective View + if ( nr < 1.0 ) + nr = 1.0; + if ( fr < 2.0 ) + fr = 2.0; - if ( nr > fr ) { - // add: swap them when needed - GLfloat tmp = nr; - nr = fr; - fr = tmp; - } + if ( nr > fr ) { + // add: swap them when needed + GLfloat tmp = nr; + nr = fr; + fr = tmp; + } - if ( (fr - nr) < 0.00001f ) { - // add: ensure distance - nr = 1.0; - fr = 2.0; - } + if ( (fr - nr) < 0.00001f ) { + // add: ensure distance + nr = 1.0; + fr = 2.0; + } - GLdouble h2 = tan( ( cfg.fov / Zoom ) / 360 * M_PI ) * nr; - GLdouble w2 = h2 * aspect; - glFrustum( -w2, +w2, -h2, +h2, nr, fr ); - } else { - // Orthographic View - GLdouble h2 = Dist / Zoom; - GLdouble w2 = h2 * aspect; - glOrtho( -w2, +w2, -h2, +h2, nr, fr ); - } + GLdouble h2 = tan( ( cfg.fov / Zoom ) / 360 * M_PI ) * nr; + GLdouble w2 = h2 * aspect; + glFrustum( -w2, +w2, -h2, +h2, nr, fr ); + } else { + // Orthographic View + GLdouble h2 = Dist / Zoom; + GLdouble w2 = h2 * aspect; + glOrtho( -w2, +w2, -h2, +h2, nr, fr ); + } - glMatrixMode( GL_MODELVIEW ); - glLoadIdentity(); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); } #ifdef USE_GL_QPAINTER void GLView::paintEvent( QPaintEvent * event ) { - makeCurrent(); + makeCurrent(); - QPainter painter; - painter.begin( this ); - painter.setRenderHint( QPainter::TextAntialiasing ); + QPainter painter; + painter.begin( this ); + painter.setRenderHint( QPainter::TextAntialiasing ); #else void GLView::paintGL() { #endif - // Save GL state - glPushAttrib( GL_ALL_ATTRIB_BITS ); - glMatrixMode( GL_PROJECTION ); - glPushMatrix(); - glMatrixMode( GL_MODELVIEW ); - glPushMatrix(); - - // Clear Viewport - if ( scene->visMode & Scene::VisSilhouette ) { - qglClearColor( QColor( 255, 255, 255, 255 ) ); - } - //glViewport( 0, 0, width(), height() ); - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); - - - // Compile the model - if ( doCompile ) { - textures->setNifFolder( model->getFolder() ); - scene->make( model ); - scene->transform( Transform(), scene->timeMin() ); - axis = (scene->bounds().radius <= 0) ? 1024.0 : scene->bounds().radius; - - if ( scene->timeMin() != scene->timeMax() ) { - if ( time < scene->timeMin() || time > scene->timeMax() ) - time = scene->timeMin(); - - emit sequencesUpdated(); - - } else if ( scene->timeMax() == 0 ) { - // No Animations in this NIF - emit sequencesDisabled( true ); - } - emit sceneTimeChanged( time, scene->timeMin(), scene->timeMax() ); - doCompile = false; - } - - // Center the model - if ( doCenter ) { - setCenter(); - doCenter = false; - } - - // Transform the scene - Matrix ap; - - if ( cfg.upAxis == YAxis ) { - ap( 0, 0 ) = 0; ap( 0, 1 ) = 0; ap( 0, 2 ) = 1; - ap( 1, 0 ) = 1; ap( 1, 1 ) = 0; ap( 1, 2 ) = 0; - ap( 2, 0 ) = 0; ap( 2, 1 ) = 1; ap( 2, 2 ) = 0; - } else if ( cfg.upAxis == XAxis ) { - ap( 0, 0 ) = 0; ap( 0, 1 ) = 1; ap( 0, 2 ) = 0; - ap( 1, 0 ) = 0; ap( 1, 1 ) = 0; ap( 1, 2 ) = 1; - ap( 2, 0 ) = 1; ap( 2, 1 ) = 0; ap( 2, 2 ) = 0; - } - - viewTrans.rotation.fromEuler( Rot[0] / 180.0 * PI, Rot[1] / 180.0 * PI, Rot[2] / 180.0 * PI ); - viewTrans.translation = viewTrans.rotation * Pos; - viewTrans.rotation = viewTrans.rotation * ap; - - if ( view != ViewWalk ) - viewTrans.translation[2] -= Dist * 2; - - scene->transform( viewTrans, time ); - - // Setup projection mode - glProjection(); - glLoadIdentity(); - - // Draw the grid - if ( scene->options & Scene::ShowGrid ) { - glDisable( GL_ALPHA_TEST ); - glDisable( GL_BLEND ); - glDisable( GL_LIGHTING ); - glDisable( GL_COLOR_MATERIAL ); - glEnable( GL_DEPTH_TEST ); - glDepthMask( GL_TRUE ); - glDepthFunc( GL_LESS ); - glDisable( GL_TEXTURE_2D ); - glDisable( GL_NORMALIZE ); - glLineWidth( 2.0f ); - - // Keep the grid "grounded" regardless of Up Axis - Transform gridTrans = viewTrans; - if ( cfg.upAxis != ZAxis ) - gridTrans.rotation = viewTrans.rotation * ap.inverted(); - - glPushMatrix(); - glLoadMatrix( gridTrans ); - - // TODO: Configurable grid in Settings - // 1024 game units, major lines every 128, minor lines every 64 - drawGrid( 1024, 128, 2 ); - - glPopMatrix(); - } + // Save GL state + glPushAttrib( GL_ALL_ATTRIB_BITS ); + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + + // Clear Viewport + if ( scene->visMode & Scene::VisSilhouette ) { + qglClearColor( QColor( 255, 255, 255, 255 ) ); + } + //glViewport( 0, 0, width(), height() ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); + + + // Compile the model + if ( doCompile ) { + textures->setNifFolder( model->getFolder() ); + scene->make( model ); + scene->transform( Transform(), scene->timeMin() ); + axis = (scene->bounds().radius <= 0) ? 1024.0 : scene->bounds().radius; + + if ( scene->timeMin() != scene->timeMax() ) { + if ( time < scene->timeMin() || time > scene->timeMax() ) + time = scene->timeMin(); + + emit sequencesUpdated(); + + } else if ( scene->timeMax() == 0 ) { + // No Animations in this NIF + emit sequencesDisabled( true ); + } + emit sceneTimeChanged( time, scene->timeMin(), scene->timeMax() ); + doCompile = false; + } + + // Center the model + if ( doCenter ) { + setCenter(); + doCenter = false; + } + + // Transform the scene + Matrix ap; + + if ( cfg.upAxis == YAxis ) { + ap( 0, 0 ) = 0; ap( 0, 1 ) = 0; ap( 0, 2 ) = 1; + ap( 1, 0 ) = 1; ap( 1, 1 ) = 0; ap( 1, 2 ) = 0; + ap( 2, 0 ) = 0; ap( 2, 1 ) = 1; ap( 2, 2 ) = 0; + } else if ( cfg.upAxis == XAxis ) { + ap( 0, 0 ) = 0; ap( 0, 1 ) = 1; ap( 0, 2 ) = 0; + ap( 1, 0 ) = 0; ap( 1, 1 ) = 0; ap( 1, 2 ) = 1; + ap( 2, 0 ) = 1; ap( 2, 1 ) = 0; ap( 2, 2 ) = 0; + } + + viewTrans.rotation.fromEuler( Rot[0] / 180.0 * PI, Rot[1] / 180.0 * PI, Rot[2] / 180.0 * PI ); + viewTrans.translation = viewTrans.rotation * Pos; + viewTrans.rotation = viewTrans.rotation * ap; + + if ( view != ViewWalk ) + viewTrans.translation[2] -= Dist * 2; + + scene->transform( viewTrans, time ); + + // Setup projection mode + glProjection(); + glLoadIdentity(); + + // Draw the grid + if ( scene->options & Scene::ShowGrid ) { + glDisable( GL_ALPHA_TEST ); + glDisable( GL_BLEND ); + glDisable( GL_LIGHTING ); + glDisable( GL_COLOR_MATERIAL ); + glEnable( GL_DEPTH_TEST ); + glDepthMask( GL_TRUE ); + glDepthFunc( GL_LESS ); + glDisable( GL_TEXTURE_2D ); + glDisable( GL_NORMALIZE ); + glLineWidth( 2.0f ); + + // Keep the grid "grounded" regardless of Up Axis + Transform gridTrans = viewTrans; + if ( cfg.upAxis != ZAxis ) + gridTrans.rotation = viewTrans.rotation * ap.inverted(); + + glPushMatrix(); + glLoadMatrix( gridTrans ); + + // TODO: Configurable grid in Settings + // 1024 game units, major lines every 128, minor lines every 64 + drawGrid( 1024, 128, 2 ); + + glPopMatrix(); + } #ifndef QT_NO_DEBUG - // Debug scene bounds - glEnable( GL_DEPTH_TEST ); - glDepthMask( GL_TRUE ); - glDepthFunc( GL_LESS ); - glPushMatrix(); - glLoadMatrix( viewTrans ); - if ( debugMode == DbgBounds ) { - BoundSphere bs = scene->bounds(); - bs |= BoundSphere( Vector3(), axis ); - drawSphere( bs.center, bs.radius ); - } - glPopMatrix(); + // Debug scene bounds + glEnable( GL_DEPTH_TEST ); + glDepthMask( GL_TRUE ); + glDepthFunc( GL_LESS ); + glPushMatrix(); + glLoadMatrix( viewTrans ); + if ( debugMode == DbgBounds ) { + BoundSphere bs = scene->bounds(); + bs |= BoundSphere( Vector3(), axis ); + drawSphere( bs.center, bs.radius ); + } + glPopMatrix(); #endif - GLfloat mat_spec[] = { 0.0f, 0.0f, 0.0f, 1.0f }; - - if ( scene->options & Scene::DoLighting ) { - // Setup light - Vector4 lightDir( 0.0, 0.0, 1.0, 0.0 ); - - if ( !frontalLight ) { - float decl = declination / 180.0 * PI; - Vector3 v( sin( decl ), 0, cos( decl ) ); - Matrix m; m.fromEuler( 0, 0, planarAngle / 180.0 * PI ); - v = m * v; - lightDir = Vector4( viewTrans.rotation * v, 0.0 ); - - if ( scene->visMode & Scene::VisLightPos ) { - glEnable( GL_BLEND ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - glEnable( GL_DEPTH_TEST ); - glDepthMask( GL_TRUE ); - glDepthFunc( GL_LESS ); - - glPushMatrix(); - glLoadMatrix( viewTrans ); - - glLineWidth( 2.0f ); - glColor4f( 1.0f, 1.0f, 1.0f, 0.5f ); - - // Scale the distance a bit - float l = axis + 64.0; - l = (l < 128) ? axis * 1.5 : l; - l = (l > 2048) ? axis * 0.66 : l; - l = (l > 1024) ? axis * 0.75 : l; - - drawDashLine( Vector3( 0, 0, 0 ), v * l, 30 ); - drawSphere( v * l, axis / 10 ); - glPopMatrix(); - glDisable( GL_BLEND ); - } - } - - float amb = ambient; - if ( (scene->visMode & Scene::VisNormalsOnly) - && (scene->options & Scene::DoTexturing) - && !(scene->options & Scene::DisableShaders) ) - { - amb = 0.1f; - } - - GLfloat mat_amb[] = { amb, amb, amb, 1.0f }; - GLfloat mat_diff[] = { brightness, brightness, brightness, 1.0f }; - - - glShadeModel( GL_SMOOTH ); - //glEnable( GL_LIGHTING ); - glEnable( GL_LIGHT0 ); - glLightfv( GL_LIGHT0, GL_AMBIENT, mat_amb ); - glLightfv( GL_LIGHT0, GL_DIFFUSE, mat_diff ); - glLightfv( GL_LIGHT0, GL_SPECULAR, mat_diff ); - glLightfv( GL_LIGHT0, GL_POSITION, lightDir.data() ); - } else { - float amb = 0.5f; - if ( scene->options & Scene::DisableShaders ) { - amb = 0.0f; - } - - GLfloat mat_amb[] = { amb, amb, amb, 1.0f }; - GLfloat mat_diff[] = { 1.0f, 1.0f, 1.0f, 1.0f }; - - - glShadeModel( GL_SMOOTH ); - //glEnable( GL_LIGHTING ); - glEnable( GL_LIGHT0 ); - glLightfv( GL_LIGHT0, GL_AMBIENT, mat_amb ); - glLightfv( GL_LIGHT0, GL_DIFFUSE, mat_diff ); - glLightfv( GL_LIGHT0, GL_SPECULAR, mat_spec ); - } - - if ( scene->visMode & Scene::VisSilhouette ) { - GLfloat mat_diff[] = { 0.0f, 0.0f, 0.0f, 1.0f }; - GLfloat mat_amb[] = { 0.0f, 0.0f, 0.0f, 1.0f }; - - glShadeModel( GL_FLAT ); - //glEnable( GL_LIGHTING ); - glEnable( GL_LIGHT0 ); - glLightModelfv( GL_LIGHT_MODEL_AMBIENT, mat_diff ); - glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_diff ); - glLightfv( GL_LIGHT0, GL_AMBIENT, mat_amb ); - glLightfv( GL_LIGHT0, GL_DIFFUSE, mat_diff ); - glLightfv( GL_LIGHT0, GL_SPECULAR, mat_spec ); - } - - if ( scene->options & Scene::DoMultisampling ) - glEnable( GL_MULTISAMPLE_ARB ); + GLfloat mat_spec[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + + if ( scene->options & Scene::DoLighting ) { + // Setup light + Vector4 lightDir( 0.0, 0.0, 1.0, 0.0 ); + + if ( !frontalLight ) { + float decl = declination / 180.0 * PI; + Vector3 v( sin( decl ), 0, cos( decl ) ); + Matrix m; m.fromEuler( 0, 0, planarAngle / 180.0 * PI ); + v = m * v; + lightDir = Vector4( viewTrans.rotation * v, 0.0 ); + + if ( scene->visMode & Scene::VisLightPos ) { + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable( GL_DEPTH_TEST ); + glDepthMask( GL_TRUE ); + glDepthFunc( GL_LESS ); + + glPushMatrix(); + glLoadMatrix( viewTrans ); + + glLineWidth( 2.0f ); + glColor4f( 1.0f, 1.0f, 1.0f, 0.5f ); + + // Scale the distance a bit + float l = axis + 64.0; + l = (l < 128) ? axis * 1.5 : l; + l = (l > 2048) ? axis * 0.66 : l; + l = (l > 1024) ? axis * 0.75 : l; + + drawDashLine( Vector3( 0, 0, 0 ), v * l, 30 ); + drawSphere( v * l, axis / 10 ); + glPopMatrix(); + glDisable( GL_BLEND ); + } + } + + float amb = ambient; + if ( (scene->visMode & Scene::VisNormalsOnly) + && (scene->options & Scene::DoTexturing) + && !(scene->options & Scene::DisableShaders) ) + { + amb = 0.1f; + } + + GLfloat mat_amb[] = { amb, amb, amb, 1.0f }; + GLfloat mat_diff[] = { brightness, brightness, brightness, 1.0f }; + + + glShadeModel( GL_SMOOTH ); + //glEnable( GL_LIGHTING ); + glEnable( GL_LIGHT0 ); + glLightfv( GL_LIGHT0, GL_AMBIENT, mat_amb ); + glLightfv( GL_LIGHT0, GL_DIFFUSE, mat_diff ); + glLightfv( GL_LIGHT0, GL_SPECULAR, mat_diff ); + glLightfv( GL_LIGHT0, GL_POSITION, lightDir.data() ); + } else { + float amb = 0.5f; + if ( scene->options & Scene::DisableShaders ) { + amb = 0.0f; + } + + GLfloat mat_amb[] = { amb, amb, amb, 1.0f }; + GLfloat mat_diff[] = { 1.0f, 1.0f, 1.0f, 1.0f }; + + + glShadeModel( GL_SMOOTH ); + //glEnable( GL_LIGHTING ); + glEnable( GL_LIGHT0 ); + glLightfv( GL_LIGHT0, GL_AMBIENT, mat_amb ); + glLightfv( GL_LIGHT0, GL_DIFFUSE, mat_diff ); + glLightfv( GL_LIGHT0, GL_SPECULAR, mat_spec ); + } + + if ( scene->visMode & Scene::VisSilhouette ) { + GLfloat mat_diff[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + GLfloat mat_amb[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + + glShadeModel( GL_FLAT ); + //glEnable( GL_LIGHTING ); + glEnable( GL_LIGHT0 ); + glLightModelfv( GL_LIGHT_MODEL_AMBIENT, mat_diff ); + glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_diff ); + glLightfv( GL_LIGHT0, GL_AMBIENT, mat_amb ); + glLightfv( GL_LIGHT0, GL_DIFFUSE, mat_diff ); + glLightfv( GL_LIGHT0, GL_SPECULAR, mat_spec ); + } + + if ( scene->options & Scene::DoMultisampling ) + glEnable( GL_MULTISAMPLE_ARB ); #ifndef QT_NO_DEBUG - // Color Key debug - if ( debugMode == DbgColorPicker ) { - glDisable( GL_MULTISAMPLE ); - glDisable( GL_LINE_SMOOTH ); - glDisable( GL_TEXTURE_2D ); - glDisable( GL_BLEND ); - glDisable( GL_DITHER ); - glDisable( GL_LIGHTING ); - glShadeModel( GL_FLAT ); - glDisable( GL_FOG ); - glDisable( GL_MULTISAMPLE_ARB ); - glEnable( GL_DEPTH_TEST ); - glDepthFunc( GL_LEQUAL ); - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - Node::SELECTING = 1; - } else { - Node::SELECTING = 0; - } + // Color Key debug + if ( debugMode == DbgColorPicker ) { + glDisable( GL_MULTISAMPLE ); + glDisable( GL_LINE_SMOOTH ); + glDisable( GL_TEXTURE_2D ); + glDisable( GL_BLEND ); + glDisable( GL_DITHER ); + glDisable( GL_LIGHTING ); + glShadeModel( GL_FLAT ); + glDisable( GL_FOG ); + glDisable( GL_MULTISAMPLE_ARB ); + glEnable( GL_DEPTH_TEST ); + glDepthFunc( GL_LEQUAL ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + Node::SELECTING = 1; + } else { + Node::SELECTING = 0; + } #endif - // Draw the model - scene->draw(); + // Draw the model + scene->draw(); - if ( scene->options & Scene::ShowAxes ) { - // Resize viewport to small corner of screen - int axesSize = std::min( width() / 10, 125 ); - glViewport( 0, 0, axesSize * devicePixelRatioF(), axesSize * devicePixelRatioF() ); + if ( scene->options & Scene::ShowAxes ) { + // Resize viewport to small corner of screen + int axesSize = std::min( width() / 10, 125 ); + glViewport( 0, 0, axesSize * devicePixelRatioF(), axesSize * devicePixelRatioF() ); - // Reset matrices - glMatrixMode( GL_PROJECTION ); - glLoadIdentity(); + // Reset matrices + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); - // Square frustum - auto nr = 1.0; - auto fr = 250.0; - GLdouble h2 = tan( cfg.fov / 360 * M_PI ) * nr; - GLdouble w2 = h2; - glFrustum( -w2, +w2, -h2, +h2, nr, fr ); + // Square frustum + auto nr = 1.0; + auto fr = 250.0; + GLdouble h2 = tan( cfg.fov / 360 * M_PI ) * nr; + GLdouble w2 = h2; + glFrustum( -w2, +w2, -h2, +h2, nr, fr ); - // Reset matrices - glMatrixMode( GL_MODELVIEW ); - glLoadIdentity(); + // Reset matrices + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); - glPushMatrix(); + glPushMatrix(); - // Store and reset viewTrans translation - auto viewTransOrig = viewTrans.translation; + // Store and reset viewTrans translation + auto viewTransOrig = viewTrans.translation; - // Zoom out slightly - viewTrans.translation = { 0, 0, -150.0 }; + // Zoom out slightly + viewTrans.translation = { 0, 0, -150.0 }; - // Load modified viewTrans - glLoadMatrix( viewTrans ); + // Load modified viewTrans + glLoadMatrix( viewTrans ); - // Restore original viewTrans translation - viewTrans.translation = viewTransOrig; + // Restore original viewTrans translation + viewTrans.translation = viewTransOrig; - // Find direction of axes - auto vtr = viewTrans.rotation; - QVector axesDots = { vtr( 2, 0 ), vtr( 2, 1 ), vtr( 2, 2 ) }; + // Find direction of axes + auto vtr = viewTrans.rotation; + QVector axesDots = { vtr( 2, 0 ), vtr( 2, 1 ), vtr( 2, 2 ) }; - drawAxesOverlay( { 0, 0, 0 }, 50.0, sortAxes( axesDots ) ); + drawAxesOverlay( { 0, 0, 0 }, 50.0, sortAxes( axesDots ) ); - glPopMatrix(); + glPopMatrix(); - // Restore viewport size - glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); + // Restore viewport size + glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); - // Restore matrices - glProjection(); - } + // Restore matrices + glProjection(); + } - // Draw mouse tool - if (scene->actionMode & Scene::Paint) - { - // Reset matrices - glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); - glMatrixMode( GL_PROJECTION ); - glLoadIdentity(); - glOrtho(0, width(), height(), 0, -1, 1); - glMatrixMode( GL_MODELVIEW ); - glLoadIdentity(); - glDisable( GL_LIGHTING ); - glDepthFunc( GL_ALWAYS ); - glLineWidth( 2.0f ); - glColor3f( 1.0, 0.0, 0.0 ); - drawCircle(Vector3(lastPos.x(), lastPos.y(), 0), Vector3(0,0,1), cfg.vertexPaintSettings.brushSize + 2.0f); + // Draw mouse tool + if (scene->actionMode & Scene::Paint) + { + // Reset matrices + glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho(0, width(), height(), 0, -1, 1); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + glDisable( GL_LIGHTING ); + glDepthFunc( GL_ALWAYS ); + glLineWidth( 2.0f ); + glColor3f( 1.0, 0.0, 0.0 ); + drawCircle(Vector3(lastPos.x(), lastPos.y(), 0), Vector3(0,0,1), cfg.vertexPaintSettings.brushSize + 2.0f); - // Restore viewport and projection - glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); - glProjection(); - } + // Restore viewport and projection + glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF()); + glProjection(); + } - // Restore GL state - glPopAttrib(); - glMatrixMode( GL_MODELVIEW ); - glPopMatrix(); - glMatrixMode( GL_PROJECTION ); - glPopMatrix(); + // Restore GL state + glPopAttrib(); + glMatrixMode( GL_MODELVIEW ); + glPopMatrix(); + glMatrixMode( GL_PROJECTION ); + glPopMatrix(); - // Check for errors - GLenum err; - while ( ( err = glGetError() ) != GL_NO_ERROR ) - qDebug() << tr( "glview.cpp - GL ERROR (paint): " ) << (const char *)gluErrorString( err ); + // Check for errors + GLenum err; + while ( ( err = glGetError() ) != GL_NO_ERROR ) + qDebug() << tr( "glview.cpp - GL ERROR (paint): " ) << (const char *)gluErrorString( err ); - emit paintUpdate(); + emit paintUpdate(); - // Manually handle the buffer swap - swapBuffers(); + // Manually handle the buffer swap + swapBuffers(); #ifdef USE_GL_QPAINTER - painter.end(); + painter.end(); #endif } void GLView::resizeGL( int width, int height ) { - resize( width, height ); + resize( width, height ); - makeCurrent(); - aspect = (GLdouble)width / (GLdouble)height; - glViewport( 0, 0, width, height ); - qglClearColor( cfg.background ); + makeCurrent(); + aspect = (GLdouble)width / (GLdouble)height; + glViewport( 0, 0, width, height ); + qglClearColor( cfg.background ); - update(); + update(); } void GLView::resizeEvent( QResizeEvent * e ) { - Q_UNUSED( e ); - // This function should never be called. - // Moved to NifSkope::eventFilter() + Q_UNUSED( e ); + // This function should never be called. + // Moved to NifSkope::eventFilter() } void GLView::setFrontalLight( bool frontal ) { - frontalLight = frontal; - update(); + frontalLight = frontal; + update(); } void GLView::setBrightness( int value ) { - if ( value > 900 ) { - value += pow(value - 900, 1.5); - } + if ( value > 900 ) { + value += pow(value - 900, 1.5); + } - brightness = float(value) / 720.0; - update(); + brightness = float(value) / 720.0; + update(); } void GLView::setAmbient( int value ) { - ambient = float( value ) / 1440.0; - update(); + ambient = float( value ) / 1440.0; + update(); } void GLView::setDeclination( int decl ) { - declination = float(decl) / 4; // Divide by 4 because sliders are -720 <-> 720 - lightVisTimer->start( lightVisTimeout ); - setVisMode( Scene::VisLightPos, true ); - update(); + declination = float(decl) / 4; // Divide by 4 because sliders are -720 <-> 720 + lightVisTimer->start( lightVisTimeout ); + setVisMode( Scene::VisLightPos, true ); + update(); } void GLView::setPlanarAngle( int angle ) { - planarAngle = float(angle) / 4; // Divide by 4 because sliders are -720 <-> 720 - lightVisTimer->start( lightVisTimeout ); - setVisMode( Scene::VisLightPos, true ); - update(); + planarAngle = float(angle) / 4; // Divide by 4 because sliders are -720 <-> 720 + lightVisTimer->start( lightVisTimeout ); + setVisMode( Scene::VisLightPos, true ); + update(); } void GLView::setDebugMode( DebugMode mode ) { - debugMode = mode; + debugMode = mode; } void GLView::setVisMode( Scene::VisMode mode, bool checked ) { - if ( checked ) - scene->visMode |= mode; - else - scene->visMode &= ~mode; + if ( checked ) + scene->visMode |= mode; + else + scene->visMode &= ~mode; - update(); + update(); } typedef void (Scene::* DrawFunc)( void ); QImage GLView::renderIndexImage() { - makeCurrent(); - - glPushAttrib( GL_ALL_ATTRIB_BITS ); - glMatrixMode( GL_PROJECTION ); - glPushMatrix(); - glMatrixMode( GL_MODELVIEW ); - glPushMatrix(); - - glViewport( 0, 0, width(), height() ); - glProjection(); - - QList drawFuncs; - if ( scene->options & Scene::ShowCollision ) - drawFuncs << &Scene::drawHavok; - - if ( scene->options & Scene::ShowNodes ) - drawFuncs << &Scene::drawNodes; - - if ( scene->options & Scene::ShowMarkers ) - drawFuncs << &Scene::drawFurn; - - drawFuncs << &Scene::drawShapes; - // Color Key O(1) selection - // Open GL 3.0 says glRenderMode is deprecated - // ATI OpenGL API implementation of GL_SELECT corrupts NifSkope memory - // - // Create FBO for sharp edges and no shading. - // Texturing, blending, dithering, lighting and smooth shading should be disabled. - // The FBO can be used for the drawing operations to keep the drawing operations invisible to the user. - - GLint viewport[4]; - glGetIntegerv( GL_VIEWPORT, viewport ); - - // Create new FBO with multisampling disabled - QOpenGLFramebufferObjectFormat fboFmt; - fboFmt.setTextureTarget( GL_TEXTURE_2D ); - fboFmt.setInternalTextureFormat( GL_RGB32F_ARB ); - fboFmt.setAttachment( QOpenGLFramebufferObject::Attachment::CombinedDepthStencil ); - - QOpenGLFramebufferObject fbo( viewport[2], viewport[3], fboFmt ); - fbo.bind(); - - glEnable( GL_LIGHTING ); - glDisable( GL_MULTISAMPLE ); - glDisable( GL_MULTISAMPLE_ARB ); - glDisable( GL_LINE_SMOOTH ); - glDisable( GL_POINT_SMOOTH ); - glDisable( GL_POLYGON_SMOOTH ); - glDisable( GL_TEXTURE_1D ); - glDisable( GL_TEXTURE_2D ); - glDisable( GL_TEXTURE_3D ); - glDisable( GL_BLEND ); - glDisable( GL_DITHER ); - glDisable( GL_FOG ); - glDisable( GL_LIGHTING ); - glShadeModel( GL_FLAT ); - glEnable( GL_DEPTH_TEST ); - glDepthFunc( GL_LEQUAL ); - glClearColor( 0, 0, 0, 1 ); - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - - // Rasterize the scene - Node::SELECTING = 1; - for ( DrawFunc df : drawFuncs ) { - (scene->*df)(); - } - Node::SELECTING = 0; - - fbo.release(); - - glPopAttrib(); - glMatrixMode( GL_MODELVIEW ); - glPopMatrix(); - glMatrixMode( GL_PROJECTION ); - glPopMatrix(); - - return fbo.toImage(); + makeCurrent(); + + glPushAttrib( GL_ALL_ATTRIB_BITS ); + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + + glViewport( 0, 0, width(), height() ); + glProjection(); + + QList drawFuncs; + if ( scene->options & Scene::ShowCollision ) + drawFuncs << &Scene::drawHavok; + + if ( scene->options & Scene::ShowNodes ) + drawFuncs << &Scene::drawNodes; + + if ( scene->options & Scene::ShowMarkers ) + drawFuncs << &Scene::drawFurn; + + drawFuncs << &Scene::drawShapes; + // Color Key O(1) selection + // Open GL 3.0 says glRenderMode is deprecated + // ATI OpenGL API implementation of GL_SELECT corrupts NifSkope memory + // + // Create FBO for sharp edges and no shading. + // Texturing, blending, dithering, lighting and smooth shading should be disabled. + // The FBO can be used for the drawing operations to keep the drawing operations invisible to the user. + + GLint viewport[4]; + glGetIntegerv( GL_VIEWPORT, viewport ); + + // Create new FBO with multisampling disabled + QOpenGLFramebufferObjectFormat fboFmt; + fboFmt.setTextureTarget( GL_TEXTURE_2D ); + fboFmt.setInternalTextureFormat( GL_RGB32F_ARB ); + fboFmt.setAttachment( QOpenGLFramebufferObject::Attachment::CombinedDepthStencil ); + + QOpenGLFramebufferObject fbo( viewport[2], viewport[3], fboFmt ); + fbo.bind(); + + glEnable( GL_LIGHTING ); + glDisable( GL_MULTISAMPLE ); + glDisable( GL_MULTISAMPLE_ARB ); + glDisable( GL_LINE_SMOOTH ); + glDisable( GL_POINT_SMOOTH ); + glDisable( GL_POLYGON_SMOOTH ); + glDisable( GL_TEXTURE_1D ); + glDisable( GL_TEXTURE_2D ); + glDisable( GL_TEXTURE_3D ); + glDisable( GL_BLEND ); + glDisable( GL_DITHER ); + glDisable( GL_FOG ); + glDisable( GL_LIGHTING ); + glShadeModel( GL_FLAT ); + glEnable( GL_DEPTH_TEST ); + glDepthFunc( GL_LEQUAL ); + glClearColor( 0, 0, 0, 1 ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // Rasterize the scene + Node::SELECTING = 1; + for ( DrawFunc df : drawFuncs ) { + (scene->*df)(); + } + Node::SELECTING = 0; + + fbo.release(); + + glPopAttrib(); + glMatrixMode( GL_MODELVIEW ); + glPopMatrix(); + glMatrixMode( GL_PROJECTION ); + glPopMatrix(); + + return fbo.toImage(); } QModelIndex GLView::sampleIndexImagePoint(const QImage& img, const QPoint& pos, NifModel* model) { - if (img.rect().contains(pos)) - { - QColor pixel = img.pixel( pos ); - return colorIndexToModelIndex(pixel, model); - } - return QModelIndex(); + if (img.rect().contains(pos)) + { + QColor pixel = img.pixel( pos ); + return colorIndexToModelIndex(pixel, model); + } + return QModelIndex(); } QVector GLView::sampleIndexImageCircle(const QImage& img, const QPoint& pos, float radius, NifModel* model) { - QVector indices; - if (img.rect().contains(pos)) - { - QPointF startf = (QPointF)pos - QPointF(radius, radius); - QPointF endf = (QPointF)pos + QPointF(radius, radius); - - QPoint start(std::round(startf.x()), std::round(startf.y())); - QPoint end(std::round(endf.x()), std::round(endf.y())); - - QVector2D origin(pos); - QVector colors; - for (int y=start.y(); y <= end.y(); y++) - { - for (int x=start.x(); x <= end.x(); x++) - { - if (img.rect().contains(x,y)) - { - float dist = (QVector2D(x,y) - origin).length(); - QColor pixel = img.pixel(x,y); - if (dist <= radius && !colors.contains(pixel)) - { - colors.push_back(pixel); - } - } - } - } - - for (const QColor& indColor : colors) - { - QModelIndex modelInd = colorIndexToModelIndex(indColor, model); - if (modelInd.isValid()) - indices.push_back(modelInd); - } - } - return indices; + QVector indices; + if (img.rect().contains(pos)) + { + QPointF startf = (QPointF)pos - QPointF(radius, radius); + QPointF endf = (QPointF)pos + QPointF(radius, radius); + + QPoint start(std::round(startf.x()), std::round(startf.y())); + QPoint end(std::round(endf.x()), std::round(endf.y())); + + QVector2D origin(pos); + QVector colors; + for (int y=start.y(); y <= end.y(); y++) + { + for (int x=start.x(); x <= end.x(); x++) + { + if (img.rect().contains(x,y)) + { + float dist = (QVector2D(x,y) - origin).length(); + QColor pixel = img.pixel(x,y); + if (dist <= radius && !colors.contains(pixel)) + { + colors.push_back(pixel); + } + } + } + } + + for (const QColor& indColor : colors) + { + QModelIndex modelInd = colorIndexToModelIndex(indColor, model); + if (modelInd.isValid()) + indices.push_back(modelInd); + } + } + return indices; } QModelIndex GLView::colorIndexToModelIndex(const QColor& color, NifModel* model) { - // Encode RGB to Int - int a = 0; - a |= color.red() << 0; - a |= color.green() << 8; - a |= color.blue() << 16; + // Encode RGB to Int + int a = 0; + a |= color.red() << 0; + a |= color.green() << 8; + a |= color.blue() << 16; - // Decode: - // R = (id & 0x000000FF) >> 0 - // G = (id & 0x0000FF00) >> 8 - // B = (id & 0x00FF0000) >> 16 + // Decode: + // R = (id & 0x000000FF) >> 0 + // G = (id & 0x0000FF00) >> 8 + // B = (id & 0x00FF0000) >> 16 - int choose = COLORKEY2ID( a ); - int furn = -1; + int choose = COLORKEY2ID( a ); + int furn = -1; - // Pick BSFurnitureMarker - if ( choose > 0 ) { - auto furnBlock = model->getBlock( model->index( 3, 0, model->getBlock( choose & 0x0ffff ) ), "BSFurnitureMarker" ); + // Pick BSFurnitureMarker + if ( choose > 0 ) { + auto furnBlock = model->getBlock( model->index( 3, 0, model->getBlock( choose & 0x0ffff ) ), "BSFurnitureMarker" ); - if ( furnBlock.isValid() ) { - furn = choose >> 16; - choose &= 0x0ffff; - } - } + if ( furnBlock.isValid() ) { + furn = choose >> 16; + choose &= 0x0ffff; + } + } - QModelIndex chooseIndex; + QModelIndex chooseIndex; - if (scene->actionMode & Scene::Vertex ) { - // Vertex - int block = choose >> 16; - int vert = choose - (block << 16); + if (scene->actionMode & Scene::Vertex ) { + // Vertex + int block = choose >> 16; + int vert = choose - (block << 16); - auto shape = scene->shapes.value( block ); - if ( shape ) - chooseIndex = shape->vertexAt( vert ); - } else if ( choose != -1 ) { - // Block Index - chooseIndex = model->getBlock( choose ); + auto shape = scene->shapes.value( block ); + if ( shape ) + chooseIndex = shape->vertexAt( vert ); + } else if ( choose != -1 ) { + // Block Index + chooseIndex = model->getBlock( choose ); - if ( furn != -1 ) { - // Furniture Row @ Block Index - chooseIndex = model->index( furn, 0, model->index( 3, 0, chooseIndex ) ); - } - } + if ( furn != -1 ) { + // Furniture Row @ Block Index + chooseIndex = model->index( furn, 0, model->index( 3, 0, chooseIndex ) ); + } + } - return chooseIndex; + return chooseIndex; } QModelIndex GLView::indexAt(const QPoint & pos, int cycle) { - Q_UNUSED(cycle); + Q_UNUSED(cycle); - if ( !(model && isVisible() && height()) ) - return QModelIndex(); + if ( !(model && isVisible() && height()) ) + return QModelIndex(); - QImage img = renderIndexImage(); + QImage img = renderIndexImage(); #ifndef QT_NO_DEBUG - img.save( "fbo.png" ); + img.save( "fbo.png" ); #endif - return sampleIndexImagePoint(img, pos, model); + return sampleIndexImagePoint(img, pos, model); } void GLView::center() { - doCenter = true; - update(); + doCenter = true; + update(); } void GLView::move( float x, float y, float z ) { - Pos += Matrix::euler( Rot[0] / 180 * PI, Rot[1] / 180 * PI, Rot[2] / 180 * PI ).inverted() * Vector3( x, y, z ); - updateViewpoint(); - update(); + Pos += Matrix::euler( Rot[0] / 180 * PI, Rot[1] / 180 * PI, Rot[2] / 180 * PI ).inverted() * Vector3( x, y, z ); + updateViewpoint(); + update(); } void GLView::rotate( float x, float y, float z ) { - Rot += Vector3( x, y, z ); - updateViewpoint(); - update(); + Rot += Vector3( x, y, z ); + updateViewpoint(); + update(); } void GLView::zoom( float z ) { - Zoom *= z; + Zoom *= z; - if ( Zoom < ZOOM_MIN ) - Zoom = ZOOM_MIN; + if ( Zoom < ZOOM_MIN ) + Zoom = ZOOM_MIN; - if ( Zoom > ZOOM_MAX ) - Zoom = ZOOM_MAX; + if ( Zoom > ZOOM_MAX ) + Zoom = ZOOM_MAX; - update(); + update(); } void GLView::setVertexPaintSettings(GLView::PaintSettings settings) { - cfg.vertexPaintSettings = settings; - update(); + cfg.vertexPaintSettings = settings; + update(); } void GLView::startVertexPaint(const QPoint& point) { - mousePaint = true; - mousePaintVerts.clear(); - mousePaintHitDetectImg = renderIndexImage(); - vertexPaint(point); - QCursor cursor(Qt::BlankCursor); - QApplication::setOverrideCursor(cursor); - QApplication::changeOverrideCursor(cursor); + mousePaint = true; + mousePaintVerts.clear(); + mousePaintHitDetectImg = renderIndexImage(); + vertexPaint(point); + QCursor cursor(Qt::BlankCursor); + QApplication::setOverrideCursor(cursor); + QApplication::changeOverrideCursor(cursor); } void GLView::vertexPaint(const QPoint& point) { - bool updated = false; - if (mousePaint) - { - // Let's do an absolutely shit job and act like it's decent - auto inds = sampleIndexImageCircle(mousePaintHitDetectImg, point, cfg.vertexPaintSettings.brushSize, model); - Color4 brushColorAndOpacity = cfg.vertexPaintSettings.brushColor * cfg.vertexPaintSettings.brushOpacity; - - for (const auto& ind : inds) - { - if ( ind.isValid() && !mousePaintVerts.contains(ind)) - { - mousePaintVerts.push_back(ind); - Color4 base = model->get(ind.parent(), "Vertex Colors"); - Color4 blend; - - if (cfg.vertexPaintSettings.brushMode == PaintBlendMode::BlendNormal) - { - blend = brushColorAndOpacity + (base * (Color4() - cfg.vertexPaintSettings.brushOpacity)); - } - else if (cfg.vertexPaintSettings.brushMode == PaintBlendMode::BlendMultiply) - { - blend = (brushColorAndOpacity + (Color4() - cfg.vertexPaintSettings.brushOpacity)) * base; - } - else if (cfg.vertexPaintSettings.brushMode == PaintBlendMode::BlendAdd) - { - blend = brushColorAndOpacity + base; - } - - model->set(ind.parent(), "Vertex Colors", ByteColor4::fromColor4(blend)); - updated = true; - } - } - } - - if (!updated) - { - update(); - } + bool updated = false; + if (mousePaint) + { + // Let's do an absolutely shit job and act like it's decent + auto inds = sampleIndexImageCircle(mousePaintHitDetectImg, point, cfg.vertexPaintSettings.brushSize, model); + Color4 brushColorAndOpacity = cfg.vertexPaintSettings.brushColor * cfg.vertexPaintSettings.brushOpacity; + + // Expand ind to include all vertices in the same mesh with the same location + auto count = inds.size(); + for (int i=0; i < count; ++i) + { + // Get the X, Y, Z location of this vertex + Vector3 indLoc = model->get(inds[i]); + QModelIndex vertexCollection = inds[i].parent().parent(); + + // Get all the children of the vertex container + int vertexCount = model->rowCount(vertexCollection); + for (int j=0; j < vertexCount; ++j) + { + QModelIndex siblingId = vertexCollection.child(j,0); + + // Get the position of each child + Vector3 siblingLocation = model->get(siblingId, "Vertex"); + + // If XYZ matches, add sibling to inds list + if (siblingLocation == indLoc) + { + inds.push_back(model->getIndex(siblingId, "Vertex")); + } + } + } + + for (const auto& ind : inds) + { + if ( ind.isValid() && !mousePaintVerts.contains(ind)) + { + mousePaintVerts.push_back(ind); + Color4 base = model->get(ind.parent(), "Vertex Colors"); + Color4 blend; + + if (cfg.vertexPaintSettings.brushMode == PaintBlendMode::BlendNormal) + { + blend = brushColorAndOpacity + (base * (Color4() - cfg.vertexPaintSettings.brushOpacity)); + } + else if (cfg.vertexPaintSettings.brushMode == PaintBlendMode::BlendMultiply) + { + blend = (brushColorAndOpacity + (Color4() - cfg.vertexPaintSettings.brushOpacity)) * base; + } + else if (cfg.vertexPaintSettings.brushMode == PaintBlendMode::BlendAdd) + { + blend = brushColorAndOpacity + base; + } + + model->set(ind.parent(), "Vertex Colors", ByteColor4::fromColor4(blend)); + updated = true; + } + } + } + + if (!updated) + { + update(); + } } void GLView::endVertexPaint() { - mousePaint = false; - mousePaintVerts.clear(); - QApplication::restoreOverrideCursor(); + mousePaint = false; + mousePaintVerts.clear(); + QApplication::restoreOverrideCursor(); } void GLView::setCenter() { - Node * node = scene->getNode( model, scene->currentBlock ); + Node * node = scene->getNode( model, scene->currentBlock ); - if ( node ) { - // Center on selected node - BoundSphere bs = node->bounds(); + if ( node ) { + // Center on selected node + BoundSphere bs = node->bounds(); - this->setPosition( -bs.center ); + this->setPosition( -bs.center ); - if ( bs.radius > 0 ) { - setDistance( bs.radius * 1.2 ); - } - } else { - // Center on entire mesh - BoundSphere bs = scene->bounds(); + if ( bs.radius > 0 ) { + setDistance( bs.radius * 1.2 ); + } + } else { + // Center on entire mesh + BoundSphere bs = scene->bounds(); - if ( bs.radius < 1 ) - bs.radius = 1024.0; + if ( bs.radius < 1 ) + bs.radius = 1024.0; - setDistance( bs.radius * 1.2 ); - setZoom( 1.0 ); + setDistance( bs.radius * 1.2 ); + setZoom( 1.0 ); - setPosition( -bs.center ); + setPosition( -bs.center ); - setOrientation( view ); - } + setOrientation( view ); + } } void GLView::setDistance( float x ) { - Dist = x; - update(); + Dist = x; + update(); } void GLView::setPosition( float x, float y, float z ) { - Pos = { x, y, z }; - update(); + Pos = { x, y, z }; + update(); } void GLView::setPosition( const Vector3 & v ) { - Pos = v; - update(); + Pos = v; + update(); } void GLView::setProjection( bool isPersp ) { - perspectiveMode = isPersp; - update(); + perspectiveMode = isPersp; + update(); } void GLView::setRotation( float x, float y, float z ) { - Rot = { x, y, z }; - update(); + Rot = { x, y, z }; + update(); } void GLView::setZoom( float z ) { - Zoom = z; - update(); + Zoom = z; + update(); } void GLView::flipOrientation() { - ViewState tmp = ViewDefault; - - switch ( view ) { - case ViewTop: - tmp = ViewBottom; - break; - case ViewBottom: - tmp = ViewTop; - break; - case ViewLeft: - tmp = ViewRight; - break; - case ViewRight: - tmp = ViewLeft; - break; - case ViewFront: - tmp = ViewBack; - break; - case ViewBack: - tmp = ViewFront; - break; - case ViewUser: - default: - { - // TODO: Flip any other view also? - } - break; - } - - setOrientation( tmp, false ); + ViewState tmp = ViewDefault; + + switch ( view ) { + case ViewTop: + tmp = ViewBottom; + break; + case ViewBottom: + tmp = ViewTop; + break; + case ViewLeft: + tmp = ViewRight; + break; + case ViewRight: + tmp = ViewLeft; + break; + case ViewFront: + tmp = ViewBack; + break; + case ViewBack: + tmp = ViewFront; + break; + case ViewUser: + default: + { + // TODO: Flip any other view also? + } + break; + } + + setOrientation( tmp, false ); } void GLView::setOrientation( GLView::ViewState state, bool recenter ) { - if ( state == view ) - return; - - switch ( state ) { - case ViewBottom: - setRotation( 180, 0, 0 ); // Bottom - break; - case ViewTop: - setRotation( 0, 0, 0 ); // Top - break; - case ViewBack: - setRotation( -90, 0, 0 ); // Back - break; - case ViewFront: - setRotation( -90, 0, 180 ); // Front - break; - case ViewRight: - setRotation( -90, 0, 90 ); // Right - break; - case ViewLeft: - setRotation( -90, 0, -90 ); // Left - break; - default: - break; - } - - view = state; - - // Recenter - if ( recenter ) - center(); + if ( state == view ) + return; + + switch ( state ) { + case ViewBottom: + setRotation( 180, 0, 0 ); // Bottom + break; + case ViewTop: + setRotation( 0, 0, 0 ); // Top + break; + case ViewBack: + setRotation( -90, 0, 0 ); // Back + break; + case ViewFront: + setRotation( -90, 0, 180 ); // Front + break; + case ViewRight: + setRotation( -90, 0, 90 ); // Right + break; + case ViewLeft: + setRotation( -90, 0, -90 ); // Left + break; + default: + break; + } + + view = state; + + // Recenter + if ( recenter ) + center(); } void GLView::updateViewpoint() { - switch ( view ) { - case ViewTop: - case ViewBottom: - case ViewLeft: - case ViewRight: - case ViewFront: - case ViewBack: - case ViewUser: - emit viewpointChanged(); - break; - default: - break; - } + switch ( view ) { + case ViewTop: + case ViewBottom: + case ViewLeft: + case ViewRight: + case ViewFront: + case ViewBack: + case ViewUser: + emit viewpointChanged(); + break; + default: + break; + } } void GLView::flush() { - if ( textures ) - textures->flush(); + if ( textures ) + textures->flush(); } @@ -1245,101 +1270,101 @@ void GLView::flush() void GLView::setNif( NifModel * nif ) { - if ( model ) { - // disconnect( model ) may not work with new Qt5 syntax... - // it says the calls need to remain symmetric to the connect() ones. - // Otherwise, use QMetaObject::Connection - disconnect( model ); - } + if ( model ) { + // disconnect( model ) may not work with new Qt5 syntax... + // it says the calls need to remain symmetric to the connect() ones. + // Otherwise, use QMetaObject::Connection + disconnect( model ); + } - model = nif; + model = nif; - if ( model ) { - connect( model, &NifModel::dataChanged, this, &GLView::dataChanged ); - connect( model, &NifModel::linksChanged, this, &GLView::modelLinked ); - connect( model, &NifModel::modelReset, this, &GLView::modelChanged ); - connect( model, &NifModel::destroyed, this, &GLView::modelDestroyed ); - } + if ( model ) { + connect( model, &NifModel::dataChanged, this, &GLView::dataChanged ); + connect( model, &NifModel::linksChanged, this, &GLView::modelLinked ); + connect( model, &NifModel::modelReset, this, &GLView::modelChanged ); + connect( model, &NifModel::destroyed, this, &GLView::modelDestroyed ); + } - doCompile = true; + doCompile = true; } void GLView::setCurrentIndex( const QModelIndex & index ) { - if ( !( model && index.model() == model ) ) - return; + if ( !( model && index.model() == model ) ) + return; - scene->currentBlock = model->getBlock( index ); - scene->currentIndex = index.sibling( index.row(), 0 ); + scene->currentBlock = model->getBlock( index ); + scene->currentIndex = index.sibling( index.row(), 0 ); - update(); + update(); } QModelIndex parent( QModelIndex ix, QModelIndex xi ) { - ix = ix.sibling( ix.row(), 0 ); - xi = xi.sibling( xi.row(), 0 ); + ix = ix.sibling( ix.row(), 0 ); + xi = xi.sibling( xi.row(), 0 ); - while ( ix.isValid() ) { - QModelIndex x = xi; + while ( ix.isValid() ) { + QModelIndex x = xi; - while ( x.isValid() ) { - if ( ix == x ) - return ix; + while ( x.isValid() ) { + if ( ix == x ) + return ix; - x = x.parent(); - } + x = x.parent(); + } - ix = ix.parent(); - } + ix = ix.parent(); + } - return QModelIndex(); + return QModelIndex(); } void GLView::dataChanged( const QModelIndex & idx, const QModelIndex & xdi ) { - if ( doCompile ) - return; + if ( doCompile ) + return; - QModelIndex ix = idx; + QModelIndex ix = idx; - if ( idx == xdi ) { - if ( idx.column() != 0 ) - ix = idx.sibling( idx.row(), 0 ); - } else { - ix = ::parent( idx, xdi ); - } + if ( idx == xdi ) { + if ( idx.column() != 0 ) + ix = idx.sibling( idx.row(), 0 ); + } else { + ix = ::parent( idx, xdi ); + } - if ( ix.isValid() ) { - scene->update( model, idx ); - update(); - } else { - modelChanged(); - } + if ( ix.isValid() ) { + scene->update( model, idx ); + update(); + } else { + modelChanged(); + } } void GLView::modelChanged() { - if ( doCompile ) - return; + if ( doCompile ) + return; - doCompile = true; - //doCenter = true; - update(); + doCompile = true; + //doCenter = true; + update(); } void GLView::modelLinked() { - if ( doCompile ) - return; + if ( doCompile ) + return; - doCompile = true; //scene->update( model, QModelIndex() ); - update(); + doCompile = true; //scene->update( model, QModelIndex() ); + update(); } void GLView::modelDestroyed() { - setNif( nullptr ); + setNif( nullptr ); } @@ -1349,348 +1374,348 @@ void GLView::modelDestroyed() void GLView::setSceneTime( float t ) { - time = t; - update(); - emit sceneTimeChanged( time, scene->timeMin(), scene->timeMax() ); + time = t; + update(); + emit sceneTimeChanged( time, scene->timeMin(), scene->timeMax() ); } void GLView::setSceneSequence( const QString & seqname ) { - // Update UI - QAction * action = qobject_cast(sender()); - if ( !action ) { - // Called from self and not UI - emit sequenceChanged( seqname ); - } + // Update UI + QAction * action = qobject_cast(sender()); + if ( !action ) { + // Called from self and not UI + emit sequenceChanged( seqname ); + } - scene->setSequence( seqname ); - time = scene->timeMin(); - emit sceneTimeChanged( time, scene->timeMin(), scene->timeMax() ); - update(); + scene->setSequence( seqname ); + time = scene->timeMin(); + emit sceneTimeChanged( time, scene->timeMin(), scene->timeMax() ); + update(); } // TODO: Multiple user views, ala Recent Files void GLView::saveUserView() { - QSettings settings; - settings.beginGroup( "GLView" ); - settings.beginGroup( "User View" ); - settings.setValue( "RotX", Rot[0] ); - settings.setValue( "RotY", Rot[1] ); - settings.setValue( "RotZ", Rot[2] ); - settings.setValue( "PosX", Pos[0] ); - settings.setValue( "PosY", Pos[1] ); - settings.setValue( "PosZ", Pos[2] ); - settings.setValue( "Dist", Dist ); - settings.endGroup(); - settings.endGroup(); + QSettings settings; + settings.beginGroup( "GLView" ); + settings.beginGroup( "User View" ); + settings.setValue( "RotX", Rot[0] ); + settings.setValue( "RotY", Rot[1] ); + settings.setValue( "RotZ", Rot[2] ); + settings.setValue( "PosX", Pos[0] ); + settings.setValue( "PosY", Pos[1] ); + settings.setValue( "PosZ", Pos[2] ); + settings.setValue( "Dist", Dist ); + settings.endGroup(); + settings.endGroup(); } void GLView::loadUserView() { - QSettings settings; - settings.beginGroup( "GLView" ); - settings.beginGroup( "User View" ); - setRotation( settings.value( "RotX" ).toDouble(), settings.value( "RotY" ).toDouble(), settings.value( "RotZ" ).toDouble() ); - setPosition( settings.value( "PosX" ).toDouble(), settings.value( "PosY" ).toDouble(), settings.value( "PosZ" ).toDouble() ); - setDistance( settings.value( "Dist" ).toDouble() ); - settings.endGroup(); - settings.endGroup(); + QSettings settings; + settings.beginGroup( "GLView" ); + settings.beginGroup( "User View" ); + setRotation( settings.value( "RotX" ).toDouble(), settings.value( "RotY" ).toDouble(), settings.value( "RotZ" ).toDouble() ); + setPosition( settings.value( "PosX" ).toDouble(), settings.value( "PosY" ).toDouble(), settings.value( "PosZ" ).toDouble() ); + setDistance( settings.value( "Dist" ).toDouble() ); + settings.endGroup(); + settings.endGroup(); } void GLView::advanceGears() { - QTime t = QTime::currentTime(); - float dT = lastTime.msecsTo( t ) / 1000.0; - dT = (dT < 0) ? 0 : ((dT > 1.0) ? 1.0 : dT); - - lastTime = t; - - if ( !isVisible() ) - return; - - if ( ( animState & AnimEnabled ) && ( animState & AnimPlay ) - && scene->timeMin() != scene->timeMax() ) - { - time += dT; - - if ( time > scene->timeMax() ) { - if ( ( animState & AnimSwitch ) && !scene->animGroups.isEmpty() ) { - int ix = scene->animGroups.indexOf( scene->animGroup ); - - if ( ++ix >= scene->animGroups.count() ) - ix -= scene->animGroups.count(); - - setSceneSequence( scene->animGroups.value( ix ) ); - } else if ( animState & AnimLoop ) { - time = scene->timeMin(); - } else { - // Animation has completed and is not looping - // or cycling through animations. - // Reset time and state and then inform UI it has stopped. - time = scene->timeMin(); - animState &= ~AnimPlay; - emit sequenceStopped(); - } - } else { - // Animation is not done yet - } - - emit sceneTimeChanged( time, scene->timeMin(), scene->timeMax() ); - update(); - } - - // TODO: Some kind of input class for choosing the appropriate - // keys based on user preferences of what app they would like to - // emulate for the control scheme - // Rotation - if ( kbd[ Qt::Key_Up ] ) rotate( -cfg.rotSpd * dT, 0, 0 ); - if ( kbd[ Qt::Key_Down ] ) rotate( +cfg.rotSpd * dT, 0, 0 ); - if ( kbd[ Qt::Key_Left ] ) rotate( 0, 0, -cfg.rotSpd * dT ); - if ( kbd[ Qt::Key_Right ] ) rotate( 0, 0, +cfg.rotSpd * dT ); - - // Movement - if ( kbd[ Qt::Key_A ] ) move( +cfg.moveSpd * dT, 0, 0 ); - if ( kbd[ Qt::Key_D ] ) move( -cfg.moveSpd * dT, 0, 0 ); - if ( kbd[ Qt::Key_W ] ) move( 0, 0, +cfg.moveSpd * dT ); - if ( kbd[ Qt::Key_S ] ) move( 0, 0, -cfg.moveSpd * dT ); - //if ( kbd[ Qt::Key_F ] ) move( 0, +MOV_SPD * dT, 0 ); - //if ( kbd[ Qt::Key_R ] ) move( 0, -MOV_SPD * dT, 0 ); - - // Zoom - if ( kbd[ Qt::Key_Q ] ) setDistance( Dist * 1.0 / 1.1 ); - if ( kbd[ Qt::Key_E ] ) setDistance( Dist * 1.1 ); - - // Focal Length - if ( kbd[ Qt::Key_PageUp ] ) zoom( 1.1f ); - if ( kbd[ Qt::Key_PageDown ] ) zoom( 1 / 1.1f ); - - if ( mouseMov[0] != 0 || mouseMov[1] != 0 || mouseMov[2] != 0 ) { - move( mouseMov[0], mouseMov[1], mouseMov[2] ); - mouseMov = Vector3(); - } - - if ( mouseRot[0] != 0 || mouseRot[1] != 0 || mouseRot[2] != 0 ) { - rotate( mouseRot[0], mouseRot[1], mouseRot[2] ); - mouseRot = Vector3(); - } + QTime t = QTime::currentTime(); + float dT = lastTime.msecsTo( t ) / 1000.0; + dT = (dT < 0) ? 0 : ((dT > 1.0) ? 1.0 : dT); + + lastTime = t; + + if ( !isVisible() ) + return; + + if ( ( animState & AnimEnabled ) && ( animState & AnimPlay ) + && scene->timeMin() != scene->timeMax() ) + { + time += dT; + + if ( time > scene->timeMax() ) { + if ( ( animState & AnimSwitch ) && !scene->animGroups.isEmpty() ) { + int ix = scene->animGroups.indexOf( scene->animGroup ); + + if ( ++ix >= scene->animGroups.count() ) + ix -= scene->animGroups.count(); + + setSceneSequence( scene->animGroups.value( ix ) ); + } else if ( animState & AnimLoop ) { + time = scene->timeMin(); + } else { + // Animation has completed and is not looping + // or cycling through animations. + // Reset time and state and then inform UI it has stopped. + time = scene->timeMin(); + animState &= ~AnimPlay; + emit sequenceStopped(); + } + } else { + // Animation is not done yet + } + + emit sceneTimeChanged( time, scene->timeMin(), scene->timeMax() ); + update(); + } + + // TODO: Some kind of input class for choosing the appropriate + // keys based on user preferences of what app they would like to + // emulate for the control scheme + // Rotation + if ( kbd[ Qt::Key_Up ] ) rotate( -cfg.rotSpd * dT, 0, 0 ); + if ( kbd[ Qt::Key_Down ] ) rotate( +cfg.rotSpd * dT, 0, 0 ); + if ( kbd[ Qt::Key_Left ] ) rotate( 0, 0, -cfg.rotSpd * dT ); + if ( kbd[ Qt::Key_Right ] ) rotate( 0, 0, +cfg.rotSpd * dT ); + + // Movement + if ( kbd[ Qt::Key_A ] ) move( +cfg.moveSpd * dT, 0, 0 ); + if ( kbd[ Qt::Key_D ] ) move( -cfg.moveSpd * dT, 0, 0 ); + if ( kbd[ Qt::Key_W ] ) move( 0, 0, +cfg.moveSpd * dT ); + if ( kbd[ Qt::Key_S ] ) move( 0, 0, -cfg.moveSpd * dT ); + //if ( kbd[ Qt::Key_F ] ) move( 0, +MOV_SPD * dT, 0 ); + //if ( kbd[ Qt::Key_R ] ) move( 0, -MOV_SPD * dT, 0 ); + + // Zoom + if ( kbd[ Qt::Key_Q ] ) setDistance( Dist * 1.0 / 1.1 ); + if ( kbd[ Qt::Key_E ] ) setDistance( Dist * 1.1 ); + + // Focal Length + if ( kbd[ Qt::Key_PageUp ] ) zoom( 1.1f ); + if ( kbd[ Qt::Key_PageDown ] ) zoom( 1 / 1.1f ); + + if ( mouseMov[0] != 0 || mouseMov[1] != 0 || mouseMov[2] != 0 ) { + move( mouseMov[0], mouseMov[1], mouseMov[2] ); + mouseMov = Vector3(); + } + + if ( mouseRot[0] != 0 || mouseRot[1] != 0 || mouseRot[2] != 0 ) { + rotate( mouseRot[0], mouseRot[1], mouseRot[2] ); + mouseRot = Vector3(); + } } // TODO: Separate widget void GLView::saveImage() { - auto dlg = new QDialog( qApp->activeWindow() ); - QGridLayout * lay = new QGridLayout( dlg ); - dlg->setWindowTitle( tr( "Save View" ) ); - dlg->setLayout( lay ); - dlg->setMinimumWidth( 400 ); - - QString date = QDateTime::currentDateTime().toString( "yyyyMMdd_HH-mm-ss" ); - QString name = model->getFilename(); - - QString nifFolder = model->getFolder(); - // TODO: Default extension in Settings - QString filename = name + (!name.isEmpty() ? "_" : "") + date + ".jpg"; - - // Default: NifSkope directory - // TODO: User-configurable default screenshot path in Options - QString nifskopePath = "screenshots/" + filename; - // Absolute: NIF directory - QString nifPath = nifFolder + (!nifFolder.isEmpty() ? "/" : "") + filename; - - FileSelector * file = new FileSelector( FileSelector::SaveFile, tr( "File" ), QBoxLayout::LeftToRight ); - file->setParent( dlg ); - // TODO: Default extension in Settings - file->setFilter( { "Images (*.jpg *.png *.webp *.bmp)", "JPEG (*.jpg)", "PNG (*.png)", "WebP (*.webp)", "BMP (*.bmp)" } ); - file->setFile( nifskopePath ); - lay->addWidget( file, 0, 0, 1, -1 ); - - auto grpDir = new QButtonGroup( dlg ); - - QRadioButton * nifskopeDir = new QRadioButton( tr( "NifSkope Directory" ), dlg ); - nifskopeDir->setChecked( true ); - nifskopeDir->setToolTip( tr( "Save to NifSkope screenshots directory" ) ); - - QRadioButton * niffileDir = new QRadioButton( tr( "NIF Directory" ), dlg ); - niffileDir->setChecked( false ); - niffileDir->setDisabled( nifFolder.isEmpty() ); - niffileDir->setToolTip( tr( "Save to NIF file directory" ) ); - - grpDir->addButton( nifskopeDir ); - grpDir->addButton( niffileDir ); - grpDir->setExclusive( true ); - - lay->addWidget( nifskopeDir, 1, 0, 1, 1 ); - lay->addWidget( niffileDir, 1, 1, 1, 1 ); - - // Save JPEG Quality - QSettings settings; - int jpegQuality = settings.value( "JPEG/Quality", 90 ).toInt(); - settings.setValue( "JPEG/Quality", jpegQuality ); - - QHBoxLayout * pixBox = new QHBoxLayout; - pixBox->setAlignment( Qt::AlignRight ); - QSpinBox * pixQuality = new QSpinBox( dlg ); - pixQuality->setRange( -1, 100 ); - pixQuality->setSingleStep( 10 ); - pixQuality->setValue( jpegQuality ); - pixQuality->setSpecialValueText( tr( "Auto" ) ); - pixQuality->setMaximumWidth( pixQuality->minimumSizeHint().width() ); - pixBox->addWidget( new QLabel( tr( "JPEG Quality" ), dlg ) ); - pixBox->addWidget( pixQuality ); - lay->addLayout( pixBox, 1, 2, Qt::AlignRight ); - - - // Image Size radio button lambda - auto btnSize = [dlg]( const QString & name ) { - auto btn = new QRadioButton( name, dlg ); - btn->setCheckable( true ); - - return btn; - }; - - // Get max viewport size for platform - GLint dims; - glGetIntegerv( GL_MAX_VIEWPORT_DIMS, &dims ); - int maxSize = dims; - - // Default size - auto btnOneX = btnSize( "1x" ); - btnOneX->setChecked( true ); - // Disable any of these that would exceed the max viewport size of the platform - auto btnTwoX = btnSize( "2x" ); - btnTwoX->setDisabled( (width() * 2) > maxSize || (height() * 2) > maxSize ); - auto btnFourX = btnSize( "4x" ); - btnFourX->setDisabled( (width() * 4) > maxSize || (height() * 4) > maxSize ); - auto btnEightX = btnSize( "8x" ); - btnEightX->setDisabled( (width() * 8) > maxSize || (height() * 8) > maxSize ); - - - auto grpBox = new QGroupBox( tr( "Image Size" ), dlg ); - auto grpBoxLayout = new QHBoxLayout; - grpBoxLayout->addWidget( btnOneX ); - grpBoxLayout->addWidget( btnTwoX ); - grpBoxLayout->addWidget( btnFourX ); - grpBoxLayout->addWidget( btnEightX ); - grpBoxLayout->addWidget( new QLabel( "Caution:
4x and 8x may be memory intensive.", dlg ) ); - grpBoxLayout->addStretch( 1 ); - grpBox->setLayout( grpBoxLayout ); - - auto grpSize = new QButtonGroup( dlg ); - grpSize->addButton( btnOneX, 1 ); - grpSize->addButton( btnTwoX, 2 ); - grpSize->addButton( btnFourX, 4 ); - grpSize->addButton( btnEightX, 8 ); - - grpSize->setExclusive( true ); - - lay->addWidget( grpBox, 2, 0, 1, -1 ); - - - QHBoxLayout * hBox = new QHBoxLayout; - QPushButton * btnOk = new QPushButton( tr( "Save" ), dlg ); - QPushButton * btnCancel = new QPushButton( tr( "Cancel" ), dlg ); - hBox->addWidget( btnOk ); - hBox->addWidget( btnCancel ); - lay->addLayout( hBox, 3, 0, 1, -1 ); - - // Set FileSelector to NifSkope dir (relative) - connect( nifskopeDir, &QRadioButton::clicked, [=]() - { - file->setText( nifskopePath ); - file->setFile( nifskopePath ); - } - ); - // Set FileSelector to NIF File dir (absolute) - connect( niffileDir, &QRadioButton::clicked, [=]() - { - file->setText( nifPath ); - file->setFile( nifPath ); - } - ); - - // Validate on OK - connect( btnOk, &QPushButton::clicked, [&]() - { - // Save JPEG Quality - QSettings settings; - settings.setValue( "JPEG/Quality", pixQuality->value() ); - - // TODO: Set up creation of screenshots directory in Options - if ( nifskopeDir->isChecked() ) { - QDir workingDir; - workingDir.mkpath( "screenshots" ); - } - - // Supersampling - int ss = grpSize->checkedId(); - - int w, h; - - w = width(); - h = height(); - - // Resize viewport for supersampling - if ( ss > 1 ) { - w *= ss; - h *= ss; - - resizeGL( w, h ); - } - - QOpenGLFramebufferObjectFormat fboFmt; - fboFmt.setTextureTarget( GL_TEXTURE_2D ); - fboFmt.setInternalTextureFormat( GL_RGB ); - fboFmt.setMipmap( false ); - fboFmt.setAttachment( QOpenGLFramebufferObject::Attachment::Depth ); - fboFmt.setSamples( 16 / ss ); - - QOpenGLFramebufferObject fbo( w, h, fboFmt ); - fbo.bind(); - - update(); - updateGL(); - - fbo.release(); - - QImage * img = new QImage(fbo.toImage()); - - // Return viewport to original size - if ( ss > 1 ) - resizeGL( width(), height() ); - - - QImageWriter writer( file->file() ); - - // Set Compression for formats that can use it - writer.setCompression( 1 ); - - // Handle JPEG/WebP Quality exclusively - // PNG will not use compression if Quality is set - if ( file->file().endsWith( ".jpg", Qt::CaseInsensitive ) ) { - writer.setFormat( "jpg" ); - writer.setQuality( 50 + pixQuality->value() / 2 ); - } else if ( file->file().endsWith( ".webp", Qt::CaseInsensitive ) ) { - writer.setFormat( "webp" ); - writer.setQuality( 75 + pixQuality->value() / 4 ); - } + auto dlg = new QDialog( qApp->activeWindow() ); + QGridLayout * lay = new QGridLayout( dlg ); + dlg->setWindowTitle( tr( "Save View" ) ); + dlg->setLayout( lay ); + dlg->setMinimumWidth( 400 ); + + QString date = QDateTime::currentDateTime().toString( "yyyyMMdd_HH-mm-ss" ); + QString name = model->getFilename(); + + QString nifFolder = model->getFolder(); + // TODO: Default extension in Settings + QString filename = name + (!name.isEmpty() ? "_" : "") + date + ".jpg"; + + // Default: NifSkope directory + // TODO: User-configurable default screenshot path in Options + QString nifskopePath = "screenshots/" + filename; + // Absolute: NIF directory + QString nifPath = nifFolder + (!nifFolder.isEmpty() ? "/" : "") + filename; + + FileSelector * file = new FileSelector( FileSelector::SaveFile, tr( "File" ), QBoxLayout::LeftToRight ); + file->setParent( dlg ); + // TODO: Default extension in Settings + file->setFilter( { "Images (*.jpg *.png *.webp *.bmp)", "JPEG (*.jpg)", "PNG (*.png)", "WebP (*.webp)", "BMP (*.bmp)" } ); + file->setFile( nifskopePath ); + lay->addWidget( file, 0, 0, 1, -1 ); + + auto grpDir = new QButtonGroup( dlg ); + + QRadioButton * nifskopeDir = new QRadioButton( tr( "NifSkope Directory" ), dlg ); + nifskopeDir->setChecked( true ); + nifskopeDir->setToolTip( tr( "Save to NifSkope screenshots directory" ) ); + + QRadioButton * niffileDir = new QRadioButton( tr( "NIF Directory" ), dlg ); + niffileDir->setChecked( false ); + niffileDir->setDisabled( nifFolder.isEmpty() ); + niffileDir->setToolTip( tr( "Save to NIF file directory" ) ); + + grpDir->addButton( nifskopeDir ); + grpDir->addButton( niffileDir ); + grpDir->setExclusive( true ); + + lay->addWidget( nifskopeDir, 1, 0, 1, 1 ); + lay->addWidget( niffileDir, 1, 1, 1, 1 ); + + // Save JPEG Quality + QSettings settings; + int jpegQuality = settings.value( "JPEG/Quality", 90 ).toInt(); + settings.setValue( "JPEG/Quality", jpegQuality ); + + QHBoxLayout * pixBox = new QHBoxLayout; + pixBox->setAlignment( Qt::AlignRight ); + QSpinBox * pixQuality = new QSpinBox( dlg ); + pixQuality->setRange( -1, 100 ); + pixQuality->setSingleStep( 10 ); + pixQuality->setValue( jpegQuality ); + pixQuality->setSpecialValueText( tr( "Auto" ) ); + pixQuality->setMaximumWidth( pixQuality->minimumSizeHint().width() ); + pixBox->addWidget( new QLabel( tr( "JPEG Quality" ), dlg ) ); + pixBox->addWidget( pixQuality ); + lay->addLayout( pixBox, 1, 2, Qt::AlignRight ); + + + // Image Size radio button lambda + auto btnSize = [dlg]( const QString & name ) { + auto btn = new QRadioButton( name, dlg ); + btn->setCheckable( true ); + + return btn; + }; + + // Get max viewport size for platform + GLint dims; + glGetIntegerv( GL_MAX_VIEWPORT_DIMS, &dims ); + int maxSize = dims; + + // Default size + auto btnOneX = btnSize( "1x" ); + btnOneX->setChecked( true ); + // Disable any of these that would exceed the max viewport size of the platform + auto btnTwoX = btnSize( "2x" ); + btnTwoX->setDisabled( (width() * 2) > maxSize || (height() * 2) > maxSize ); + auto btnFourX = btnSize( "4x" ); + btnFourX->setDisabled( (width() * 4) > maxSize || (height() * 4) > maxSize ); + auto btnEightX = btnSize( "8x" ); + btnEightX->setDisabled( (width() * 8) > maxSize || (height() * 8) > maxSize ); + + + auto grpBox = new QGroupBox( tr( "Image Size" ), dlg ); + auto grpBoxLayout = new QHBoxLayout; + grpBoxLayout->addWidget( btnOneX ); + grpBoxLayout->addWidget( btnTwoX ); + grpBoxLayout->addWidget( btnFourX ); + grpBoxLayout->addWidget( btnEightX ); + grpBoxLayout->addWidget( new QLabel( "Caution:
4x and 8x may be memory intensive.", dlg ) ); + grpBoxLayout->addStretch( 1 ); + grpBox->setLayout( grpBoxLayout ); + + auto grpSize = new QButtonGroup( dlg ); + grpSize->addButton( btnOneX, 1 ); + grpSize->addButton( btnTwoX, 2 ); + grpSize->addButton( btnFourX, 4 ); + grpSize->addButton( btnEightX, 8 ); + + grpSize->setExclusive( true ); + + lay->addWidget( grpBox, 2, 0, 1, -1 ); + + + QHBoxLayout * hBox = new QHBoxLayout; + QPushButton * btnOk = new QPushButton( tr( "Save" ), dlg ); + QPushButton * btnCancel = new QPushButton( tr( "Cancel" ), dlg ); + hBox->addWidget( btnOk ); + hBox->addWidget( btnCancel ); + lay->addLayout( hBox, 3, 0, 1, -1 ); + + // Set FileSelector to NifSkope dir (relative) + connect( nifskopeDir, &QRadioButton::clicked, [=]() + { + file->setText( nifskopePath ); + file->setFile( nifskopePath ); + } + ); + // Set FileSelector to NIF File dir (absolute) + connect( niffileDir, &QRadioButton::clicked, [=]() + { + file->setText( nifPath ); + file->setFile( nifPath ); + } + ); + + // Validate on OK + connect( btnOk, &QPushButton::clicked, [&]() + { + // Save JPEG Quality + QSettings settings; + settings.setValue( "JPEG/Quality", pixQuality->value() ); + + // TODO: Set up creation of screenshots directory in Options + if ( nifskopeDir->isChecked() ) { + QDir workingDir; + workingDir.mkpath( "screenshots" ); + } + + // Supersampling + int ss = grpSize->checkedId(); + + int w, h; + + w = width(); + h = height(); + + // Resize viewport for supersampling + if ( ss > 1 ) { + w *= ss; + h *= ss; + + resizeGL( w, h ); + } + + QOpenGLFramebufferObjectFormat fboFmt; + fboFmt.setTextureTarget( GL_TEXTURE_2D ); + fboFmt.setInternalTextureFormat( GL_RGB ); + fboFmt.setMipmap( false ); + fboFmt.setAttachment( QOpenGLFramebufferObject::Attachment::Depth ); + fboFmt.setSamples( 16 / ss ); + + QOpenGLFramebufferObject fbo( w, h, fboFmt ); + fbo.bind(); + + update(); + updateGL(); + + fbo.release(); + + QImage * img = new QImage(fbo.toImage()); + + // Return viewport to original size + if ( ss > 1 ) + resizeGL( width(), height() ); + + + QImageWriter writer( file->file() ); + + // Set Compression for formats that can use it + writer.setCompression( 1 ); + + // Handle JPEG/WebP Quality exclusively + // PNG will not use compression if Quality is set + if ( file->file().endsWith( ".jpg", Qt::CaseInsensitive ) ) { + writer.setFormat( "jpg" ); + writer.setQuality( 50 + pixQuality->value() / 2 ); + } else if ( file->file().endsWith( ".webp", Qt::CaseInsensitive ) ) { + writer.setFormat( "webp" ); + writer.setQuality( 75 + pixQuality->value() / 4 ); + } - if ( writer.write( *img ) ) { - dlg->accept(); - } else { - Message::critical( this, tr( "Could not save %1" ).arg( file->file() ) ); - } + if ( writer.write( *img ) ) { + dlg->accept(); + } else { + Message::critical( this, tr( "Could not save %1" ).arg( file->file() ) ); + } - delete img; - img = nullptr; - } - ); - connect( btnCancel, &QPushButton::clicked, dlg, &QDialog::reject ); - - if ( dlg->exec() != QDialog::Accepted ) { - return; - } + delete img; + img = nullptr; + } + ); + connect( btnCancel, &QPushButton::clicked, dlg, &QDialog::reject ); + + if ( dlg->exec() != QDialog::Accepted ) { + return; + } } @@ -1700,273 +1725,273 @@ void GLView::saveImage() void GLView::dragEnterEvent( QDragEnterEvent * e ) { - auto md = e->mimeData(); - if ( md && md->hasUrls() && md->urls().count() == 1 ) { - QUrl url = md->urls().first(); + auto md = e->mimeData(); + if ( md && md->hasUrls() && md->urls().count() == 1 ) { + QUrl url = md->urls().first(); - if ( url.scheme() == "file" ) { - QString fn = url.toLocalFile(); + if ( url.scheme() == "file" ) { + QString fn = url.toLocalFile(); - if ( textures->canLoad( fn ) ) { - fnDragTex = textures->stripPath( fn, model->getFolder() ); - e->accept(); - return; - } - } - } + if ( textures->canLoad( fn ) ) { + fnDragTex = textures->stripPath( fn, model->getFolder() ); + e->accept(); + return; + } + } + } - e->ignore(); + e->ignore(); } void GLView::dragLeaveEvent( QDragLeaveEvent * e ) { - Q_UNUSED( e ); + Q_UNUSED( e ); - if ( iDragTarget.isValid() ) { - model->set( iDragTarget, fnDragTexOrg ); - iDragTarget = QModelIndex(); - fnDragTex = fnDragTexOrg = QString(); - } + if ( iDragTarget.isValid() ) { + model->set( iDragTarget, fnDragTexOrg ); + iDragTarget = QModelIndex(); + fnDragTex = fnDragTexOrg = QString(); + } } void GLView::dragMoveEvent( QDragMoveEvent * e ) { - if ( iDragTarget.isValid() ) { - model->set( iDragTarget, fnDragTexOrg ); - iDragTarget = QModelIndex(); - fnDragTexOrg = QString(); - } + if ( iDragTarget.isValid() ) { + model->set( iDragTarget, fnDragTexOrg ); + iDragTarget = QModelIndex(); + fnDragTexOrg = QString(); + } - QModelIndex iObj = model->getBlock( indexAt( e->pos() ), "NiAVObject" ); + QModelIndex iObj = model->getBlock( indexAt( e->pos() ), "NiAVObject" ); - if ( iObj.isValid() ) { - for ( const auto l : model->getChildLinks( model->getBlockNumber( iObj ) ) ) { - QModelIndex iTxt = model->getBlock( l, "NiTexturingProperty" ); + if ( iObj.isValid() ) { + for ( const auto l : model->getChildLinks( model->getBlockNumber( iObj ) ) ) { + QModelIndex iTxt = model->getBlock( l, "NiTexturingProperty" ); - if ( iTxt.isValid() ) { - QModelIndex iSrc = model->getBlock( model->getLink( iTxt, "Base Texture/Source" ), "NiSourceTexture" ); + if ( iTxt.isValid() ) { + QModelIndex iSrc = model->getBlock( model->getLink( iTxt, "Base Texture/Source" ), "NiSourceTexture" ); - if ( iSrc.isValid() ) { - iDragTarget = model->getIndex( iSrc, "File Name" ); + if ( iSrc.isValid() ) { + iDragTarget = model->getIndex( iSrc, "File Name" ); - if ( iDragTarget.isValid() ) { - fnDragTexOrg = model->get( iDragTarget ); - model->set( iDragTarget, fnDragTex ); - e->accept(); - return; - } - } - } - } - } + if ( iDragTarget.isValid() ) { + fnDragTexOrg = model->get( iDragTarget ); + model->set( iDragTarget, fnDragTex ); + e->accept(); + return; + } + } + } + } + } - e->ignore(); + e->ignore(); } void GLView::dropEvent( QDropEvent * e ) { - iDragTarget = QModelIndex(); - fnDragTex = fnDragTexOrg = QString(); - e->accept(); + iDragTarget = QModelIndex(); + fnDragTex = fnDragTexOrg = QString(); + e->accept(); } void GLView::focusOutEvent( QFocusEvent * ) { - kbd.clear(); + kbd.clear(); } void GLView::keyPressEvent( QKeyEvent * event ) { - switch ( event->key() ) { - case Qt::Key_Up: - case Qt::Key_Down: - case Qt::Key_Left: - case Qt::Key_Right: - case Qt::Key_PageUp: - case Qt::Key_PageDown: - case Qt::Key_A: - case Qt::Key_D: - case Qt::Key_W: - case Qt::Key_S: - //case Qt::Key_R: - //case Qt::Key_F: - case Qt::Key_Q: - case Qt::Key_E: - case Qt::Key_Space: - kbd[event->key()] = true; - break; - case Qt::Key_Escape: - doCompile = true; - - if ( view == ViewWalk ) - doCenter = true; - - update(); - break; - default: - event->ignore(); - break; - } + switch ( event->key() ) { + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_PageUp: + case Qt::Key_PageDown: + case Qt::Key_A: + case Qt::Key_D: + case Qt::Key_W: + case Qt::Key_S: + //case Qt::Key_R: + //case Qt::Key_F: + case Qt::Key_Q: + case Qt::Key_E: + case Qt::Key_Space: + kbd[event->key()] = true; + break; + case Qt::Key_Escape: + doCompile = true; + + if ( view == ViewWalk ) + doCenter = true; + + update(); + break; + default: + event->ignore(); + break; + } } void GLView::keyReleaseEvent( QKeyEvent * event ) { - switch ( event->key() ) { - case Qt::Key_Up: - case Qt::Key_Down: - case Qt::Key_Left: - case Qt::Key_Right: - case Qt::Key_PageUp: - case Qt::Key_PageDown: - case Qt::Key_A: - case Qt::Key_D: - case Qt::Key_W: - case Qt::Key_S: - //case Qt::Key_R: - //case Qt::Key_F: - case Qt::Key_Q: - case Qt::Key_E: - case Qt::Key_Space: - kbd[event->key()] = false; - break; - default: - event->ignore(); - break; - } + switch ( event->key() ) { + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_PageUp: + case Qt::Key_PageDown: + case Qt::Key_A: + case Qt::Key_D: + case Qt::Key_W: + case Qt::Key_S: + //case Qt::Key_R: + //case Qt::Key_F: + case Qt::Key_Q: + case Qt::Key_E: + case Qt::Key_Space: + kbd[event->key()] = false; + break; + default: + event->ignore(); + break; + } } void GLView::mouseDoubleClickEvent( QMouseEvent * ) { - /* - doCompile = true; - if ( ! aViewWalk->isChecked() ) - doCenter = true; - update(); - */ + /* + doCompile = true; + if ( ! aViewWalk->isChecked() ) + doCenter = true; + update(); + */ } void GLView::mouseMoveEvent( QMouseEvent * event ) { - int dx = event->x() - lastPos.x(); - int dy = event->y() - lastPos.y(); + int dx = event->x() - lastPos.x(); + int dy = event->y() - lastPos.y(); - if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { - vertexPaint(event->pos()); - } else if ( event->buttons() & Qt::LeftButton && !kbd[Qt::Key_Space] ) { - mouseRot += Vector3( dy * .5, 0, dx * .5 ); - } else if ( (event->buttons() & Qt::MidButton) || (event->buttons() & Qt::LeftButton && kbd[Qt::Key_Space]) ) { - float d = axis / (qMax( width(), height() ) + 1); - mouseMov += Vector3( dx * d, -dy * d, 0 ); - } else if ( event->buttons() & Qt::RightButton ) { - setDistance( Dist - (dx + dy) * (axis / (qMax( width(), height() ) + 1)) ); - } + if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { + vertexPaint(event->pos()); + } else if ( event->buttons() & Qt::LeftButton && !kbd[Qt::Key_Space] ) { + mouseRot += Vector3( dy * .5, 0, dx * .5 ); + } else if ( (event->buttons() & Qt::MidButton) || (event->buttons() & Qt::LeftButton && kbd[Qt::Key_Space]) ) { + float d = axis / (qMax( width(), height() ) + 1); + mouseMov += Vector3( dx * d, -dy * d, 0 ); + } else if ( event->buttons() & Qt::RightButton ) { + setDistance( Dist - (dx + dy) * (axis / (qMax( width(), height() ) + 1)) ); + } - lastPos = event->pos(); + lastPos = event->pos(); } void GLView::mousePressEvent( QMouseEvent * event ) { - if ( event->button() == Qt::ForwardButton || event->button() == Qt::BackButton ) { - event->ignore(); - return; - } + if ( event->button() == Qt::ForwardButton || event->button() == Qt::BackButton ) { + event->ignore(); + return; + } - if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { - startVertexPaint(event->pos()); - } else { - if ( (pressPos - event->pos()).manhattanLength() <= 3 ) - cycleSelect++; - else - cycleSelect = 0; - } + if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { + startVertexPaint(event->pos()); + } else { + if ( (pressPos - event->pos()).manhattanLength() <= 3 ) + cycleSelect++; + else + cycleSelect = 0; + } - lastPos = event->pos(); - pressPos = event->pos(); + lastPos = event->pos(); + pressPos = event->pos(); } void GLView::mouseReleaseEvent( QMouseEvent * event ) { - if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { - endVertexPaint(); - return; - } + if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) { + endVertexPaint(); + return; + } - if ( !(model && (pressPos - event->pos()).manhattanLength() <= 3) ) - return; + if ( !(model && (pressPos - event->pos()).manhattanLength() <= 3) ) + return; - if ( event->button() == Qt::ForwardButton || event->button() == Qt::BackButton || event->button() == Qt::MiddleButton ) { - event->ignore(); - return; - } + if ( event->button() == Qt::ForwardButton || event->button() == Qt::BackButton || event->button() == Qt::MiddleButton ) { + event->ignore(); + return; + } - auto mods = event->modifiers(); + auto mods = event->modifiers(); - if ( !(mods & Qt::AltModifier) ) { - QModelIndex idx = indexAt( event->pos(), cycleSelect ); - scene->currentBlock = model->getBlock( idx ); - scene->currentIndex = idx.sibling( idx.row(), 0 ); + if ( !(mods & Qt::AltModifier) ) { + QModelIndex idx = indexAt( event->pos(), cycleSelect ); + scene->currentBlock = model->getBlock( idx ); + scene->currentIndex = idx.sibling( idx.row(), 0 ); - if ( idx.isValid() ) { - emit clicked( QModelIndex() ); // HACK: To get Block Details to update - emit clicked( idx ); - } + if ( idx.isValid() ) { + emit clicked( QModelIndex() ); // HACK: To get Block Details to update + emit clicked( idx ); + } - } else { - // Color Picker / Eyedrop tool - QOpenGLFramebufferObjectFormat fboFmt; - fboFmt.setTextureTarget( GL_TEXTURE_2D ); - fboFmt.setInternalTextureFormat( GL_RGB ); - fboFmt.setMipmap( false ); - fboFmt.setAttachment( QOpenGLFramebufferObject::Attachment::Depth ); + } else { + // Color Picker / Eyedrop tool + QOpenGLFramebufferObjectFormat fboFmt; + fboFmt.setTextureTarget( GL_TEXTURE_2D ); + fboFmt.setInternalTextureFormat( GL_RGB ); + fboFmt.setMipmap( false ); + fboFmt.setAttachment( QOpenGLFramebufferObject::Attachment::Depth ); - QOpenGLFramebufferObject fbo( width(), height(), fboFmt ); - fbo.bind(); + QOpenGLFramebufferObject fbo( width(), height(), fboFmt ); + fbo.bind(); - update(); - updateGL(); + update(); + updateGL(); - fbo.release(); + fbo.release(); - QImage * img = new QImage( fbo.toImage() ); + QImage * img = new QImage( fbo.toImage() ); - auto what = img->pixel( event->pos() ); + auto what = img->pixel( event->pos() ); - qglClearColor( QColor( what ) ); - // qDebug() << QColor( what ); + qglClearColor( QColor( what ) ); + // qDebug() << QColor( what ); - delete img; - } + delete img; + } - update(); + update(); } void GLView::wheelEvent( QWheelEvent * event ) { - if ( view == ViewWalk ) - mouseMov += Vector3( 0, 0, event->delta() ); - else - setDistance( Dist * (event->delta() < 0 ? 1.0 / 0.8 : 0.8) ); + if ( view == ViewWalk ) + mouseMov += Vector3( 0, 0, event->delta() ); + else + setDistance( Dist * (event->delta() < 0 ? 1.0 / 0.8 : 0.8) ); } void GLGraphicsView::setupViewport( QWidget * viewport ) { - GLView * glWidget = qobject_cast(viewport); - if ( glWidget ) { - //glWidget->installEventFilter( this ); - } + GLView * glWidget = qobject_cast(viewport); + if ( glWidget ) { + //glWidget->installEventFilter( this ); + } - QGraphicsView::setupViewport( viewport ); + QGraphicsView::setupViewport( viewport ); } bool GLGraphicsView::eventFilter( QObject * o, QEvent * e ) { - //GLView * glWidget = qobject_cast(o); - //if ( glWidget ) { - // - //} + //GLView * glWidget = qobject_cast(o); + //if ( glWidget ) { + // + //} - return QGraphicsView::eventFilter( o, e ); + return QGraphicsView::eventFilter( o, e ); } //void GLGraphicsView::paintEvent( QPaintEvent * e ) @@ -1981,147 +2006,147 @@ bool GLGraphicsView::eventFilter( QObject * o, QEvent * e ) void GLGraphicsView::drawForeground( QPainter * painter, const QRectF & rect ) { - QGraphicsView::drawForeground( painter, rect ); + QGraphicsView::drawForeground( painter, rect ); } void GLGraphicsView::drawBackground( QPainter * painter, const QRectF & rect ) { - Q_UNUSED( painter ); Q_UNUSED( rect ); + Q_UNUSED( painter ); Q_UNUSED( rect ); - GLView * glWidget = qobject_cast(viewport()); - if ( glWidget ) { - glWidget->updateGL(); - } + GLView * glWidget = qobject_cast(viewport()); + if ( glWidget ) { + glWidget->updateGL(); + } - //QGraphicsView::drawBackground( painter, rect ); + //QGraphicsView::drawBackground( painter, rect ); } void GLGraphicsView::dragEnterEvent( QDragEnterEvent * e ) { - // Intercept NIF files - if ( e->mimeData()->hasUrls() ) { - QList urls = e->mimeData()->urls(); - for ( auto url : urls ) { - if ( url.scheme() == "file" ) { - QString fn = url.toLocalFile(); - QFileInfo finfo( fn ); - if ( finfo.exists() && NifSkope::fileExtensions().contains( finfo.suffix(), Qt::CaseInsensitive ) ) { - draggedNifs << finfo.absoluteFilePath(); - } - } - } - - if ( !draggedNifs.isEmpty() ) { - e->accept(); - return; - } - } - - // Pass event on to viewport for any texture drag/drops - GLView * glWidget = qobject_cast(viewport()); - if ( glWidget ) { - glWidget->dragEnterEvent( e ); - } + // Intercept NIF files + if ( e->mimeData()->hasUrls() ) { + QList urls = e->mimeData()->urls(); + for ( auto url : urls ) { + if ( url.scheme() == "file" ) { + QString fn = url.toLocalFile(); + QFileInfo finfo( fn ); + if ( finfo.exists() && NifSkope::fileExtensions().contains( finfo.suffix(), Qt::CaseInsensitive ) ) { + draggedNifs << finfo.absoluteFilePath(); + } + } + } + + if ( !draggedNifs.isEmpty() ) { + e->accept(); + return; + } + } + + // Pass event on to viewport for any texture drag/drops + GLView * glWidget = qobject_cast(viewport()); + if ( glWidget ) { + glWidget->dragEnterEvent( e ); + } } void GLGraphicsView::dragLeaveEvent( QDragLeaveEvent * e ) { - if ( !draggedNifs.isEmpty() ) { - draggedNifs.clear(); - e->ignore(); - return; - } + if ( !draggedNifs.isEmpty() ) { + draggedNifs.clear(); + e->ignore(); + return; + } - // Pass event on to viewport for any texture drag/drops - GLView * glWidget = qobject_cast(viewport()); - if ( glWidget ) { - glWidget->dragLeaveEvent( e ); - } + // Pass event on to viewport for any texture drag/drops + GLView * glWidget = qobject_cast(viewport()); + if ( glWidget ) { + glWidget->dragLeaveEvent( e ); + } } void GLGraphicsView::dragMoveEvent( QDragMoveEvent * e ) { - if ( !draggedNifs.isEmpty() ) { - e->accept(); - return; - } + if ( !draggedNifs.isEmpty() ) { + e->accept(); + return; + } - // Pass event on to viewport for any texture drag/drops - GLView * glWidget = qobject_cast(viewport()); - if ( glWidget ) { - glWidget->dragMoveEvent( e ); - } + // Pass event on to viewport for any texture drag/drops + GLView * glWidget = qobject_cast(viewport()); + if ( glWidget ) { + glWidget->dragMoveEvent( e ); + } } void GLGraphicsView::dropEvent( QDropEvent * e ) { - if ( !draggedNifs.isEmpty() ) { - auto ns = qobject_cast(parentWidget()); - if ( ns ) { - ns->openFiles( draggedNifs ); - } + if ( !draggedNifs.isEmpty() ) { + auto ns = qobject_cast(parentWidget()); + if ( ns ) { + ns->openFiles( draggedNifs ); + } - draggedNifs.clear(); - e->accept(); - return; - } + draggedNifs.clear(); + e->accept(); + return; + } - // Pass event on to viewport for any texture drag/drops - GLView * glWidget = qobject_cast(viewport()); - if ( glWidget ) { - glWidget->dropEvent( e ); - } + // Pass event on to viewport for any texture drag/drops + GLView * glWidget = qobject_cast(viewport()); + if ( glWidget ) { + glWidget->dropEvent( e ); + } } void GLGraphicsView::focusOutEvent( QFocusEvent * e ) { - GLView * glWidget = qobject_cast(viewport()); - if ( glWidget ) { - glWidget->focusOutEvent( e ); - } + GLView * glWidget = qobject_cast(viewport()); + if ( glWidget ) { + glWidget->focusOutEvent( e ); + } } void GLGraphicsView::keyPressEvent( QKeyEvent * e ) { - GLView * glWidget = qobject_cast(viewport()); - if ( glWidget ) { - glWidget->keyPressEvent( e ); - } + GLView * glWidget = qobject_cast(viewport()); + if ( glWidget ) { + glWidget->keyPressEvent( e ); + } } void GLGraphicsView::keyReleaseEvent( QKeyEvent * e ) { - GLView * glWidget = qobject_cast(viewport()); - if ( glWidget ) { - glWidget->keyReleaseEvent( e ); - } + GLView * glWidget = qobject_cast(viewport()); + if ( glWidget ) { + glWidget->keyReleaseEvent( e ); + } } void GLGraphicsView::mouseDoubleClickEvent( QMouseEvent * e ) { - GLView * glWidget = qobject_cast(viewport()); - if ( glWidget ) { - glWidget->mouseDoubleClickEvent( e ); - } + GLView * glWidget = qobject_cast(viewport()); + if ( glWidget ) { + glWidget->mouseDoubleClickEvent( e ); + } } void GLGraphicsView::mouseMoveEvent( QMouseEvent * e ) { - GLView * glWidget = qobject_cast(viewport()); - if ( glWidget ) { - glWidget->mouseMoveEvent( e ); - } + GLView * glWidget = qobject_cast(viewport()); + if ( glWidget ) { + glWidget->mouseMoveEvent( e ); + } } void GLGraphicsView::mousePressEvent( QMouseEvent * e ) { - GLView * glWidget = qobject_cast(viewport()); - if ( glWidget ) { - glWidget->mousePressEvent( e ); - } + GLView * glWidget = qobject_cast(viewport()); + if ( glWidget ) { + glWidget->mousePressEvent( e ); + } } void GLGraphicsView::mouseReleaseEvent( QMouseEvent * e ) { - GLView * glWidget = qobject_cast(viewport()); - if ( glWidget ) { - glWidget->mouseReleaseEvent( e ); - } + GLView * glWidget = qobject_cast(viewport()); + if ( glWidget ) { + glWidget->mouseReleaseEvent( e ); + } } void GLGraphicsView::wheelEvent( QWheelEvent * e ) { - GLView * glWidget = qobject_cast(viewport()); - if ( glWidget ) { - glWidget->wheelEvent( e ); - } + GLView * glWidget = qobject_cast(viewport()); + if ( glWidget ) { + glWidget->wheelEvent( e ); + } }