1 #include "Viewer.h"
2 #include <CGAL/Three/Scene_draw_interface.h>
3 #include <QMouseEvent>
4 #include <QKeyEvent>
5 #include <QSettings>
6 #include <QDebug>
7 #include <QSettings>
8 #include <QOpenGLShader>
9 #include <QFileDialog>
10 #include <QOpenGLShaderProgram>
11 #include <QOpenGLFramebufferObject>
12 #include <QMessageBox>
13 #include <QColorDialog>
14 #include <QInputDialog>
15 #include <cmath>
16 #include <QApplication>
17 #include <QOpenGLDebugLogger>
18 #include <QStyleFactory>
19 #include <QAction>
20 #include <QMultipleInputDialog.h>
21 #include <QRegularExpressionMatch>
22 #ifdef CGAL_USE_WEBSOCKETS
23 #include <QtWebSockets/QWebSocket>
24 #endif
25 
26 #include <CGAL/Three/Three.h>
27 
28 #include "ui_LightingDialog.h"
29 #include "CGAL_double_edit.h"
30 
31 #if defined(_WIN32)
32 #include <QMimeData>
33 #include <QByteArray>
34 #include <QBuffer>
35 #endif
36 #define ORIGINAL_FOV 0.94853805396568136
37 
38 class Viewer_impl {
39 public:
40   CGAL::Three::Scene_draw_interface* scene;
41   Viewer *viewer;
42   Viewer *shareViewer;
43   bool antialiasing;
44   bool twosides;
45   bool macro_mode;
46   bool inFastDrawing;
47   bool inDrawWithNames;
48   bool clipping;
49   bool projection_is_ortho;
50   bool cam_sharing;
51   bool scene_scaling;
52   GLfloat gl_point_size;
53   QVector4D clipbox[6];
54   QVector3D scaler;
55   QPainter *painter;
56 
57   // L i g h t i n g
58   QVector4D position;
59   QVector4D ambient;
60   QVector4D diffuse;
61   QVector4D specular;
62   float spec_power;
63 
64   //Back and Front Colors
65   QColor front_color;
66   QColor back_color;
67 
68   // M e s s a g e s
69   QString message;
70   bool _displayMessage;
71   QTimer messageTimer;
72   QOpenGLFunctions_4_3_Core* _recentFunctions;
73   bool is_2d_selection_mode;
74 
75   // D e p t h  P e e l i n g
76   // \param pass the current pass in the Depth Peeling (transparency) algorithm.
77   // -1 means that no depth peeling is applied.
78   // \param writing_depth means that the color of the faces will be drawn in a grayscale
79   // according to the depth of the fragment in the shader. It is used by the transparency.
80   // \param fbo contains the texture used by the Depth Peeling algorithm.
81   // Should be NULL if pass <= 0;
82   int current_pass;
83   bool writing_depth;
84   int total_pass;
85   int current_total_pass;
86   QOpenGLFramebufferObject* dp_fbo;
87   QOpenGLDebugLogger *logger;
88 
89 
90   //! The buffers used to draw the axis system
91   QOpenGLBuffer buffer;
92   //! The VAO used to draw the axis system
93   QOpenGLVertexArrayObject vao;
94   //! The rendering program used to draw the distance
95   QOpenGLShaderProgram rendering_program_dist;
96   QList<TextItem*>  distance_text;
97   //! Decides if the text is displayed in the drawVisualHints function.
98   bool has_text;
99   //! Decides if the distance between APoint and BPoint must be drawn;
100   bool distance_is_displayed;
101   bool i_is_pressed;
102   bool z_is_pressed;
103   QImage static_image;
104   //!Draws the distance between two selected points.
105   void showDistance(QPoint);
106   CGAL::qglviewer::Vec APoint;
107   CGAL::qglviewer::Vec BPoint;
108   bool is_d_pressed;
109   bool extension_is_found;
110   int quality;
111 
112   TextRenderer *textRenderer;
113   //!Clears the distance display
114   void clearDistancedisplay();
115   void draw_aux(bool with_names, Viewer*);
116   //! Contains all the programs for the item rendering.
117   static std::vector<QOpenGLShaderProgram*> shader_programs;
118   QMatrix4x4 projectionMatrix;
119   void sendSnapshotToClipboard(Viewer*);
shaderPrograms()120   std::vector<QOpenGLShaderProgram*>& shaderPrograms()
121   {
122     return shader_programs;
123   }
124 
125 #ifdef CGAL_USE_WEBSOCKETS
126   QWebSocket m_webSocket;
127 #endif
128   bool is_connected;
129   QString session;
130   QUrl m_url;
131 };
132 
133 class LightingDialog :
134     public QDialog,
135     public Ui::LightingDialog
136 {
137   Q_OBJECT
138 public:
139   QColor ambient, diffuse, specular;
LightingDialog(Viewer_impl * d)140   LightingDialog(Viewer_impl* d)
141   {
142     setupUi(this);
143     position_lineEdit->setText(QString("%1,%2,%3")
144                                .arg(d->position.x())
145                                .arg(d->position.y())
146                                .arg(d->position.z()));
147     QPalette palette;
148     ambient=QColor::fromRgbF(d->ambient.x(),
149                              d->ambient.y(),
150                              d->ambient.z());
151     palette.setColor(QPalette::Button,ambient);
152     ambientButton->setPalette(palette);
153     ambientButton->setStyle(QStyleFactory::create("Fusion"));
154 
155     diffuse=QColor::fromRgbF(d->diffuse.x(),
156                              d->diffuse.y(),
157                              d->diffuse.z());
158     palette.setColor(QPalette::Button,diffuse);
159     diffuseButton->setPalette(palette);
160     diffuseButton->setStyle(QStyleFactory::create("Fusion"));
161 
162     specular=QColor::fromRgbF(d->specular.x(),
163                               d->specular.y(),
164                               d->specular.z());
165     palette.setColor(QPalette::Button,specular);
166     specularButton->setPalette(palette);
167     specularButton->setStyle(QStyleFactory::create("Fusion"));
168     spec_powrSlider->setValue(static_cast<int>(d->spec_power));
169 
170     connect(&ambient_dial, &QColorDialog::currentColorChanged, this, &LightingDialog::ambient_changed );
171     connect(&diffuse_dial, &QColorDialog::currentColorChanged, this, &LightingDialog::diffuse_changed );
172     connect(&spec_dial, &QColorDialog::currentColorChanged, this,    &LightingDialog::specular_changed);
173 
174     connect(ambientButton, &QPushButton::clicked,
175             [this](){
176       ambient_dial.setCurrentColor(ambient);
177       ambient_dial.exec();
178       ambient = ambient_dial.selectedColor();
179       QPalette palette;
180       palette.setColor(QPalette::Button, ambient);
181       ambientButton->setPalette(palette);
182     });
183     connect(diffuseButton, &QPushButton::clicked,
184             [this](){
185       diffuse_dial.setCurrentColor(diffuse);
186       diffuse_dial.exec();
187       diffuse = diffuse_dial.selectedColor();
188       QPalette palette;
189       palette.setColor(QPalette::Button, diffuse);
190       diffuseButton->setPalette(palette);
191     });
192     connect(specularButton, &QPushButton::clicked,
193             [this](){
194       spec_dial.setCurrentColor(specular);
195       spec_dial.exec();
196       specular = spec_dial.selectedColor();
197       QPalette palette;
198       palette.setColor(QPalette::Button, specular);
199       specularButton->setPalette(palette);
200     });
201 
202     //D e f a u l t - S e t t i n g s
203     connect(buttonBox->button(QDialogButtonBox::StandardButton::RestoreDefaults), &QPushButton::clicked,
204             [this](){
205       position_lineEdit->setText(QString("0,0,1"));
206       ambient=QColor(77,77,77);
207       diffuse=QColor(204,204,204);
208       specular=QColor(0,0,0);
209       spec_powrSlider->setValue(51);
210       QPalette palette;
211       palette.setColor(QPalette::Button, ambient);
212       ambientButton->setPalette(palette);
213       palette.setColor(QPalette::Button, diffuse);
214       diffuseButton->setPalette(palette);
215       palette.setColor(QPalette::Button, specular);
216       specularButton->setPalette(palette);
217     });
218   }
219 private Q_SLOTS:
diffuse_changed()220   void diffuse_changed()
221   {
222     diffuse = diffuse_dial.currentColor();
223     s_diffuse_changed();
224   }
ambient_changed()225   void ambient_changed()
226   {
227     ambient = ambient_dial.currentColor();
228     s_ambient_changed();
229   }
specular_changed()230   void specular_changed()
231   {
232     specular = spec_dial.currentColor();
233     s_specular_changed();
234   }
235 Q_SIGNALS:
236   void s_diffuse_changed();
237   void s_ambient_changed();
238   void s_specular_changed();
239 private:
240   QColorDialog diffuse_dial;
241   QColorDialog ambient_dial;
242   QColorDialog spec_dial;
243 };
244 
245 std::vector<QOpenGLShaderProgram*> Viewer_impl::shader_programs =
246     std::vector<QOpenGLShaderProgram*>(Viewer::NB_OF_PROGRAMS);
doBindings()247 void Viewer::doBindings()
248 {
249   QSettings viewer_settings;
250   // enable anti-aliasing
251   QString cam_pos = viewer_settings.value("cam_pos", QString("0.0,0.0,1.0")).toString();
252   d->position = QVector4D(cam_pos.split(",").at(0).toFloat(),
253                           cam_pos.split(",").at(1).toFloat(),
254                           cam_pos.split(",").at(2).toFloat(),
255                           1.0f);
256 
257   QString ambient = viewer_settings.value("ambient", QString("0.4,0.4,0.4")).toString();
258   d->ambient = QVector4D(ambient.split(",").at(0).toFloat(),
259                          ambient.split(",").at(1).toFloat(),
260                          ambient.split(",").at(2).toFloat(),
261                          1.0f);
262 
263   QString diffuse = viewer_settings.value("diffuse", QString("1.0,1.0,1.0")).toString();
264   d->diffuse = QVector4D(diffuse.split(",").at(0).toFloat(),
265                          diffuse.split(",").at(1).toFloat(),
266                          diffuse.split(",").at(2).toFloat(),
267                          1.0f);
268 
269   QString specular = viewer_settings.value("specular", QString("0.0,0.0,0.0")).toString();
270   d->specular = QVector4D(specular.split(",").at(0).toFloat(),
271                           specular.split(",").at(1).toFloat(),
272                           specular.split(",").at(2).toFloat(),
273                           1.0f);
274 
275   QString front_color = viewer_settings.value("front_color", QString("1.0,0.0,0.0")).toString();
276   d->front_color= QColor::fromRgbF(front_color.split(",").at(0).toFloat(),
277                                    front_color.split(",").at(1).toFloat(),
278                                    front_color.split(",").at(2).toFloat(),
279                          1.0f);
280   QString back_color = viewer_settings.value("back_color", QString("0.0,0.0,1.0")).toString();
281   d->back_color= QColor::fromRgbF( back_color.split(",").at(0).toFloat(),
282                                    back_color.split(",").at(1).toFloat(),
283                                    back_color.split(",").at(2).toFloat(),
284                          1.0f);
285   d->spec_power = viewer_settings.value("spec_power", 51.8).toFloat();
286   d->scene = nullptr;
287   d->projection_is_ortho = false;
288   d->cam_sharing = false;
289   d->twosides = false;
290   this->setProperty("draw_two_sides", false);
291   this->setProperty("back_front_shading", false);
292   d->macro_mode = false;
293   d->inFastDrawing = true;
294   d->inDrawWithNames = false;
295   d->clipping = false;
296   d->shader_programs.resize(NB_OF_PROGRAMS);
297   d->textRenderer = new TextRenderer();
298   d->is_2d_selection_mode = false;
299   d->is_connected = false;
300   d->scene_scaling = false;
301   d->scaler = QVector3D(1,1,1);
302 
303   connect( d->textRenderer, SIGNAL(sendMessage(QString,int)),
304            this, SLOT(printMessage(QString,int)) );
305   connect(&d->messageTimer, SIGNAL(timeout()), SLOT(hideMessage()));
306   setShortcut(CGAL::qglviewer::EXIT_VIEWER, 0);
307   setKeyDescription(Qt::Key_T,
308                     tr("Turn the camera by 180 degrees"));
309   setKeyDescription(Qt::Key_M,
310                     tr("Toggle macro mode: useful to view details very near from the camera, "
311                        "but decrease the z-buffer precision"));
312   setKeyDescription(Qt::Key_I + Qt::CTRL,
313                       tr("Toggle the primitive IDs visibility of the selected Item, for the types selected in the context menu of the said item."));
314   setKeyDescription(Qt::Key_D,
315                       tr("Disable the distance between two points  visibility."));
316   setKeyDescription(Qt::Key_F5,
317                     tr("Reload selected items if possible."));
318 
319   //modify mouse bindings that have been updated
320   setMouseBinding(Qt::Key(0), Qt::NoModifier, Qt::LeftButton, CGAL::qglviewer::RAP_FROM_PIXEL, true, Qt::RightButton);
321   setMouseBinding(Qt::ShiftModifier, Qt::RightButton, CGAL::qglviewer::NO_CLICK_ACTION, false, Qt::NoButton);
322   setMouseBindingDescription(Qt::ShiftModifier, Qt::RightButton,
323                              tr("Select and pop context menu"));
324   setMouseBinding(Qt::Key_R, Qt::NoModifier, Qt::LeftButton, CGAL::qglviewer::RAP_FROM_PIXEL);
325 
326   //use the new API for these
327   setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, CGAL::qglviewer::SELECT);
328 
329   setMouseBindingDescription(Qt::Key_I, Qt::NoModifier, Qt::LeftButton,
330                              tr("Show/hide the primitive ID of the types selected in the context menu of the picked item."));
331   setMouseBindingDescription(Qt::Key_D, Qt::NoModifier, Qt::LeftButton,
332                              tr("Selects a point. When the second point is selected,  "
333                                 "displays the two points and the distance between them."));
334   setMouseBindingDescription(Qt::Key_O, Qt::NoModifier, Qt::LeftButton,
335                              tr("Move the camera orthogonally to the picked facet of a Scene_surface_mesh_item or "
336                                 "to the current selection of a Scene_points_with_normal_item."));
337   setKeyDescription(Qt::Key_F5,
338                     tr("Reloads the selected item if possible."));
339   setKeyDescription(Qt::Key_F11,
340                     tr("Toggle the viewer's fullscreen mode."));
341 
342   prev_radius = sceneRadius();
343   d->has_text = false;
344   d->i_is_pressed = false;
345   d->z_is_pressed = false;
346   d->distance_is_displayed = false;
347   d->is_d_pressed = false;
348   d->viewer = this;
349   setTextIsEnabled(true);
350 }
351 
Viewer(QWidget * parent,bool antialiasing)352 Viewer::Viewer(QWidget* parent, bool antialiasing)
353   : CGAL::Three::Viewer_interface(parent)
354 {
355   d = new Viewer_impl;
356   d->antialiasing = antialiasing;
357   doBindings();
358 }
359 
Viewer(QWidget * parent,Viewer * sharedWidget,bool antialiasing)360 Viewer::Viewer(QWidget* parent,
361                Viewer* sharedWidget,
362                bool antialiasing)
363   : CGAL::Three::Viewer_interface(parent, sharedWidget)
364 {
365   d = new Viewer_impl;
366   d->viewer = this;
367   d->shareViewer = sharedWidget;
368   is_sharing = true;
369   d->antialiasing = antialiasing;
370   this->setProperty("draw_two_sides", false);
371   this->setProperty("back_front_shading", false);
372   this->setProperty("helpText", QString("This is a sub-viewer. It displays the scene "
373                                         "from another point of view. \n "));
374   is_ogl_4_3 = sharedWidget->is_ogl_4_3;
375   d->_recentFunctions = sharedWidget->d->_recentFunctions;
376   doBindings();
377   d->total_pass = sharedWidget->total_pass();
378   setOffset(sharedWidget->offset());
379 }
380 
~Viewer()381 Viewer::~Viewer()
382 {
383   makeCurrent();
384     QSettings viewer_settings;
385     viewer_settings.setValue("cam_pos",
386                              QString("%1,%2,%3")
387                              .arg(d->position.x())
388                              .arg(d->position.y())
389                              .arg(d->position.z()));
390     viewer_settings.setValue("ambient",
391                              QString("%1,%2,%3")
392                              .arg(d->ambient.x())
393                              .arg(d->ambient.y())
394                              .arg(d->ambient.z()));
395     viewer_settings.setValue("diffuse",
396                              QString("%1,%2,%3")
397                              .arg(d->diffuse.x())
398                              .arg(d->diffuse.y())
399                              .arg(d->diffuse.z()));
400     viewer_settings.setValue("specular",
401                              QString("%1,%2,%3")
402                              .arg(d->specular.x())
403                              .arg(d->specular.y())
404                              .arg(d->specular.z()));
405     viewer_settings.setValue("spec_power",
406                              d->spec_power);
407     viewer_settings.setValue("front_color",
408                              QString("%1,%2,%3")
409                              .arg(d->front_color.redF())
410                              .arg(d->front_color.greenF())
411                              .arg(d->front_color.blueF()));
412     viewer_settings.setValue("back_color",
413                              QString("%1,%2,%3")
414                              .arg(d->back_color.redF())
415                              .arg(d->back_color.greenF())
416                              .arg(d->back_color.blueF()));
417     makeCurrent();
418     d->vao.destroy();
419     if(d->_recentFunctions)
420       delete d->_recentFunctions;
421     if(d->painter)
422       delete d->painter;
423     if(d->textRenderer)
424       d->textRenderer->deleteLater();
425   delete d;
426 
427 }
428 
setScene(CGAL::Three::Scene_draw_interface * scene)429 void Viewer::setScene(CGAL::Three::Scene_draw_interface* scene)
430 {
431   d->scene = scene;
432 }
433 
antiAliasing() const434 bool Viewer::antiAliasing() const
435 {
436   return d->antialiasing;
437 }
438 
setAntiAliasing(bool b)439 void Viewer::setAntiAliasing(bool b)
440 {
441   d->antialiasing = b;
442   update();
443 }
444 
setTwoSides(bool b)445 void Viewer::setTwoSides(bool b)
446 {
447   this->setProperty("draw_two_sides", b);
448   d->twosides = b;
449   update();
450 }
451 
452 
setBackFrontShading(bool b)453 void Viewer::setBackFrontShading(bool b)
454 {
455   this->setProperty("back_front_shading", b);
456   update();
457 }
458 
459 
setFastDrawing(bool b)460 void Viewer::setFastDrawing(bool b)
461 {
462   d->inFastDrawing = b;
463   update();
464 }
465 
inFastDrawing() const466 bool Viewer::inFastDrawing() const
467 {
468   return (d->inFastDrawing
469           && (camera()->frame()->isSpinning()
470               || camera()->frame()->isManipulated()));
471 }
472 
draw()473 void Viewer::draw()
474 {
475   glEnable(GL_DEPTH_TEST);
476   d->draw_aux(false, this);
477 }
478 
fastDraw()479 void Viewer::fastDraw()
480 {
481   d->draw_aux(false, this);
482 }
483 
init()484 void Viewer::init()
485 {
486   if(!isOpenGL_4_3())
487   {
488     std::cerr<<"The openGL context initialization failed "
489     "and the default context (2.0 ES) will be used. \n"
490     " This means, among other things, that no widelines can be displayed,"
491     " which makes selected edges harder to see." <<std::endl;
492   }
493   else
494   {
495     d->_recentFunctions = new QOpenGLFunctions_4_3_Core();
496     d->_recentFunctions->initializeOpenGLFunctions();
497   }
498   d->logger = new QOpenGLDebugLogger(this);
499   if(!d->logger->initialize())
500     qDebug()<<"logger could not init.";
501   else{
502     connect(d->logger, SIGNAL(messageLogged(QOpenGLDebugMessage)), this, SLOT(messageLogged(QOpenGLDebugMessage)));
503     d->logger->startLogging();
504   }
505   glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDARBPROC)this->context()->getProcAddress("glDrawArraysInstancedARB");
506   if(!glDrawArraysInstanced)
507   {
508       qDebug()<<"glDrawArraysInstancedARB : extension not found. Spheres will be displayed as points.";
509       d->extension_is_found = false;
510   }
511   else
512       d->extension_is_found = true;
513 
514   glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORARBPROC)this->context()->getProcAddress("glVertexAttribDivisorARB");
515   if(!glDrawArraysInstanced)
516   {
517       qDebug()<<"glVertexAttribDivisorARB : extension not found. Spheres will be displayed as points.";
518       d->extension_is_found = false;
519   }
520   else
521       d->extension_is_found = true;
522   QSettings settings;
523   QString colorname = settings.value("background_color", "#ffffff").toString();
524   QColor bc(colorname);
525   setBackgroundColor(bc);
526   d->vao.create();
527   d->buffer.create();
528 
529   //setting the program used for the distance
530   if(!is_linked)
531   {
532     //Vertex source code
533     const char vertex_source_dist[] =
534     {
535       "#version 150  \n"
536       "in vec4 vertex;\n"
537       "uniform mat4 mvp_matrix;\n"
538       "uniform float point_size;\n"
539       "void main(void)\n"
540       "{\n"
541       "   gl_PointSize = point_size; \n"
542       "   gl_Position = mvp_matrix * vertex; \n"
543       "} \n"
544       "\n"
545     };
546     const char vertex_source_comp_dist[] =
547     {
548       "attribute highp vec4 vertex;\n"
549       "uniform highp mat4 mvp_matrix;\n"
550       "uniform highp float point_size;\n"
551       "void main(void)\n"
552       "{\n"
553       "   gl_PointSize = point_size; \n"
554       "   gl_Position = mvp_matrix * vertex; \n"
555       "} \n"
556       "\n"
557     };
558     //Fragment source code
559     const char fragment_source_dist[] =
560     {
561       "#version 150  \n"
562       "out vec4 out_color; \n"
563       "void main(void) { \n"
564       "out_color = vec4(0.0,0.0,0.0,1.0); \n"
565       "} \n"
566       "\n"
567     };
568     const char fragment_source_comp_dist[] =
569     {
570       "void main(void) { \n"
571       "gl_FragColor = vec4(0.0,0.0,0.0,1.0); \n"
572       "} \n"
573       "\n"
574     };
575     QOpenGLShader vertex_shader(QOpenGLShader::Vertex);
576     QOpenGLShader fragment_shader(QOpenGLShader::Fragment);
577     if(isOpenGL_4_3())
578     {
579       if(!vertex_shader.compileSourceCode(vertex_source_dist))
580       {
581         std::cerr<<"Compiling vertex source FAILED"<<std::endl;
582       }
583 
584       if(!fragment_shader.compileSourceCode(fragment_source_dist))
585       {
586         std::cerr<<"Compiling fragmentsource FAILED"<<std::endl;
587       }
588     }
589     else
590     {
591       if(!vertex_shader.compileSourceCode(vertex_source_comp_dist))
592       {
593         std::cerr<<"Compiling vertex source FAILED"<<std::endl;
594       }
595 
596       if(!fragment_shader.compileSourceCode(fragment_source_comp_dist))
597       {
598         std::cerr<<"Compiling fragmentsource FAILED"<<std::endl;
599       }
600     }
601     if(!d->rendering_program_dist.addShader(&vertex_shader))
602     {
603       std::cerr<<"adding vertex shader FAILED"<<std::endl;
604     }
605     if(!d->rendering_program_dist.addShader(&fragment_shader))
606     {
607       std::cerr<<"adding fragment shader FAILED"<<std::endl;
608     }
609     if(!d->rendering_program_dist.link())
610     {
611       qDebug() << d->rendering_program_dist.log();
612     }
613   }
614   d->painter = new QPainter();
615 }
616 
617 #include <QMouseEvent>
618 
mousePressEvent(QMouseEvent * event)619 void Viewer::mousePressEvent(QMouseEvent* event)
620 {
621   makeCurrent();
622   if(event->button() == Qt::RightButton &&
623      event->modifiers().testFlag(Qt::ShiftModifier))
624   {
625     select(event->pos());
626     requestContextMenu(event->globalPos());
627     event->accept();
628   }
629   else if(!event->modifiers()
630           && event->button() == Qt::LeftButton
631           && d->i_is_pressed)
632   {
633       d->scene->printPrimitiveId(event->pos(), this);
634   }
635   else if(!event->modifiers()
636           && event->button() == Qt::LeftButton
637           && d->z_is_pressed)
638   {
639       d->scene->zoomToPosition(event->pos(), this);
640   }
641   else if(!event->modifiers()
642           && event->button() == Qt::LeftButton
643           && d->is_d_pressed)
644   {
645       d->showDistance(event->pos());
646       event->accept();
647   }
648   else{
649     makeCurrent();
650     CGAL::QGLViewer::mousePressEvent(event);
651   }
652 }
mouseDoubleClickEvent(QMouseEvent * event)653 void Viewer::mouseDoubleClickEvent(QMouseEvent* event)
654 {
655   makeCurrent();
656   CGAL::QGLViewer::mouseDoubleClickEvent(event);
657 }
658 
659 #include <QContextMenuEvent>
contextMenuEvent(QContextMenuEvent * event)660 void Viewer::contextMenuEvent(QContextMenuEvent* event)
661 {
662   if(event->reason() != QContextMenuEvent::Mouse) {
663     requestContextMenu(event->globalPos());
664     event->accept();
665   }
666   else {
667     CGAL::QGLViewer::contextMenuEvent(event);
668   }
669 }
670 
keyPressEvent(QKeyEvent * e)671 void Viewer::keyPressEvent(QKeyEvent* e)
672 {
673   if(!e->modifiers()) {
674     if(e->key() == Qt::Key_T) {
675       turnCameraBy180Degres();
676       return;
677     }
678     else if(e->key() == Qt::Key_M) {
679       d->macro_mode = ! d->macro_mode;
680       switch(camera()->type()){
681       case CGAL::qglviewer::Camera::PERSPECTIVE:
682         if(d->macro_mode) {
683           camera()->setZNearCoefficient(0.0005f);
684         } else {
685           camera()->setZNearCoefficient(0.005f);
686         }
687         break;
688         case CGAL::qglviewer::Camera::ORTHOGRAPHIC:
689         if(d->macro_mode) {
690           camera()->setOrthoZNear(-0.5f);
691         } else {
692           camera()->setOrthoZNear(0.0f);
693         }
694         break;
695         default:
696         break;
697       }
698       this->displayMessage(tr("Macro mode: %1").
699                            arg(d->macro_mode ? tr("on") : tr("off")));
700 
701 
702 
703       return;
704     }
705     else if(e->key() == Qt::Key_I) {
706           d->i_is_pressed = true;
707         }
708     else if(e->key() == Qt::Key_O) {
709           d->z_is_pressed = true;
710         }
711     else if(e->key() == Qt::Key_D) {
712         if(e->isAutoRepeat())
713         {
714             return;
715         }
716         if(!d->is_d_pressed)
717         {
718             d->clearDistancedisplay();
719         }
720         d->is_d_pressed = true;
721         update();
722         return;
723     }
724   }
725   else if(e->key() == Qt::Key_I && e->modifiers() & Qt::ControlModifier){
726     d->scene->printAllIds();
727     update();
728     return;
729   }
730 
731   else if(e->key() == Qt::Key_C && e->modifiers() & Qt::ControlModifier){
732     d->sendSnapshotToClipboard(this);
733     return;
734   }
735 
736   else if(e->key() == Qt::Key_S && e->modifiers() & Qt::ControlModifier){
737     this->saveSnapshot();
738     return;
739   }
740 
741   //forward the event to the scene (item handling of the event)
742   if (! d->scene->keyPressEvent(e) )
743     CGAL::QGLViewer::keyPressEvent(e);
744 }
745 
keyReleaseEvent(QKeyEvent * e)746 void Viewer::keyReleaseEvent(QKeyEvent *e)
747 {
748   if(e->key() == Qt::Key_I) {
749     d->i_is_pressed = false;
750   }
751   else if(e->key() == Qt::Key_O) {
752     d->z_is_pressed = false;
753   }
754   else if(!e->modifiers() && e->key() == Qt::Key_D)
755   {
756     if(e->isAutoRepeat())
757     {
758       return;
759     }
760     d->is_d_pressed = false;
761   }
762   CGAL::QGLViewer::keyReleaseEvent(e);
763 }
764 
turnCameraBy180Degres()765 void Viewer::turnCameraBy180Degres() {
766   CGAL::qglviewer::Camera* camera = this->camera();
767   using CGAL::qglviewer::ManipulatedCameraFrame;
768 
769   ManipulatedCameraFrame frame_from(*camera->frame());
770   camera->setViewDirection(-camera->viewDirection());
771   ManipulatedCameraFrame frame_to(*camera->frame());
772 
773   camera->setOrientation(frame_from.orientation());
774   camera->interpolateTo(frame_to, 0.5f);
775 }
776 
draw_aux(bool with_names,Viewer * viewer)777 void Viewer_impl::draw_aux(bool with_names, Viewer* viewer)
778 {
779   if(scene == nullptr)
780     return;
781   current_total_pass = viewer->inFastDrawing() ? total_pass/2 : total_pass;
782   viewer->setGlPointSize(2.f);
783   viewer->glEnable(GL_POLYGON_OFFSET_FILL);
784   viewer->glPolygonOffset(1.0f,1.0f);
785 
786   if(!with_names && antialiasing)
787   {
788     viewer->glEnable(GL_BLEND);
789     viewer->glEnable(GL_LINE_SMOOTH);
790     viewer->glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
791     //viewer->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
792     viewer->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
793   }
794   else
795   {
796     viewer->glDisable(GL_BLEND);
797     viewer->glDisable(GL_LINE_SMOOTH);
798     viewer->glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);
799     //viewer->glBlendFunc(GL_ONE, GL_ZERO);
800     viewer->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
801   }
802   inDrawWithNames = with_names;
803   if(with_names)
804     scene->drawWithNames(viewer);
805   else
806     scene->draw(viewer);
807   viewer->glDisable(GL_POLYGON_OFFSET_FILL);
808 }
809 
inDrawWithNames() const810 bool Viewer::inDrawWithNames() const {
811   return d->inDrawWithNames;
812 }
813 
drawWithNames()814 void Viewer::drawWithNames()
815 {
816   CGAL::QGLViewer::draw();
817   d->draw_aux(true, this);
818 }
819 
postSelection(const QPoint & pixel)820 void Viewer::postSelection(const QPoint& pixel)
821 {
822   Q_EMIT selected(this->selectedName());
823   CGAL::qglviewer::Vec point;
824   bool found = true;
825   if(property("picked_point").isValid()) {
826     if(!property("picked_point").toList().isEmpty())
827     {
828       QList<QVariant> picked_point = property("picked_point").toList();
829       point = CGAL::qglviewer::Vec (picked_point[0].toDouble(),
830           picked_point[1].toDouble(),
831           picked_point[2].toDouble());
832     }
833     else{
834       found = false;
835     }
836   }
837   else{
838     point = camera()->pointUnderPixel(pixel, found) - offset();
839   }
840   if(found) {
841     QVector3D transformed_point(point.x,
842                                 point.y,
843                                 point.z);
844     if(d->scene_scaling)
845     {
846       transformed_point = QVector3D(
847             point.x+offset().x,
848             point.y+offset().y,
849             point.z+offset().z);
850       transformed_point = transformed_point/d->scaler;
851       transformed_point[0] -=offset().x ;
852       transformed_point[1] -=offset().y ;
853       transformed_point[2] -=offset().z ;
854     }
855     Q_EMIT selectedPoint(transformed_point.x(),
856                          transformed_point.y(),
857                          transformed_point.z());
858     CGAL::qglviewer::Vec dir;
859     CGAL::qglviewer::Vec orig;
860     if(d->projection_is_ortho)
861     {
862       dir = camera()->viewDirection();
863       orig = point;
864     }
865     else{
866       orig = camera()->position() - offset();
867       dir = point - orig;
868     }
869     this->setProperty("performing_selection", true);
870     Q_EMIT selectionRay(orig.x, orig.y, orig.z,
871                         dir.x, dir.y, dir.z);
872     this->setProperty("performing_selection", false);
873   }
874 }
readFrame(QString s,CGAL::qglviewer::Frame & frame)875 bool CGAL::Three::Viewer_interface::readFrame(QString s, CGAL::qglviewer::Frame& frame)
876 {
877   QStringList list = s.split(" ", CGAL_QT_SKIP_EMPTY_PARTS);
878   if(list.size() != 7)
879     return false;
880   float vec[3];
881   for(int i = 0; i < 3; ++i)
882   {
883     bool ok;
884     vec[i] = list[i].toFloat(&ok);
885     if(!ok) return false;
886   }
887   double orient[4];
888   for(int i = 0; i < 4; ++i)
889   {
890     bool ok;
891     orient[i] = list[i + 3].toDouble(&ok);
892     if(!ok) return false;
893   }
894   frame.setPosition(CGAL::qglviewer::Vec(vec[0],
895                                    vec[1],
896                                    vec[2]));
897   frame.setOrientation(orient[0],
898                        orient[1],
899                        orient[2],
900                        orient[3]);
901   return true;
902 }
903 
dumpFrame(const CGAL::qglviewer::Frame & frame)904 QString CGAL::Three::Viewer_interface::dumpFrame(const CGAL::qglviewer::Frame& frame) {
905   const CGAL::qglviewer::Vec pos = frame.position();
906   const CGAL::qglviewer::Quaternion q = frame.orientation();
907 
908   return QString("%1 %2 %3 %4 %5 %6 %7")
909     .arg(pos[0])
910     .arg(pos[1])
911     .arg(pos[2])
912     .arg(q[0])
913     .arg(q[1])
914     .arg(q[2])
915     .arg(q[3]);
916 }
917 
moveCameraToCoordinates(QString s,float animation_duration)918 bool Viewer::moveCameraToCoordinates(QString s, float animation_duration) {
919   CGAL::qglviewer::Frame new_frame;
920   if(readFrame(s, new_frame)) {
921     camera()->interpolateTo(new_frame, animation_duration);
922     return true;
923   }
924   else
925     return false;
926 }
927 
dumpCameraCoordinates()928 QString Viewer::dumpCameraCoordinates()
929 {
930   if(camera()->frame()) {
931     return dumpFrame(*camera()->frame());
932   } else {
933     return QString();
934   }
935 }
936 
attribBuffers(int program_name) const937 void Viewer::attribBuffers(int program_name) const {
938     //ModelViewMatrix used for the transformation of the camera.
939     QMatrix4x4 mvp_mat;
940     // ModelView Matrix used for the lighting system
941     QMatrix4x4 mv_mat;
942     // transformation of the manipulated frame
943     QMatrix4x4 f_mat;
944     // ModelView Matrix that is modified just for the normal matrix in case of scene scaling
945     QMatrix4x4 norm_mat;
946 
947     f_mat.setToIdentity();
948     //fills the MVP and MV matrices.
949     GLdouble d_mat[16];
950     this->camera()->getModelViewMatrix(d_mat);
951     for (int i=0; i<16; ++i)
952         mv_mat.data()[i] = GLfloat(d_mat[i]);
953     this->camera()->getModelViewProjectionMatrix(d_mat);
954     for (int i=0; i<16; ++i)
955         mvp_mat.data()[i] = GLfloat(d_mat[i]);
956 
957     norm_mat = mv_mat;
958 
959     if(d->scene_scaling){
960       mvp_mat.scale(d->scaler);
961       mv_mat.scale(d->scaler);
962       QVector3D scale_norm(1.0/d->scaler.x(), 1.0/d->scaler.y(), 1.0/d->scaler.z());
963       norm_mat.scale(scale_norm);
964     }
965 
966     QOpenGLShaderProgram* program = getShaderProgram(program_name);
967     program->bind();
968     program->setUniformValue("point_size", getGlPointSize());
969     program->setUniformValue("mvp_matrix", mvp_mat);
970     QMatrix4x4 id_mat;
971     id_mat.setToIdentity();
972     program->setUniformValue("f_matrix", id_mat);
973     program->setUniformValue("is_clipbox_on", d->clipping);
974     if(d->clipping)
975     {
976       QMatrix4x4 clipbox1;
977       QMatrix4x4 clipbox2;
978       for(int i=0;i<12;++i)
979       {
980         clipbox1.data()[i]=d->clipbox[i/4][i%4];
981         clipbox2.data()[i]=d->clipbox[(i+12)/4][(i+12)%4];
982       }
983       program->setUniformValue("clipbox1", clipbox1);
984       program->setUniformValue("clipbox2", clipbox2);
985     }
986     QVector4D light_pos(d->position.x(),
987                         d->position.y(),
988                         d->position.z(),
989                         1.0f);
990     switch(program_name)
991     {
992     case PROGRAM_WITH_LIGHT:
993     case PROGRAM_SPHERES:
994     case PROGRAM_CUTPLANE_SPHERES:
995     case PROGRAM_NO_SELECTION:
996     case PROGRAM_HEAT_INTENSITY:
997       program->setUniformValue("alpha", 1.0f); //overriden in item draw() if necessary
998     default:
999       break;
1000     }
1001     switch(program_name)
1002     {
1003     case PROGRAM_SPHERES:
1004     case PROGRAM_DARK_SPHERES:
1005     case PROGRAM_WITH_LIGHT:
1006     case PROGRAM_OLD_FLAT:
1007       program->setUniformValue("f_matrix",f_mat);
1008     default:
1009       break;
1010     }
1011 
1012     switch(program_name)
1013     {
1014     case PROGRAM_WITH_LIGHT:
1015     case PROGRAM_C3T3:
1016     case PROGRAM_PLANE_TWO_FACES:
1017     case PROGRAM_INSTANCED:
1018     case PROGRAM_WITH_TEXTURE:
1019     case PROGRAM_CUTPLANE_SPHERES:
1020     case PROGRAM_SPHERES:
1021     case PROGRAM_OLD_FLAT:
1022     case PROGRAM_FLAT:
1023     case PROGRAM_NO_INTERPOLATION:
1024     case PROGRAM_HEAT_INTENSITY:
1025         program->setUniformValue("light_pos", light_pos);
1026         program->setUniformValue("light_diff",d->diffuse);
1027         program->setUniformValue("light_spec", d->specular);
1028         program->setUniformValue("light_amb", d->ambient);
1029         program->setUniformValue("spec_power", d->spec_power);
1030         program->setUniformValue("front_color", d->front_color);
1031         program->setUniformValue("back_color", d->back_color);
1032         program->setUniformValue("is_two_side", d->twosides);
1033         program->setUniformValue("back_front_shading", this->property("back_front_shading").toBool());
1034         break;
1035     }
1036     switch(program_name)
1037     {
1038     case PROGRAM_WITH_LIGHT:
1039     case PROGRAM_C3T3:
1040     case PROGRAM_PLANE_TWO_FACES:
1041     case PROGRAM_INSTANCED:
1042     case PROGRAM_CUTPLANE_SPHERES:
1043     case PROGRAM_SPHERES:
1044     case PROGRAM_OLD_FLAT:
1045     case PROGRAM_FLAT:
1046     case PROGRAM_NO_INTERPOLATION:
1047     case PROGRAM_HEAT_INTENSITY:
1048       program->setUniformValue("mv_matrix", mv_mat);
1049       program->setUniformValue("norm_matrix", norm_mat);
1050       break;
1051     case PROGRAM_WITHOUT_LIGHT:
1052     case PROGRAM_SOLID_WIREFRAME:
1053       break;
1054     case PROGRAM_WITH_TEXTURE:
1055       program->setUniformValue("mv_matrix", mv_mat);
1056       program->setUniformValue("norm_matrix", norm_mat);
1057       program->setUniformValue("s_texture",0);
1058       program->setUniformValue("f_matrix",f_mat);
1059       break;
1060     case PROGRAM_WITH_TEXTURED_EDGES:
1061         program->setUniformValue("s_texture",0);
1062         break;
1063     case PROGRAM_NO_SELECTION:
1064         program->setUniformValue("f_matrix",f_mat);
1065         break;
1066     }
1067     program->release();
1068 }
1069 
beginSelection(const QPoint & point)1070 void Viewer::beginSelection(const QPoint &point)
1071 {
1072   CGAL::QGLViewer::beginSelection(point);
1073   d->scene->setPickedPixel(point);
1074 }
endSelection(const QPoint & point)1075 void Viewer::endSelection(const QPoint& point)
1076 {
1077   CGAL::QGLViewer::endSelection(point);
1078     //redraw the true scene for the glReadPixel in postSelection();
1079     d->draw_aux(false, this);
1080 }
1081 
drawVisualHints()1082 void Viewer::drawVisualHints()
1083 {
1084 
1085     CGAL::QGLViewer::drawVisualHints();
1086 
1087     if(d->distance_is_displayed)
1088     {
1089         glDisable(GL_DEPTH_TEST);
1090         QMatrix4x4 mvpMatrix;
1091         double mat[16];
1092         camera()->getModelViewProjectionMatrix(mat);
1093         for(int i=0; i < 16; i++)
1094         {
1095           mvpMatrix.data()[i] = (float)mat[i];
1096         }
1097         if(!isOpenGL_4_3())
1098         {
1099           //draws the distance
1100           //nullifies the translation
1101           d->rendering_program_dist.bind();
1102           d->rendering_program_dist.setUniformValue("mvp_matrix", mvpMatrix);
1103           d->rendering_program_dist.setUniformValue("point_size", GLfloat(6.0f));
1104           d->vao.bind();
1105           glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(2));
1106           glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(2));
1107           d->vao.release();
1108           d->rendering_program_dist.release();
1109           glEnable(GL_DEPTH_TEST);
1110         }
1111         else
1112         {
1113           QOpenGLShaderProgram* program = getShaderProgram(PROGRAM_SOLID_WIREFRAME);
1114           program->bind();
1115           QVector2D vp(width(), height());
1116           program->setUniformValue("viewport", vp);
1117           program->setUniformValue("near",(GLfloat)camera()->zNear());
1118           program->setUniformValue("far",(GLfloat)camera()->zFar());
1119           program->setUniformValue("width", GLfloat(3.0f));
1120           program->setAttributeValue("colors", QColor(Qt::black));
1121           program->setUniformValue("mvp_matrix", mvpMatrix);
1122           QMatrix4x4 f_mat;
1123           f_mat.setToIdentity();
1124           program->setUniformValue("f_matrix", f_mat);
1125           d->vao.bind();
1126           glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(2));
1127           d->vao.release();
1128           program->release();
1129 
1130           program = getShaderProgram(PROGRAM_NO_SELECTION);
1131           program->bind();
1132           program->setAttributeValue("colors", QColor(Qt::black));
1133           program->setAttributeValue("point_size", 6.0f);
1134           program->setUniformValue("mvp_matrix", mvpMatrix);
1135           program->setUniformValue("f_matrix", f_mat);
1136           d->vao.bind();
1137           glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(2));
1138           d->vao.release();
1139           program->release();
1140         }
1141 
1142     }
1143     if (!d->painter->isActive())
1144       d->painter->begin(this);
1145     //So that the text is drawn in front of everything
1146     d->painter->beginNativePainting();
1147     glDisable(GL_DEPTH_TEST);
1148     d->painter->endNativePainting();
1149     //Prints the displayMessage
1150     QFont font = QFont();
1151     QFontMetrics fm(font);
1152     TextItem *message_text = new TextItem(float(10 +
1153                                             #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
1154                                                    fm.horizontalAdvance(d->message)/2)
1155                                             #else
1156                                                    fm.width(d->message)/2)
1157                                             #endif
1158                                           ,
1159                                           float(height()-20),
1160                                           0, d->message, false,
1161                                           QFont(), Qt::gray );
1162     if (d->_displayMessage)
1163     {
1164       d->textRenderer->addText(message_text);
1165     }
1166     d->textRenderer->draw(this, d->scaler);
1167 
1168     if (d->_displayMessage)
1169       d->textRenderer->removeText(message_text);
1170     delete message_text;
1171 }
1172 
declare_program(int name,const char * v_shader,const char * f_shader) const1173 QOpenGLShaderProgram* Viewer::declare_program(int name,
1174                                       const char* v_shader,
1175                                       const char* f_shader) const
1176 {
1177   // workaround constness issues in Qt
1178   Viewer* viewer = const_cast<Viewer*>(this);
1179 
1180   if(d->shader_programs[name])
1181   {
1182     return d->shader_programs[name];
1183   }
1184 
1185   else
1186   {
1187 
1188     QOpenGLShaderProgram *program = new QOpenGLShaderProgram(viewer);
1189     if(!program->addShaderFromSourceFile(QOpenGLShader::Vertex,v_shader))
1190     {
1191       std::cerr<<"adding vertex shader FAILED"<<std::endl;
1192     }
1193     if(!program->addShaderFromSourceFile(QOpenGLShader::Fragment,f_shader))
1194     {
1195       std::cerr<<"adding fragment shader FAILED"<<std::endl;
1196     }
1197     if(isOpenGL_4_3())
1198     {
1199       if(strcmp(f_shader,":/cgal/Polyhedron_3/resources/shader_flat.frag" ) == 0)
1200       {
1201         if(!program->addShaderFromSourceFile(QOpenGLShader::Geometry,":/cgal/Polyhedron_3/resources/shader_flat.geom" ))
1202         {
1203           std::cerr<<"adding geometry shader FAILED"<<std::endl;
1204         }
1205       }
1206       if(strcmp(f_shader,":/cgal/Polyhedron_3/resources/solid_wireframe_shader.frag" ) == 0)
1207       {
1208         if(!program->addShaderFromSourceFile(QOpenGLShader::Geometry,":/cgal/Polyhedron_3/resources/solid_wireframe_shader.geom" ))
1209         {
1210           std::cerr<<"adding geometry shader FAILED"<<std::endl;
1211         }
1212       }
1213       if(strcmp(f_shader,":/cgal/Polyhedron_3/resources/no_interpolation_shader.frag" ) == 0)
1214       {
1215         if(!program->addShaderFromSourceFile(QOpenGLShader::Geometry,":/cgal/Polyhedron_3/resources/no_interpolation_shader.geom" ))
1216         {
1217           std::cerr<<"adding geometry shader FAILED"<<std::endl;
1218         }
1219       }
1220     }
1221     program->bindAttributeLocation("colors", 1);
1222     program->link();
1223     d->shader_programs[name] = program;
1224     return program;
1225   }
1226 }
getShaderProgram(int name) const1227 QOpenGLShaderProgram* Viewer::getShaderProgram(int name) const
1228 {
1229   switch(name)
1230   {
1231   case PROGRAM_C3T3:
1232   {
1233     QOpenGLShaderProgram* program = isOpenGL_4_3()
1234         ? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_c3t3.vert" , ":/cgal/Polyhedron_3/resources/shader_c3t3.frag")
1235         : declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_c3t3.vert" ,
1236                           ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_c3t3.frag");
1237     program->setProperty("hasLight", true);
1238     program->setProperty("hasNormals", true);
1239     program->setProperty("hasCutPlane", true);
1240     program->setProperty("hasTransparency", true);
1241     program->setProperty("hasCenter", true);
1242     program->setProperty("hasSurfaceMode", true);
1243     return program;
1244   }
1245   case PROGRAM_C3T3_EDGES:
1246   {
1247     QOpenGLShaderProgram* program = isOpenGL_4_3()
1248         ? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_c3t3_edges.vert" , ":/cgal/Polyhedron_3/resources/shader_c3t3_edges.frag")
1249         : declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_c3t3_edges.vert" ,
1250                           ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_c3t3_edges.frag");
1251     program->setProperty("hasCutPlane", true);
1252     program->setProperty("hasSurfaceMode", true);
1253     return program;
1254   }
1255   case PROGRAM_WITH_LIGHT:
1256   {
1257     QOpenGLShaderProgram* program = isOpenGL_4_3()
1258         ? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_with_light.vert" , ":/cgal/Polyhedron_3/resources/shader_with_light.frag")
1259         : declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_light.vert" ,
1260                           ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_light.frag");
1261     program->setProperty("hasLight", true);
1262     program->setProperty("hasNormals", true);
1263     program->setProperty("hasTransparency", true);
1264     program->setProperty("hasFMatrix", true);
1265     return program;
1266   }
1267   case PROGRAM_HEAT_INTENSITY:
1268   {
1269     QOpenGLShaderProgram* program = isOpenGL_4_3()
1270         ? declare_program(name, ":/cgal/Polyhedron_3/resources/heat_intensity_shader.vert" , ":/cgal/Polyhedron_3/resources/heat_intensity_shader.frag")
1271         : declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/heat_intensity_shader.vert" ,
1272                           ":/cgal/Polyhedron_3/resources/compatibility_shaders/heat_intensity_shader.frag");
1273     program->setProperty("hasLight", true);
1274     program->setProperty("hasNormals", true);
1275     program->setProperty("hasTransparency", true);
1276     program->setProperty("hasDistanceValues", true);
1277     return program;
1278   }
1279   case PROGRAM_WITHOUT_LIGHT:
1280   {
1281     QOpenGLShaderProgram* program = isOpenGL_4_3()
1282         ? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_without_light.vert" , ":/cgal/Polyhedron_3/resources/shader_without_light.frag")
1283         : declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_without_light.vert" ,
1284                           ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_without_light.frag");
1285     program->setProperty("hasFMatrix", true);
1286     return program;
1287   }
1288   case PROGRAM_NO_SELECTION:
1289   {
1290     QOpenGLShaderProgram* program = isOpenGL_4_3()
1291         ? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_without_light.vert" , ":/cgal/Polyhedron_3/resources/shader_no_light_no_selection.frag")
1292         : declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_without_light.vert" ,
1293                           ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_no_light_no_selection.frag");
1294     program->setProperty("hasFMatrix", true);
1295     program->setProperty("hasTransparency", true);
1296     return program;
1297   }
1298   case PROGRAM_WITH_TEXTURE:
1299   {
1300     QOpenGLShaderProgram* program = isOpenGL_4_3()
1301         ? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_with_texture.vert" , ":/cgal/Polyhedron_3/resources/shader_with_texture.frag")
1302         : declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_texture.vert" ,
1303                           ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_texture.frag");
1304     program->setProperty("hasLight", true);
1305     program->setProperty("hasNormals", true);
1306     program->setProperty("hasFMatrix", true);
1307     program->setProperty("hasTexture", true);
1308     return program;
1309   }
1310   case PROGRAM_PLANE_TWO_FACES:
1311   {
1312     QOpenGLShaderProgram* program = isOpenGL_4_3()
1313         ?declare_program(name, ":/cgal/Polyhedron_3/resources/shader_without_light.vert" , ":/cgal/Polyhedron_3/resources/shader_plane_two_faces.frag")
1314        : declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_without_light.vert" ,
1315                          ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_plane_two_faces.frag");
1316     program->setProperty("hasLight", true);
1317     program->setProperty("hasNormals", true);
1318     program->setProperty("hasFMatrix", true);
1319     return program;
1320   }
1321   case PROGRAM_WITH_TEXTURED_EDGES:
1322   {
1323     QOpenGLShaderProgram* program = isOpenGL_4_3()
1324         ? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_with_textured_edges.vert" , ":/cgal/Polyhedron_3/resources/shader_with_textured_edges.frag")
1325         : declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_textured_edges.vert" ,
1326                           ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_textured_edges.frag");
1327     program->setProperty("hasFMatrix", true);
1328     program->setProperty("hasTexture", true);
1329     return program;
1330   }
1331   case PROGRAM_INSTANCED:
1332   {
1333     QOpenGLShaderProgram* program = isOpenGL_4_3()
1334         ? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_instanced.vert" , ":/cgal/Polyhedron_3/resources/shader_with_light.frag")
1335         : declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_instanced.vert" ,
1336                           ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_light.frag");
1337 
1338     program->setProperty("hasLight", true);
1339     program->setProperty("hasNormals", true);
1340     program->setProperty("isInstanced", true);
1341     return program;
1342   }
1343   case PROGRAM_INSTANCED_WIRE:
1344   {
1345     QOpenGLShaderProgram* program = isOpenGL_4_3()
1346         ? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_instanced.vert" , ":/cgal/Polyhedron_3/resources/shader_without_light.frag")
1347         : declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_instanced.vert" ,
1348                           ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_without_light.frag");
1349     program->setProperty("hasLight", true);
1350     program->setProperty("hasNormals", true);
1351     program->setProperty("hasCenter", true);
1352     program->setProperty("isInstanced", true);
1353     return program;
1354   }
1355   case PROGRAM_CUTPLANE_SPHERES:
1356   {
1357     QOpenGLShaderProgram* program = isOpenGL_4_3()
1358         ? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_c3t3_spheres.vert" , ":/cgal/Polyhedron_3/resources/shader_c3t3.frag")
1359         : declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_c3t3_spheres.vert" ,
1360                           ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_c3t3.frag");
1361     program->setProperty("hasLight", true);
1362     program->setProperty("hasNormals", true);
1363     program->setProperty("hasCenter", true);
1364     program->setProperty("hasRadius", true);
1365     program->setProperty("isInstanced", true);
1366     program->setProperty("hasCutPlane", true);
1367     return program;
1368   }
1369   case PROGRAM_SPHERES:
1370   {
1371     QOpenGLShaderProgram* program = isOpenGL_4_3()
1372         ?declare_program(name, ":/cgal/Polyhedron_3/resources/shader_spheres.vert" , ":/cgal/Polyhedron_3/resources/shader_with_light.frag")
1373        : declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_spheres.vert" ,
1374                          ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_light.frag");
1375     program->setProperty("hasLight", true);
1376     program->setProperty("hasNormals", true);
1377     program->setProperty("hasCenter", true);
1378     program->setProperty("hasRadius", true);
1379     program->setProperty("hasTransparency", true);
1380     program->setProperty("isInstanced", true);
1381     program->setProperty("hasFMatrix", true);
1382     return program;
1383   }
1384   case PROGRAM_DARK_SPHERES:
1385   {
1386     QOpenGLShaderProgram* program = isOpenGL_4_3()
1387         ?declare_program(name, ":/cgal/Polyhedron_3/resources/shader_dark_spheres.vert" , ":/cgal/Polyhedron_3/resources/shader_no_light_no_selection.frag")
1388        : declare_program(name, ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_dark_spheres.vert" ,
1389                          ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_no_light_no_selection.frag");
1390     program->setProperty("hasCenter", true);
1391     program->setProperty("hasRadius", true);
1392     program->setProperty("isInstanced", true);
1393     program->setProperty("hasFMatrix", true);
1394     return program;
1395   }
1396   case PROGRAM_FLAT:
1397   {
1398     if(!isOpenGL_4_3())
1399     {
1400       std::cerr<<"An OpenGL context of version 4.3 is required for the program ("<<name<<")."<<std::endl;
1401       return nullptr;
1402     }
1403     QOpenGLShaderProgram* program = declare_program(name, ":/cgal/Polyhedron_3/resources/shader_flat.vert", ":/cgal/Polyhedron_3/resources/shader_flat.frag");
1404     program->setProperty("hasLight", true);
1405     program->setProperty("hasNormals", true);
1406     return program;
1407   }
1408   case PROGRAM_OLD_FLAT:
1409   {
1410     QOpenGLShaderProgram* program = isOpenGL_4_3()
1411         ? declare_program(name, ":/cgal/Polyhedron_3/resources/shader_with_light.vert", ":/cgal/Polyhedron_3/resources/shader_old_flat.frag")
1412         : declare_program(name,
1413                           ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_with_light.vert",
1414                           ":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_old_flat.frag");
1415     program->setProperty("hasLight", true);
1416     program->setProperty("hasNormals", true);
1417     return program;
1418   }
1419   case PROGRAM_SOLID_WIREFRAME:
1420   {
1421     if(!isOpenGL_4_3())
1422     {
1423       std::cerr<<"An OpenGL context of version 4.3 is required for the program ("<<name<<")."<<std::endl;
1424       return nullptr;
1425     }
1426     QOpenGLShaderProgram* program = declare_program(name,
1427                                                     ":/cgal/Polyhedron_3/resources/solid_wireframe_shader.vert",
1428                                                     ":/cgal/Polyhedron_3/resources/solid_wireframe_shader.frag");
1429     program->setProperty("hasViewport", true);
1430     program->setProperty("hasWidth", true);
1431     program->setProperty("hasFMatrix", true);
1432     return program;
1433   }
1434   case PROGRAM_NO_INTERPOLATION:
1435   {
1436     if(!isOpenGL_4_3())
1437     {
1438       std::cerr<<"An OpenGL context of version 4.3 is required for the program ("<<name<<")."<<std::endl;
1439       return nullptr;
1440     }
1441     QOpenGLShaderProgram* program = declare_program(name,
1442                                                     ":/cgal/Polyhedron_3/resources/no_interpolation_shader.vert",
1443                                                     ":/cgal/Polyhedron_3/resources/no_interpolation_shader.frag");
1444     program->setProperty("hasLight", true);
1445     program->setProperty("hasNormals", true);
1446     program->setProperty("drawLinesAdjacency", true);
1447     return program;
1448   }
1449   default:
1450     std::cerr<<"ERROR : Program not found."<<std::endl;
1451     return nullptr;
1452   }
1453 }
1454 
wheelEvent(QWheelEvent * e)1455 void Viewer::wheelEvent(QWheelEvent* e)
1456 {
1457   if(e->modifiers().testFlag(Qt::ShiftModifier))
1458   {
1459     double delta = e->angleDelta().y();
1460     if(delta>0)
1461     {
1462       switch(camera()->type())
1463       {
1464       case CGAL::qglviewer::Camera::ORTHOGRAPHIC:
1465         camera()->setOrthoZNear(camera()->orthoZNear() + 0.01);
1466         break;
1467       case CGAL::qglviewer::Camera::PERSPECTIVE:
1468         camera()->setZNearCoefficient(camera()->zNearCoefficient() * 1.01);
1469         break;
1470       default:
1471         break;
1472       }
1473     }
1474     else
1475       switch(camera()->type())
1476       {
1477       case CGAL::qglviewer::Camera::ORTHOGRAPHIC:
1478         camera()->setOrthoZNear(camera()->orthoZNear() - 0.01);
1479         break;
1480       case CGAL::qglviewer::Camera::PERSPECTIVE:
1481         camera()->setZNearCoefficient(camera()->zNearCoefficient() / 1.01);
1482         break;
1483       default:
1484         break;
1485       }
1486     update();
1487   }
1488   else
1489     CGAL::QGLViewer::wheelEvent(e);
1490 }
1491 
testDisplayId(double x,double y,double z)1492 bool Viewer::testDisplayId(double x, double y, double z)
1493 {
1494     return d->scene->testDisplayId(x,y,z,this);
1495 }
1496 
getPainter()1497 QPainter* Viewer::getPainter(){return d->painter;}
1498 
paintEvent(QPaintEvent *)1499 void Viewer::paintEvent(QPaintEvent *)
1500 {
1501   paintGL();
1502 }
1503 
paintGL()1504 void Viewer::paintGL()
1505 {
1506   makeCurrent();
1507   if (!d->painter->isActive())
1508     d->painter->begin(this);
1509   if(d->is_2d_selection_mode)
1510   {
1511     d->painter->drawImage(QPoint(0,0), d->static_image);
1512   }
1513   else
1514   {
1515     d->painter->beginNativePainting();
1516     glClearColor(GLfloat(backgroundColor().redF()),
1517                  GLfloat(backgroundColor().greenF()),
1518                  GLfloat(backgroundColor().blueF()),
1519                  1.f);
1520     glClearDepthf(1.0f);
1521     glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
1522     //set the default frustum
1523     if(d->projection_is_ortho)
1524       camera()->setType(CGAL::qglviewer::Camera::ORTHOGRAPHIC);
1525     else
1526       camera()->setType(CGAL::qglviewer::Camera::PERSPECTIVE);
1527     preDraw();
1528     draw();
1529     postDraw();
1530     d->painter->endNativePainting();
1531   }
1532   d->painter->end();
1533   doneCurrent();
1534 }
1535 
displayMessage(const QString & _message,int delay)1536 void Viewer::displayMessage(const QString &_message, int delay)
1537 {
1538           d->message = _message;
1539           d->_displayMessage = true;
1540           // Was set to single shot in defaultConstructor.
1541           d->messageTimer.start(delay);
1542           if (textIsEnabled())
1543                   update();
1544 }
hideMessage()1545 void Viewer::hideMessage()
1546 {
1547         d->_displayMessage = false;
1548         if (textIsEnabled())
1549                 update();
1550 }
printMessage(QString _message,int ms_delay)1551 void Viewer::printMessage(QString _message, int ms_delay)
1552 {
1553   displayMessage(_message, ms_delay);
1554 }
1555 
showDistance(QPoint pixel)1556 void Viewer_impl::showDistance(QPoint pixel)
1557 {
1558     static bool isAset = false;
1559     bool found;
1560     CGAL::qglviewer::Vec point;
1561     point = viewer->camera()->pointUnderPixel(pixel, found);
1562     if(!isAset && found)
1563     {
1564         //set APoint
1565         APoint = point;
1566         isAset = true;
1567         clearDistancedisplay();
1568     }
1569     else if (found)
1570     {
1571         //set BPoint
1572         BPoint = point;
1573         isAset = false;
1574 
1575         // fills the buffers
1576         std::vector<float> v;
1577         v.resize(6);
1578         v[0] = float(APoint.x); v[1] = float(APoint.y); v[2] = float(APoint.z);
1579         v[3] = float(BPoint.x); v[4] = float(BPoint.y); v[5] = float(BPoint.z);
1580 
1581         vao.bind();
1582         buffer.bind();
1583         buffer.allocate(v.data(),6*sizeof(float));
1584         rendering_program_dist.enableAttributeArray("vertex");
1585         rendering_program_dist.setAttributeBuffer("vertex",GL_FLOAT,0,3);
1586         buffer.release();
1587         vao.release();
1588         distance_is_displayed = true;
1589         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());
1590         QFont font;
1591         font.setBold(true);
1592         TextItem *ACoord = new TextItem(float(APoint.x),
1593                                         float(APoint.y),
1594                                         float(APoint.z),
1595                                         QString("A(%1,%2,%3)")
1596                                         .arg(APoint.x/scaler.x()-viewer->offset().x, 0, 'g', 10)
1597                                         .arg(APoint.y/scaler.y()-viewer->offset().y, 0, 'g', 10)
1598                                         .arg(APoint.z/scaler.z()-viewer->offset().z, 0, 'g', 10), true, font, Qt::red, true);
1599         distance_text.append(ACoord);
1600         TextItem *BCoord = new TextItem(float(BPoint.x),
1601                                         float(BPoint.y),
1602                                         float(BPoint.z),
1603                                         QString("B(%1,%2,%3)")
1604                                         .arg(BPoint.x/scaler.x()-viewer->offset().x, 0, 'g', 10)
1605                                         .arg(BPoint.y/scaler.y()-viewer->offset().y, 0, 'g', 10)
1606                                         .arg(BPoint.z/scaler.z()-viewer->offset().z, 0, 'g', 10), true, font, Qt::red, true);
1607 
1608         distance_text.append(BCoord);
1609         CGAL::qglviewer::Vec centerPoint = 0.5*(BPoint+APoint);
1610         TextItem *centerCoord = new TextItem(float(centerPoint.x),
1611                                              float(centerPoint.y),
1612                                              float(centerPoint.z),
1613                                              QString(" distance: %1").arg(dist), true, font, Qt::red, true);
1614 
1615         distance_text.append(centerCoord);
1616         Q_FOREACH(TextItem* ti, distance_text)
1617           textRenderer->addText(ti);
1618         Q_EMIT(viewer->sendMessage(QString("First point : A(%1,%2,%3), second point : B(%4,%5,%6), distance between them : %7")
1619                   .arg(APoint.x/scaler.x()-viewer->offset().x)
1620                   .arg(APoint.y/scaler.y()-viewer->offset().y)
1621                   .arg(APoint.z/scaler.z()-viewer->offset().z)
1622                   .arg(BPoint.x/scaler.x()-viewer->offset().x)
1623                   .arg(BPoint.y/scaler.y()-viewer->offset().y)
1624                   .arg(BPoint.z/scaler.z()-viewer->offset().z)
1625                   .arg(dist, 0, 'g', 10)));
1626     }
1627 
1628 }
1629 
clearDistancedisplay()1630 void Viewer_impl::clearDistancedisplay()
1631 {
1632   distance_is_displayed = false;
1633   Q_FOREACH(TextItem* ti, distance_text)
1634   {
1635     textRenderer->removeText(ti);
1636     delete ti;
1637   }
1638   distance_text.clear();
1639 }
1640 
sendSnapshotToClipboard(Viewer * viewer)1641 void Viewer_impl::sendSnapshotToClipboard(Viewer *viewer)
1642 {
1643   QImage * snap = viewer->takeSnapshot(CGAL::qglviewer::TRANSPARENT_BACKGROUND, 2*viewer->size(), 1, true);
1644   if(snap)
1645   {
1646 #if defined(_WIN32)
1647     QApplication::clipboard()->setImage(*snap);
1648     QMimeData *mimeData = new QMimeData();
1649     QByteArray ba;
1650     QBuffer buffer(&ba);
1651     buffer.open(QIODevice::WriteOnly);
1652     snap->save(&buffer, "PNG"); // writes image into ba in PNG format
1653     buffer.close();
1654     mimeData->setData("PNG", ba);
1655     //According to the doc, the ownership of mime_data is transferred to
1656     //clipboard, so this is not a memory leak.
1657     QApplication::clipboard()->setMimeData(mimeData);
1658 #else
1659     QApplication::clipboard()->setImage(*snap);
1660 #endif
1661     delete snap;
1662   }
1663 }
SetOrthoProjection(bool b)1664 void Viewer::SetOrthoProjection(bool b)
1665 {
1666   d->projection_is_ortho = b;
1667   update();
1668 }
1669 
updateIds(CGAL::Three::Scene_item * item)1670 void Viewer::updateIds(CGAL::Three::Scene_item * item)
1671 {
1672   //all ids are computed when they are displayed the first time.
1673   //Calling printPrimitiveIds twice hides and show the ids again, so they are re-computed.
1674 
1675   d->scene->updatePrimitiveIds(item);
1676   d->scene->updatePrimitiveIds(item);
1677 }
1678 
1679 
textRenderer()1680 TextRenderer* Viewer::textRenderer()
1681 {
1682   return d->textRenderer;
1683 }
1684 
isExtensionFound()1685 bool Viewer::isExtensionFound()
1686 {
1687   return d->extension_is_found;
1688 
1689 }
1690 
disableClippingBox()1691 void Viewer::disableClippingBox()
1692 {
1693   d->clipping = false;
1694 }
1695 
enableClippingBox(QVector4D box[6])1696 void Viewer::enableClippingBox(QVector4D box[6])
1697 {
1698   d->clipping = true;
1699   for(int i=0; i<6; ++i)
1700     d->clipbox[i] = box[i];
1701 }
1702 
openGL_4_3_functions()1703 QOpenGLFunctions_4_3_Core *Viewer::openGL_4_3_functions() { return d->_recentFunctions; }
1704 
set2DSelectionMode(bool b)1705 void Viewer::set2DSelectionMode(bool b) { d->is_2d_selection_mode = b; }
1706 
setStaticImage(QImage image)1707 void Viewer::setStaticImage(QImage image) { d->static_image = image; }
1708 
staticImage() const1709 const QImage& Viewer:: staticImage() const { return d->static_image; }
1710 
1711 
setCurrentPass(int pass)1712 void Viewer::setCurrentPass(int pass) { d->current_pass = pass; }
1713 
setDepthWriting(bool writing_depth)1714 void Viewer::setDepthWriting(bool writing_depth) { d->writing_depth = writing_depth; }
1715 
setDepthPeelingFbo(QOpenGLFramebufferObject * fbo)1716 void Viewer::setDepthPeelingFbo(QOpenGLFramebufferObject* fbo) { d->dp_fbo = fbo; }
1717 
currentPass() const1718 int Viewer::currentPass()const{ return d->current_pass; }
isDepthWriting() const1719 bool Viewer::isDepthWriting()const{ return d->writing_depth; }
depthPeelingFbo()1720 QOpenGLFramebufferObject *Viewer::depthPeelingFbo(){ return d->dp_fbo; }
total_pass()1721 float Viewer::total_pass()
1722 {
1723   return d->current_total_pass * 1.0f;
1724 }
setTotalPass(int p)1725 void Viewer::setTotalPass(int p)
1726 {
1727   d->total_pass = p;
1728   update();
1729 }
1730 
messageLogged(QOpenGLDebugMessage msg)1731 void Viewer::messageLogged(QOpenGLDebugMessage msg)
1732 {
1733   //filter out useless warning
1734   // From those two links, we decided we didn't care for this warning:
1735   // https://community.khronos.org/t/vertex-shader-in-program-2-is-being-recompiled-based-on-gl-state/76019
1736   // https://stackoverflow.com/questions/12004396/opengl-debug-context-performance-warning
1737   if(msg.message().contains("is being recompiled"))
1738     return;
1739   QString error;
1740 
1741   // Format based on severity
1742   switch (msg.severity())
1743   {
1744   case QOpenGLDebugMessage::NotificationSeverity:
1745     return;
1746     break;
1747   case QOpenGLDebugMessage::HighSeverity:
1748     error += "GL ERROR :";
1749     break;
1750   case QOpenGLDebugMessage::MediumSeverity:
1751     error += "GL WARNING :";
1752     break;
1753   case QOpenGLDebugMessage::LowSeverity:
1754     error += "GL NOTE :";
1755     break;
1756   default:
1757     break;
1758   }
1759 
1760   error += " (";
1761 
1762   // Format based on source
1763 #define CASE(c) case QOpenGLDebugMessage::c: error += #c; break
1764   switch (msg.source())
1765   {
1766   CASE(APISource);
1767   CASE(WindowSystemSource);
1768   CASE(ShaderCompilerSource);
1769   CASE(ThirdPartySource);
1770   CASE(ApplicationSource);
1771   CASE(OtherSource);
1772   CASE(InvalidSource);
1773   default:
1774     break;
1775   }
1776 #undef CASE
1777 
1778   error += " : ";
1779 
1780   // Format based on type
1781 #define CASE(c) case QOpenGLDebugMessage::c: error += #c; break
1782   switch (msg.type())
1783   {
1784   CASE(ErrorType);
1785   CASE(DeprecatedBehaviorType);
1786   CASE(UndefinedBehaviorType);
1787   CASE(PortabilityType);
1788   CASE(PerformanceType);
1789   CASE(OtherType);
1790   CASE(MarkerType);
1791   CASE(GroupPushType);
1792   CASE(GroupPopType);
1793   default:
1794     break;
1795   }
1796 #undef CASE
1797 
1798   error += ")";
1799   qDebug() << qPrintable(error) << "\n" << qPrintable(msg.message()) << "\n";
1800 }
1801 
setLighting()1802 void Viewer::setLighting()
1803 {
1804 
1805   //save current settings;
1806   float prev_spec = d->spec_power;
1807   QVector4D prev_pos = d->position;
1808   QVector4D prev_ambient = d->ambient;
1809   QVector4D prev_diffuse = d->diffuse;
1810   QVector4D prev_spec_color = d->specular;
1811   //open dialog
1812   LightingDialog* dialog = new LightingDialog(d);
1813   //set specular
1814   connect(dialog->spec_powrSlider, &QSlider::valueChanged,
1815           [this, dialog]()
1816   {
1817     d->spec_power = dialog->spec_powrSlider->value();
1818     update();
1819   });
1820   //set position
1821   connect(dialog->position_lineEdit, &QLineEdit::editingFinished,
1822           [this, dialog]()
1823   {
1824     QStringList list = dialog->position_lineEdit->text().split(QRegExp(","), CGAL_QT_SKIP_EMPTY_PARTS);
1825     if (list.isEmpty()) return;
1826     if (list.size()!=3){
1827       QMessageBox *msgBox = new QMessageBox;
1828       msgBox->setWindowTitle("Error");
1829       msgBox->setText("ERROR : Input should consists of 3 floats.");
1830       msgBox->exec();
1831       return;
1832     }
1833     float coords[3];
1834     for(int j=0; j<3; ++j)
1835     {
1836       bool ok;
1837       coords[j] = list.at(j).toFloat(&ok);
1838       if(!ok)
1839       {
1840           QMessageBox *msgBox = new QMessageBox;
1841           msgBox->setWindowTitle("Error");
1842           msgBox->setText("ERROR : Coordinates are invalid.");
1843           msgBox->exec();
1844           return;
1845       }
1846     }
1847     d->position = QVector4D(coords[0], coords[1], coords[2], 1.0f);
1848     update();
1849   });
1850 
1851 
1852   //set ambient
1853   connect(dialog, &LightingDialog::s_ambient_changed,
1854           [this, dialog](){
1855     d->ambient=QVector4D((float)dialog->ambient.redF(),
1856                          (float)dialog->ambient.greenF(),
1857                          (float)dialog->ambient.blueF(),
1858                          1.0f);
1859     update();
1860   });
1861 
1862   //set diffuse
1863   connect(dialog, &LightingDialog::s_diffuse_changed,
1864           [this, dialog](){
1865     d->diffuse=QVector4D((float)dialog->diffuse.redF(),
1866                          (float)dialog->diffuse.greenF(),
1867                          (float)dialog->diffuse.blueF(),
1868                          1.0f);
1869     update();
1870   });
1871   //set specular
1872   connect(dialog, &LightingDialog::s_specular_changed,
1873           [this, dialog](){
1874     d->specular=QVector4D((float)dialog->specular.redF(),
1875                           (float)dialog->specular.greenF(),
1876                           (float)dialog->specular.blueF(),
1877                          1.0f);
1878     update();
1879 
1880   });
1881 
1882   //reset default
1883   connect(dialog->buttonBox->button(QDialogButtonBox::StandardButton::RestoreDefaults), &QPushButton::clicked,
1884           [this](){
1885     d->position = QVector4D(0,0,1,1);
1886     d->ambient=QVector4D(77.0f/255,77.0f/255,77.0f/255, 1.0);
1887     d->diffuse=QVector4D(204.0f/255,204.0f/255,204.0f/255,1.0);
1888     d->specular=QVector4D(0,0,0,1.0);
1889     d->spec_power = 51;
1890     update();
1891 
1892   });
1893   if(!dialog->exec())
1894   {
1895     //restore previous settings
1896     d->spec_power = prev_spec;
1897     d->position = prev_pos;
1898     d->ambient = prev_ambient;
1899     d->diffuse = prev_diffuse;
1900     d->specular = prev_spec_color;
1901     return;
1902   }
1903 }
1904 
setBackFrontColors()1905 void Viewer::setBackFrontColors()
1906 {
1907 
1908   //save current settings;
1909 
1910   QColor prev_front_color = d->front_color;
1911   QColor prev_back_color = d->back_color;
1912   QDialog *dialog = new QDialog(this);
1913   QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
1914                                    | QDialogButtonBox::Cancel, dialog);
1915 
1916   connect(buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
1917   connect(buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
1918 
1919   QGridLayout* layout = new QGridLayout(dialog);
1920   layout->addWidget(new QLabel("Front color: ",dialog),0,0);
1921   QPalette front_palette;
1922   front_palette.setColor(QPalette::Button, d->front_color);
1923   QPushButton* frontButton = new QPushButton(dialog);
1924   frontButton->setPalette(front_palette);
1925   QPalette back_palette;
1926   back_palette.setColor(QPalette::Button, d->back_color);
1927   QPushButton* backButton = new QPushButton(dialog);
1928   backButton->setPalette(back_palette);
1929   layout->addWidget(frontButton,0,1);
1930   layout->addWidget(new QLabel("Back color: ",dialog),1,0);
1931   layout->addWidget(backButton,1,1);
1932   layout->addWidget(buttonBox);
1933   dialog->setLayout(layout);
1934   connect(frontButton, &QPushButton::clicked,
1935           [this, dialog, frontButton](){
1936     QColorDialog *color_dial = new QColorDialog(dialog);
1937     color_dial->exec();
1938     QColor front_color = color_dial->selectedColor();
1939     QPalette palette;
1940     palette.setColor(QPalette::Button, front_color);
1941     frontButton->setPalette(palette);
1942     d->front_color= front_color;
1943   });
1944   connect(backButton, &QPushButton::clicked,
1945           [this, dialog, backButton](){
1946     QColorDialog *color_dial = new QColorDialog(dialog);
1947     color_dial->exec();
1948     QColor back_color = color_dial->selectedColor();
1949     QPalette palette;
1950     palette.setColor(QPalette::Button, back_color);
1951     backButton->setPalette(palette);
1952     d->back_color= back_color;
1953 
1954   });
1955   if(!dialog->exec())
1956   {
1957     //restore previous settings
1958     d->front_color= prev_front_color;
1959     d->back_color= prev_back_color;
1960     return;
1961   }
1962 }
1963 
setGlPointSize(const GLfloat & p)1964 void Viewer::setGlPointSize(const GLfloat &p) { d->gl_point_size = p; }
1965 
getGlPointSize() const1966 const GLfloat& Viewer::getGlPointSize() const { return d->gl_point_size; }
1967 
resetFov()1968 void Viewer::resetFov()
1969 {
1970   camera()->setHorizontalFieldOfView(ORIGINAL_FOV);
1971 }
1972 
initializeGL()1973 void Viewer::initializeGL()
1974 {
1975   QGLViewer::initializeGL();
1976   doneInitGL(this);
1977 }
1978 
makeCurrent()1979 void Viewer::makeCurrent()
1980 {
1981   CGAL::Three::Three::setCurrentViewer(this);
1982   QOpenGLWidget::makeCurrent();
1983 }
1984 
clipBox() const1985 QVector4D* Viewer::clipBox() const
1986 {
1987   return d->clipbox;
1988 }
1989 
isClipping() const1990 bool Viewer::isClipping() const
1991 {
1992   return d->clipping;
1993 }
1994 
scaleScene()1995 void Viewer::scaleScene()
1996 {
1997   CGAL::Bbox_3 bbox = CGAL::Three::Three::scene()->bbox();
1998   if(!d->scene_scaling)
1999   {
2000     QMultipleInputDialog dialog ("Scale Scene", CGAL::Three::Three::mainWindow());
2001     DoubleEdit* x_val = dialog.add<DoubleEdit> ("Scale along X");
2002     DoubleEdit* y_val = dialog.add<DoubleEdit> ("Scale along Y");
2003     DoubleEdit* z_val = dialog.add<DoubleEdit> ("Scale along Z");
2004     x_val->setMinimum(0);
2005     y_val->setMinimum(0);
2006     z_val->setMinimum(0);
2007     if(bbox != CGAL::Bbox_3(0,0,0,0,0,0))
2008     {
2009       QPushButton* norm_button = dialog.add<QPushButton> ("");
2010       norm_button->setText("Normalize");
2011       norm_button->setToolTip("Automatically fill values to display the scene in a unit cube.");
2012 
2013       connect(norm_button, &QPushButton::clicked, this,
2014               [x_val, y_val, z_val, &bbox](){
2015         x_val->setValue(1.0/(bbox.xmax()-bbox.xmin()));
2016         y_val->setValue(1.0/(bbox.ymax()-bbox.ymin()));
2017         z_val->setValue(1.0/(bbox.zmax()-bbox.zmin()));
2018       });
2019     }
2020     if (dialog.exec() != QDialog::Accepted)
2021     {
2022       parent()->findChild<QAction*>("actionScaleScene")->setChecked(false);
2023       return;
2024     }
2025     d->scaler.setX(x_val->text()==""?1.0:x_val->value());
2026     d->scaler.setY(y_val->text()==""?1.0:y_val->value());
2027     d->scaler.setZ(z_val->text()==""?1.0:z_val->value());
2028 
2029     if(d->scaler.x() == 0.0 || d->scaler.y() == 0.0 || d->scaler.z()== 0.0)
2030     {
2031       parent()->findChild<QAction*>("actionScaleScene")->setChecked(false);
2032       return;
2033     }
2034   }
2035   else
2036     d->scaler = QVector3D(1,1,1);
2037 
2038   CGAL::qglviewer::Vec vmin(((float)bbox.xmin()+offset().x)*d->scaler.x(), ((float)bbox.ymin()+offset().y)*d->scaler.y(), ((float)bbox.zmin()+offset().z)*d->scaler.z()),
2039       vmax(((float)bbox.xmax()+offset().x)*d->scaler.x(), ((float)bbox.ymax()+offset().y)*d->scaler.y(), ((float)bbox.zmax()+offset().z)*d->scaler.z());
2040   camera()->setPivotPoint((vmin+vmax)*0.5);
2041   camera()->setSceneBoundingBox(vmin, vmax);
2042   camera()->fitBoundingBox(vmin, vmax);
2043   d->scene_scaling = !d->scene_scaling;
2044 }
2045 #ifdef CGAL_USE_WEBSOCKETS
setShareCam(bool b,QString session)2046 void Viewer::setShareCam(bool b, QString session)
2047 {
2048   static bool init = false;
2049   if(b)
2050   {
2051     d->cam_sharing = b;
2052     d->session = session;
2053     QString ws_url
2054         = CGAL::Three::Three::mainWindow()->property("ws_url").toString();
2055     if(ws_url.isEmpty())
2056     {
2057       QMessageBox::warning(this, "Error", "No Server configured. Please go to Edit->Preferences->Network Settings and fill the \"Camera Synchronization Server\" Field.");
2058     }
2059     else{
2060       if(!init)
2061       {
2062         connect(&d->m_webSocket, &QWebSocket::connected, this, &Viewer::onSocketConnected);
2063         connect(&d->m_webSocket, &QWebSocket::disconnected, this,[this]()
2064         {
2065           d->is_connected = false;
2066           Viewer::socketClosed();
2067         });
2068         init = true;
2069       }
2070       d->m_webSocket.open(QUrl(ws_url));
2071       QApplication::setOverrideCursor(Qt::WaitCursor);
2072       QTimer::singleShot(1000, this, [this](){
2073         QApplication::restoreOverrideCursor();
2074         if(!d->is_connected){
2075           QMessageBox::warning(CGAL::Three::Three::mainWindow(),
2076                                "Connection failure",
2077                                "The requested server was not found.");
2078           setShareCam(false, "");
2079         }
2080       });
2081     }
2082   }
2083   else
2084   {
2085     QAction* action = findChild<QAction*>("actionShareCamera");
2086     action->setChecked(false);
2087     d->m_webSocket.close();
2088   }
2089 }
2090 
onSocketConnected()2091 void Viewer::onSocketConnected()
2092 {
2093   connect(&d->m_webSocket, &QWebSocket::textMessageReceived,
2094           this, &Viewer::onTextMessageSocketReceived);
2095   connect(camera()->frame(), &CGAL::qglviewer::ManipulatedCameraFrame::manipulated,
2096           this, [this](){
2097     if(d->cam_sharing){
2098       QString cam_state = QString("[%1] %2").arg(d->session).arg(dumpCameraCoordinates());
2099       //send to server
2100       d->m_webSocket.sendTextMessage(cam_state);
2101     }
2102   });
2103   d->is_connected = true;
2104 }
2105 
onTextMessageSocketReceived(QString message)2106 void Viewer::onTextMessageSocketReceived(QString message)
2107 {
2108   QString session;
2109   QString position;
2110   QRegularExpression re("\\[(.*)\\] (.*)");
2111   QRegularExpressionMatch match = re.match(message);
2112   session = match.captured(1);
2113   position = match.captured(2);
2114   if(session != d->session){
2115     return;
2116   }
2117   QStringList sl = position.split(" ");
2118   if(sl.size() != 7)
2119     return;
2120 
2121   CGAL::qglviewer::Vec pos(sl[0].toDouble(),sl[1].toDouble(),sl[2].toDouble());
2122   CGAL::qglviewer::Quaternion q(sl[3].toDouble(),sl[4].toDouble(),
2123       sl[5].toDouble(),sl[6].toDouble());
2124   camera()->frame()->setPositionAndOrientation(pos, q);
2125   update();
2126 }
2127 #endif
2128 
scaler() const2129 const QVector3D& Viewer::scaler()const { return d->scaler; }
showEntireScene()2130 void Viewer::showEntireScene()
2131 {
2132   CGAL::QGLViewer::showEntireScene();
2133   CGAL::Bbox_3 bbox = CGAL::Three::Three::scene()->bbox();
2134 
2135   CGAL::qglviewer::Vec vmin(((float)bbox.xmin()+offset().x)*d->scaler.x(), ((float)bbox.ymin()+offset().y)*d->scaler.y(), ((float)bbox.zmin()+offset().z)*d->scaler.z()),
2136       vmax(((float)bbox.xmax()+offset().x)*d->scaler.x(), ((float)bbox.ymax()+offset().y)*d->scaler.y(), ((float)bbox.zmax()+offset().z)*d->scaler.z());
2137   camera()->setPivotPoint((vmin+vmax)*0.5);
2138   camera()->setSceneBoundingBox(vmin, vmax);
2139   camera()->fitBoundingBox(vmin, vmax);
2140 }
2141 #include "Viewer.moc"
2142