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