diff --git a/gl/cutting_quad.frag b/gl/cutting_quad.frag
new file mode 100644
index 00000000..eb2f954c
--- /dev/null
+++ b/gl/cutting_quad.frag
@@ -0,0 +1,24 @@
+#version 120
+
+uniform float zoom;
+
+varying vec3 ec_pos;
+
+
+void main() {
+ //Color of the cutting plane
+ vec3 cbase3 = vec3(1.0, 0.0, 0.0);
+ vec3 cbase2 = vec3(1.0, 0.0, 0.0);
+ vec3 cbase00 = vec3(0.5, 0.0, 0.0);
+
+ vec3 ec_normal = normalize(cross(dFdx(ec_pos), dFdy(ec_pos)));
+ ec_normal.z *= zoom;
+ ec_normal = normalize(ec_normal);
+
+ float a = dot(ec_normal, vec3(0.0, 0.0, 1.0));
+ float b = dot(ec_normal, vec3(-0.57, -0.57, 0.57));
+
+
+ gl_FragColor = vec4((a*cbase2 + (1-a)*cbase00)*0.5 +
+ (b*cbase3 + (1-b)*cbase00)*0.5, 1.0);
+}
diff --git a/gl/gl.qrc b/gl/gl.qrc
index a8065307..5fe77e36 100644
--- a/gl/gl.qrc
+++ b/gl/gl.qrc
@@ -7,6 +7,7 @@
mesh_light.frag
quad.frag
quad.vert
+ cutting_quad.frag
colored_lines.frag
colored_lines.vert
sphere.stl
diff --git a/gl/mesh.frag b/gl/mesh.frag
index d7a54d55..d8249187 100644
--- a/gl/mesh.frag
+++ b/gl/mesh.frag
@@ -4,6 +4,7 @@ uniform float zoom;
varying vec3 ec_pos;
+
void main() {
vec3 base3 = vec3(0.99, 0.96, 0.89);
vec3 base2 = vec3(0.92, 0.91, 0.83);
@@ -17,5 +18,5 @@ void main() {
float b = dot(ec_normal, vec3(-0.57, -0.57, 0.57));
gl_FragColor = vec4((a*base2 + (1-a)*base00)*0.5 +
- (b*base3 + (1-b)*base00)*0.5, 1.0);
+ (b*base3 + (1-b)*base00)*0.5, 1.0);
}
diff --git a/gl/mesh.vert b/gl/mesh.vert
index e60e76bb..d3987de4 100644
--- a/gl/mesh.vert
+++ b/gl/mesh.vert
@@ -1,13 +1,21 @@
-#version 120
+#version 140
attribute vec3 vertex_position;
uniform mat4 transform_matrix;
uniform mat4 view_matrix;
+uniform mat4 cutting_plane_matrix;
varying vec3 ec_pos;
void main() {
- gl_Position = view_matrix*transform_matrix*
- vec4(vertex_position, 1.0);
+ vec4 origpos = transform_matrix*vec4(vertex_position, 1.0);
+ gl_Position = view_matrix*origpos;
ec_pos = gl_Position.xyz;
+
+ vec4 vPos = view_matrix * transform_matrix* vec4(vertex_position, 1.0);
+
+ vec4 u_plane0 = transpose(inverse(cutting_plane_matrix))*vec4(0,0,1,0);
+ gl_ClipDistance[0] = dot(u_plane0, origpos); //If you want to clip using the camera coordinate system
+ //gl_ClipDistance[0] = dot(u_plane0, in_Vertex); //If you want to clip using the local coordinate system
+ //gl_ClipDistance[0] = dot(u_plane0, camMat * modelViewM * in_Vertex); //If you want it in global system
}
diff --git a/src/canvas.cpp b/src/canvas.cpp
index ad4ec37b..987fd5df 100644
--- a/src/canvas.cpp
+++ b/src/canvas.cpp
@@ -225,6 +225,16 @@ void Canvas::set_drawMode(enum DrawMode mode)
update();
}
+void Canvas::set_enableCut(bool d) {
+ enableCut = d;
+ update();
+}
+
+void Canvas::set_wheelMode(enum WheelMode mode) {
+ wheelMode = mode;
+ update();
+}
+
void Canvas::clear_status()
{
status = "";
@@ -235,8 +245,16 @@ void Canvas::initializeGL()
{
initializeOpenGLFunctions();
+ cutting_quad_fragshader = new QOpenGLShader(QOpenGLShader::Fragment);
+ cutting_quad_fragshader->compileSourceFile(":/gl/cutting_quad.frag");
+
mesh_vertshader = new QOpenGLShader(QOpenGLShader::Vertex);
mesh_vertshader->compileSourceFile(":/gl/mesh.vert");
+
+ cutting_quad_shaderprog.addShader(mesh_vertshader);
+ cutting_quad_shaderprog.addShader(cutting_quad_fragshader);
+ cutting_quad_shaderprog.link();
+
mesh_shader.addShader(mesh_vertshader);
mesh_shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl/mesh.frag");
mesh_shader.link();
@@ -249,7 +267,7 @@ void Canvas::initializeGL()
mesh_meshlight_shader.addShader(mesh_vertshader);
mesh_meshlight_shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl/mesh_light.frag");
mesh_meshlight_shader.link();
-
+
backdrop = new Backdrop();
axis = new Axis();
}
@@ -336,8 +354,88 @@ void Canvas::draw_mesh()
const GLuint vp = selected_mesh_shader->attributeLocation("vertex_position");
glEnableVertexAttribArray(vp);
- // Then draw the mesh with that vertex position
- mesh->draw(vp);
+ if (enableCut) {
+ glUniformMatrix4fv(
+ selected_mesh_shader->uniformLocation("cutting_plane_matrix"),
+ 1, GL_FALSE, cutting_plane_matrix().data());
+ // Calculate clipping plane
+ glEnable(GL_CLIP_DISTANCE0);
+ glEnable(GL_STENCIL_TEST);
+ glEnable(GL_CULL_FACE);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ glDisable(GL_DEPTH_TEST);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+
+ glStencilFunc(GL_ALWAYS, 0, 0);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
+ glCullFace(GL_FRONT); // render back faces
+ mesh -> draw(vp);
+
+ glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
+ glCullFace(GL_BACK); // render front faces
+ mesh -> draw(vp);
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glDisable(GL_CULL_FACE);
+ glEnable(GL_DEPTH_TEST);
+ glDisable(GL_CLIP_DISTANCE0);
+ glStencilFunc(GL_NOTEQUAL, 0, ~0);
+
+ // Rendering the mesh
+ glDisable(GL_STENCIL_TEST);
+ glEnable(GL_CLIP_DISTANCE0);
+ glUniformMatrix4fv(
+ selected_mesh_shader->uniformLocation("transform_matrix"),
+ 1, GL_FALSE, transform_matrix().data());
+
+ mesh->draw(vp);
+
+ // reload shader program to draw the colored clipping plane
+ selected_mesh_shader->release();
+ selected_mesh_shader = &cutting_quad_shaderprog;
+ selected_mesh_shader->bind();
+
+ glUniform1f(selected_mesh_shader->uniformLocation("zoom"), 1/zoom);
+ glUniformMatrix4fv(
+ selected_mesh_shader->uniformLocation("transform_matrix"),
+ 1, GL_FALSE, cutting_plane_matrix().data());
+ glUniformMatrix4fv(
+ selected_mesh_shader->uniformLocation("view_matrix"),
+ 1, GL_FALSE, view_matrix().data());
+
+ glEnable(GL_STENCIL_TEST);
+ glDisable(GL_CLIP_DISTANCE0);
+
+ // Define a BIG quad to render. It should be big to assure covering of clip area
+ unsigned int VBO;
+ float vertices[] = {
+ -1000.0f, 1000.0f, 0.0f,
+ -1000.0f, -1000.0f, 0.0f,
+ 1000.0f, -1000.0f, 0.0f,
+ 1000.0f, 1000.0f, 0.0f};
+
+ glBindBuffer(GL_ARRAY_BUFFER, VBO);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
+ glEnableVertexAttribArray(0);
+
+ // render quad
+ glDrawArrays(GL_QUADS, 0, 4);
+
+ // release buffers
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glDeleteBuffers(1, &VBO);
+
+ glDisable(GL_STENCIL_TEST);
+ glEnable(GL_CLIP_DISTANCE0);
+ }
+ else
+ {
+ glDisable(GL_CLIP_DISTANCE0);
+
+ // Then draw the mesh with that vertex position
+ mesh->draw(vp);
+ }
// Reset draw mode for the background and anything else that needs to be drawn
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
@@ -346,11 +444,13 @@ void Canvas::draw_mesh()
glDisableVertexAttribArray(vp);
selected_mesh_shader->release();
}
+
QMatrix4x4 Canvas::orient_matrix() const
{
QMatrix4x4 m = currentTransform;
return m;
}
+
QMatrix4x4 Canvas::transform_matrix() const
{
QMatrix4x4 m = orient_matrix();
@@ -379,6 +479,20 @@ QMatrix4x4 Canvas::view_matrix() const
return m;
}
+QMatrix4x4 Canvas::cutting_plane_matrix() const
+{
+ QMatrix4x4 m = QMatrix4x4(
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+
+ m.rotate(cp_tilt, QVector3D(1, 0, 0));
+ m.rotate(cp_yaw, QVector3D(0, 1, 0));
+ m.translate(0, 0, cp_shift);
+ return m;
+}
+
void Canvas::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton ||
@@ -458,20 +572,27 @@ void Canvas::mouseMoveEvent(QMouseEvent* event)
auto d = p - mouse_pos;
- if (event->buttons() & Qt::LeftButton)
+ if (event->modifiers() & Qt::ControlModifier)
+ {
+ if (event->buttons() & Qt::LeftButton)
+ {
+ cp_yaw = fmod(cp_yaw - d.x(), 360);
+ cp_tilt = fmod(cp_tilt - d.y(), 360);
+ update();
+ }
+ } else if (event->buttons() & Qt::LeftButton)
{
QPointF p1r = changeMouseCoordinates(mouse_pos);
QPointF p2r = changeMouseCoordinates(p);
calcArcballTransform(p1r,p2r);
update();
- }
- else if (event->buttons() & Qt::RightButton)
+ } else if (event->buttons() & Qt::RightButton)
{
center = transform_matrix().inverted() *
- view_matrix().inverted() *
- QVector3D(-d.x() / (0.5*width()),
- d.y() / (0.5*height()), 0);
+ view_matrix().inverted() *
+ QVector3D(-d.x() / (0.5*width()),
+ d.y() / (0.5*height()), 0);
update();
}
mouse_pos = p;
@@ -487,27 +608,32 @@ void Canvas::wheelEvent(QWheelEvent *event)
QVector3D a = transform_matrix().inverted() *
view_matrix().inverted() * v;
- if (event->angleDelta().y() < 0)
- {
- for (int i=0; i > event->angleDelta().y(); --i)
- if (invertZoom)
- zoom /= 1.001;
- else
- zoom *= 1.001;
- }
- else if (event->angleDelta().y() > 0)
+ if (event->modifiers() & Qt::ControlModifier || wheelMode == wheelcut)
+ cp_shift += (event->delta())/10000.0f;
+ else
{
- for (int i=0; i < event->angleDelta().y(); ++i)
- if (invertZoom)
- zoom *= 1.001;
- else
- zoom /= 1.001;
+ if (event->angleDelta().y() < 0)
+ {
+ for (int i=0; i > event->angleDelta().y(); --i)
+ if (invertZoom)
+ zoom /= 1.001;
+ else
+ zoom *= 1.001;
+ }
+ else if (event->angleDelta().y() > 0)
+ {
+ for (int i=0; i < event->angleDelta().y(); ++i)
+ if (invertZoom)
+ zoom *= 1.001;
+ else
+ zoom /= 1.001;
+ }
+ // Then find the cursor's GL position post-zoom and adjust center.
+ QVector3D b = transform_matrix().inverted() *
+ view_matrix().inverted() * v;
+ center += b - a;
}
- // Then find the cursor's GL position post-zoom and adjust center.
- QVector3D b = transform_matrix().inverted() *
- view_matrix().inverted() * v;
- center += b - a;
update();
}
diff --git a/src/canvas.h b/src/canvas.h
index 8dab30c4..33c5b7f8 100644
--- a/src/canvas.h
+++ b/src/canvas.h
@@ -12,6 +12,7 @@ class Axis;
enum ViewPoint {centerview, isoview, topview, bottomview, leftview, rightview, frontview, backview};
enum DrawMode {shaded, wireframe, surfaceangle, meshlight, DRAWMODECOUNT};
+enum WheelMode {wheelzoom, wheelcut};
class Canvas : public QOpenGLWidget, protected QOpenGLFunctions
{
@@ -28,6 +29,8 @@ class Canvas : public QOpenGLWidget, protected QOpenGLFunctions
void draw_axes(bool d);
void invert_zoom(bool d);
void set_drawMode(enum DrawMode mode);
+ void set_enableCut(bool d);
+ void set_wheelMode(enum WheelMode mode);
void common_view_change(enum ViewPoint c);
void setResetTransformOnLoad(bool d);
@@ -73,10 +76,15 @@ public slots:
QMatrix4x4 transform_matrix() const;
QMatrix4x4 aspect_matrix() const;
QMatrix4x4 view_matrix() const;
+ QMatrix4x4 cutting_plane_matrix() const;
+
void resetTransform();
QPointF changeMouseCoordinates(QPoint p);
void calcArcballTransform(QPointF p1, QPointF p2);
+ QOpenGLShader* cutting_quad_fragshader;
+ QOpenGLShaderProgram cutting_quad_shaderprog;
+
QOpenGLShader* mesh_vertshader;
QOpenGLShaderProgram mesh_shader;
QOpenGLShaderProgram mesh_wireframe_shader;
@@ -112,9 +120,17 @@ public slots:
float zoom;
QMatrix4x4 currentTransform;
+
+ float cp_tilt;
+ float cp_yaw;
+ float cp_shift;
+
float perspective;
enum DrawMode drawMode;
+ enum WheelMode wheelMode;
+
bool drawAxes;
+ bool enableCut;
bool invertZoom;
bool resetTransformOnLoad;
Q_PROPERTY(float perspective MEMBER perspective WRITE set_perspective);
diff --git a/src/window.cpp b/src/window.cpp
index c8aaba76..ee29b0b8 100644
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -11,7 +11,9 @@ const QString Window::INVERT_ZOOM_KEY = "invertZoom";
const QString Window::AUTORELOAD_KEY = "autoreload";
const QString Window::DRAW_AXES_KEY = "drawAxes";
const QString Window::PROJECTION_KEY = "projection";
+const QString Window::WHEELMODE_KEY = "wheelMode";
const QString Window::DRAW_MODE_KEY = "drawMode";
+const QString Window::CUTTING_PLANE_KEY = "cuttingplane";
const QString Window::WINDOW_GEOM_KEY = "windowGeometry";
const QString Window::RESET_TRANSFORM_ON_LOAD_KEY = "resetTransformOnLoad";
@@ -35,6 +37,9 @@ Window::Window(QWidget *parent) :
wireframe_action(new QAction("Wireframe", this)),
surfaceangle_action(new QAction("Surface Angle", this)),
meshlight_action(new QAction("Shaded ambient and directive light source", this)),
+ cuttingplane_action(new QAction("Enable cutting plane", this)),
+ wheelmode_cut_action(new QAction("Wheel controls cut", this)),
+ wheelmode_zoom_action(new QAction("Wheel controls zoom", this)),
drawModePrefs_action(new QAction("Draw Mode Settings")),
axes_action(new QAction("Draw Axes", this)),
invert_zoom_action(new QAction("Invert Zoom", this)),
@@ -135,6 +140,19 @@ Window::Window(QWidget *parent) :
QObject::connect(projections, &QActionGroup::triggered,
this, &Window::on_projection);
+ const auto wheel_menu = view_menu->addMenu("Wheel Mode");
+ wheel_menu->addAction(wheelmode_cut_action);
+ wheel_menu->addAction(wheelmode_zoom_action);
+ const auto wheelmodes = new QActionGroup(wheel_menu);
+ for (auto p : {wheelmode_cut_action, wheelmode_zoom_action})
+ {
+ wheelmodes->addAction(p);
+ p->setCheckable(true);
+ }
+ wheelmodes->setExclusive(true);
+ QObject::connect(wheelmodes, &QActionGroup::triggered,
+ this, &Window::on_wheelmode);
+
const auto draw_menu = view_menu->addMenu("Draw Mode");
draw_menu->addAction(shaded_action);
draw_menu->addAction(wireframe_action);
@@ -186,6 +204,11 @@ Window::Window(QWidget *parent) :
QObject::connect(axes_action, &QAction::triggered,
this, &Window::on_drawAxes);
+ view_menu->addAction(cuttingplane_action);
+ cuttingplane_action->setCheckable(true);
+ QObject::connect(cuttingplane_action, &QAction::triggered,
+ this, &Window::on_cuttingplane);
+
view_menu->addAction(invert_zoom_action);
invert_zoom_action->setCheckable(true);
QObject::connect(invert_zoom_action, &QAction::triggered,
@@ -233,6 +256,10 @@ void Window::load_persist_settings(){
canvas->draw_axes(draw_axes);
axes_action->setChecked(draw_axes);
+ bool cuttingplane_b = settings.value(CUTTING_PLANE_KEY, false).toBool();
+ cuttingplane_action->setChecked(cuttingplane_b);
+ canvas->set_enableCut(cuttingplane_b);
+
QString projection = settings.value(PROJECTION_KEY, "perspective").toString();
if(projection == "perspective"){
canvas->view_perspective(Canvas::P_PERSPECTIVE, false);
@@ -242,6 +269,12 @@ void Window::load_persist_settings(){
orthographic_action->setChecked(true);
}
+ QString wheelmode = settings.value(WHEELMODE_KEY, "wheelMode").toString();
+ if(wheelmode == "wheelzoom")
+ wheelmode_zoom_action->trigger();
+ else
+ wheelmode_cut_action->trigger();
+
QString path = settings.value(OPEN_EXTERNAL_KEY, "").toString();
if (!QDir::isAbsolutePath(path) && !path.isEmpty())
{
@@ -389,6 +422,21 @@ void Window::on_projection(QAction* proj)
}
}
+void Window::on_wheelmode(QAction* mode)
+{
+ if (mode == wheelmode_cut_action)
+ {
+ canvas->set_wheelMode(wheelcut);
+ QSettings().setValue(WHEELMODE_KEY, "wheelcut");
+ }
+ else
+ {
+ canvas->set_wheelMode(wheelzoom);
+ QSettings().setValue(WHEELMODE_KEY, "wheelzoom");
+ }
+}
+
+
void Window::on_drawMode(QAction* act)
{
// On mode change hide prefs first
@@ -415,10 +463,17 @@ void Window::on_drawMode(QAction* act)
drawModePrefs_action->setEnabled(true);
mode = meshlight;
}
+
canvas->set_drawMode(mode);
QSettings().setValue(DRAW_MODE_KEY, mode);
}
+void Window::on_cuttingplane(bool d)
+{
+ canvas->set_enableCut(d);
+ QSettings().setValue(CUTTING_PLANE_KEY, d);
+}
+
void Window::on_drawAxes(bool d)
{
canvas->draw_axes(d);
diff --git a/src/window.h b/src/window.h
index f13137b8..ac8ec77e 100644
--- a/src/window.h
+++ b/src/window.h
@@ -40,7 +40,9 @@ public slots:
private slots:
void on_projection(QAction* proj);
+ void on_wheelmode(QAction* mode);
void on_drawMode(QAction* mode);
+ void on_cuttingplane(bool d);
void on_drawAxes(bool d);
void on_invertZoom(bool d);
void on_resetTransformOnLoad(bool d);
@@ -81,6 +83,9 @@ private slots:
QAction* const wireframe_action;
QAction* const surfaceangle_action;
QAction* const meshlight_action;
+ QAction* const cuttingplane_action;
+ QAction* const wheelmode_cut_action;
+ QAction* const wheelmode_zoom_action;
QAction* const drawModePrefs_action;
QAction* const axes_action;
QAction* const invert_zoom_action;
@@ -101,7 +106,9 @@ private slots:
const static QString AUTORELOAD_KEY;
const static QString DRAW_AXES_KEY;
const static QString PROJECTION_KEY;
+ const static QString WHEELMODE_KEY;
const static QString DRAW_MODE_KEY;
+ const static QString CUTTING_PLANE_KEY;
const static QString WINDOW_GEOM_KEY;
const static QString RESET_TRANSFORM_ON_LOAD_KEY;