mirror of https://github.com/CGAL/cgal
2159 lines
70 KiB
C++
2159 lines
70 KiB
C++
#include "Viewer.h"
|
|
#include <CGAL/Three/Scene_draw_interface.h>
|
|
#include <QMouseEvent>
|
|
#include <QKeyEvent>
|
|
#include <QSettings>
|
|
#include <QDebug>
|
|
#include <QSettings>
|
|
#include <QOpenGLShader>
|
|
#include <QFileDialog>
|
|
#include <QOpenGLShaderProgram>
|
|
#include <QOpenGLFramebufferObject>
|
|
#include <QMessageBox>
|
|
#include <QColorDialog>
|
|
#include <QInputDialog>
|
|
#include <cmath>
|
|
#include <QApplication>
|
|
#include <QOpenGLDebugLogger>
|
|
#include <QStyleFactory>
|
|
#include <QAction>
|
|
#include <QMultipleInputDialog.h>
|
|
#include <QRegularExpressionMatch>
|
|
#ifdef CGAL_USE_WEBSOCKETS
|
|
#include <QtWebSockets/QWebSocket>
|
|
#endif
|
|
|
|
#include <CGAL/Three/Three.h>
|
|
|
|
#include "ui_LightingDialog.h"
|
|
#include "CGAL_double_edit.h"
|
|
|
|
#if defined(_WIN32)
|
|
#include <QMimeData>
|
|
#include <QByteArray>
|
|
#include <QBuffer>
|
|
#endif
|
|
#define ORIGINAL_FOV 0.94853805396568136
|
|
|
|
class Viewer_impl {
|
|
public:
|
|
CGAL::Three::Scene_draw_interface* scene;
|
|
Viewer *viewer;
|
|
Viewer *shareViewer;
|
|
bool antialiasing;
|
|
bool twosides;
|
|
bool macro_mode;
|
|
bool inFastDrawing;
|
|
bool inDrawWithNames;
|
|
bool clipping;
|
|
bool projection_is_ortho;
|
|
bool cam_sharing;
|
|
bool scene_scaling;
|
|
GLfloat gl_point_size;
|
|
QVector4D clipbox[6];
|
|
QVector3D scaler;
|
|
QPainter *painter;
|
|
|
|
// L i g h t i n g
|
|
QVector4D position;
|
|
QVector4D ambient;
|
|
QVector4D diffuse;
|
|
QVector4D specular;
|
|
float spec_power;
|
|
|
|
//Back and Front Colors
|
|
QColor front_color;
|
|
QColor back_color;
|
|
|
|
// M e s s a g e s
|
|
QString message;
|
|
bool _displayMessage;
|
|
QTimer messageTimer;
|
|
QOpenGLFunctions_4_3_Core* _recentFunctions;
|
|
bool is_2d_selection_mode;
|
|
|
|
// D e p t h P e e l i n g
|
|
// \param pass the current pass in the Depth Peeling (transparency) algorithm.
|
|
// -1 means that no depth peeling is applied.
|
|
// \param writing_depth means that the color of the faces will be drawn in a grayscale
|
|
// according to the depth of the fragment in the shader. It is used by the transparency.
|
|
// \param fbo contains the texture used by the Depth Peeling algorithm.
|
|
// Should be NULL if pass <= 0;
|
|
int current_pass;
|
|
bool writing_depth;
|
|
int total_pass;
|
|
int current_total_pass;
|
|
QOpenGLFramebufferObject* dp_fbo;
|
|
QOpenGLDebugLogger *logger;
|
|
|
|
|
|
//! The buffers used to draw the axis system
|
|
QOpenGLBuffer buffer;
|
|
//! The VAO used to draw the axis system
|
|
QOpenGLVertexArrayObject vao;
|
|
//! The rendering program used to draw the distance
|
|
QOpenGLShaderProgram rendering_program_dist;
|
|
QList<TextItem*> distance_text;
|
|
//! Decides if the text is displayed in the drawVisualHints function.
|
|
bool has_text;
|
|
//! Decides if the distance between APoint and BPoint must be drawn;
|
|
bool distance_is_displayed;
|
|
bool i_is_pressed;
|
|
bool z_is_pressed;
|
|
QImage static_image;
|
|
//!Draws the distance between two selected points.
|
|
void showDistance(QPoint);
|
|
CGAL::qglviewer::Vec APoint;
|
|
CGAL::qglviewer::Vec BPoint;
|
|
bool is_d_pressed;
|
|
bool extension_is_found;
|
|
int quality;
|
|
|
|
TextRenderer *textRenderer;
|
|
//!Clears the distance display
|
|
void clearDistancedisplay();
|
|
void draw_aux(bool with_names, Viewer*);
|
|
//! Contains all the programs for the item rendering.
|
|
static std::vector<QOpenGLShaderProgram*> shader_programs;
|
|
QMatrix4x4 projectionMatrix;
|
|
void sendSnapshotToClipboard(Viewer*);
|
|
std::vector<QOpenGLShaderProgram*>& shaderPrograms()
|
|
{
|
|
return shader_programs;
|
|
}
|
|
|
|
#ifdef CGAL_USE_WEBSOCKETS
|
|
QWebSocket m_webSocket;
|
|
#endif
|
|
bool is_connected;
|
|
QString session;
|
|
QUrl m_url;
|
|
};
|
|
|
|
class LightingDialog :
|
|
public QDialog,
|
|
public Ui::LightingDialog
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
QColor ambient, diffuse, specular;
|
|
LightingDialog(Viewer_impl* d)
|
|
{
|
|
setupUi(this);
|
|
position_lineEdit->setText(QString("%1,%2,%3")
|
|
.arg(d->position.x())
|
|
.arg(d->position.y())
|
|
.arg(d->position.z()));
|
|
QPalette palette;
|
|
ambient=QColor::fromRgbF(d->ambient.x(),
|
|
d->ambient.y(),
|
|
d->ambient.z());
|
|
palette.setColor(QPalette::Button,ambient);
|
|
ambientButton->setPalette(palette);
|
|
ambientButton->setStyle(QStyleFactory::create("Fusion"));
|
|
|
|
diffuse=QColor::fromRgbF(d->diffuse.x(),
|
|
d->diffuse.y(),
|
|
d->diffuse.z());
|
|
palette.setColor(QPalette::Button,diffuse);
|
|
diffuseButton->setPalette(palette);
|
|
diffuseButton->setStyle(QStyleFactory::create("Fusion"));
|
|
|
|
specular=QColor::fromRgbF(d->specular.x(),
|
|
d->specular.y(),
|
|
d->specular.z());
|
|
palette.setColor(QPalette::Button,specular);
|
|
specularButton->setPalette(palette);
|
|
specularButton->setStyle(QStyleFactory::create("Fusion"));
|
|
spec_powrSlider->setValue(static_cast<int>(d->spec_power));
|
|
|
|
connect(&ambient_dial, &QColorDialog::currentColorChanged, this, &LightingDialog::ambient_changed );
|
|
connect(&diffuse_dial, &QColorDialog::currentColorChanged, this, &LightingDialog::diffuse_changed );
|
|
connect(&spec_dial, &QColorDialog::currentColorChanged, this, &LightingDialog::specular_changed);
|
|
|
|
connect(ambientButton, &QPushButton::clicked,
|
|
[this](){
|
|
ambient_dial.setCurrentColor(ambient);
|
|
ambient_dial.exec();
|
|
ambient = ambient_dial.selectedColor();
|
|
QPalette palette;
|
|
palette.setColor(QPalette::Button, ambient);
|
|
ambientButton->setPalette(palette);
|
|
});
|
|
connect(diffuseButton, &QPushButton::clicked,
|
|
[this](){
|
|
diffuse_dial.setCurrentColor(diffuse);
|
|
diffuse_dial.exec();
|
|
diffuse = diffuse_dial.selectedColor();
|
|
QPalette palette;
|
|
palette.setColor(QPalette::Button, diffuse);
|
|
diffuseButton->setPalette(palette);
|
|
});
|
|
connect(specularButton, &QPushButton::clicked,
|
|
[this](){
|
|
spec_dial.setCurrentColor(specular);
|
|
spec_dial.exec();
|
|
specular = spec_dial.selectedColor();
|
|
QPalette palette;
|
|
palette.setColor(QPalette::Button, specular);
|
|
specularButton->setPalette(palette);
|
|
});
|
|
|
|
//D e f a u l t - S e t t i n g s
|
|
connect(buttonBox->button(QDialogButtonBox::StandardButton::RestoreDefaults), &QPushButton::clicked,
|
|
[this](){
|
|
position_lineEdit->setText(QString("0,0,1"));
|
|
ambient=QColor(77,77,77);
|
|
diffuse=QColor(204,204,204);
|
|
specular=QColor(0,0,0);
|
|
spec_powrSlider->setValue(51);
|
|
QPalette palette;
|
|
palette.setColor(QPalette::Button, ambient);
|
|
ambientButton->setPalette(palette);
|
|
palette.setColor(QPalette::Button, diffuse);
|
|
diffuseButton->setPalette(palette);
|
|
palette.setColor(QPalette::Button, specular);
|
|
specularButton->setPalette(palette);
|
|
});
|
|
}
|
|
private Q_SLOTS:
|
|
void diffuse_changed()
|
|
{
|
|
diffuse = diffuse_dial.currentColor();
|
|
s_diffuse_changed();
|
|
}
|
|
void ambient_changed()
|
|
{
|
|
ambient = ambient_dial.currentColor();
|
|
s_ambient_changed();
|
|
}
|
|
void specular_changed()
|
|
{
|
|
specular = spec_dial.currentColor();
|
|
s_specular_changed();
|
|
}
|
|
Q_SIGNALS:
|
|
void s_diffuse_changed();
|
|
void s_ambient_changed();
|
|
void s_specular_changed();
|
|
private:
|
|
QColorDialog diffuse_dial;
|
|
QColorDialog ambient_dial;
|
|
QColorDialog spec_dial;
|
|
};
|
|
|
|
std::vector<QOpenGLShaderProgram*> Viewer_impl::shader_programs =
|
|
std::vector<QOpenGLShaderProgram*>(Viewer::NB_OF_PROGRAMS);
|
|
void Viewer::doBindings()
|
|
{
|
|
QSettings viewer_settings;
|
|
// enable anti-aliasing
|
|
QString cam_pos = viewer_settings.value("cam_pos", QString("0.0,0.0,1.0")).toString();
|
|
d->position = QVector4D(cam_pos.split(",").at(0).toFloat(),
|
|
cam_pos.split(",").at(1).toFloat(),
|
|
cam_pos.split(",").at(2).toFloat(),
|
|
1.0f);
|
|
|
|
QString ambient = viewer_settings.value("ambient", QString("0.4,0.4,0.4")).toString();
|
|
d->ambient = QVector4D(ambient.split(",").at(0).toFloat(),
|
|
ambient.split(",").at(1).toFloat(),
|
|
ambient.split(",").at(2).toFloat(),
|
|
1.0f);
|
|
|
|
QString diffuse = viewer_settings.value("diffuse", QString("1.0,1.0,1.0")).toString();
|
|
d->diffuse = QVector4D(diffuse.split(",").at(0).toFloat(),
|
|
diffuse.split(",").at(1).toFloat(),
|
|
diffuse.split(",").at(2).toFloat(),
|
|
1.0f);
|
|
|
|
QString specular = viewer_settings.value("specular", QString("0.0,0.0,0.0")).toString();
|
|
d->specular = QVector4D(specular.split(",").at(0).toFloat(),
|
|
specular.split(",").at(1).toFloat(),
|
|
specular.split(",").at(2).toFloat(),
|
|
1.0f);
|
|
|
|
QString front_color = viewer_settings.value("front_color", QString("1.0,0.0,0.0")).toString();
|
|
d->front_color= QColor::fromRgbF(front_color.split(",").at(0).toFloat(),
|
|
front_color.split(",").at(1).toFloat(),
|
|
front_color.split(",").at(2).toFloat(),
|
|
1.0f);
|
|
QString back_color = viewer_settings.value("back_color", QString("0.0,0.0,1.0")).toString();
|
|
d->back_color= QColor::fromRgbF( back_color.split(",").at(0).toFloat(),
|
|
back_color.split(",").at(1).toFloat(),
|
|
back_color.split(",").at(2).toFloat(),
|
|
1.0f);
|
|
d->spec_power = viewer_settings.value("spec_power", 51.8).toFloat();
|
|
d->scene = nullptr;
|
|
d->projection_is_ortho = false;
|
|
d->cam_sharing = false;
|
|
d->twosides = false;
|
|
this->setProperty("draw_two_sides", false);
|
|
this->setProperty("back_front_shading", false);
|
|
d->macro_mode = false;
|
|
d->inFastDrawing = true;
|
|
d->inDrawWithNames = false;
|
|
d->clipping = false;
|
|
d->shader_programs.resize(NB_OF_PROGRAMS);
|
|
d->textRenderer = new TextRenderer();
|
|
d->is_2d_selection_mode = false;
|
|
d->is_connected = false;
|
|
d->scene_scaling = false;
|
|
d->scaler = QVector3D(1,1,1);
|
|
|
|
connect( d->textRenderer, SIGNAL(sendMessage(QString,int)),
|
|
this, SLOT(printMessage(QString,int)) );
|
|
connect(&d->messageTimer, SIGNAL(timeout()), SLOT(hideMessage()));
|
|
setShortcut(CGAL::qglviewer::EXIT_VIEWER, 0);
|
|
setKeyDescription(Qt::Key_T,
|
|
tr("Turn the camera by 180 degrees"));
|
|
setKeyDescription(Qt::Key_M,
|
|
tr("Toggle macro mode: useful to view details very near from the camera, "
|
|
"but decrease the z-buffer precision"));
|
|
setKeyDescription(Qt::CTRL, Qt::Key_I,
|
|
tr("Toggle the primitive IDs visibility of the selected Item, for the types selected in the context menu of the said item."));
|
|
setKeyDescription(Qt::Key_D,
|
|
tr("Disable the distance between two points visibility."));
|
|
setKeyDescription(Qt::Key_F5,
|
|
tr("Reload selected items if possible."));
|
|
|
|
//modify mouse bindings that have been updated
|
|
setMouseBinding(Qt::Key(0), Qt::NoModifier, Qt::LeftButton, CGAL::qglviewer::RAP_FROM_PIXEL, true, Qt::RightButton);
|
|
setMouseBinding(Qt::ShiftModifier, Qt::RightButton, CGAL::qglviewer::NO_CLICK_ACTION, false, Qt::NoButton);
|
|
setMouseBindingDescription(Qt::ShiftModifier, Qt::RightButton,
|
|
tr("Select and pop context menu"));
|
|
setMouseBinding(Qt::Key_R, Qt::NoModifier, Qt::LeftButton, CGAL::qglviewer::RAP_FROM_PIXEL);
|
|
|
|
//use the new API for these
|
|
setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, CGAL::qglviewer::SELECT);
|
|
|
|
setMouseBindingDescription(Qt::Key_I, Qt::NoModifier, Qt::LeftButton,
|
|
tr("Show/hide the primitive ID of the types selected in the context menu of the picked item."));
|
|
setMouseBindingDescription(Qt::Key_D, Qt::NoModifier, Qt::LeftButton,
|
|
tr("Selects a point. When the second point is selected, "
|
|
"displays the two points and the distance between them."));
|
|
setMouseBindingDescription(Qt::Key_O, Qt::NoModifier, Qt::LeftButton,
|
|
tr("Move the camera orthogonally to the picked facet of a Scene_surface_mesh_item or "
|
|
"to the current selection of a Scene_points_with_normal_item."));
|
|
setKeyDescription(Qt::Key_F5,
|
|
tr("Reloads the selected item if possible."));
|
|
setKeyDescription(Qt::Key_F11,
|
|
tr("Toggle the viewer's fullscreen mode."));
|
|
|
|
prev_radius = sceneRadius();
|
|
d->has_text = false;
|
|
d->i_is_pressed = false;
|
|
d->z_is_pressed = false;
|
|
d->distance_is_displayed = false;
|
|
d->is_d_pressed = false;
|
|
d->viewer = this;
|
|
setTextIsEnabled(true);
|
|
}
|
|
|
|
Viewer::Viewer(QWidget* parent, bool antialiasing)
|
|
: CGAL::Three::Viewer_interface(parent)
|
|
{
|
|
d = new Viewer_impl;
|
|
d->antialiasing = antialiasing;
|
|
doBindings();
|
|
}
|
|
|
|
Viewer::Viewer(QWidget* parent,
|
|
Viewer* sharedWidget,
|
|
bool antialiasing)
|
|
: CGAL::Three::Viewer_interface(parent, sharedWidget)
|
|
{
|
|
d = new Viewer_impl;
|
|
d->viewer = this;
|
|
d->shareViewer = sharedWidget;
|
|
is_sharing = true;
|
|
d->antialiasing = antialiasing;
|
|
this->setProperty("draw_two_sides", false);
|
|
this->setProperty("back_front_shading", false);
|
|
this->setProperty("helpText", QString("This is a sub-viewer. It displays the scene "
|
|
"from another point of view. \n "));
|
|
is_ogl_4_3 = sharedWidget->is_ogl_4_3;
|
|
d->_recentFunctions = sharedWidget->d->_recentFunctions;
|
|
doBindings();
|
|
d->total_pass = sharedWidget->total_pass();
|
|
setOffset(sharedWidget->offset());
|
|
}
|
|
|
|
Viewer::~Viewer()
|
|
{
|
|
makeCurrent();
|
|
QSettings viewer_settings;
|
|
viewer_settings.setValue("cam_pos",
|
|
QString("%1,%2,%3")
|
|
.arg(d->position.x())
|
|
.arg(d->position.y())
|
|
.arg(d->position.z()));
|
|
viewer_settings.setValue("ambient",
|
|
QString("%1,%2,%3")
|
|
.arg(d->ambient.x())
|
|
.arg(d->ambient.y())
|
|
.arg(d->ambient.z()));
|
|
viewer_settings.setValue("diffuse",
|
|
QString("%1,%2,%3")
|
|
.arg(d->diffuse.x())
|
|
.arg(d->diffuse.y())
|
|
.arg(d->diffuse.z()));
|
|
viewer_settings.setValue("specular",
|
|
QString("%1,%2,%3")
|
|
.arg(d->specular.x())
|
|
.arg(d->specular.y())
|
|
.arg(d->specular.z()));
|
|
viewer_settings.setValue("spec_power",
|
|
d->spec_power);
|
|
viewer_settings.setValue("front_color",
|
|
QString("%1,%2,%3")
|
|
.arg(d->front_color.redF())
|
|
.arg(d->front_color.greenF())
|
|
.arg(d->front_color.blueF()));
|
|
viewer_settings.setValue("back_color",
|
|
QString("%1,%2,%3")
|
|
.arg(d->back_color.redF())
|
|
.arg(d->back_color.greenF())
|
|
.arg(d->back_color.blueF()));
|
|
makeCurrent();
|
|
d->vao.destroy();
|
|
if(d->_recentFunctions)
|
|
delete d->_recentFunctions;
|
|
if(d->painter)
|
|
delete d->painter;
|
|
if(d->textRenderer)
|
|
d->textRenderer->deleteLater();
|
|
delete d;
|
|
|
|
}
|
|
|
|
void Viewer::setScene(CGAL::Three::Scene_draw_interface* scene)
|
|
{
|
|
d->scene = scene;
|
|
}
|
|
|
|
bool Viewer::antiAliasing() const
|
|
{
|
|
return d->antialiasing;
|
|
}
|
|
|
|
void Viewer::setAntiAliasing(bool b)
|
|
{
|
|
d->antialiasing = b;
|
|
update();
|
|
}
|
|
|
|
void Viewer::setTwoSides(bool b)
|
|
{
|
|
this->setProperty("draw_two_sides", b);
|
|
d->twosides = b;
|
|
update();
|
|
}
|
|
|
|
|
|
void Viewer::setBackFrontShading(bool b)
|
|
{
|
|
this->setProperty("back_front_shading", b);
|
|
update();
|
|
}
|
|
|
|
|
|
void Viewer::setFastDrawing(bool b)
|
|
{
|
|
d->inFastDrawing = b;
|
|
update();
|
|
}
|
|
|
|
bool Viewer::inFastDrawing() const
|
|
{
|
|
return (d->inFastDrawing
|
|
&& (camera()->frame()->isSpinning()
|
|
|| camera()->frame()->isManipulated()));
|
|
}
|
|
|
|
void Viewer::draw()
|
|
{
|
|
glEnable(GL_DEPTH_TEST);
|
|
d->draw_aux(false, this);
|
|
}
|
|
|
|
void Viewer::fastDraw()
|
|
{
|
|
d->draw_aux(false, this);
|
|
}
|
|
|
|
void Viewer::init()
|
|
{
|
|
if(!isOpenGL_4_3())
|
|
{
|
|
std::cerr<<"The openGL context initialization failed "
|
|
"and the default context (2.0 ES) will be used. \n"
|
|
" This means, among other things, that no widelines can be displayed,"
|
|
" which makes selected edges harder to see." <<std::endl;
|
|
}
|
|
else
|
|
{
|
|
d->_recentFunctions = new QOpenGLFunctions_4_3_Core();
|
|
d->_recentFunctions->initializeOpenGLFunctions();
|
|
}
|
|
d->logger = new QOpenGLDebugLogger(this);
|
|
if(!d->logger->initialize())
|
|
qDebug()<<"logger could not init.";
|
|
else{
|
|
connect(d->logger, SIGNAL(messageLogged(QOpenGLDebugMessage)), this, SLOT(messageLogged(QOpenGLDebugMessage)));
|
|
d->logger->startLogging();
|
|
}
|
|
glDrawArraysInstanced = reinterpret_cast<PFNGLDRAWARRAYSINSTANCEDARBPROC>(this->context()->getProcAddress("glDrawArraysInstancedARB"));
|
|
if(!glDrawArraysInstanced)
|
|
{
|
|
qDebug()<<"glDrawArraysInstancedARB : extension not found. Spheres will be displayed as points.";
|
|
d->extension_is_found = false;
|
|
}
|
|
else
|
|
d->extension_is_found = true;
|
|
|
|
glVertexAttribDivisor = reinterpret_cast<PFNGLVERTEXATTRIBDIVISORARBPROC>(this->context()->getProcAddress("glVertexAttribDivisorARB"));
|
|
if(!glDrawArraysInstanced)
|
|
{
|
|
qDebug()<<"glVertexAttribDivisorARB : extension not found. Spheres will be displayed as points.";
|
|
d->extension_is_found = false;
|
|
}
|
|
else
|
|
d->extension_is_found = true;
|
|
QSettings settings;
|
|
QString colorname = settings.value("background_color", "#ffffff").toString();
|
|
QColor bc(colorname);
|
|
setBackgroundColor(bc);
|
|
d->vao.create();
|
|
d->buffer.create();
|
|
|
|
//setting the program used for the distance
|
|
if(!is_linked)
|
|
{
|
|
//Vertex source code
|
|
const char vertex_source_dist[] =
|
|
{
|
|
"#version 150 \n"
|
|
"in vec4 vertex;\n"
|
|
"uniform mat4 mvp_matrix;\n"
|
|
"uniform float point_size;\n"
|
|
"void main(void)\n"
|
|
"{\n"
|
|
" gl_PointSize = point_size; \n"
|
|
" gl_Position = mvp_matrix * vertex; \n"
|
|
"} \n"
|
|
"\n"
|
|
};
|
|
const char vertex_source_comp_dist[] =
|
|
{
|
|
"attribute highp vec4 vertex;\n"
|
|
"uniform highp mat4 mvp_matrix;\n"
|
|
"uniform highp float point_size;\n"
|
|
"void main(void)\n"
|
|
"{\n"
|
|
" gl_PointSize = point_size; \n"
|
|
" gl_Position = mvp_matrix * vertex; \n"
|
|
"} \n"
|
|
"\n"
|
|
};
|
|
//Fragment source code
|
|
const char fragment_source_dist[] =
|
|
{
|
|
"#version 150 \n"
|
|
"out vec4 out_color; \n"
|
|
"void main(void) { \n"
|
|
"out_color = vec4(0.0,0.0,0.0,1.0); \n"
|
|
"} \n"
|
|
"\n"
|
|
};
|
|
const char fragment_source_comp_dist[] =
|
|
{
|
|
"void main(void) { \n"
|
|
"gl_FragColor = vec4(0.0,0.0,0.0,1.0); \n"
|
|
"} \n"
|
|
"\n"
|
|
};
|
|
QOpenGLShader vertex_shader(QOpenGLShader::Vertex);
|
|
QOpenGLShader fragment_shader(QOpenGLShader::Fragment);
|
|
if(isOpenGL_4_3())
|
|
{
|
|
if(!vertex_shader.compileSourceCode(vertex_source_dist))
|
|
{
|
|
std::cerr<<"Compiling vertex source FAILED"<<std::endl;
|
|
}
|
|
|
|
if(!fragment_shader.compileSourceCode(fragment_source_dist))
|
|
{
|
|
std::cerr<<"Compiling fragmentsource FAILED"<<std::endl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!vertex_shader.compileSourceCode(vertex_source_comp_dist))
|
|
{
|
|
std::cerr<<"Compiling vertex source FAILED"<<std::endl;
|
|
}
|
|
|
|
if(!fragment_shader.compileSourceCode(fragment_source_comp_dist))
|
|
{
|
|
std::cerr<<"Compiling fragmentsource FAILED"<<std::endl;
|
|
}
|
|
}
|
|
if(!d->rendering_program_dist.addShader(&vertex_shader))
|
|
{
|
|
std::cerr<<"adding vertex shader FAILED"<<std::endl;
|
|
}
|
|
if(!d->rendering_program_dist.addShader(&fragment_shader))
|
|
{
|
|
std::cerr<<"adding fragment shader FAILED"<<std::endl;
|
|
}
|
|
if(!d->rendering_program_dist.link())
|
|
{
|
|
qDebug() << d->rendering_program_dist.log();
|
|
}
|
|
}
|
|
d->painter = new QPainter();
|
|
}
|
|
|
|
#include <QMouseEvent>
|
|
|
|
void Viewer::mousePressEvent(QMouseEvent* event)
|
|
{
|
|
makeCurrent();
|
|
if(event->button() == Qt::RightButton &&
|
|
event->modifiers().testFlag(Qt::ShiftModifier))
|
|
{
|
|
select(event->pos());
|
|
requestContextMenu(event->globalPos());
|
|
event->accept();
|
|
}
|
|
else if(!event->modifiers()
|
|
&& event->button() == Qt::LeftButton
|
|
&& d->i_is_pressed)
|
|
{
|
|
d->scene->printPrimitiveId(event->pos(), this);
|
|
}
|
|
else if(!event->modifiers()
|
|
&& event->button() == Qt::LeftButton
|
|
&& d->z_is_pressed)
|
|
{
|
|
d->scene->zoomToPosition(event->pos(), this);
|
|
}
|
|
else if(!event->modifiers()
|
|
&& event->button() == Qt::LeftButton
|
|
&& d->is_d_pressed)
|
|
{
|
|
d->showDistance(event->pos());
|
|
event->accept();
|
|
}
|
|
else{
|
|
makeCurrent();
|
|
CGAL::QGLViewer::mousePressEvent(event);
|
|
}
|
|
}
|
|
void Viewer::mouseDoubleClickEvent(QMouseEvent* event)
|
|
{
|
|
makeCurrent();
|
|
CGAL::QGLViewer::mouseDoubleClickEvent(event);
|
|
}
|
|
|
|
#include <QContextMenuEvent>
|
|
void Viewer::contextMenuEvent(QContextMenuEvent* event)
|
|
{
|
|
if(event->reason() != QContextMenuEvent::Mouse) {
|
|
requestContextMenu(event->globalPos());
|
|
event->accept();
|
|
}
|
|
else {
|
|
CGAL::QGLViewer::contextMenuEvent(event);
|
|
}
|
|
}
|
|
|
|
void Viewer::keyPressEvent(QKeyEvent* e)
|
|
{
|
|
if(!e->modifiers()) {
|
|
if(e->key() == Qt::Key_T) {
|
|
turnCameraBy180Degres();
|
|
return;
|
|
}
|
|
else if(e->key() == Qt::Key_M) {
|
|
d->macro_mode = ! d->macro_mode;
|
|
switch(camera()->type()){
|
|
case CGAL::qglviewer::Camera::PERSPECTIVE:
|
|
if(d->macro_mode) {
|
|
camera()->setZNearCoefficient(0.0005f);
|
|
} else {
|
|
camera()->setZNearCoefficient(0.005f);
|
|
}
|
|
break;
|
|
case CGAL::qglviewer::Camera::ORTHOGRAPHIC:
|
|
if(d->macro_mode) {
|
|
camera()->setOrthoZNear(-0.5f);
|
|
} else {
|
|
camera()->setOrthoZNear(0.0f);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
this->displayMessage(tr("Macro mode: %1").
|
|
arg(d->macro_mode ? tr("on") : tr("off")));
|
|
|
|
|
|
|
|
return;
|
|
}
|
|
else if(e->key() == Qt::Key_I) {
|
|
d->i_is_pressed = true;
|
|
}
|
|
else if(e->key() == Qt::Key_O) {
|
|
d->z_is_pressed = true;
|
|
}
|
|
else if(e->key() == Qt::Key_D) {
|
|
if(e->isAutoRepeat())
|
|
{
|
|
return;
|
|
}
|
|
if(!d->is_d_pressed)
|
|
{
|
|
d->clearDistancedisplay();
|
|
}
|
|
d->is_d_pressed = true;
|
|
update();
|
|
return;
|
|
}
|
|
}
|
|
else if(e->key() == Qt::Key_I && e->modifiers() & Qt::ControlModifier){
|
|
d->scene->printAllIds();
|
|
update();
|
|
return;
|
|
}
|
|
|
|
else if(e->key() == Qt::Key_C && e->modifiers() & Qt::ControlModifier){
|
|
d->sendSnapshotToClipboard(this);
|
|
return;
|
|
}
|
|
|
|
else if(e->key() == Qt::Key_S && e->modifiers() & Qt::ControlModifier){
|
|
this->saveSnapshot();
|
|
return;
|
|
}
|
|
|
|
//forward the event to the scene (item handling of the event)
|
|
if (! d->scene->keyPressEvent(e) )
|
|
CGAL::QGLViewer::keyPressEvent(e);
|
|
}
|
|
|
|
void Viewer::keyReleaseEvent(QKeyEvent *e)
|
|
{
|
|
if(e->key() == Qt::Key_I) {
|
|
d->i_is_pressed = false;
|
|
}
|
|
else if(e->key() == Qt::Key_O) {
|
|
d->z_is_pressed = false;
|
|
}
|
|
else if(!e->modifiers() && e->key() == Qt::Key_D)
|
|
{
|
|
if(e->isAutoRepeat())
|
|
{
|
|
return;
|
|
}
|
|
d->is_d_pressed = false;
|
|
}
|
|
CGAL::QGLViewer::keyReleaseEvent(e);
|
|
}
|
|
|
|
void Viewer::turnCameraBy180Degres() {
|
|
CGAL::qglviewer::Camera* camera = this->camera();
|
|
using CGAL::qglviewer::ManipulatedCameraFrame;
|
|
|
|
ManipulatedCameraFrame frame_from(*camera->frame());
|
|
camera->setViewDirection(-camera->viewDirection());
|
|
ManipulatedCameraFrame frame_to(*camera->frame());
|
|
|
|
camera->setOrientation(frame_from.orientation());
|
|
camera->interpolateTo(frame_to, 0.5f);
|
|
}
|
|
|
|
void Viewer_impl::draw_aux(bool with_names, Viewer* viewer)
|
|
{
|
|
if(scene == nullptr)
|
|
return;
|
|
current_total_pass = viewer->inFastDrawing() ? total_pass/2 : total_pass;
|
|
viewer->setGlPointSize(2.f);
|
|
viewer->glEnable(GL_POLYGON_OFFSET_FILL);
|
|
viewer->glPolygonOffset(1.0f,1.0f);
|
|
|
|
if(!with_names && antialiasing)
|
|
{
|
|
viewer->glEnable(GL_BLEND);
|
|
viewer->glEnable(GL_LINE_SMOOTH);
|
|
viewer->glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
|
|
//viewer->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
viewer->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
else
|
|
{
|
|
viewer->glDisable(GL_BLEND);
|
|
viewer->glDisable(GL_LINE_SMOOTH);
|
|
viewer->glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);
|
|
//viewer->glBlendFunc(GL_ONE, GL_ZERO);
|
|
viewer->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
inDrawWithNames = with_names;
|
|
if(with_names)
|
|
scene->drawWithNames(viewer);
|
|
else
|
|
scene->draw(viewer);
|
|
viewer->glDisable(GL_POLYGON_OFFSET_FILL);
|
|
}
|
|
|
|
bool Viewer::inDrawWithNames() const {
|
|
return d->inDrawWithNames;
|
|
}
|
|
|
|
void Viewer::drawWithNames()
|
|
{
|
|
CGAL::QGLViewer::draw();
|
|
d->draw_aux(true, this);
|
|
}
|
|
|
|
void Viewer::postSelection(const QPoint& pixel)
|
|
{
|
|
Q_EMIT selected(this->selectedName());
|
|
CGAL::qglviewer::Vec point;
|
|
bool found = true;
|
|
if(property("picked_point").isValid()) {
|
|
if(!property("picked_point").toList().isEmpty())
|
|
{
|
|
QList<QVariant> picked_point = property("picked_point").toList();
|
|
point = CGAL::qglviewer::Vec (picked_point[0].toDouble(),
|
|
picked_point[1].toDouble(),
|
|
picked_point[2].toDouble());
|
|
}
|
|
else{
|
|
found = false;
|
|
}
|
|
}
|
|
else{
|
|
point = camera()->pointUnderPixel(pixel, found) - offset();
|
|
}
|
|
if(found) {
|
|
QVector3D transformed_point(point.x,
|
|
point.y,
|
|
point.z);
|
|
if(d->scene_scaling)
|
|
{
|
|
transformed_point = QVector3D(
|
|
point.x+offset().x,
|
|
point.y+offset().y,
|
|
point.z+offset().z);
|
|
transformed_point = transformed_point/d->scaler;
|
|
transformed_point[0] -=offset().x ;
|
|
transformed_point[1] -=offset().y ;
|
|
transformed_point[2] -=offset().z ;
|
|
}
|
|
Q_EMIT selectedPoint(transformed_point.x(),
|
|
transformed_point.y(),
|
|
transformed_point.z());
|
|
CGAL::qglviewer::Vec dir;
|
|
CGAL::qglviewer::Vec orig;
|
|
if(d->projection_is_ortho)
|
|
{
|
|
dir = camera()->viewDirection();
|
|
orig = point;
|
|
}
|
|
else{
|
|
orig = camera()->position() - offset();
|
|
dir = point - orig;
|
|
}
|
|
this->setProperty("performing_selection", true);
|
|
Q_EMIT selectionRay(orig.x, orig.y, orig.z,
|
|
dir.x, dir.y, dir.z);
|
|
this->setProperty("performing_selection", false);
|
|
}
|
|
}
|
|
bool CGAL::Three::Viewer_interface::readFrame(QString s, CGAL::qglviewer::Frame& frame)
|
|
{
|
|
QStringList list = s.split(" ", CGAL_QT_SKIP_EMPTY_PARTS);
|
|
if(list.size() != 7)
|
|
return false;
|
|
float vec[3];
|
|
for(int i = 0; i < 3; ++i)
|
|
{
|
|
bool ok;
|
|
vec[i] = list[i].toFloat(&ok);
|
|
if(!ok) return false;
|
|
}
|
|
double orient[4];
|
|
for(int i = 0; i < 4; ++i)
|
|
{
|
|
bool ok;
|
|
orient[i] = list[i + 3].toDouble(&ok);
|
|
if(!ok) return false;
|
|
}
|
|
frame.setPosition(CGAL::qglviewer::Vec(vec[0],
|
|
vec[1],
|
|
vec[2]));
|
|
frame.setOrientation(orient[0],
|
|
orient[1],
|
|
orient[2],
|
|
orient[3]);
|
|
return true;
|
|
}
|
|
|
|
QString CGAL::Three::Viewer_interface::dumpFrame(const CGAL::qglviewer::Frame& frame) {
|
|
const CGAL::qglviewer::Vec pos = frame.position();
|
|
const CGAL::qglviewer::Quaternion q = frame.orientation();
|
|
|
|
return QString("%1 %2 %3 %4 %5 %6 %7")
|
|
.arg(pos[0])
|
|
.arg(pos[1])
|
|
.arg(pos[2])
|
|
.arg(q[0])
|
|
.arg(q[1])
|
|
.arg(q[2])
|
|
.arg(q[3]);
|
|
}
|
|
|
|
bool Viewer::moveCameraToCoordinates(QString s, float animation_duration) {
|
|
CGAL::qglviewer::Frame new_frame;
|
|
if(readFrame(s, new_frame)) {
|
|
camera()->interpolateTo(new_frame, animation_duration);
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
QString Viewer::dumpCameraCoordinates()
|
|
{
|
|
if(camera()->frame()) {
|
|
return dumpFrame(*camera()->frame());
|
|
} else {
|
|
return QString();
|
|
}
|
|
}
|
|
|
|
void Viewer::attribBuffers(int program_name) const {
|
|
//ModelViewMatrix used for the transformation of the camera.
|
|
QMatrix4x4 mvp_mat;
|
|
// ModelView Matrix used for the lighting system
|
|
QMatrix4x4 mv_mat;
|
|
// transformation of the manipulated frame
|
|
QMatrix4x4 f_mat;
|
|
// ModelView Matrix that is modified just for the normal matrix in case of scene scaling
|
|
QMatrix4x4 norm_mat;
|
|
|
|
f_mat.setToIdentity();
|
|
//fills the MVP and MV matrices.
|
|
GLdouble d_mat[16];
|
|
this->camera()->getModelViewMatrix(d_mat);
|
|
for (int i=0; i<16; ++i)
|
|
mv_mat.data()[i] = GLfloat(d_mat[i]);
|
|
this->camera()->getModelViewProjectionMatrix(d_mat);
|
|
for (int i=0; i<16; ++i)
|
|
mvp_mat.data()[i] = GLfloat(d_mat[i]);
|
|
|
|
norm_mat = mv_mat;
|
|
|
|
if(d->scene_scaling){
|
|
mvp_mat.scale(d->scaler);
|
|
mv_mat.scale(d->scaler);
|
|
QVector3D scale_norm(1.0/d->scaler.x(), 1.0/d->scaler.y(), 1.0/d->scaler.z());
|
|
norm_mat.scale(scale_norm);
|
|
}
|
|
|
|
QOpenGLShaderProgram* program = getShaderProgram(program_name);
|
|
program->bind();
|
|
program->setUniformValue("point_size", getGlPointSize());
|
|
program->setUniformValue("mvp_matrix", mvp_mat);
|
|
QMatrix4x4 id_mat;
|
|
id_mat.setToIdentity();
|
|
program->setUniformValue("f_matrix", id_mat);
|
|
program->setUniformValue("is_clipbox_on", d->clipping);
|
|
if(d->clipping)
|
|
{
|
|
QMatrix4x4 clipbox1;
|
|
QMatrix4x4 clipbox2;
|
|
for(int i=0;i<12;++i)
|
|
{
|
|
clipbox1.data()[i]=d->clipbox[i/4][i%4];
|
|
clipbox2.data()[i]=d->clipbox[(i+12)/4][(i+12)%4];
|
|
}
|
|
program->setUniformValue("clipbox1", clipbox1);
|
|
program->setUniformValue("clipbox2", clipbox2);
|
|
}
|
|
QVector4D light_pos(d->position.x(),
|
|
d->position.y(),
|
|
d->position.z(),
|
|
1.0f);
|
|
switch(program_name)
|
|
{
|
|
case PROGRAM_WITH_LIGHT:
|
|
case PROGRAM_SPHERES:
|
|
case PROGRAM_CUTPLANE_SPHERES:
|
|
case PROGRAM_NO_SELECTION:
|
|
case PROGRAM_HEAT_INTENSITY:
|
|
program->setUniformValue("alpha", 1.0f); //overriden in item draw() if necessary
|
|
default:
|
|
break;
|
|
}
|
|
switch(program_name)
|
|
{
|
|
case PROGRAM_SPHERES:
|
|
case PROGRAM_DARK_SPHERES:
|
|
case PROGRAM_WITH_LIGHT:
|
|
case PROGRAM_OLD_FLAT:
|
|
program->setUniformValue("f_matrix",f_mat);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch(program_name)
|
|
{
|
|
case PROGRAM_WITH_LIGHT:
|
|
case PROGRAM_C3T3:
|
|
case PROGRAM_PLANE_TWO_FACES:
|
|
case PROGRAM_INSTANCED:
|
|
case PROGRAM_WITH_TEXTURE:
|
|
case PROGRAM_CUTPLANE_SPHERES:
|
|
case PROGRAM_SPHERES:
|
|
case PROGRAM_OLD_FLAT:
|
|
case PROGRAM_FLAT:
|
|
case PROGRAM_NO_INTERPOLATION:
|
|
case PROGRAM_HEAT_INTENSITY:
|
|
case PROGRAM_TETRA_FILTERING:
|
|
program->setUniformValue("light_pos", light_pos);
|
|
program->setUniformValue("light_diff",d->diffuse);
|
|
program->setUniformValue("light_spec", d->specular);
|
|
program->setUniformValue("light_amb", d->ambient);
|
|
program->setUniformValue("spec_power", d->spec_power);
|
|
program->setUniformValue("front_color", d->front_color);
|
|
program->setUniformValue("back_color", d->back_color);
|
|
program->setUniformValue("is_two_side", d->twosides);
|
|
program->setUniformValue("back_front_shading", this->property("back_front_shading").toBool());
|
|
break;
|
|
}
|
|
switch(program_name)
|
|
{
|
|
case PROGRAM_WITH_LIGHT:
|
|
case PROGRAM_C3T3:
|
|
case PROGRAM_PLANE_TWO_FACES:
|
|
case PROGRAM_INSTANCED:
|
|
case PROGRAM_CUTPLANE_SPHERES:
|
|
case PROGRAM_SPHERES:
|
|
case PROGRAM_OLD_FLAT:
|
|
case PROGRAM_FLAT:
|
|
case PROGRAM_NO_INTERPOLATION:
|
|
case PROGRAM_HEAT_INTENSITY:
|
|
case PROGRAM_TETRA_FILTERING:
|
|
program->setUniformValue("mv_matrix", mv_mat);
|
|
program->setUniformValue("norm_matrix", norm_mat);
|
|
break;
|
|
case PROGRAM_WITHOUT_LIGHT:
|
|
case PROGRAM_SOLID_WIREFRAME:
|
|
break;
|
|
case PROGRAM_WITH_TEXTURE:
|
|
program->setUniformValue("mv_matrix", mv_mat);
|
|
program->setUniformValue("norm_matrix", norm_mat);
|
|
program->setUniformValue("s_texture",0);
|
|
program->setUniformValue("f_matrix",f_mat);
|
|
break;
|
|
case PROGRAM_WITH_TEXTURED_EDGES:
|
|
program->setUniformValue("s_texture",0);
|
|
break;
|
|
case PROGRAM_NO_SELECTION:
|
|
program->setUniformValue("f_matrix",f_mat);
|
|
break;
|
|
}
|
|
program->release();
|
|
}
|
|
|
|
void Viewer::beginSelection(const QPoint &point)
|
|
{
|
|
CGAL::QGLViewer::beginSelection(point);
|
|
d->scene->setPickedPixel(point);
|
|
}
|
|
void Viewer::endSelection(const QPoint& point)
|
|
{
|
|
CGAL::QGLViewer::endSelection(point);
|
|
//redraw the true scene for the glReadPixel in postSelection();
|
|
d->draw_aux(false, this);
|
|
}
|
|
|
|
void Viewer::drawVisualHints()
|
|
{
|
|
|
|
CGAL::QGLViewer::drawVisualHints();
|
|
|
|
if(d->distance_is_displayed)
|
|
{
|
|
glDisable(GL_DEPTH_TEST);
|
|
QMatrix4x4 mvpMatrix;
|
|
double mat[16];
|
|
camera()->getModelViewProjectionMatrix(mat);
|
|
for(int i=0; i < 16; i++)
|
|
{
|
|
mvpMatrix.data()[i] = static_cast<float>(mat[i]);
|
|
}
|
|
if(!isOpenGL_4_3())
|
|
{
|
|
//draws the distance
|
|
//nullifies the translation
|
|
d->rendering_program_dist.bind();
|
|
d->rendering_program_dist.setUniformValue("mvp_matrix", mvpMatrix);
|
|
d->rendering_program_dist.setUniformValue("point_size", GLfloat(6.0f));
|
|
d->vao.bind();
|
|
glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(2));
|
|
glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(2));
|
|
d->vao.release();
|
|
d->rendering_program_dist.release();
|
|
glEnable(GL_DEPTH_TEST);
|
|
}
|
|
else
|
|
{
|
|
QOpenGLShaderProgram* program = getShaderProgram(PROGRAM_SOLID_WIREFRAME);
|
|
program->bind();
|
|
QVector2D vp(width(), height());
|
|
program->setUniformValue("viewport", vp);
|
|
program->setUniformValue("near",static_cast<GLfloat>(camera()->zNear()));
|
|
program->setUniformValue("far",static_cast<GLfloat>(camera()->zFar()));
|
|
program->setUniformValue("width", GLfloat(3.0f));
|
|
program->setAttributeValue("colors", QColor(Qt::black));
|
|
program->setUniformValue("mvp_matrix", mvpMatrix);
|
|
QMatrix4x4 f_mat;
|
|
f_mat.setToIdentity();
|
|
program->setUniformValue("f_matrix", f_mat);
|
|
d->vao.bind();
|
|
glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(2));
|
|
d->vao.release();
|
|
program->release();
|
|
|
|
program = getShaderProgram(PROGRAM_NO_SELECTION);
|
|
program->bind();
|
|
program->setAttributeValue("colors", QColor(Qt::black));
|
|
program->setAttributeValue("point_size", 6.0f);
|
|
program->setUniformValue("mvp_matrix", mvpMatrix);
|
|
program->setUniformValue("f_matrix", f_mat);
|
|
d->vao.bind();
|
|
glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(2));
|
|
d->vao.release();
|
|
program->release();
|
|
}
|
|
|
|
}
|
|
if (!d->painter->isActive())
|
|
d->painter->begin(this);
|
|
//So that the text is drawn in front of everything
|
|
d->painter->beginNativePainting();
|
|
glDisable(GL_DEPTH_TEST);
|
|
d->painter->endNativePainting();
|
|
//Prints the displayMessage
|
|
QFont font = QFont();
|
|
QFontMetrics fm(font);
|
|
TextItem *message_text = new TextItem(float(10 +
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
|
|
fm.horizontalAdvance(d->message)/2)
|
|
#else
|
|
fm.width(d->message)/2)
|
|
#endif
|
|
,
|
|
float(height()-20),
|
|
0, d->message, false,
|
|
QFont(), Qt::gray );
|
|
if (d->_displayMessage)
|
|
{
|
|
d->textRenderer->addText(message_text);
|
|
}
|
|
d->textRenderer->draw(this, d->scaler);
|
|
|
|
if (d->_displayMessage)
|
|
d->textRenderer->removeText(message_text);
|
|
delete message_text;
|
|
}
|
|
|
|
QOpenGLShaderProgram* Viewer::declare_program(int name,
|
|
const char* v_shader,
|
|
const char* f_shader) const
|
|
{
|
|
// workaround constness issues in Qt
|
|
Viewer* viewer = const_cast<Viewer*>(this);
|
|
|
|
if(d->shader_programs[name])
|
|
{
|
|
return d->shader_programs[name];
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
QOpenGLShaderProgram *program = new QOpenGLShaderProgram(viewer);
|
|
if(!program->addShaderFromSourceFile(QOpenGLShader::Vertex,v_shader))
|
|
{
|
|
std::cerr<<"adding vertex shader FAILED"<<std::endl;
|
|
}
|
|
if(!program->addShaderFromSourceFile(QOpenGLShader::Fragment,f_shader))
|
|
{
|
|
std::cerr<<"adding fragment shader FAILED"<<std::endl;
|
|
}
|
|
if(isOpenGL_4_3())
|
|
{
|
|
if(strcmp(f_shader,":/cgal/Polyhedron_3/resources/shader_flat.frag" ) == 0)
|
|
{
|
|
if(!program->addShaderFromSourceFile(QOpenGLShader::Geometry,":/cgal/Polyhedron_3/resources/shader_flat.geom" ))
|
|
{
|
|
std::cerr<<"adding geometry shader FAILED"<<std::endl;
|
|
}
|
|
}
|
|
if(strcmp(f_shader,":/cgal/Polyhedron_3/resources/solid_wireframe_shader.frag" ) == 0)
|
|
{
|
|
if(!program->addShaderFromSourceFile(QOpenGLShader::Geometry,":/cgal/Polyhedron_3/resources/solid_wireframe_shader.geom" ))
|
|
{
|
|
std::cerr<<"adding geometry shader FAILED"<<std::endl;
|
|
}
|
|
}
|
|
if(strcmp(f_shader,":/cgal/Polyhedron_3/resources/no_interpolation_shader.frag" ) == 0)
|
|
{
|
|
if(!program->addShaderFromSourceFile(QOpenGLShader::Geometry,":/cgal/Polyhedron_3/resources/no_interpolation_shader.geom" ))
|
|
{
|
|
std::cerr<<"adding geometry shader FAILED"<<std::endl;
|
|
}
|
|
}
|
|
}
|
|
program->bindAttributeLocation("colors", 1);
|
|
program->link();
|
|
d->shader_programs[name] = program;
|
|
return program;
|
|
}
|
|
}
|
|
QOpenGLShaderProgram* Viewer::getShaderProgram(int name) const
|
|
{
|
|
switch(name)
|
|
{
|
|
case PROGRAM_C3T3:
|
|
{
|
|
QOpenGLShaderProgram* program = isOpenGL_4_3()
|
|
? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_c3t3.vert" , ":/cgal/Polyhedron_3/resources/shader_c3t3.frag")
|
|
: declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_c3t3.vert" ,
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_c3t3.frag");
|
|
program->setProperty("hasLight", true);
|
|
program->setProperty("hasNormals", true);
|
|
program->setProperty("hasCutPlane", true);
|
|
program->setProperty("hasTransparency", true);
|
|
program->setProperty("hasCenter", true);
|
|
program->setProperty("hasSurfaceMode", true);
|
|
program->setProperty("hasSubdomainIndicesValues", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_C3T3_EDGES:
|
|
{
|
|
QOpenGLShaderProgram* program = isOpenGL_4_3()
|
|
? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_c3t3_edges.vert" , ":/cgal/Polyhedron_3/resources/shader_c3t3_edges.frag")
|
|
: declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_c3t3_edges.vert" ,
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_c3t3_edges.frag");
|
|
program->setProperty("hasCutPlane", true);
|
|
program->setProperty("hasSurfaceMode", true);
|
|
program->setProperty("hasSubdomainIndicesValues", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_WITH_LIGHT:
|
|
{
|
|
QOpenGLShaderProgram* program = isOpenGL_4_3()
|
|
? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_with_light.vert" , ":/cgal/Polyhedron_3/resources/shader_with_light.frag")
|
|
: declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_light.vert" ,
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_light.frag");
|
|
program->setProperty("hasLight", true);
|
|
program->setProperty("hasNormals", true);
|
|
program->setProperty("hasTransparency", true);
|
|
program->setProperty("hasFMatrix", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_HEAT_INTENSITY:
|
|
{
|
|
QOpenGLShaderProgram* program = isOpenGL_4_3()
|
|
? declare_program(name, ":/cgal/Polyhedron_3/resources/heat_intensity_shader.vert" , ":/cgal/Polyhedron_3/resources/heat_intensity_shader.frag")
|
|
: declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/heat_intensity_shader.vert" ,
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/heat_intensity_shader.frag");
|
|
program->setProperty("hasLight", true);
|
|
program->setProperty("hasNormals", true);
|
|
program->setProperty("hasTransparency", true);
|
|
program->setProperty("hasDistanceValues", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_WITHOUT_LIGHT:
|
|
{
|
|
QOpenGLShaderProgram* program = isOpenGL_4_3()
|
|
? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_without_light.vert" , ":/cgal/Polyhedron_3/resources/shader_without_light.frag")
|
|
: declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_without_light.vert" ,
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_without_light.frag");
|
|
program->setProperty("hasFMatrix", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_NO_SELECTION:
|
|
{
|
|
QOpenGLShaderProgram* program = isOpenGL_4_3()
|
|
? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_without_light.vert" , ":/cgal/Polyhedron_3/resources/shader_no_light_no_selection.frag")
|
|
: declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_without_light.vert" ,
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_no_light_no_selection.frag");
|
|
program->setProperty("hasFMatrix", true);
|
|
program->setProperty("hasTransparency", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_WITH_TEXTURE:
|
|
{
|
|
QOpenGLShaderProgram* program = isOpenGL_4_3()
|
|
? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_with_texture.vert" , ":/cgal/Polyhedron_3/resources/shader_with_texture.frag")
|
|
: declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_texture.vert" ,
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_texture.frag");
|
|
program->setProperty("hasLight", true);
|
|
program->setProperty("hasNormals", true);
|
|
program->setProperty("hasFMatrix", true);
|
|
program->setProperty("hasTexture", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_PLANE_TWO_FACES:
|
|
{
|
|
QOpenGLShaderProgram* program = isOpenGL_4_3()
|
|
?declare_program(name, ":/cgal/Polyhedron_3/resources/shader_without_light.vert" , ":/cgal/Polyhedron_3/resources/shader_plane_two_faces.frag")
|
|
: declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_without_light.vert" ,
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_plane_two_faces.frag");
|
|
program->setProperty("hasLight", true);
|
|
program->setProperty("hasNormals", true);
|
|
program->setProperty("hasFMatrix", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_WITH_TEXTURED_EDGES:
|
|
{
|
|
QOpenGLShaderProgram* program = isOpenGL_4_3()
|
|
? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_with_textured_edges.vert" , ":/cgal/Polyhedron_3/resources/shader_with_textured_edges.frag")
|
|
: declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_textured_edges.vert" ,
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_textured_edges.frag");
|
|
program->setProperty("hasFMatrix", true);
|
|
program->setProperty("hasTexture", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_INSTANCED:
|
|
{
|
|
QOpenGLShaderProgram* program = isOpenGL_4_3()
|
|
? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_instanced.vert" , ":/cgal/Polyhedron_3/resources/shader_with_light.frag")
|
|
: declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_instanced.vert" ,
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_light.frag");
|
|
|
|
program->setProperty("hasLight", true);
|
|
program->setProperty("hasNormals", true);
|
|
program->setProperty("isInstanced", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_INSTANCED_WIRE:
|
|
{
|
|
QOpenGLShaderProgram* program = isOpenGL_4_3()
|
|
? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_instanced.vert" , ":/cgal/Polyhedron_3/resources/shader_without_light.frag")
|
|
: declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_instanced.vert" ,
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_without_light.frag");
|
|
program->setProperty("hasLight", true);
|
|
program->setProperty("hasNormals", true);
|
|
program->setProperty("hasCenter", true);
|
|
program->setProperty("isInstanced", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_CUTPLANE_SPHERES:
|
|
{
|
|
QOpenGLShaderProgram* program = isOpenGL_4_3()
|
|
? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_c3t3_spheres.vert" , ":/cgal/Polyhedron_3/resources/shader_c3t3_spheres.frag")
|
|
: declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_c3t3_spheres.vert" ,
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_c3t3_spheres.frag");
|
|
program->setProperty("hasLight", true);
|
|
program->setProperty("hasNormals", true);
|
|
program->setProperty("hasCenter", true);
|
|
program->setProperty("hasRadius", true);
|
|
program->setProperty("isInstanced", true);
|
|
program->setProperty("hasCutPlane", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_SPHERES:
|
|
{
|
|
QOpenGLShaderProgram* program = isOpenGL_4_3()
|
|
?declare_program(name, ":/cgal/Polyhedron_3/resources/shader_spheres.vert" , ":/cgal/Polyhedron_3/resources/shader_with_light.frag")
|
|
: declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_spheres.vert" ,
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_light.frag");
|
|
program->setProperty("hasLight", true);
|
|
program->setProperty("hasNormals", true);
|
|
program->setProperty("hasCenter", true);
|
|
program->setProperty("hasRadius", true);
|
|
program->setProperty("hasTransparency", true);
|
|
program->setProperty("isInstanced", true);
|
|
program->setProperty("hasFMatrix", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_DARK_SPHERES:
|
|
{
|
|
QOpenGLShaderProgram* program = isOpenGL_4_3()
|
|
?declare_program(name, ":/cgal/Polyhedron_3/resources/shader_dark_spheres.vert" , ":/cgal/Polyhedron_3/resources/shader_no_light_no_selection.frag")
|
|
: declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_dark_spheres.vert" ,
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_no_light_no_selection.frag");
|
|
program->setProperty("hasCenter", true);
|
|
program->setProperty("hasRadius", true);
|
|
program->setProperty("isInstanced", true);
|
|
program->setProperty("hasFMatrix", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_FLAT:
|
|
{
|
|
if(!isOpenGL_4_3())
|
|
{
|
|
std::cerr<<"An OpenGL context of version 4.3 is required for the program ("<<name<<")."<<std::endl;
|
|
return nullptr;
|
|
}
|
|
QOpenGLShaderProgram* program = declare_program(name, ":/cgal/Polyhedron_3/resources/shader_flat.vert", ":/cgal/Polyhedron_3/resources/shader_flat.frag");
|
|
program->setProperty("hasLight", true);
|
|
program->setProperty("hasNormals", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_OLD_FLAT:
|
|
{
|
|
QOpenGLShaderProgram* program = isOpenGL_4_3()
|
|
? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_with_light.vert", ":/cgal/Polyhedron_3/resources/shader_old_flat.frag")
|
|
: declare_program(name,
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_light.vert",
|
|
":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_old_flat.frag");
|
|
program->setProperty("hasLight", true);
|
|
program->setProperty("hasNormals", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_SOLID_WIREFRAME:
|
|
{
|
|
if(!isOpenGL_4_3())
|
|
{
|
|
std::cerr<<"An OpenGL context of version 4.3 is required for the program ("<<name<<")."<<std::endl;
|
|
return nullptr;
|
|
}
|
|
QOpenGLShaderProgram* program = declare_program(name,
|
|
":/cgal/Polyhedron_3/resources/solid_wireframe_shader.vert",
|
|
":/cgal/Polyhedron_3/resources/solid_wireframe_shader.frag");
|
|
program->setProperty("hasViewport", true);
|
|
program->setProperty("hasWidth", true);
|
|
program->setProperty("hasFMatrix", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_NO_INTERPOLATION:
|
|
{
|
|
if(!isOpenGL_4_3())
|
|
{
|
|
std::cerr<<"An OpenGL context of version 4.3 is required for the program ("<<name<<")."<<std::endl;
|
|
return nullptr;
|
|
}
|
|
QOpenGLShaderProgram* program = declare_program(name,
|
|
":/cgal/Polyhedron_3/resources/no_interpolation_shader.vert",
|
|
":/cgal/Polyhedron_3/resources/no_interpolation_shader.frag");
|
|
program->setProperty("hasLight", true);
|
|
program->setProperty("hasNormals", true);
|
|
program->setProperty("drawLinesAdjacency", true);
|
|
return program;
|
|
}
|
|
case PROGRAM_TETRA_FILTERING:
|
|
{
|
|
if(!isOpenGL_4_3())
|
|
{
|
|
std::cerr<<"An OpenGL context of version 4.3 is required for the program ("<<name<<")."<<std::endl;
|
|
return nullptr;
|
|
}
|
|
QOpenGLShaderProgram* program = declare_program(name, ":/cgal/Polyhedron_3/resources/shader_tet_filter.vert", ":/cgal/Polyhedron_3/resources/shader_tet_filter.frag");
|
|
program->setProperty("hasLight", true);
|
|
program->setProperty("hasNormals", true);
|
|
program->setProperty("hasDistanceValues", true);
|
|
return program;
|
|
}
|
|
default:
|
|
std::cerr<<"ERROR : Program not found."<<std::endl;
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void Viewer::wheelEvent(QWheelEvent* e)
|
|
{
|
|
if(e->modifiers().testFlag(Qt::ShiftModifier))
|
|
{
|
|
double delta = e->angleDelta().y();
|
|
if(delta>0)
|
|
{
|
|
switch(camera()->type())
|
|
{
|
|
case CGAL::qglviewer::Camera::ORTHOGRAPHIC:
|
|
camera()->setOrthoZNear(camera()->orthoZNear() + 0.01);
|
|
break;
|
|
case CGAL::qglviewer::Camera::PERSPECTIVE:
|
|
camera()->setZNearCoefficient(camera()->zNearCoefficient() * 1.01);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
switch(camera()->type())
|
|
{
|
|
case CGAL::qglviewer::Camera::ORTHOGRAPHIC:
|
|
camera()->setOrthoZNear(camera()->orthoZNear() - 0.01);
|
|
break;
|
|
case CGAL::qglviewer::Camera::PERSPECTIVE:
|
|
camera()->setZNearCoefficient(camera()->zNearCoefficient() / 1.01);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
update();
|
|
}
|
|
else
|
|
CGAL::QGLViewer::wheelEvent(e);
|
|
}
|
|
|
|
bool Viewer::testDisplayId(double x, double y, double z)
|
|
{
|
|
return d->scene->testDisplayId(x,y,z,this);
|
|
}
|
|
|
|
QPainter* Viewer::getPainter(){return d->painter;}
|
|
|
|
void Viewer::paintEvent(QPaintEvent *)
|
|
{
|
|
paintGL();
|
|
}
|
|
|
|
void Viewer::paintGL()
|
|
{
|
|
makeCurrent();
|
|
if (!d->painter->isActive())
|
|
d->painter->begin(this);
|
|
if(d->is_2d_selection_mode)
|
|
{
|
|
d->painter->drawImage(QPoint(0,0), d->static_image);
|
|
}
|
|
else
|
|
{
|
|
d->painter->beginNativePainting();
|
|
glClearColor(GLfloat(backgroundColor().redF()),
|
|
GLfloat(backgroundColor().greenF()),
|
|
GLfloat(backgroundColor().blueF()),
|
|
1.f);
|
|
glClearDepthf(1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
|
|
//set the default frustum
|
|
if(d->projection_is_ortho)
|
|
camera()->setType(CGAL::qglviewer::Camera::ORTHOGRAPHIC);
|
|
else
|
|
camera()->setType(CGAL::qglviewer::Camera::PERSPECTIVE);
|
|
preDraw();
|
|
draw();
|
|
postDraw();
|
|
d->painter->endNativePainting();
|
|
}
|
|
d->painter->end();
|
|
doneCurrent();
|
|
}
|
|
|
|
void Viewer::displayMessage(const QString &_message, int delay)
|
|
{
|
|
d->message = _message;
|
|
d->_displayMessage = true;
|
|
// Was set to single shot in defaultConstructor.
|
|
d->messageTimer.start(delay);
|
|
if (textIsEnabled())
|
|
update();
|
|
}
|
|
void Viewer::hideMessage()
|
|
{
|
|
d->_displayMessage = false;
|
|
if (textIsEnabled())
|
|
update();
|
|
}
|
|
void Viewer::printMessage(QString _message, int ms_delay)
|
|
{
|
|
displayMessage(_message, ms_delay);
|
|
}
|
|
|
|
void Viewer_impl::showDistance(QPoint pixel)
|
|
{
|
|
static bool isAset = false;
|
|
bool found;
|
|
CGAL::qglviewer::Vec point;
|
|
point = viewer->camera()->pointUnderPixel(pixel, found);
|
|
if(!isAset && found)
|
|
{
|
|
//set APoint
|
|
APoint = point;
|
|
isAset = true;
|
|
clearDistancedisplay();
|
|
}
|
|
else if (found)
|
|
{
|
|
//set BPoint
|
|
BPoint = point;
|
|
isAset = false;
|
|
|
|
// fills the buffers
|
|
std::vector<float> v;
|
|
v.resize(6);
|
|
v[0] = float(APoint.x); v[1] = float(APoint.y); v[2] = float(APoint.z);
|
|
v[3] = float(BPoint.x); v[4] = float(BPoint.y); v[5] = float(BPoint.z);
|
|
|
|
vao.bind();
|
|
buffer.bind();
|
|
buffer.allocate(v.data(),6*sizeof(float));
|
|
rendering_program_dist.enableAttributeArray("vertex");
|
|
rendering_program_dist.setAttributeBuffer("vertex",GL_FLOAT,0,3);
|
|
buffer.release();
|
|
vao.release();
|
|
distance_is_displayed = true;
|
|
double dist = std::sqrt((BPoint.x-APoint.x)/scaler.x()*(BPoint.x-APoint.x)/scaler.x() + (BPoint.y-APoint.y)/scaler.y()*(BPoint.y-APoint.y)/scaler.y() + (BPoint.z-APoint.z)/scaler.z()*(BPoint.z-APoint.z)/scaler.z());
|
|
QFont font;
|
|
font.setBold(true);
|
|
TextItem *ACoord = new TextItem(float(APoint.x),
|
|
float(APoint.y),
|
|
float(APoint.z),
|
|
QString("A(%1,%2,%3)")
|
|
.arg(APoint.x/scaler.x()-viewer->offset().x, 0, 'g', 10)
|
|
.arg(APoint.y/scaler.y()-viewer->offset().y, 0, 'g', 10)
|
|
.arg(APoint.z/scaler.z()-viewer->offset().z, 0, 'g', 10), true, font, Qt::red, true);
|
|
distance_text.append(ACoord);
|
|
TextItem *BCoord = new TextItem(float(BPoint.x),
|
|
float(BPoint.y),
|
|
float(BPoint.z),
|
|
QString("B(%1,%2,%3)")
|
|
.arg(BPoint.x/scaler.x()-viewer->offset().x, 0, 'g', 10)
|
|
.arg(BPoint.y/scaler.y()-viewer->offset().y, 0, 'g', 10)
|
|
.arg(BPoint.z/scaler.z()-viewer->offset().z, 0, 'g', 10), true, font, Qt::red, true);
|
|
|
|
distance_text.append(BCoord);
|
|
CGAL::qglviewer::Vec centerPoint = 0.5*(BPoint+APoint);
|
|
TextItem *centerCoord = new TextItem(float(centerPoint.x),
|
|
float(centerPoint.y),
|
|
float(centerPoint.z),
|
|
QString(" distance: %1").arg(dist), true, font, Qt::red, true);
|
|
|
|
distance_text.append(centerCoord);
|
|
Q_FOREACH(TextItem* ti, distance_text)
|
|
textRenderer->addText(ti);
|
|
Q_EMIT(viewer->sendMessage(QString("First point : A(%1,%2,%3), second point : B(%4,%5,%6), distance between them : %7")
|
|
.arg(APoint.x/scaler.x()-viewer->offset().x)
|
|
.arg(APoint.y/scaler.y()-viewer->offset().y)
|
|
.arg(APoint.z/scaler.z()-viewer->offset().z)
|
|
.arg(BPoint.x/scaler.x()-viewer->offset().x)
|
|
.arg(BPoint.y/scaler.y()-viewer->offset().y)
|
|
.arg(BPoint.z/scaler.z()-viewer->offset().z)
|
|
.arg(dist, 0, 'g', 10)));
|
|
}
|
|
|
|
}
|
|
|
|
void Viewer_impl::clearDistancedisplay()
|
|
{
|
|
distance_is_displayed = false;
|
|
Q_FOREACH(TextItem* ti, distance_text)
|
|
{
|
|
textRenderer->removeText(ti);
|
|
delete ti;
|
|
}
|
|
distance_text.clear();
|
|
}
|
|
|
|
void Viewer_impl::sendSnapshotToClipboard(Viewer *viewer)
|
|
{
|
|
QImage * snap = viewer->takeSnapshot(CGAL::qglviewer::TRANSPARENT_BACKGROUND, 2*viewer->size(), 1, true);
|
|
if(snap)
|
|
{
|
|
#if defined(_WIN32)
|
|
QApplication::clipboard()->setImage(*snap);
|
|
QMimeData *mimeData = new QMimeData();
|
|
QByteArray ba;
|
|
QBuffer buffer(&ba);
|
|
buffer.open(QIODevice::WriteOnly);
|
|
snap->save(&buffer, "PNG"); // writes image into ba in PNG format
|
|
buffer.close();
|
|
mimeData->setData("PNG", ba);
|
|
//According to the doc, the ownership of mime_data is transferred to
|
|
//clipboard, so this is not a memory leak.
|
|
QApplication::clipboard()->setMimeData(mimeData);
|
|
#else
|
|
QApplication::clipboard()->setImage(*snap);
|
|
#endif
|
|
delete snap;
|
|
}
|
|
}
|
|
void Viewer::SetOrthoProjection(bool b)
|
|
{
|
|
d->projection_is_ortho = b;
|
|
update();
|
|
}
|
|
|
|
void Viewer::updateIds(CGAL::Three::Scene_item * item)
|
|
{
|
|
//all ids are computed when they are displayed the first time.
|
|
//Calling printPrimitiveIds twice hides and show the ids again, so they are re-computed.
|
|
|
|
d->scene->updatePrimitiveIds(item);
|
|
d->scene->updatePrimitiveIds(item);
|
|
}
|
|
|
|
|
|
TextRenderer* Viewer::textRenderer()
|
|
{
|
|
return d->textRenderer;
|
|
}
|
|
|
|
bool Viewer::isExtensionFound()
|
|
{
|
|
return d->extension_is_found;
|
|
|
|
}
|
|
|
|
void Viewer::disableClippingBox()
|
|
{
|
|
d->clipping = false;
|
|
}
|
|
|
|
void Viewer::enableClippingBox(QVector4D box[6])
|
|
{
|
|
d->clipping = true;
|
|
for(int i=0; i<6; ++i)
|
|
d->clipbox[i] = box[i];
|
|
}
|
|
|
|
QOpenGLFunctions_4_3_Core *Viewer::openGL_4_3_functions() { return d->_recentFunctions; }
|
|
|
|
void Viewer::set2DSelectionMode(bool b) { d->is_2d_selection_mode = b; }
|
|
|
|
void Viewer::setStaticImage(QImage image) { d->static_image = image; }
|
|
|
|
const QImage& Viewer:: staticImage() const { return d->static_image; }
|
|
|
|
|
|
void Viewer::setCurrentPass(int pass) { d->current_pass = pass; }
|
|
|
|
void Viewer::setDepthWriting(bool writing_depth) { d->writing_depth = writing_depth; }
|
|
|
|
void Viewer::setDepthPeelingFbo(QOpenGLFramebufferObject* fbo) { d->dp_fbo = fbo; }
|
|
|
|
int Viewer::currentPass()const{ return d->current_pass; }
|
|
bool Viewer::isDepthWriting()const{ return d->writing_depth; }
|
|
QOpenGLFramebufferObject *Viewer::depthPeelingFbo(){ return d->dp_fbo; }
|
|
float Viewer::total_pass()
|
|
{
|
|
return d->current_total_pass * 1.0f;
|
|
}
|
|
void Viewer::setTotalPass(int p)
|
|
{
|
|
d->total_pass = p;
|
|
update();
|
|
}
|
|
|
|
void Viewer::messageLogged(QOpenGLDebugMessage msg)
|
|
{
|
|
//filter out useless warning
|
|
// From those two links, we decided we didn't care for this warning:
|
|
// https://community.khronos.org/t/vertex-shader-in-program-2-is-being-recompiled-based-on-gl-state/76019
|
|
// https://stackoverflow.com/questions/12004396/opengl-debug-context-performance-warning
|
|
if(msg.message().contains("is being recompiled"))
|
|
return;
|
|
QString error;
|
|
|
|
// Format based on severity
|
|
switch (msg.severity())
|
|
{
|
|
case QOpenGLDebugMessage::NotificationSeverity:
|
|
return;
|
|
break;
|
|
case QOpenGLDebugMessage::HighSeverity:
|
|
error += "GL ERROR :";
|
|
break;
|
|
case QOpenGLDebugMessage::MediumSeverity:
|
|
error += "GL WARNING :";
|
|
break;
|
|
case QOpenGLDebugMessage::LowSeverity:
|
|
error += "GL NOTE :";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
error += " (";
|
|
|
|
// Format based on source
|
|
#define CASE(c) case QOpenGLDebugMessage::c: error += #c; break
|
|
switch (msg.source())
|
|
{
|
|
CASE(APISource);
|
|
CASE(WindowSystemSource);
|
|
CASE(ShaderCompilerSource);
|
|
CASE(ThirdPartySource);
|
|
CASE(ApplicationSource);
|
|
CASE(OtherSource);
|
|
CASE(InvalidSource);
|
|
default:
|
|
break;
|
|
}
|
|
#undef CASE
|
|
|
|
error += " : ";
|
|
|
|
// Format based on type
|
|
#define CASE(c) case QOpenGLDebugMessage::c: error += #c; break
|
|
switch (msg.type())
|
|
{
|
|
CASE(ErrorType);
|
|
CASE(DeprecatedBehaviorType);
|
|
CASE(UndefinedBehaviorType);
|
|
CASE(PortabilityType);
|
|
CASE(PerformanceType);
|
|
CASE(OtherType);
|
|
CASE(MarkerType);
|
|
CASE(GroupPushType);
|
|
CASE(GroupPopType);
|
|
default:
|
|
break;
|
|
}
|
|
#undef CASE
|
|
|
|
error += ")";
|
|
qDebug() << qPrintable(error) << "\n" << qPrintable(msg.message()) << "\n";
|
|
}
|
|
|
|
void Viewer::setLighting()
|
|
{
|
|
|
|
//save current settings;
|
|
float prev_spec = d->spec_power;
|
|
QVector4D prev_pos = d->position;
|
|
QVector4D prev_ambient = d->ambient;
|
|
QVector4D prev_diffuse = d->diffuse;
|
|
QVector4D prev_spec_color = d->specular;
|
|
//open dialog
|
|
LightingDialog* dialog = new LightingDialog(d);
|
|
//set specular
|
|
connect(dialog->spec_powrSlider, &QSlider::valueChanged,
|
|
[this, dialog]()
|
|
{
|
|
d->spec_power = dialog->spec_powrSlider->value();
|
|
update();
|
|
});
|
|
//set position
|
|
connect(dialog->position_lineEdit, &QLineEdit::editingFinished,
|
|
[this, dialog]()
|
|
{
|
|
QStringList list = dialog->position_lineEdit->text().split(QRegExp(","), CGAL_QT_SKIP_EMPTY_PARTS);
|
|
if (list.isEmpty()) return;
|
|
if (list.size()!=3){
|
|
QMessageBox *msgBox = new QMessageBox;
|
|
msgBox->setWindowTitle("Error");
|
|
msgBox->setText("ERROR : Input should consists of 3 floats.");
|
|
msgBox->exec();
|
|
return;
|
|
}
|
|
float coords[3];
|
|
for(int j=0; j<3; ++j)
|
|
{
|
|
bool ok;
|
|
coords[j] = list.at(j).toFloat(&ok);
|
|
if(!ok)
|
|
{
|
|
QMessageBox *msgBox = new QMessageBox;
|
|
msgBox->setWindowTitle("Error");
|
|
msgBox->setText("ERROR : Coordinates are invalid.");
|
|
msgBox->exec();
|
|
return;
|
|
}
|
|
}
|
|
d->position = QVector4D(coords[0], coords[1], coords[2], 1.0f);
|
|
update();
|
|
});
|
|
|
|
|
|
//set ambient
|
|
connect(dialog, &LightingDialog::s_ambient_changed,
|
|
[this, dialog](){
|
|
d->ambient=QVector4D(static_cast<float>(dialog->ambient.redF()),
|
|
static_cast<float>(dialog->ambient.greenF()),
|
|
static_cast<float>(dialog->ambient.blueF()),
|
|
1.0f);
|
|
update();
|
|
});
|
|
|
|
//set diffuse
|
|
connect(dialog, &LightingDialog::s_diffuse_changed,
|
|
[this, dialog](){
|
|
d->diffuse=QVector4D(static_cast<float>(dialog->diffuse.redF()),
|
|
static_cast<float>(dialog->diffuse.greenF()),
|
|
static_cast<float>(dialog->diffuse.blueF()),
|
|
1.0f);
|
|
update();
|
|
});
|
|
//set specular
|
|
connect(dialog, &LightingDialog::s_specular_changed,
|
|
[this, dialog](){
|
|
d->specular=QVector4D(static_cast<float>(dialog->specular.redF()),
|
|
static_cast<float>(dialog->specular.greenF()),
|
|
static_cast<float>(dialog->specular.blueF()),
|
|
1.0f);
|
|
update();
|
|
|
|
});
|
|
|
|
//reset default
|
|
connect(dialog->buttonBox->button(QDialogButtonBox::StandardButton::RestoreDefaults), &QPushButton::clicked,
|
|
[this](){
|
|
d->position = QVector4D(0,0,1,1);
|
|
d->ambient=QVector4D(77.0f/255,77.0f/255,77.0f/255, 1.0);
|
|
d->diffuse=QVector4D(204.0f/255,204.0f/255,204.0f/255,1.0);
|
|
d->specular=QVector4D(0,0,0,1.0);
|
|
d->spec_power = 51;
|
|
update();
|
|
|
|
});
|
|
if(!dialog->exec())
|
|
{
|
|
//restore previous settings
|
|
d->spec_power = prev_spec;
|
|
d->position = prev_pos;
|
|
d->ambient = prev_ambient;
|
|
d->diffuse = prev_diffuse;
|
|
d->specular = prev_spec_color;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Viewer::setBackFrontColors()
|
|
{
|
|
|
|
//save current settings;
|
|
|
|
QColor prev_front_color = d->front_color;
|
|
QColor prev_back_color = d->back_color;
|
|
QDialog *dialog = new QDialog(this);
|
|
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
|
|
| QDialogButtonBox::Cancel, dialog);
|
|
|
|
connect(buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
|
|
connect(buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
|
|
|
|
QGridLayout* layout = new QGridLayout(dialog);
|
|
layout->addWidget(new QLabel("Front color: ",dialog),0,0);
|
|
QPalette front_palette;
|
|
front_palette.setColor(QPalette::Button, d->front_color);
|
|
QPushButton* frontButton = new QPushButton(dialog);
|
|
frontButton->setPalette(front_palette);
|
|
QPalette back_palette;
|
|
back_palette.setColor(QPalette::Button, d->back_color);
|
|
QPushButton* backButton = new QPushButton(dialog);
|
|
backButton->setPalette(back_palette);
|
|
layout->addWidget(frontButton,0,1);
|
|
layout->addWidget(new QLabel("Back color: ",dialog),1,0);
|
|
layout->addWidget(backButton,1,1);
|
|
layout->addWidget(buttonBox);
|
|
dialog->setLayout(layout);
|
|
connect(frontButton, &QPushButton::clicked,
|
|
[this, dialog, frontButton](){
|
|
QColorDialog *color_dial = new QColorDialog(dialog);
|
|
color_dial->exec();
|
|
QColor front_color = color_dial->selectedColor();
|
|
QPalette palette;
|
|
palette.setColor(QPalette::Button, front_color);
|
|
frontButton->setPalette(palette);
|
|
d->front_color= front_color;
|
|
});
|
|
connect(backButton, &QPushButton::clicked,
|
|
[this, dialog, backButton](){
|
|
QColorDialog *color_dial = new QColorDialog(dialog);
|
|
color_dial->exec();
|
|
QColor back_color = color_dial->selectedColor();
|
|
QPalette palette;
|
|
palette.setColor(QPalette::Button, back_color);
|
|
backButton->setPalette(palette);
|
|
d->back_color= back_color;
|
|
|
|
});
|
|
if(!dialog->exec())
|
|
{
|
|
//restore previous settings
|
|
d->front_color= prev_front_color;
|
|
d->back_color= prev_back_color;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Viewer::setGlPointSize(const GLfloat &p) { d->gl_point_size = p; }
|
|
|
|
const GLfloat& Viewer::getGlPointSize() const { return d->gl_point_size; }
|
|
|
|
void Viewer::resetFov()
|
|
{
|
|
camera()->setHorizontalFieldOfView(ORIGINAL_FOV);
|
|
}
|
|
|
|
void Viewer::initializeGL()
|
|
{
|
|
QGLViewer::initializeGL();
|
|
doneInitGL(this);
|
|
}
|
|
|
|
void Viewer::makeCurrent()
|
|
{
|
|
CGAL::Three::Three::setCurrentViewer(this);
|
|
QOpenGLWidget::makeCurrent();
|
|
}
|
|
|
|
QVector4D* Viewer::clipBox() const
|
|
{
|
|
return d->clipbox;
|
|
}
|
|
|
|
bool Viewer::isClipping() const
|
|
{
|
|
return d->clipping;
|
|
}
|
|
|
|
void Viewer::scaleScene()
|
|
{
|
|
CGAL::Bbox_3 bbox = CGAL::Three::Three::scene()->bbox();
|
|
if(!d->scene_scaling)
|
|
{
|
|
QMultipleInputDialog dialog ("Scale Scene", CGAL::Three::Three::mainWindow());
|
|
DoubleEdit* x_val = dialog.add<DoubleEdit> ("Scale along X");
|
|
DoubleEdit* y_val = dialog.add<DoubleEdit> ("Scale along Y");
|
|
DoubleEdit* z_val = dialog.add<DoubleEdit> ("Scale along Z");
|
|
x_val->setMinimum(0);
|
|
y_val->setMinimum(0);
|
|
z_val->setMinimum(0);
|
|
if(bbox != CGAL::Bbox_3(0,0,0,0,0,0))
|
|
{
|
|
QPushButton* norm_button = dialog.add<QPushButton> ("");
|
|
norm_button->setText("Normalize");
|
|
norm_button->setToolTip("Automatically fill values to display the scene in a unit cube.");
|
|
|
|
connect(norm_button, &QPushButton::clicked, this,
|
|
[x_val, y_val, z_val, &bbox](){
|
|
x_val->setValue(1.0/(bbox.xmax()-bbox.xmin()));
|
|
y_val->setValue(1.0/(bbox.ymax()-bbox.ymin()));
|
|
z_val->setValue(1.0/(bbox.zmax()-bbox.zmin()));
|
|
});
|
|
}
|
|
if (dialog.exec() != QDialog::Accepted)
|
|
{
|
|
parent()->findChild<QAction*>("actionScaleScene")->setChecked(false);
|
|
return;
|
|
}
|
|
d->scaler.setX(x_val->text()==""?1.0:x_val->value());
|
|
d->scaler.setY(y_val->text()==""?1.0:y_val->value());
|
|
d->scaler.setZ(z_val->text()==""?1.0:z_val->value());
|
|
|
|
if(d->scaler.x() == 0.0 || d->scaler.y() == 0.0 || d->scaler.z()== 0.0)
|
|
{
|
|
parent()->findChild<QAction*>("actionScaleScene")->setChecked(false);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
d->scaler = QVector3D(1,1,1);
|
|
|
|
CGAL::qglviewer::Vec vmin((static_cast<float>(bbox.xmin())+offset().x)*d->scaler.x(), (static_cast<float>(bbox.ymin())+offset().y)*d->scaler.y(), (static_cast<float>(bbox.zmin())+offset().z)*d->scaler.z()),
|
|
vmax((static_cast<float>(bbox.xmax())+offset().x)*d->scaler.x(), (static_cast<float>(bbox.ymax())+offset().y)*d->scaler.y(), (static_cast<float>(bbox.zmax())+offset().z)*d->scaler.z());
|
|
camera()->setPivotPoint((vmin+vmax)*0.5);
|
|
camera()->setSceneBoundingBox(vmin, vmax);
|
|
camera()->fitBoundingBox(vmin, vmax);
|
|
d->scene_scaling = !d->scene_scaling;
|
|
}
|
|
#ifdef CGAL_USE_WEBSOCKETS
|
|
void Viewer::setShareCam(bool b, QString session)
|
|
{
|
|
static bool init = false;
|
|
if(b)
|
|
{
|
|
d->cam_sharing = b;
|
|
d->session = session;
|
|
QString ws_url
|
|
= CGAL::Three::Three::mainWindow()->property("ws_url").toString();
|
|
if(ws_url.isEmpty())
|
|
{
|
|
QMessageBox::warning(this, "Error", "No Server configured. Please go to Edit->Preferences->Network Settings and fill the \"Camera Synchronization Server\" Field.");
|
|
}
|
|
else{
|
|
if(!init)
|
|
{
|
|
connect(&d->m_webSocket, &QWebSocket::connected, this, &Viewer::onSocketConnected);
|
|
connect(&d->m_webSocket, &QWebSocket::disconnected, this,[this]()
|
|
{
|
|
d->is_connected = false;
|
|
Viewer::socketClosed();
|
|
});
|
|
init = true;
|
|
}
|
|
d->m_webSocket.open(QUrl(ws_url));
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
QTimer::singleShot(1000, this, [this](){
|
|
QApplication::restoreOverrideCursor();
|
|
if(!d->is_connected){
|
|
QMessageBox::warning(CGAL::Three::Three::mainWindow(),
|
|
"Connection failure",
|
|
"The requested server was not found.");
|
|
setShareCam(false, "");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QAction* action = findChild<QAction*>("actionShareCamera");
|
|
action->setChecked(false);
|
|
d->m_webSocket.close();
|
|
}
|
|
}
|
|
|
|
void Viewer::onSocketConnected()
|
|
{
|
|
connect(&d->m_webSocket, &QWebSocket::textMessageReceived,
|
|
this, &Viewer::onTextMessageSocketReceived);
|
|
connect(camera()->frame(), &CGAL::qglviewer::ManipulatedCameraFrame::manipulated,
|
|
this, [this](){
|
|
if(d->cam_sharing){
|
|
QString cam_state = QString("[%1] %2").arg(d->session).arg(dumpCameraCoordinates());
|
|
//send to server
|
|
d->m_webSocket.sendTextMessage(cam_state);
|
|
}
|
|
});
|
|
d->is_connected = true;
|
|
}
|
|
|
|
void Viewer::onTextMessageSocketReceived(QString message)
|
|
{
|
|
QString session;
|
|
QString position;
|
|
QRegularExpression re("\\[(.*)\\] (.*)");
|
|
QRegularExpressionMatch match = re.match(message);
|
|
session = match.captured(1);
|
|
position = match.captured(2);
|
|
if(session != d->session){
|
|
return;
|
|
}
|
|
QStringList sl = position.split(" ");
|
|
if(sl.size() != 7)
|
|
return;
|
|
|
|
CGAL::qglviewer::Vec pos(sl[0].toDouble(),sl[1].toDouble(),sl[2].toDouble());
|
|
CGAL::qglviewer::Quaternion q(sl[3].toDouble(),sl[4].toDouble(),
|
|
sl[5].toDouble(),sl[6].toDouble());
|
|
camera()->frame()->setPositionAndOrientation(pos, q);
|
|
update();
|
|
}
|
|
#endif
|
|
|
|
const QVector3D& Viewer::scaler()const { return d->scaler; }
|
|
void Viewer::showEntireScene()
|
|
{
|
|
CGAL::QGLViewer::showEntireScene();
|
|
CGAL::Bbox_3 bbox = CGAL::Three::Three::scene()->bbox();
|
|
|
|
CGAL::qglviewer::Vec vmin((static_cast<float>(bbox.xmin())+offset().x)*d->scaler.x(), (static_cast<float>(bbox.ymin())+offset().y)*d->scaler.y(), (static_cast<float>(bbox.zmin())+offset().z)*d->scaler.z()),
|
|
vmax((static_cast<float>(bbox.xmax())+offset().x)*d->scaler.x(), (static_cast<float>(bbox.ymax())+offset().y)*d->scaler.y(), (static_cast<float>(bbox.zmax())+offset().z)*d->scaler.z());
|
|
camera()->setPivotPoint((vmin+vmax)*0.5);
|
|
camera()->setSceneBoundingBox(vmin, vmax);
|
|
camera()->fitBoundingBox(vmin, vmax);
|
|
}
|
|
#include "Viewer.moc"
|