diff --git a/NifSkope.pro b/NifSkope.pro
index 1c0bc5a69..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
###############################
@@ -346,6 +349,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/res/icon/paint-verts.png b/res/icon/paint-verts.png
new file mode 100644
index 000000000..0274ebc68
Binary files /dev/null and b/res/icon/paint-verts.png differ
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..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,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;
+ }
};
@@ -1936,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 7a1a43943..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;
if ( Node::SELECTING ) {
- if ( scene->selMode & Scene::SelObject ) {
+ if ( scene->actionMode & Scene::Object ) {
int s_nodeId = ID2COLORKEY( nodeId );
glColor4ubv( (GLubyte *)&s_nodeId );
} else {
- glColor4f( 0, 0, 0, 1 );
+ drawPolygons = false;
}
}
@@ -384,85 +384,87 @@ 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 (drawPolygons) {
- 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;
+ // Render polygon fill slightly behind alpha transparency and wireframe
+ if ( !drawSecond ) {
+ glEnable( GL_POLYGON_OFFSET_FILL );
+ glPolygonOffset( 1.0f, 2.0f );
+ }
- 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 ) );
+ 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 )
+ 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 ( !Node::SELECTING )
- scene->renderer->stopProgram();
+ 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;
+ }
+ }
- glDisableClientState( GL_VERTEX_ARRAY );
- glDisableClientState( GL_NORMAL_ARRAY );
- glDisableClientState( GL_COLOR_ARRAY );
+ if ( !Node::SELECTING )
+ scene->renderer->stopProgram();
- glDisable( GL_POLYGON_OFFSET_FILL );
+ glDisableClientState( GL_VERTEX_ARRAY );
+ glDisableClientState( GL_NORMAL_ARRAY );
+ glDisableClientState( GL_COLOR_ARRAY );
+ glDisable( GL_POLYGON_OFFSET_FILL );
+ }
- if ( scene->selMode & Scene::SelVertex ) {
+ if ( scene->actionMode & Scene::Vertex ) {
drawVerts();
}
@@ -515,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;
@@ -545,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 );
@@ -591,7 +593,7 @@ void BSShape::drawSelection() const
if ( normalScale < 0.1f )
normalScale = 0.1f;
-
+
// Draw All Verts lambda
auto allv = [this]( float size ) {
@@ -611,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" ) {
@@ -699,8 +701,8 @@ void BSShape::drawSelection() const
glVertex( transVerts.value( s ) );
glEnd();
}
- }
-
+ }
+
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
// Draw Lines lambda
@@ -732,7 +734,7 @@ void BSShape::drawSelection() const
glLineWidth( lineWidth );
}
};
-
+
// Draw Normals
if ( n.contains( "Normal" ) ) {
lines( transNorms );
@@ -766,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 );
@@ -838,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 2c84ef2ff..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,20 +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
/*
- 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, "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
+ 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
- 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;
@@ -403,7 +417,7 @@ template bool interpolate( T & value, const QModelIndex & array, fl
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 )
@@ -504,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 );
@@ -633,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 794cb7359..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,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 ) {
+ if ( scene->actionMode & Scene::Object ) {
int s_nodeId = ID2COLORKEY( nodeId );
glColor4ubv( (GLubyte *)&s_nodeId );
} else {
- glColor4f( 0, 0, 0, 1 );
+ drawPolygons = false;
}
}
@@ -1035,97 +1036,100 @@ 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 );
+ if (drawPolygons) {
- glEnableClientState( GL_VERTEX_ARRAY );
- glVertexPointer( 3, GL_FLOAT, 0, transVerts.constData() );
+ // Render polygon fill slightly behind alpha transparency and wireframe
+ glEnable( GL_POLYGON_OFFSET_FILL );
+ glPolygonOffset( 1.0f, 2.0f );
- if ( !Node::SELECTING ) {
- if ( transNorms.count() ) {
- glEnableClientState( GL_NORMAL_ARRAY );
- glNormalPointer( GL_FLOAT, 0, transNorms.constData() );
- }
+ glEnableClientState( GL_VERTEX_ARRAY );
+ glVertexPointer( 3, GL_FLOAT, 0, transVerts.constData() );
- // Do VCs if legacy or if either bslsp or bsesp is set
- bool doVCs = (!bssp) || (bssp && (bssp->getFlags2() & ShaderFlags::SLSF2_Vertex_Colors));
+ if ( !Node::SELECTING ) {
+ if ( transNorms.count() ) {
+ glEnableClientState( GL_NORMAL_ARRAY );
+ glNormalPointer( GL_FLOAT, 0, transNorms.constData() );
+ }
- 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 ) );
+ // 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 {
- glColor( Color3( 1.0f, 1.0f, 1.0f ) );
+ 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 );
+ // TODO: Hotspot. See about optimizing this.
+ if ( !Node::SELECTING )
+ shader = scene->renderer->setupProgram( this, shader );
- if ( isDoubleSided ) {
- glDisable( GL_CULL_FACE );
- }
+ 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;
+ 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() );
+ // 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 ( isDoubleSided ) {
+ glEnable( GL_CULL_FACE );
+ }
- if ( !Node::SELECTING )
- scene->renderer->stopProgram();
+ if ( !Node::SELECTING )
+ scene->renderer->stopProgram();
- glDisableClientState( GL_VERTEX_ARRAY );
- glDisableClientState( GL_NORMAL_ARRAY );
- glDisableClientState( GL_COLOR_ARRAY );
+ glDisableClientState( GL_VERTEX_ARRAY );
+ glDisableClientState( GL_NORMAL_ARRAY );
+ glDisableClientState( GL_COLOR_ARRAY );
- glDisable( GL_POLYGON_OFFSET_FILL );
+ glDisable( GL_POLYGON_OFFSET_FILL );
+ }
glPointSize( 8.5 );
- if ( scene->selMode & Scene::SelVertex ) {
+ if ( scene->actionMode & Scene::Vertex ) {
drawVerts();
}
@@ -1165,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;
@@ -1176,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;
}
@@ -1226,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 d9cbb9da3..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->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;
@@ -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->selMode & Scene::SelObject) )
+ 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->selMode & Scene::SelObject) )
+ 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 265d07a1e..82c8539b8 100644
--- a/src/gl/glscene.cpp
+++ b/src/gl/glscene.cpp
@@ -68,7 +68,7 @@ Scene::Scene( TexCache * texcache, QOpenGLContext * context, QOpenGLFunctions *
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..da27eb45b 100644
--- a/src/gl/glscene.h
+++ b/src/gl/glscene.h
@@ -129,16 +129,18 @@ class Scene final : public QObject
VisMode visMode;
- enum SelModes
+ enum ActionModes
{
- SelNone = 0,
- SelObject = 1,
- SelVertex = 2
+ 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
{
@@ -149,7 +151,7 @@ class Scene final : public QObject
LodLevel lodLevel;
-
+
Renderer * renderer;
NodeList nodes;
@@ -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..539a16eaf 100644
--- a/src/glview.cpp
+++ b/src/glview.cpp
@@ -72,15 +72,15 @@ 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
// 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
@@ -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 );
-
- installEventFilter( parent );
+ setContextMenuPolicy( Qt::CustomContextMenu );
+ setFocusPolicy( Qt::ClickFocus );
+ setAcceptDrops( true );
+
+ installEventFilter( parent );
}
GLGraphicsView::~GLGraphicsView() {}
@@ -110,175 +110,176 @@ 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 );
- 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;
}
-/*
+/*
* Scene
*/
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();
+ }
}
@@ -288,817 +289,978 @@ 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;
- //qDebug() << "System does not support multisampling";
- } /* else {
- GLint maxSamples;
- glGetIntegerv( GL_MAX_SAMPLES, &maxSamples );
- qDebug() << "Max samples:" << maxSamples;
- }*/
- }
+ 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;
+ }*/
+ }
- 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, axesSize );
+ // 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(), height() );
- // Restore matrices
- glProjection();
- }
+ // Restore matrices
+ glProjection();
+ }
- // Restore GL state
- glPopAttrib();
- glMatrixMode( GL_MODELVIEW );
- glPopMatrix();
- glMatrixMode( GL_PROJECTION );
- glPopMatrix();
+ // 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);
- // Check for errors
- GLenum err;
- while ( ( err = glGetError() ) != GL_NO_ERROR )
- qDebug() << tr( "glview.cpp - GL ERROR (paint): " ) << (const char *)gluErrorString( err );
+ // Restore viewport and projection
+ glViewport( 0, 0, width()*devicePixelRatioF(), height()*devicePixelRatioF());
+ glProjection();
+ }
- emit paintUpdate();
+ // Restore GL state
+ glPopAttrib();
+ glMatrixMode( GL_MODELVIEW );
+ glPopMatrix();
+ glMatrixMode( GL_PROJECTION );
+ glPopMatrix();
- // Manually handle the buffer swap
- swapBuffers();
+ // Check for errors
+ GLenum err;
+ while ( ( err = glGetError() ) != GL_NO_ERROR )
+ qDebug() << tr( "glview.cpp - GL ERROR (paint): " ) << (const char *)gluErrorString( err );
+
+ emit paintUpdate();
+
+ // 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 );
-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 );
+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 );
+ 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)
+{
+ Q_UNUSED(cycle);
+
+ if ( !(model && isVisible() && height()) )
+ return QModelIndex();
+
+ QImage img = renderIndexImage();
#ifndef QT_NO_DEBUG
- img.save( "fbo.png" );
+ 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;
- }
- }
-
- //qDebug() << "Key:" << a << " R" << pixel.red() << " G" << pixel.green() << " B" << pixel.blue();
- return choose;
+ return sampleIndexImagePoint(img, pos, model);
}
-QModelIndex GLView::indexAt( const QPoint & pos, int cycle )
+void GLView::center()
{
- if ( !(model && isVisible() && height()) )
- return QModelIndex();
-
- makeCurrent();
+ doCenter = true;
+ update();
+}
- glPushAttrib( GL_ALL_ATTRIB_BITS );
- glMatrixMode( GL_PROJECTION );
- glPushMatrix();
- glMatrixMode( GL_MODELVIEW );
- glPushMatrix();
+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();
+}
- glViewport( 0, 0, width(), height() );
- glProjection( pos.x(), pos.y() );
+void GLView::rotate( float x, float y, float z )
+{
+ Rot += Vector3( x, y, z );
+ updateViewpoint();
+ update();
+}
- QList df;
+void GLView::zoom( float z )
+{
+ Zoom *= z;
- if ( scene->options & Scene::ShowCollision )
- df << &Scene::drawHavok;
+ if ( Zoom < ZOOM_MIN )
+ Zoom = ZOOM_MIN;
- if ( scene->options & Scene::ShowNodes )
- df << &Scene::drawNodes;
+ if ( Zoom > ZOOM_MAX )
+ Zoom = ZOOM_MAX;
- if ( scene->options & Scene::ShowMarkers )
- df << &Scene::drawFurn;
+ update();
+}
- df << &Scene::drawShapes;
+void GLView::setVertexPaintSettings(GLView::PaintSettings settings)
+{
+ cfg.vertexPaintSettings = settings;
+ update();
+}
- int choose = -1, furn = -1;
- choose = ::indexAt( model, scene, df, cycle, pos, /*out*/ furn );
+void GLView::startVertexPaint(const QPoint& point)
+{
+ mousePaint = true;
+ mousePaintVerts.clear();
+ mousePaintHitDetectImg = renderIndexImage();
+ vertexPaint(point);
+ QCursor cursor(Qt::BlankCursor);
+ QApplication::setOverrideCursor(cursor);
+ QApplication::changeOverrideCursor(cursor);
+}
- glPopAttrib();
- glMatrixMode( GL_MODELVIEW );
- glPopMatrix();
- glMatrixMode( GL_PROJECTION );
- glPopMatrix();
+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;
- QModelIndex chooseIndex;
+ // 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();
- if ( scene->selMode & Scene::SelVertex ) {
- // Vertex
- int block = choose >> 16;
- int vert = choose - (block << 16);
+ // 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);
- auto shape = scene->shapes.value( block );
- if ( shape )
- chooseIndex = shape->vertexAt( vert );
- } else if ( choose != -1 ) {
- // Block Index
- chooseIndex = model->getBlock( choose );
+ // Get the position of each child
+ Vector3 siblingLocation = model->get(siblingId, "Vertex");
- if ( furn != -1 ) {
- // Furniture Row @ Block Index
- chooseIndex = model->index( furn, 0, model->index( 3, 0, chooseIndex ) );
- }
- }
+ // If XYZ matches, add sibling to inds list
+ if (siblingLocation == indLoc)
+ {
+ inds.push_back(model->getIndex(siblingId, "Vertex"));
+ }
+ }
+ }
- return chooseIndex;
-}
+ 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;
-void GLView::center()
-{
- doCenter = true;
- update();
-}
+ 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;
+ }
-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();
-}
+ model->set(ind.parent(), "Vertex Colors", ByteColor4::fromColor4(blend));
+ updated = true;
+ }
+ }
+ }
-void GLView::rotate( float x, float y, float z )
-{
- Rot += Vector3( x, y, z );
- updateViewpoint();
- update();
+ if (!updated)
+ {
+ update();
+ }
}
-void GLView::zoom( float z )
+void GLView::endVertexPaint()
{
- Zoom *= z;
-
- if ( Zoom < ZOOM_MIN )
- Zoom = ZOOM_MIN;
-
- if ( Zoom > ZOOM_MAX )
- Zoom = ZOOM_MAX;
-
- update();
+ 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();
}
@@ -1108,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 );
}
@@ -1212,614 +1374,624 @@ 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 );
- }
-
- scene->setSequence( seqname );
- time = scene->timeMin();
- emit sceneTimeChanged( time, scene->timeMin(), scene->timeMax() );
- update();
+ // 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();
}
// 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 );
- }
-
- 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;
- }
-}
-
-
-/*
- * QWidget Event Handlers
+ 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() ) );
+ }
+
+ delete img;
+ img = nullptr;
+ }
+ );
+ connect( btnCancel, &QPushButton::clicked, dlg, &QDialog::reject );
+
+ if ( dlg->exec() != QDialog::Accepted ) {
+ return;
+ }
+}
+
+
+/*
+ * QWidget Event Handlers
*/
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 ( 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;
- }
-
- lastPos = event->pos();
+ if ( event->button() == Qt::ForwardButton || event->button() == Qt::BackButton ) {
+ event->ignore();
+ return;
+ }
- 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;
+ }
- pressPos = event->pos();
+ lastPos = event->pos();
+ pressPos = event->pos();
}
void GLView::mouseReleaseEvent( QMouseEvent * event )
{
- if ( !(model && (pressPos - event->pos()).manhattanLength() <= 3) )
- return;
+ if ( (scene->actionMode & (Scene::Paint | Scene::Vertex)) == (Scene::Paint | Scene::Vertex)) {
+ endVertexPaint();
+ 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 )
@@ -1834,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 );
+ }
}
diff --git a/src/glview.h b/src/glview.h
index acfea7d74..c4eb5da5f 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,21 @@ 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;
+ };
+
void setNif( NifModel * );
Scene * getScene();
@@ -132,6 +148,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,6 +167,11 @@ 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 );
@@ -170,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 & );
@@ -237,12 +263,16 @@ protected slots:
Transform viewTrans;
GLdouble aspect;
-
+
QHash kbd;
QPoint lastPos;
QPoint pressPos;
Vector3 mouseMov;
Vector3 mouseRot;
+ bool mousePaint;
+ QVector mousePaintVerts;
+ QImage mousePaintHitDetectImg;
+
int cycleSelect;
QPersistentModelIndex iDragTarget;
@@ -262,6 +292,8 @@ protected slots:
float rotSpd = 45;
UpAxis upAxis = ZAxis;
+
+ PaintSettings vertexPaintSettings;
} cfg;
private slots:
diff --git a/src/nifskope.h b/src/nifskope.h
index e783f8831..f6b1f13a9 100644
--- a/src/nifskope.h
+++ b/src/nifskope.h
@@ -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,6 +379,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..c4e785add 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"
@@ -143,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()
@@ -183,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,
@@ -225,8 +226,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,7 +241,7 @@ void NifSkope::initActions()
return ag;
};
- selectActions = agroup( { ui->aSelectObject, ui->aSelectVertex }, true );
+ 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,
@@ -315,7 +317,7 @@ void NifSkope::initActions()
ogl->setDebugMode( GLView::DbgColorPicker );
else
ogl->setDebugMode( GLView::DbgNone );
-
+
ogl->update();
} );
@@ -372,6 +374,7 @@ void NifSkope::initDockWidgets()
dTree = ui->TreeDock;
dHeader = ui->HeaderDock;
dInsp = ui->InspectDock;
+ dPaint = ui->PaintDock;
dKfm = ui->KfmDock;
dBrowser = ui->BrowserDock;
@@ -385,17 +388,24 @@ 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 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 );
}
void NifSkope::initMenu()
@@ -501,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();
@@ -511,7 +521,6 @@ void NifSkope::initMenu()
}
}
-
void NifSkope::initToolBars()
{
// Disable without NIF loaded
@@ -521,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 ) );
@@ -575,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 );
@@ -645,12 +654,11 @@ void NifSkope::initConnections()
connect( this, &NifSkope::completeSave, this, &NifSkope::onSaveComplete );
}
-
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,
@@ -665,7 +673,6 @@ QMenu * NifSkope::lightingWidget()
return mLight;
}
-
QWidget * NifSkope::filePathWidget( QWidget * parent )
{
// Show Filepath of loaded NIF
@@ -727,7 +734,6 @@ QWidget * NifSkope::filePathWidget( QWidget * parent )
return filepathWidget;
}
-
void NifSkope::archiveDlg()
{
QString file = QFileDialog::getOpenFileName( this, tr( "Open Archive" ), "", "Archives (*.bsa *.ba2)" );
@@ -808,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();
@@ -829,6 +835,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();
@@ -836,7 +845,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 +943,6 @@ void NifSkope::saveUi() const
settings.setValue( "GLView/Perspective", ui->aViewPerspective->isChecked() );
}
-
void NifSkope::restoreUi()
{
QSettings settings;
@@ -1096,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"
@@ -1219,7 +1226,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,7 +1312,6 @@ bool NifSkope::eventFilter( QObject * o, QEvent * e )
* **********************
*/
-
void NifSkope::contextMenu( const QPoint & pos )
{
QModelIndex idx;
@@ -1451,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/nifskope.ui b/src/ui/nifskope.ui
index 2ecad1d36..6001bf1ed 100644
--- a/src/ui/nifskope.ui
+++ b/src/ui/nifskope.ui
@@ -10,7 +10,7 @@
800
-
+
@@ -69,6 +69,7 @@
+
@@ -278,6 +279,7 @@
+
@@ -1021,6 +1023,33 @@ background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0
+
+
+ Paint Vertex
+
+
+ 2
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+
+
true
@@ -2093,6 +2122,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
+
+
@@ -2105,6 +2160,12 @@ background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0
QTextBrowser
+
+ PaintSettingsWidget
+ QWidget
+ ui/widgets/vertexpaintwidget.h
+ 1
+
@@ -2654,6 +2715,70 @@ 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
+
+
+
openURL()
diff --git a/src/ui/widgets/uvedit.cpp b/src/ui/widgets/uvedit.cpp
index 1360e7a53..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, height );
+ 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
new file mode 100644
index 000000000..cdc653a99
--- /dev/null
+++ b/src/ui/widgets/vertexpaintwidget.cpp
@@ -0,0 +1,267 @@
+#include "vertexpaintwidget.h"
+#include "ui_vertexpaintwidget.h"
+#include
+
+enum OpacityMode : int
+{
+ Color = 0,
+ Alpha = 1,
+ ColorAndAlpha = 2
+};
+
+PaintSettingsWidget::PaintSettingsWidget(QWidget *parent) :
+ 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(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)
+{
+ 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;
+
+ // 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() == 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;
+}
+
+void PaintSettingsWidget::setHexFromColor()
+{
+ 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;
+
+ 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);
+}
+
+PaintSettingsWidget::~PaintSettingsWidget()
+{
+ delete ui;
+}
diff --git a/src/ui/widgets/vertexpaintwidget.h b/src/ui/widgets/vertexpaintwidget.h
new file mode 100644
index 000000000..7017d62c1
--- /dev/null
+++ b/src/ui/widgets/vertexpaintwidget.h
@@ -0,0 +1,41 @@
+#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 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
new file mode 100644
index 000000000..8a0f4bf42
--- /dev/null
+++ b/src/ui/widgets/vertexpaintwidget.ui
@@ -0,0 +1,468 @@
+
+
+ PaintSettingsWidget
+
+
+
+ 0
+ 0
+ 366
+ 183
+
+
+
+ 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
+
+
+
+ true
+
+
+ QFrame::StyledPanel
+
+
+ 2
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ true
+
+
+
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ true
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Opacity
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ 255
+
+
+ 255
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 255
+
+
+ 255
+
+
+
+
+
+
+ -
+
+
+ Blend Mode
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+
+
+ -1
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
+ ColorWheel
+ QWidget
+
+ 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
+
+
+
+
+
diff --git a/src/xml/kfmxml.cpp b/src/xml/kfmxml.cpp
index 878fd0e0e..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,9 +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"
#endif
);
for ( const QString& str : xmlList ) {
diff --git a/src/xml/nifxml.cpp b/src/xml/nifxml.cpp
index 182e0629c..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,9 +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"
#endif
);
for ( const QString& str : xmlList ) {