1 // Copyright (C) 2012-2019 The VPaint Developers.
2 // See the COPYRIGHT file at the top-level directory of this distribution
3 // and at https://github.com/dalboris/vpaint/blob/master/COPYRIGHT
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #include "View3D.h"
18 #include "Scene.h"
19 #include "Timeline.h"
20 #include <QtDebug>
21 #include "Global.h"
22 #include "View.h"
23 #include "Background/Background.h"
24 #include "Background/BackgroundRenderer.h"
25 
26 #include "VectorAnimationComplex/VAC.h"
27 #include "VectorAnimationComplex/KeyCell.h"
28 
29 // define mouse actions
30 
31 #define DRAW_ACTION 10
32 #define SELECT_ACTION 20
33 #define ADDSELECT_ACTION 21
34 #define DESELECT_ACTION 22
35 #define TOGGLESELECT_ACTION 23
36 #define DESELECTALL_ACTION 24
37 
View3D(Scene * scene,QWidget * parent)38 View3D::View3D(Scene *scene, QWidget *parent) :
39     GLWidget(parent, false), // Difference from View here
40     scene_(scene),
41     displayedTimes_(),
42     pickingImg_(0),
43     //frame_(0),
44     vac_(0)
45 {
46     // Make renderers
47     // XXX Make it work with layers
48     //Background * bg = scene_->background();
49     //backgroundRenderers_[bg] = new BackgroundRenderer(bg, context(), this);
50 
51 
52     cameraTravellingIsEnabled_ = true;
53     drawingIsEnable_ = false;
54 
55     // behave as a separate window
56     this->setWindowFlags(Qt::Window);
57     resize(600,600);
58     setWindowTitle("3D View [Beta]");
59 
60     // Redraw when moving the camera
61     //connect(this, SIGNAL(viewIsGoingToChange(int, int)), this, SLOT(updatePicking()));
62     ////connect(this, SIGNAL(viewIsGoingToChange(int, int)), this, SLOT(updateHighlightedObject(int, int)));
63     //connect(this, SIGNAL(viewIsGoingToChange(int, int)), this, SLOT(update()));
64 
65     ////connect(this, SIGNAL(viewIsBeingChanged(int, int)), this, SLOT(updatePicking()));
66     ////connect(this, SIGNAL(viewIsBeingChanged(int, int)), this, SLOT(updateHighlightedObject(int, int)));
67     connect(this, SIGNAL(viewIsBeingChanged(int, int)), this, SLOT(update()));
68 
69     //connect(this, SIGNAL(viewChanged(int, int)), this, SLOT(updatePicking()));
70     //connect(this, SIGNAL(viewChanged(int, int)), this, SLOT(updateHighlightedObject(int, int)));
71     connect(this, SIGNAL(viewChanged(int, int)), this, SLOT(update()));
72 
73     //connect(global(),SIGNAL(keyboardModifiersChanged()),this,SLOT(handleNewKeyboardModifiers()));
74 
75     connect(global()->timeline(), SIGNAL(playingWindowChanged()), this, SLOT(update()));
76 }
77 
~View3D()78 View3D::~View3D()
79 {
80     deletePicking();
81 }
82 
settings()83 View3DSettings * View3D::settings() {
84     return &viewSettings_;
85 }
86 
closeEvent(QCloseEvent * event)87 void View3D::closeEvent(QCloseEvent * event)
88 {
89     emit closed();
90     event->accept();
91 }
92 
keyPressEvent(QKeyEvent * event)93 void View3D::keyPressEvent(QKeyEvent *event)
94 {
95     GLWidget::keyPressEvent(event);
96 
97     // Nothing changed  in the scene, but keyboard  state can affect
98     // the display settings, hence should redraw the scene. Pass the
99     // event to multiview to do this
100     if(!event->isAccepted())
101         event->ignore();
102 }
103 
104 
mouseEvent() const105 View3D::MouseEvent View3D::mouseEvent() const
106 {
107     MouseEvent me;
108     me.x = mouse_PressEvent_X_;
109     me.y = mouse_PressEvent_Y_;
110     me.left = mouse_LeftButton_;
111     me.mid = mouse_MidButton_;
112     me.right = mouse_RightButton_;
113     me.alt = mouse_AltWasDown_;
114     me.control = mouse_ControlWasDown_;
115     me.shift = mouse_ShiftWasDown_;
116     return me;
117 }
118 
MoveEvent(double x,double y)119 void View3D::MoveEvent(double x, double y)
120 {
121     bool hasChanged = updateHighlightedObject(x, y);
122     if(hasChanged)
123     {
124         if(highlightedObject_.isNull())
125             scene_->setNoHoveredObject();
126         else
127             scene_->setHoveredObject(
128                 //Timeline::time(highlightedObject_.time()),
129                 Time(), // ignored by VAC anyway...
130                 highlightedObject_.index(),
131                 highlightedObject_.id());
132     }
133 }
134 
decideClicAction()135 int View3D::decideClicAction()
136 {
137     if(mouse_LeftButton_)
138     {
139         if(!mouse_AltWasDown_ &&
140            !mouse_ControlWasDown_ &&
141            !mouse_ShiftWasDown_)
142         {
143             if(highlightedObject_.isNull())
144                 return DESELECTALL_ACTION;
145             else
146                 return SELECT_ACTION;
147         }
148         if(!mouse_AltWasDown_ &&
149            !mouse_ControlWasDown_ &&
150            mouse_ShiftWasDown_)
151         {
152             return ADDSELECT_ACTION;
153         }
154         if(mouse_AltWasDown_ &&
155            !mouse_ControlWasDown_ &&
156            !mouse_ShiftWasDown_)
157         {
158             return DESELECT_ACTION;
159         }
160         if(mouse_AltWasDown_ &&
161            !mouse_ControlWasDown_ &&
162            mouse_ShiftWasDown_)
163         {
164             return TOGGLESELECT_ACTION;
165         }
166     }
167     return GLWidget::decideClicAction();
168 }
169 
decidePMRAction()170 int View3D::decidePMRAction()
171 {
172     return GLWidget::decidePMRAction();
173 }
174 
ClicEvent(int action,double x,double y)175 void View3D::ClicEvent(int action, double x, double y)
176 {
177     if(action==SELECT_ACTION)
178     {
179         if(!highlightedObject_.isNull())
180         {
181             scene_->deselectAll(); // deselect       at      all
182                              // times. different from 2D behaviour
183             scene_->select(Time() /*Timeline::time(highlightedObject_.time())*/,
184                        highlightedObject_.index(),
185                        highlightedObject_.id());
186         }
187     }
188     else if(action==DESELECTALL_ACTION)
189     {
190         // same here: deselect at all times
191         // different from 2D version
192         scene_->deselectAll();
193 
194     }
195     else if(action==ADDSELECT_ACTION)
196     {
197         if(!highlightedObject_.isNull())
198         {
199             scene_->select(Time()/*Timeline::time(highlightedObject_.time())*/,
200                        highlightedObject_.index(),
201                        highlightedObject_.id());
202         }
203     }
204     else if(action==DESELECT_ACTION)
205     {
206         if(!highlightedObject_.isNull())
207         {
208             scene_->deselect(Time()/*Timeline::time(highlightedObject_.time())*/,
209                          highlightedObject_.index(),
210                          highlightedObject_.id());
211         }
212     }
213     else if(action==TOGGLESELECT_ACTION)
214     {
215         if(!highlightedObject_.isNull())
216         {
217             scene_->toggle(Time()/*Timeline::time(highlightedObject_.time())*/,
218                        highlightedObject_.index(),
219                        highlightedObject_.id());
220         }
221     }
222     GLWidget::ClicEvent(action, x, y);
223 }
224 
225 
226 
PMRPressEvent(int action,double x,double y)227 void View3D::PMRPressEvent(int action, double x, double y)
228 {
229     GLWidget::PMRPressEvent(action, x, y);
230 }
231 
PMRMoveEvent(int action,double x,double y)232 void View3D::PMRMoveEvent(int action, double x, double y)
233 {
234     GLWidget::PMRMoveEvent(action, x, y);
235 }
236 
PMRReleaseEvent(int action,double x,double y)237 void View3D::PMRReleaseEvent(int action, double x, double y)
238 {
239     GLWidget::PMRReleaseEvent(action, x, y);
240 }
241 
242 /***********************************************************
243  *              DRAWING
244  */
245 
246 namespace
247 {
248 /*
249 void drawSphere(double r, int lats, int longs)
250 {
251     int i, j;
252     for(i = 0; i <= lats; i++)
253     {
254         double lat0 = M_PI * (-0.5 + (double) (i - 1) / lats);
255         double z0  = sin(lat0);
256         double zr0 =  cos(lat0);
257 
258         double lat1 = M_PI * (-0.5 + (double) i / lats);
259         double z1 = sin(lat1);
260         double zr1 = cos(lat1);
261 
262         glBegin(GL_QUAD_STRIP);
263         for(j = 0; j <= longs; j++)
264         {
265             double lng = 2 * M_PI * (double) (j - 1) / longs;
266             double x = cos(lng);
267             double y = sin(lng);
268 
269             glNormal3f(x * zr0, y * zr0, z0);
270             glVertex3f(r * x * zr0, r * y * zr0, r * z0);
271             glNormal3f(x * zr1, y * zr1, z1);
272             glVertex3f(r * x * zr1, r * y * zr1, r * z1);
273         }
274         glEnd();
275     }
276 }
277 */
278 }
279 
activeFrame() const280 int View3D::activeFrame() const
281 {
282     return std::floor(activeTime().floatTime());
283 }
284 
activeTime() const285 Time View3D::activeTime() const
286 {
287     return global()->activeTime(); // XXX should refactor this
288 }
289 
drawBackground_(Background * background,double t)290 void View3D::drawBackground_(Background * background, double t)
291 {
292     // Get canvas boundary
293     double x1 = scene_->left();
294     double y1 = scene_->top();
295     double w = scene_->width();
296     double h = scene_->height();
297     double x2 = x1 + w;
298     double y2 = y1 + h;
299 
300     // Convert to 3D coords
301     x1 = viewSettings_.xFromX2D(x1);
302     x2 = viewSettings_.xFromX2D(x2);
303     y1 = viewSettings_.yFromY2D(y1);
304     y2 = viewSettings_.yFromY2D(y2);
305 
306     // Draw background
307     backgroundRenderers_[background]->draw(
308                 Time(t).frame(),
309                 true, // = showCanvas
310                 x1, y1, w, h,
311                 0, 0, 0, 0);
312 }
313 
314 // XXX Refactor this: move it to a CanvasRenderer class
315 // Right now, this codes duplicates part of Scene::drawCanvas()
drawCanvas_()316 void View3D::drawCanvas_()
317 {
318     // Get canvas boundary
319     double x1 = scene_->left();
320     double y1 = scene_->top();
321     double w = scene_->width();
322     double h = scene_->height();
323     double x2 = x1 + w;
324     double y2 = y1 - h;
325 
326     // Convert to 3D coords
327     x1 = viewSettings_.xFromX2D(x1);
328     x2 = viewSettings_.xFromX2D(x2);
329     y1 = viewSettings_.yFromY2D(y1);
330     y2 = viewSettings_.yFromY2D(y2);
331 
332     // Draw quad boundary
333     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
334     glBegin(GL_QUADS);
335     {
336         glColor4f(0.0, 0.0, 0.0, 1.0);
337         glVertex2d(x1, y1);
338         glVertex2d(x2, y1);
339         glVertex2d(x2, y2);
340         glVertex2d(x1, y2);
341     }
342     glEnd();
343     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
344 }
345 
drawScene()346 void View3D::drawScene()
347 {
348     using namespace VectorAnimationComplex;
349 
350     // Get VAC
351     VAC * vac = scene_->activeVAC();
352     if (!vac) {
353         return;
354     }
355 
356     // Get t-position of camera eye to determine back-to front order
357     double zEye = camera_.position()[2];
358     double tEye = - zEye / viewSettings_.timeScale();
359     if(viewSettings_.cameraFollowActiveTime())
360         tEye += activeTime().floatTime();
361 
362     // Scale and translate view
363     glEnable(GL_NORMALIZE);
364     double s = viewSettings_.spaceScale();
365     glPushMatrix();
366     glScaled(s,s,s);
367     if(viewSettings_.cameraFollowActiveTime())
368         glTranslated(0,0,-viewSettings_.zFromT(global()->activeTime()));
369 
370 
371     // ----- Draw opaque objects first, with depth test enabled -----
372 
373     // Here, depth buffer writing is enabled by default
374 
375     // Disable lighting
376     glDisable(GL_LIGHTING);
377 
378     // Draw inbetween cells
379     if(viewSettings_.drawInbetweenCells())
380         vac->drawInbetweenCells3D(viewSettings_);
381 
382 
383     // ----- Then, draw transparent objects, back to front, with depth buffer writing disabled -----
384 
385     // Set 2D settings from 3D settings
386     ViewSettings view2DSettings = global()->activeView()->viewSettings();
387     view2DSettings.setScreenRelative(false);
388     view2DSettings.setVertexTopologySize(viewSettings_.vertexTopologySize());
389     view2DSettings.setEdgeTopologyWidth(viewSettings_.edgeTopologyWidth());
390     view2DSettings.setDrawTopologyFaces(viewSettings_.drawTopologyFaces());
391 
392     // Disable writing to depth buffer
393     glDepthMask(false);
394 
395     // Get the list of all ordered cells
396     const ZOrderedCells & cells = vac->zOrdering();
397     typedef ZOrderedCells::ConstIterator Iter;
398 
399     // Find what times to draw, and for each the following parameters:
400     //    1. Should we draw no cells (i.e., just the canvas), only key cells, or all cells?
401     //    2. Should we draw as topology or as illustration?
402     //    3. should we draw canvas (+ background)?
403 
404     enum WhatCells { NoCells, KeyCells, AllCells };
405 
406     struct Params {
407         WhatCells whatCellsToDraw;
408         bool drawAsTopology;
409         bool drawCanvas;
410     };
411 
412     QMap<double, Params> timesToDraw;
413     typedef QMapIterator<double, Params> MapIter;
414 
415     // Key cells
416     if(viewSettings_.drawKeyCells())
417     {
418         // Params for drawing key cells
419         Params params;
420         params.whatCellsToDraw = KeyCells;
421         params.drawAsTopology = viewSettings_.drawFramesAsTopology();
422         params.drawCanvas = false;
423 
424         // Find times with key cells
425         for(Iter it = cells.cbegin(); it != cells.cend(); ++it)
426         {
427             KeyCell * kc = (*it)->toKeyCell();
428             if(kc)
429                 timesToDraw[kc->time().floatTime()] = params;
430         }
431     }
432 
433     // All frames
434     if(viewSettings_.drawAllFrames())
435     {
436         // Params for drawing key cells
437         Params params;
438         params.whatCellsToDraw = AllCells;
439         params.drawAsTopology = viewSettings_.drawFramesAsTopology();
440         params.drawCanvas = false;
441 
442         // Find times for all cells
443         Timeline * timeline = global()->timeline();
444         int firstFrame = timeline->firstFrame();
445         int lastFrame = timeline->lastFrame();
446         for(int i=firstFrame; i<=lastFrame; ++i)
447             timesToDraw[(double)i] = params;
448     }
449 
450     // Current frame
451     if(viewSettings_.drawTimePlane() || viewSettings_.drawCurrentFrame())
452     {
453         // Params for drawing key cells
454         Params params;
455         params.whatCellsToDraw = viewSettings_.drawCurrentFrame() ? AllCells : NoCells;
456         params.drawAsTopology = viewSettings_.drawCurrentFrameAsTopology();
457         params.drawCanvas = viewSettings_.drawTimePlane();
458 
459         // Add current time to list of times to draw
460         timesToDraw[global()->activeTime().floatTime()] = params;
461     }
462 
463     // Then, now that we have all times, find out in which order to draw them
464     QList<double> timesBeforeEye;
465     QList<double> timesAfterEye;
466     QList<double> sortedTimes;
467     MapIter i(timesToDraw);
468     while (i.hasNext())
469     {
470         i.next();
471         double t = i.key();
472         if (t < tEye)
473             timesBeforeEye << t;
474         else
475             timesAfterEye << t;
476     }
477     std::sort(timesBeforeEye.begin(), timesBeforeEye.end());
478     std::sort(timesAfterEye.begin(), timesAfterEye.end());
479     for (int i=0; i<timesBeforeEye.size(); ++i)
480         sortedTimes << timesBeforeEye[i];
481     for (int i=timesAfterEye.size()-1; i>=0; --i)
482         sortedTimes << timesAfterEye[i];
483 
484     // Now, we "just" have to draw them!
485 
486     // Disable lighting
487     glDisable(GL_LIGHTING);
488 
489     // Iterate times
490     for (double t: sortedTimes)
491     {
492         // Get params for that time
493         Params params = timesToDraw[t];
494 
495         // Translate to appropriate z value
496         glPushMatrix();
497         glScaled(1, -1, 1);
498         glTranslated(0,0,viewSettings_.zFromT(t));
499 
500         // Draw canvas + background
501         if(params.drawCanvas)
502         {
503             drawCanvas_();
504             // XXX Make it work with layers
505             //drawBackground_(scene_->background(), t);
506         }
507 
508         // Draw cells
509         if (params.whatCellsToDraw != NoCells)
510         {
511             if (params.drawAsTopology)
512             {
513                 if (params.whatCellsToDraw == KeyCells)
514                 {
515                     for(Iter it = cells.cbegin(); it != cells.cend(); ++it)
516                     {
517                         if ((*it)->toKeyCell())
518                             (*it)->drawTopology(t, view2DSettings);
519                     }
520                 }
521                 else
522                 {
523                     for(Iter it = cells.cbegin(); it != cells.cend(); ++it)
524                     {
525                         (*it)->drawTopology(t, view2DSettings);
526                     }
527                 }
528             }
529             else // params.drawAsTopology == false
530             {
531                 if (params.whatCellsToDraw == KeyCells)
532                 {
533                     for(Iter it = cells.cbegin(); it != cells.cend(); ++it)
534                     {
535                         if ((*it)->toKeyCell())
536                             (*it)->draw(t, view2DSettings);
537                     }
538                 }
539                 else
540                 {
541                     for(Iter it = cells.cbegin(); it != cells.cend(); ++it)
542                     {
543                         (*it)->draw(t, view2DSettings);
544                     }
545                 }
546             }
547         }
548 
549         // Translate back
550         glPopMatrix();
551     }
552 
553     // Restore state
554     glDepthMask(true);
555     glPopMatrix();
556 }
557 
558 
559 /***********************************************************
560  *              PICKING
561  */
562 
drawPick3D()563 void View3D::drawPick3D()
564 {
565     if(scene_->activeVAC())
566     {
567         scene_->activeVAC()->drawPick3D(viewSettings_);
568     }
569 }
570 
updateHighlightedObject(int x,int y)571 bool View3D::updateHighlightedObject(int x, int y)
572 {
573     if(!pickingImg_)
574         return false; // otherwise the scene will keep updating
575 
576     Picking::Object old = highlightedObject_;
577     if(x<0 || x>=pickingWidth_ || y<0 || y>=pickingHeight_)
578     {
579         highlightedObject_ = Picking::Object();
580     }
581     else
582     {
583         highlightedObject_ = getCloserObject(x, y);
584     }
585     return !(highlightedObject_ == old);
586 }
587 
pickingImg(int x,int y)588 uchar * View3D::pickingImg(int x, int y)
589 {
590     int k = 4*( (pickingHeight_ - y - 1)*pickingWidth_ + x);
591     return &pickingImg_[k];
592 }
593 
594 
getCloserObject(int x,int y)595 Picking::Object View3D::getCloserObject(int x, int y)
596 {
597     Picking::Object noObject;
598 
599     if(!pickingImg_)
600         return noObject;
601 
602     int leftBorderDist = x;
603     int rightBorderDist = pickingWidth_-1-x;
604     int topBorderDist = y;
605     int bottomBorderDist = pickingHeight_-1-y;
606 
607     int borderDist = qMin(qMin(leftBorderDist, rightBorderDist),
608                     qMin(topBorderDist, bottomBorderDist));
609 
610     if(borderDist<0)
611         return noObject;
612 
613     int D = 10;
614     if(borderDist < D)
615         D = borderDist;
616 
617     for(int d=0; d<=D; d++)
618     {
619         if(d==0)
620         {
621             uchar * p = pickingImg(x,y);
622             uchar r=p[0], g=p[1], b=p[2];
623             if(r!=255 || g!=255 || b!=255)
624                 return Picking::objectFromRGB(r,g,b);
625         }
626         else
627         {
628             // top row
629             for(int varX=x-d; varX<=x+d; varX++)
630             {
631                 uchar * p = pickingImg(varX,y-d);
632                 uchar r=p[0], g=p[1], b=p[2];
633                 if(r!=255 || g!=255 || b!=255)
634                     return Picking::objectFromRGB(r,g,b);
635             }
636             // bottom row
637             for(int varX=x-d; varX<=x+d; varX++)
638             {
639                 uchar * p = pickingImg(varX,y+d);
640                 uchar r=p[0], g=p[1], b=p[2];
641                 if(r!=255 || g!=255 || b!=255)
642                     return Picking::objectFromRGB(r,g,b);
643             }
644             // left column
645             for(int varY=y-d; varY<=y+d; varY++)
646             {
647                 uchar * p = pickingImg(x-d,varY);
648                 uchar r=p[0], g=p[1], b=p[2];
649                 if(r!=255 || g!=255 || b!=255)
650                     return Picking::objectFromRGB(r,g,b);
651             }
652             // right column
653             for(int varY=y-d; varY<=y+d; varY++)
654             {
655                 uchar * p = pickingImg(x+d,varY);
656                 uchar r=p[0], g=p[1], b=p[2];
657                 if(r!=255 || g!=255 || b!=255)
658                     return Picking::objectFromRGB(r,g,b);
659             }
660 
661         }
662     }
663 
664     // no object within the range
665     return noObject;
666 }
667 
deletePicking()668 void View3D::deletePicking()
669 {
670     if(pickingImg_)
671     {
672         gl_fbo_->glDeleteFramebuffers(1, &fboId_);
673         gl_fbo_->glDeleteRenderbuffers(1, &rboId_);
674         glDeleteTextures(1, &textureId_);
675         highlightedObject_ = Picking::Object();
676         delete[] pickingImg_;
677         pickingImg_ = 0;
678         pickingWidth_ = 0;
679         pickingHeight_ = 0;
680     }
681 }
682 
newPicking()683 void View3D::newPicking()
684 {
685     pickingWidth_ = width();
686     pickingHeight_ = height();
687     pickingImg_ = new uchar[4 * pickingWidth_ * pickingHeight_];
688 
689     //  code adapted from http://www.songho.ca/opengl/gl_fbo.html
690 
691     // create a texture object
692     glGenTextures(1, &textureId_);
693     glBindTexture(GL_TEXTURE_2D, textureId_);
694     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
695     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
696     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
697     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
698     glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap
699     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, pickingWidth_, pickingHeight_, 0,
700                  GL_RGBA, GL_UNSIGNED_BYTE, 0);
701     glBindTexture(GL_TEXTURE_2D, 0);
702 
703     // create a renderbuffer object to store depth info
704     gl_fbo_->glGenRenderbuffers(1, &rboId_);
705     gl_fbo_->glBindRenderbuffer(GL_RENDERBUFFER, rboId_);
706     gl_fbo_->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,
707                                    pickingWidth_, pickingHeight_);
708     gl_fbo_->glBindRenderbuffer(GL_RENDERBUFFER, 0);
709 
710     // create a framebuffer object
711     gl_fbo_->glGenFramebuffers(1, &fboId_);
712     gl_fbo_->glBindFramebuffer(GL_FRAMEBUFFER, fboId_);
713 
714     // attach the texture to FBO color attachment point
715     gl_fbo_->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
716                                     GL_TEXTURE_2D, textureId_, 0);
717 
718     // attach the renderbuffer to depth attachment point
719     gl_fbo_->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
720                                        GL_RENDERBUFFER, rboId_);
721 
722     // check FBO status
723     GLenum status = gl_fbo_->glCheckFramebufferStatus(GL_FRAMEBUFFER);
724     if(status != GL_FRAMEBUFFER_COMPLETE)
725     {
726         qDebug() << "ERROR void View::newPicking()"
727                << "FBO status != GL_FRAMEBUFFER_COMPLETE";
728         return;
729     }
730 
731     // switch back to window-system-provided framebuffer
732     gl_fbo_->glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
733 }
734 
735 
updatePicking()736 void View3D::updatePicking()
737 {
738     // Make this widget's rendering context the current OpenGL context
739     makeCurrent();
740 
741     // get the viewport size, allocate memory if necessary
742     if( !(width()>0) || !(height()>0))
743     {
744         deletePicking();
745         return;
746     }
747     else if(
748         pickingImg_
749         && (pickingWidth_ == width())
750         && (pickingHeight_ == height()))
751     {
752         // necessary objects already created: do nothing
753     }
754     else
755     {
756         deletePicking();
757         newPicking();
758     }
759 
760     // set rendering destination to FBO
761     gl_fbo_->glBindFramebuffer(GL_FRAMEBUFFER, fboId_);
762 
763     // clear buffers
764     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
765 
766     // clear buffers
767     glClearColor(1.0, 1.0, 1.0, 1.0);
768     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
769 
770     // Should we setup other things? (e.g., disabling antialiasing)
771     // Seems to work as is. If issues, check GLWidget::initilizeGL()
772 
773     // Set viewport
774     GLint oldViewport[4];
775     glGetIntegerv(GL_VIEWPORT, oldViewport);
776     glViewport(0, 0, pickingWidth_, pickingHeight_);
777 
778     // Setup camera position and orientation
779     setCameraPositionAndOrientation();
780 
781     // draw the picking
782     drawPick3D();
783 
784     // Restore viewport
785     glViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
786 
787     // unbind FBO
788     gl_fbo_->glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
789 
790     // extract the texture info from GPU to RAM
791     glBindTexture(GL_TEXTURE_2D, textureId_);
792     glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pickingImg_);
793     glBindTexture(GL_TEXTURE_2D, 0);
794 }
795