vtkOpenGLProjectedTetrahedraMapper.cxx 38.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkOpenGLProjectedTetrahedraMapper.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

/*
 * Copyright 2003 Sandia Corporation.
 * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
 * license for use of this work by or on behalf of the
 * U.S. Government. Redistribution and use in source and binary forms, with
 * or without modification, are permitted provided that this Notice and any
 * statement of authorship are reproduced on all copies.
 */

#include "vtkOpenGLProjectedTetrahedraMapper.h"

#include "vtkCamera.h"
#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkCellIterator.h"
#include "vtkFloatArray.h"
#include "vtkIdList.h"
#include "vtkIdTypeArray.h"
#include "vtkMath.h"
#include "vtkMatrix3x3.h"
#include "vtkMatrix4x4.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkOpenGLCamera.h"
40
#include "vtkOpenGLError.h"
41
#include "vtkOpenGLFramebufferObject.h"
Ken Martin's avatar
Ken Martin committed
42
#include "vtkOpenGLIndexBufferObject.h"
43
#include "vtkOpenGLRenderUtilities.h"
44 45
#include "vtkOpenGLRenderWindow.h"
#include "vtkOpenGLShaderCache.h"
Ken Martin's avatar
Ken Martin committed
46
#include "vtkOpenGLVertexArrayObject.h"
Ken Martin's avatar
Ken Martin committed
47
#include "vtkOpenGLVertexBufferObject.h"
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
#include "vtkPointData.h"
#include "vtkRenderer.h"
#include "vtkShaderProgram.h"
#include "vtkSmartPointer.h"
#include "vtkTimerLog.h"
#include "vtkUnsignedCharArray.h"
#include "vtkUnstructuredGrid.h"
#include "vtkVisibilitySort.h"
#include "vtkVolume.h"
#include "vtkVolumeProperty.h"

#include <cmath>
#include <algorithm>

// bring in shader code
#include "vtkglProjectedTetrahedraVS.h"
#include "vtkglProjectedTetrahedraFS.h"

66 67 68 69
namespace
{
void annotate(const std::string& message)
{
70
  vtkOpenGLRenderUtilities::MarkDebugEvent(message);
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
}

class scoped_annotate
{
  std::string Message;
public:
  scoped_annotate(const std::string& message)
    : Message(message)
  {
    annotate("start " + message);
  }
  ~scoped_annotate() { annotate("end " + this->Message); }
};
}

86 87 88 89 90 91 92 93 94 95 96 97 98
static int tet_edges[6][2] = { {0,1}, {1,2}, {2,0},
                               {0,3}, {1,3}, {2,3} };

const int SqrtTableSize = 2048;

//-----------------------------------------------------------------------------
vtkStandardNewMacro(vtkOpenGLProjectedTetrahedraMapper);

//-----------------------------------------------------------------------------
vtkOpenGLProjectedTetrahedraMapper::vtkOpenGLProjectedTetrahedraMapper()
{
  this->TransformedPoints = vtkFloatArray::New();
  this->Colors = vtkUnsignedCharArray::New();
99
  this->LastProperty = nullptr;
100 101 102 103 104 105 106 107
  this->MaxCellSize = 0;
  this->GaveError = 0;
  this->SqrtTable = new float[SqrtTableSize];
  this->SqrtTableBias = 0.0;
  this->Initialized = false;
  this->CurrentFBOWidth = -1;
  this->CurrentFBOHeight = -1;
  this->FloatingPointFrameBufferResourcesAllocated = false;
108
  this->Framebuffer = vtkOpenGLFramebufferObject::New();
109 110 111
  this->UseFloatingPointFrameBuffer = true;
  this->CanDoFloatingPointFrameBuffer = false;
  this->HasHardwareSupport = false;
Ken Martin's avatar
Ken Martin committed
112
  this->VBO = vtkOpenGLVertexBufferObject::New();
113 114 115 116 117
}

//-----------------------------------------------------------------------------
vtkOpenGLProjectedTetrahedraMapper::~vtkOpenGLProjectedTetrahedraMapper()
{
118
  this->ReleaseGraphicsResources(nullptr);
119 120 121
  this->TransformedPoints->Delete();
  this->Colors->Delete();
  delete[] this->SqrtTable;
Ken Martin's avatar
Ken Martin committed
122
  this->VBO->Delete();
123
  this->Framebuffer->Delete();
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
}

//-----------------------------------------------------------------------------
void vtkOpenGLProjectedTetrahedraMapper::PrintSelf(ostream &os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "VisibilitySort: " << this->VisibilitySort << endl;
  os << indent << "UseFloatingPointFrameBuffer: "
     << (this->UseFloatingPointFrameBuffer ? "True" : "False") << endl;
}

//-----------------------------------------------------------------------------
bool vtkOpenGLProjectedTetrahedraMapper::IsSupported(vtkRenderWindow *rwin)
{
  vtkOpenGLRenderWindow *context = vtkOpenGLRenderWindow::SafeDownCast(rwin);
  if (!context)
140
  {
141 142 143
    vtkErrorMacro(
      << "Support for " << rwin->GetClassName() << " not implemented");
    return false;
144
  }
145 146 147 148

  // use render to FBO when it's supported
  this->CanDoFloatingPointFrameBuffer = false;
  if (this->UseFloatingPointFrameBuffer)
149
  {
150
#if GL_ES_VERSION_3_0 != 1
Aashish Chaudhary's avatar
Aashish Chaudhary committed
151
    if (vtkOpenGLRenderWindow::GetContextSupportsOpenGL32())
152
    {
Aashish Chaudhary's avatar
Aashish Chaudhary committed
153 154
      this->CanDoFloatingPointFrameBuffer = true;
      return true;
155
    }
156
    this->CanDoFloatingPointFrameBuffer
157
      = (glewIsSupported("GL_ARB_texture_float") != 0);
158
#else
159
    this->CanDoFloatingPointFrameBuffer
160 161
      = true;
#endif
162

163
    if (!this->CanDoFloatingPointFrameBuffer)
164
    {
165 166 167
      vtkWarningMacro(
        "Missing FBO support. The algorithm may produce visual artifacts.");
    }
168
  }
169 170 171 172 173 174 175 176

  return true;
}

//-----------------------------------------------------------------------------
void vtkOpenGLProjectedTetrahedraMapper::Initialize(vtkRenderer *renderer)
{
  if (this->Initialized)
177
  {
178
    return;
179
  }
180 181 182 183 184

  this->Initialized = true;

  vtkOpenGLRenderWindow *renwin
    = vtkOpenGLRenderWindow::SafeDownCast(renderer->GetRenderWindow());
185
  this->HasHardwareSupport = renwin != nullptr && this->IsSupported(renwin);
186
  if (!this->HasHardwareSupport)
187
  {
188 189
    // this is an error since there's no fallback.
    vtkErrorMacro("The required extensions are not supported.");
190
  }
191 192 193
}

//-----------------------------------------------------------------------------
194
bool vtkOpenGLProjectedTetrahedraMapper::AllocateFOResources(vtkRenderer *r)
195 196
{
  vtkOpenGLClearErrorMacro();
197
  scoped_annotate annotator("PTM::AllocateFOResources");
198 199

  int *size = r->GetSize();
200

201 202 203 204 205
  if ( this->UseFloatingPointFrameBuffer
    && this->CanDoFloatingPointFrameBuffer
    && (!this->FloatingPointFrameBufferResourcesAllocated
    || (size[0] != this->CurrentFBOWidth)
    || (size[0] != this->CurrentFBOHeight)) )
206
  {
207 208
    vtkOpenGLRenderWindow *rw =
      static_cast<vtkOpenGLRenderWindow *>(r->GetRenderWindow());
209 210

    if (!this->FloatingPointFrameBufferResourcesAllocated)
211
    {
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
      // determine if we have MSAA
      GLint winSampleBuffers = 0;
      glGetIntegerv(GL_SAMPLE_BUFFERS, &winSampleBuffers);
      GLint winSamples = 0;
      if (winSampleBuffers)
      {
        glGetIntegerv(GL_SAMPLES, &winSamples);
      }

      int dsize = rw->GetDepthBufferSize();
      if (dsize == 0)
      {
        dsize = 24;
      }

      vtkOpenGLFramebufferObject *fo = this->Framebuffer;
      fo->SetContext(rw);
      fo->SaveCurrentBindingsAndBuffers();
230

231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
      const char *desc;

      // if we failed to get a framebuffer and we wanted
      // multisamples, then try again without multisamples
      if (!fo->PopulateFramebuffer(size[0], size[1],
          true, // use textures
          1, VTK_FLOAT, // 1 color buffer of float
          true, dsize, // yes depth buffer
          winSamples) // possibly multisampled
          && winSamples > 0)
      {
        fo->PopulateFramebuffer(size[0], size[1],
          true, // use textures
          1, VTK_FLOAT, // 1 color buffer of float
          true, dsize, // yes depth buffer
          0); // no multisamples
      }
248 249 250

      this->FloatingPointFrameBufferResourcesAllocated = true;

251 252 253 254 255 256 257 258 259 260 261 262 263
      if(!fo->GetFrameBufferStatus(fo->GetDrawMode(), desc))
      {
        vtkWarningMacro(
          "Missing FBO support. The algorithm may produce visual artifacts.");
        this->CanDoFloatingPointFrameBuffer = false;
        fo->RestorePreviousBindingsAndBuffers();
        return false;
      }
      this->Framebuffer->UnBind();
      fo->RestorePreviousBindingsAndBuffers();
      this->CanDoFloatingPointFrameBuffer = true;
    }
    else
264
    {
265 266 267 268 269 270 271
      // need resize
      vtkOpenGLFramebufferObject *fo = this->Framebuffer;
      fo->SaveCurrentBindingsAndBuffers();
      fo->Bind();
      fo->Resize(size[0], size[1]);
      this->Framebuffer->UnBind();
      fo->RestorePreviousBindingsAndBuffers();
272
    }
273 274
    this->CurrentFBOWidth = size[0];
    this->CurrentFBOHeight = size[1];
275
  }
276 277 278 279 280 281 282 283 284
  return true;
}

//-----------------------------------------------------------------------------
void vtkOpenGLProjectedTetrahedraMapper::ReleaseGraphicsResources(vtkWindow *win)
{
  this->Initialized = false;

  if (this->FloatingPointFrameBufferResourcesAllocated)
285
  {
286 287
    this->FloatingPointFrameBufferResourcesAllocated = false;
    this->Framebuffer->ReleaseGraphicsResources(win);
288
  }
289

Ken Martin's avatar
Ken Martin committed
290
  this->VBO->ReleaseGraphicsResources();
291
  this->Tris.ReleaseGraphicsResources(win);
292 293 294 295 296 297 298 299 300

  this->Superclass::ReleaseGraphicsResources(win);
}

//-----------------------------------------------------------------------------
void vtkOpenGLProjectedTetrahedraMapper::Render(vtkRenderer *renderer,
                                                vtkVolume *volume)
{
  vtkOpenGLClearErrorMacro();
301
  scoped_annotate annotator("PTM::Render");
302 303 304 305 306

  // load required extensions
  this->Initialize(renderer);

  if (!this->HasHardwareSupport)
307
  {
308
    return;
309
  }
310 311

  // make sure our shader program is loaded and ready to go
312 313 314
  vtkOpenGLRenderWindow *renWin =
    vtkOpenGLRenderWindow::SafeDownCast(renderer->GetRenderWindow());

315
  if (renWin == nullptr)
316
  {
317
    vtkErrorMacro("Invalid vtkOpenGLRenderWindow");
318
  }
319 320 321 322 323 324

  vtkUnstructuredGridBase *input = this->GetInput();
  vtkVolumeProperty *property = volume->GetProperty();

  // has something changed that would require us to recreate the shader?
  if (!this->Tris.Program)
325
  {
326 327 328 329 330 331 332
    // build the shader source code
    std::string VSSource = vtkglProjectedTetrahedraVS;
    std::string FSSource = vtkglProjectedTetrahedraFS;
    std::string GSSource;

    // compile and bind it if needed
    vtkShaderProgram *newShader =
333
      renWin->GetShaderCache()->ReadyShaderProgram(VSSource.c_str(),
334 335 336 337 338
                                            FSSource.c_str(),
                                            GSSource.c_str());

    // if the shader changed reinitialize the VAO
    if (newShader != this->Tris.Program)
339
    {
340
      this->Tris.Program = newShader;
Ken Martin's avatar
Ken Martin committed
341
      this->Tris.VAO->ShaderProgramChanged(); // reset the VAO as the shader has changed
342
    }
343 344

    this->Tris.ShaderSourceTime.Modified();
345
  }
346
  else
347
  {
348
    renWin->GetShaderCache()->ReadyShaderProgram(this->Tris.Program);
349
  }
350 351 352 353

  // Check to see if input changed.
  if (   (this->InputAnalyzedTime < this->MTime)
      || (this->InputAnalyzedTime < input->GetMTime()) )
354
  {
355 356 357 358
    this->GaveError = 0;
    float max_cell_size2 = 0;

    if (input->GetNumberOfCells() == 0)
359
    {
360 361
      // Apparently, the input has no cells.  Just do nothing.
      return;
362
    }
363 364 365 366 367

    vtkSmartPointer<vtkCellIterator> cellIter =
        vtkSmartPointer<vtkCellIterator>::Take(input->NewCellIterator());
    for (cellIter->InitTraversal(); !cellIter->IsDoneWithTraversal();
         cellIter->GoToNextCell())
368
    {
369 370
      vtkIdType npts = cellIter->GetNumberOfPoints();
      if (npts != 4)
371
      {
372
        if (!this->GaveError)
373
        {
374 375 376
          vtkErrorMacro("Encountered non-tetrahedra cell!");
          this->GaveError = 1;
        }
377 378
        continue;
      }
379 380
      vtkIdType *pts = cellIter->GetPointIds()->GetPointer(0);
      for (int j = 0; j < 6; j++)
381
      {
382 383 384 385 386
        double p1[3], p2[3];
        input->GetPoint(pts[tet_edges[j][0]], p1);
        input->GetPoint(pts[tet_edges[j][1]], p2);
        float size2 = (float)vtkMath::Distance2BetweenPoints(p1, p2);
        if (size2 > max_cell_size2)
387
        {
388 389 390
          max_cell_size2 = size2;
        }
      }
391
    }
392 393 394 395 396 397 398 399

    this->MaxCellSize = (float)sqrt(max_cell_size2);

    // Build a sqrt lookup table for measuring distances.  During perspective
    // modes we have to take a lot of square roots, and a table is much faster
    // than calling the sqrt function.
    this->SqrtTableBias = (SqrtTableSize-1)/max_cell_size2;
    for (int i = 0; i < SqrtTableSize; i++)
400
    {
401
      this->SqrtTable[i] = (float)sqrt(i/this->SqrtTableBias);
402
    }
403 404

    this->InputAnalyzedTime.Modified();
405
  }
406 407

  if (renderer->GetRenderWindow()->CheckAbortStatus() || this->GaveError)
408
  {
409 410
    vtkOpenGLCheckErrorMacro("failed during Render");
    return;
411
  }
412 413

  if (renderer->GetRenderWindow()->CheckAbortStatus())
414
  {
415 416
    vtkOpenGLCheckErrorMacro("failed during Render");
    return;
417
  }
418 419 420 421 422 423

  // Check to see if we need to remap colors.
  if (   (this->ColorsMappedTime < this->MTime)
      || (this->ColorsMappedTime < input->GetMTime())
      || (this->LastProperty != property)
      || (this->ColorsMappedTime < property->GetMTime()) )
424
  {
425 426 427 428 429
    vtkDataArray *scalars = this->GetScalars(input, this->ScalarMode,
                                             this->ArrayAccessMode,
                                             this->ArrayId, this->ArrayName,
                                             this->UsingCellColors);
    if (!scalars)
430
    {
431 432 433
      vtkErrorMacro(<< "Can't use projected tetrahedra without scalars!");
      vtkOpenGLCheckErrorMacro("failed during Render");
      return;
434
    }
435 436 437 438 439 440

    vtkProjectedTetrahedraMapper::MapScalarsToColors(this->Colors, property,
                                                     scalars);

    this->ColorsMappedTime.Modified();
    this->LastProperty = property;
441
  }
442
  if (renderer->GetRenderWindow()->CheckAbortStatus())
443
  {
444 445
    vtkOpenGLCheckErrorMacro("failed during Render");
    return;
446
  }
447 448 449

  this->Timer->StartTimer();

450
  this->ProjectTetrahedra(renderer, volume, renWin);
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465

  this->Timer->StopTimer();
  this->TimeToDraw = this->Timer->GetElapsedTime();
  vtkOpenGLCheckErrorMacro("failed after Render");
}

//-----------------------------------------------------------------------------

inline float vtkOpenGLProjectedTetrahedraMapper::GetCorrectedDepth(
                                         float x, float y, float z1, float z2,
                                         const float inverse_projection_mat[16],
                                         int use_linear_depth_correction,
                                         float linear_depth_correction)
{
  if (use_linear_depth_correction)
466
  {
467 468 469
    float depth = linear_depth_correction*(z1 - z2);
    if (depth < 0) depth = -depth;
    return depth;
470
  }
471
  else
472
  {
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
    float eye1[3], eye2[3], invw;

    // This code does the same as the commented code above, but also collects
    // common arithmetic between the two matrix x vector operations.  An
    // optimizing compiler may or may not pick up on that.
    float common[4];

    common[0] = (  inverse_projection_mat[ 0]*x
                 + inverse_projection_mat[ 4]*y
                 + inverse_projection_mat[12] );
    common[1] = (  inverse_projection_mat[ 1]*x
                 + inverse_projection_mat[ 5]*y
                 + inverse_projection_mat[13] );
    common[2] = (  inverse_projection_mat[ 2]*x
                 + inverse_projection_mat[ 6]*y
                 + inverse_projection_mat[10]*z1
                 + inverse_projection_mat[14] );
    common[3] = (  inverse_projection_mat[ 3]*x
                 + inverse_projection_mat[ 7]*y
                 + inverse_projection_mat[15] );

    invw = 1/(common[3] + inverse_projection_mat[11]*z1);
    eye1[0] = invw*(common[0] + inverse_projection_mat[ 8]*z1);
    eye1[1] = invw*(common[1] + inverse_projection_mat[ 9]*z1);
    eye1[2] = invw*(common[2] + inverse_projection_mat[10]*z1);

    invw = 1/(common[3] + inverse_projection_mat[11]*z2);
    eye2[0] = invw*(common[0] + inverse_projection_mat[ 8]*z2);
    eye2[1] = invw*(common[1] + inverse_projection_mat[ 9]*z2);
    eye2[2] = invw*(common[2] + inverse_projection_mat[10]*z2);

    float dist2 = vtkMath::Distance2BetweenPoints(eye1, eye2);
    return this->SqrtTable[(int)(dist2*this->SqrtTableBias)];
506
  }
507 508 509
}

//-----------------------------------------------------------------------------
510 511
void vtkOpenGLProjectedTetrahedraMapper::ProjectTetrahedra(
  vtkRenderer* renderer, vtkVolume* volume, vtkOpenGLRenderWindow* window)
512 513
{
  vtkOpenGLClearErrorMacro();
514
  scoped_annotate annotator("PTM::ProjectTetrahedra");
515 516 517

  // after mucking about with FBO bindings be sure
  // we're saving the default fbo attributes/blend function
518
  this->AllocateFOResources(renderer);
519

520
  vtkOpenGLFramebufferObject *fo = nullptr;
521

luzpaz's avatar
luzpaz committed
522
  // Copy existing Depth/Color buffers to FO
523 524
  if (this->UseFloatingPointFrameBuffer
    && this->CanDoFloatingPointFrameBuffer)
525
  {
526
    scoped_annotate annotator2("PTM::UseFloatingPointFrameBuffer");
527
    fo = this->Framebuffer;
528

529 530 531 532
    // bind draw+read to set it up
    fo->SaveCurrentBindingsAndBuffers();
    fo->Bind(fo->GetDrawMode());
    fo->ActivateDrawBuffer(0);
533

534
    if (!fo->CheckFrameBufferStatus(fo->GetDrawMode()))
535
    {
536
      vtkErrorMacro("FO is incomplete ");
537
    }
538 539

    glBlitFramebuffer(0, 0,
540 541 542 543 544
                      this->CurrentFBOWidth, this->CurrentFBOHeight,
                      0, 0,
                      this->CurrentFBOWidth, this->CurrentFBOHeight,
                      GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
                      GL_NEAREST);
545 546

    vtkOpenGLCheckErrorMacro("failed at glBlitFramebuffer");
547
  }
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564

  // TODO:
  // There are some caching optimizations that could be used
  // here to skip various expensive operations (eg sorting
  // cells could be skipped if input data and MVP matrices
  // haven't changed).

  vtkUnstructuredGridBase *input = this->GetInput();
  this->VisibilitySort->SetInput(input);
  this->VisibilitySort->SetDirectionToBackToFront();
  this->VisibilitySort->SetModelTransform(volume->GetMatrix());
  this->VisibilitySort->SetCamera(renderer->GetActiveCamera());
  this->VisibilitySort->SetMaxCellsReturned(1000);

  this->VisibilitySort->InitTraversal();

  if (renderer->GetRenderWindow()->CheckAbortStatus())
565
  {
566 567 568 569
    if (fo)
    {
      fo->RestorePreviousBindingsAndBuffers();
    }
570
    return;
571
  }
572

Ken Martin's avatar
Ken Martin committed
573
  vtkMatrix4x4 *wcdc;
574 575 576 577
  vtkMatrix4x4 *wcvc;
  vtkMatrix3x3 *norms;
  vtkMatrix4x4 *vcdc;
  vtkOpenGLCamera *cam = (vtkOpenGLCamera *)(renderer->GetActiveCamera());
Ken Martin's avatar
Ken Martin committed
578
  cam->GetKeyMatrices(renderer,wcvc,norms,vcdc,wcdc);
579 580
  float projection_mat[16];
  for(int i = 0; i < 4; ++i)
581
  {
582
    for (int j = 0; j < 4; ++j)
583
    {
584 585
      projection_mat[i*4+j] = vcdc->GetElement(i, j);
    }
586
  }
587 588 589

  float modelview_mat[16];
  if (!volume->GetIsIdentity())
590
  {
591
    vtkMatrix4x4 *tmpMat = vtkMatrix4x4::New();
592
    vtkMatrix4x4 *tmpMat2 = vtkMatrix4x4::New();
593
    vtkMatrix4x4 *mcwc = volume->GetMatrix();
594 595
    tmpMat2->DeepCopy(wcvc);
    tmpMat2->Transpose();
596
    vtkMatrix4x4::Multiply4x4(tmpMat2, mcwc, tmpMat);
597
    tmpMat->Transpose();
598
    for(int i = 0; i < 4; ++i)
599
    {
600
      for (int j = 0; j < 4; ++j)
601
      {
602 603
        modelview_mat[i*4+j] = tmpMat->GetElement(i, j);
      }
604
    }
605
    tmpMat->Delete();
606
    tmpMat2->Delete();
607
  }
608
  else
609
  {
610
    for(int i = 0; i < 4; ++i)
611
    {
612
      for (int j = 0; j < 4; ++j)
613
      {
614 615 616
        modelview_mat[i*4+j] = wcvc->GetElement(i, j);
      }
    }
617
  }
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643

  // Get the inverse projection matrix so that we can convert distances in
  // clipping space to distances in world or eye space.
  float inverse_projection_mat[16];
  float linear_depth_correction = 1;
  int use_linear_depth_correction;
  double tmp_mat[16];

  // VTK's matrix functions use doubles.
  std::copy(projection_mat, projection_mat+16, tmp_mat);
  // VTK and OpenGL store their matrices differently.  Correct.
  vtkMatrix4x4::Transpose(tmp_mat, tmp_mat);
  // Take the inverse.
  vtkMatrix4x4::Invert(tmp_mat, tmp_mat);
  // Restore back to OpenGL form.
  vtkMatrix4x4::Transpose(tmp_mat, tmp_mat);
  // Copy back to float for faster computation.
  std::copy(tmp_mat, tmp_mat+16, inverse_projection_mat);

  // Check to see if we can just do a linear depth correction from clipping
  // space to eye space.
  use_linear_depth_correction = (   (projection_mat[ 3] == 0.0)
                                 && (projection_mat[ 7] == 0.0)
                                 && (projection_mat[11] == 0.0)
                                 && (projection_mat[15] == 1.0) );
  if (use_linear_depth_correction)
644
  {
645 646 647 648 649 650 651 652 653
    float pos1[3], *pos2;

    pos1[0] = inverse_projection_mat[8] + inverse_projection_mat[12];
    pos1[1] = inverse_projection_mat[9] + inverse_projection_mat[13];
    pos1[2] = inverse_projection_mat[10] + inverse_projection_mat[14];

    pos2 = inverse_projection_mat + 12;

    linear_depth_correction = sqrt(vtkMath::Distance2BetweenPoints(pos1, pos2));
654
  }
655 656 657 658 659 660 661
  // Transform all the points.
  vtkProjectedTetrahedraMapper::TransformPoints(input->GetPoints(),
                                                projection_mat, modelview_mat,
                                                this->TransformedPoints);
  float *points = this->TransformedPoints->GetPointer(0);

  if (renderer->GetRenderWindow()->CheckAbortStatus())
662
  {
663 664 665 666
    if (fo)
    {
      fo->RestorePreviousBindingsAndBuffers();
    }
667
    return;
668
  }
669 670 671 672 673

  glDepthMask(GL_FALSE);

  glDisable(GL_CULL_FACE);

674 675 676 677 678 679 680 681 682 683
  GLint blendSrcA = GL_ONE;
  GLint blendDstA = GL_ONE_MINUS_SRC_ALPHA;
  GLint blendSrcC = GL_SRC_ALPHA;
  GLint blendDstC = GL_ONE_MINUS_SRC_ALPHA;
  glGetIntegerv(GL_BLEND_SRC_ALPHA, &blendSrcA);
  glGetIntegerv(GL_BLEND_DST_ALPHA, &blendDstA);
  glGetIntegerv(GL_BLEND_SRC_RGB, &blendSrcC);
  glGetIntegerv(GL_BLEND_DST_RGB, &blendDstC);
  glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
    GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
684 685 686 687 688

  float unit_distance = volume->GetProperty()->GetScalarOpacityUnitDistance();

  // build the VBO and IBOs,  we so these in chuncks as based on
  // the settings of the VisibilitySort tclass
689
  this->VBO->SetStride(6*sizeof(float));
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708

  // Establish vertex arrays.
  // tets have 4 points, 5th point here is used
  // to insert a point in case of intersections
  float tet_points[5*3] = {0.0f};
  unsigned char tet_colors[5*3] = {0};
  float tet_texcoords[5*2] = {0.0f};

  unsigned char *colors = this->Colors->GetPointer(0);
  vtkIdType totalnumcells = input->GetNumberOfCells();
  vtkIdType numcellsrendered = 0;
  vtkNew<vtkIdList> cellPointIds;

  std::vector<float> packedVBO;
  packedVBO.reserve(6 * 5 * this->VisibilitySort->GetMaxCellsReturned());

  std::vector<unsigned int> indexArray;
  indexArray.reserve(3 * 4 * this->VisibilitySort->GetMaxCellsReturned());

709 710
  double progressNext = 0.0;

711 712
  // Let's do it!
  for (vtkIdTypeArray *sorted_cell_ids = this->VisibilitySort->GetNextCells();
713
       sorted_cell_ids != nullptr;
714
       sorted_cell_ids = this->VisibilitySort->GetNextCells())
715
  {
716 717 718 719 720 721 722
    const double progress = static_cast<double>(numcellsrendered) / totalnumcells;
    if (progress >= progressNext)
    {
      this->GLSafeUpdateProgress(progress, window);
      progressNext += 0.1; // we report progress in 10% increments to avoid
                           // over-reporting .
    }
723 724

    if (renderer->GetRenderWindow()->CheckAbortStatus())
725
    {
726
      break;
727
    }
728 729 730 731 732 733 734 735 736
    vtkIdType *cell_ids = sorted_cell_ids->GetPointer(0);
    vtkIdType num_cell_ids = sorted_cell_ids->GetNumberOfTuples();

    packedVBO.resize(6 * 5 * num_cell_ids);
    std::vector<float>::iterator it = packedVBO.begin();
    int numPts = 0;
    indexArray.resize(0);

    for (vtkIdType i = 0; i < num_cell_ids; i++)
737
    {
738
      vtkIdType cell = cell_ids[i];
739
      input->GetCellPoints(cell, cellPointIds);
740 741 742 743
      int j;

      // Get the data for the tetrahedra.
      for (j = 0; j < 4; j++)
744
      {
745 746 747 748 749 750 751 752 753
        // Assuming we only have tetrahedra, each entry in cells has 5
        // components.
        const float *p = points + 3 * cellPointIds->GetId(j);
        tet_points[j*3 + 0] = p[0];
        tet_points[j*3 + 1] = p[1];
        tet_points[j*3 + 2] = p[2];

        const unsigned char *c;
        if (this->UsingCellColors)
754
        {
755
          c = colors + 4*cell;
756
        }
757
        else
758
        {
759
          c = colors + 4 * cellPointIds->GetId(j);
760
        }
761 762 763 764 765 766 767

        tet_colors[j*3 + 0] = c[0];
        tet_colors[j*3 + 1] = c[1];
        tet_colors[j*3 + 2] = c[2];

        tet_texcoords[j*2 + 0] = static_cast<float>(c[3])/255.0f;
        tet_texcoords[j*2 + 1] = 0;
768
      }
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785

      // Do not render this cell if it is outside of the cutting planes.  For
      // most planes, cut if all points are outside.  For the near plane, cut if
      // any points are outside because things can go very wrong if one of the
      // points is behind the view.
      if (   (   (tet_points[0*3+0] >  1.0f) && (tet_points[1*3+0] >  1.0f)
              && (tet_points[2*3+0] >  1.0f) && (tet_points[3*3+0] >  1.0f) )
          || (   (tet_points[0*3+0] < -1.0f) && (tet_points[1*3+0] < -1.0f)
              && (tet_points[2*3+0] < -1.0f) && (tet_points[3*3+0] < -1.0f) )
          || (   (tet_points[0*3+1] >  1.0f) && (tet_points[1*3+1] >  1.0f)
              && (tet_points[2*3+1] >  1.0f) && (tet_points[3*3+1] >  1.0f) )
          || (   (tet_points[0*3+1] < -1.0f) && (tet_points[1*3+1] < -1.0f)
              && (tet_points[2*3+1] < -1.0f) && (tet_points[3*3+1] < -1.0f) )
          || (   (tet_points[0*3+2] >  1.0f) && (tet_points[1*3+2] >  1.0f)
              && (tet_points[2*3+2] >  1.0f) && (tet_points[3*3+2] >  1.0f) )
          || (   (tet_points[0*3+2] < -1.0f) || (tet_points[1*3+2] < -1.0f)
              || (tet_points[2*3+2] < -1.0f) || (tet_points[3*3+2] < -1.0f) ) )
786
      {
787
        continue;
788
      }
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815

      // The classic PT algorithm uses face normals to determine the
      // projection class and then do calculations individually.  However,
      // Wylie 2002 shows how to use the intersection of two segments to
      // calculate the depth of the thick part for any case.  Here, we use
      // face normals to determine which segments to use.  One segment
      // should be between two faces that are either both front facing or
      // back facing.  Obviously, we only need to test three faces to find
      // two such faces.  We test the three faces connected to point 0.
      vtkIdType segment1[2];
      vtkIdType segment2[2];

      float v1[2], v2[2], v3[3];
      v1[0] = tet_points[1*3 + 0] - tet_points[0*3 + 0];
      v1[1] = tet_points[1*3 + 1] - tet_points[0*3 + 1];
      v2[0] = tet_points[2*3 + 0] - tet_points[0*3 + 0];
      v2[1] = tet_points[2*3 + 1] - tet_points[0*3 + 1];
      v3[0] = tet_points[3*3 + 0] - tet_points[0*3 + 0];
      v3[1] = tet_points[3*3 + 1] - tet_points[0*3 + 1];

      float face_dir1 = v3[0]*v2[1] - v3[1]*v2[0];
      float face_dir2 = v1[0]*v3[1] - v1[1]*v3[0];
      float face_dir3 = v2[0]*v1[1] - v2[1]*v1[0];

      if (   (face_dir1 * face_dir2 >= 0)
          && (   (face_dir1 != 0)       // Handle a special case where 2 faces
              || (face_dir2 != 0) ) )   // are perpendicular to the view plane.
816
      {
817 818
        segment1[0] = 0;  segment1[1] = 3;
        segment2[0] = 1;  segment2[1] = 2;
819
      }
820
      else if (face_dir1 * face_dir3 >= 0)
821
      {
822 823
        segment1[0] = 0;  segment1[1] = 2;
        segment2[0] = 1;  segment2[1] = 3;
824
      }
825
      else      // Unless the tet is degenerate, face_dir2*face_dir3 >= 0
826
      {
827 828
        segment1[0] = 0;  segment1[1] = 1;
        segment2[0] = 2;  segment2[1] = 3;
829
      }
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864

#define VEC3SUB(Z,X,Y)          \
  (Z)[0] = (X)[0] - (Y)[0];     \
  (Z)[1] = (X)[1] - (Y)[1];     \
  (Z)[2] = (X)[2] - (Y)[2];
#define P1 (tet_points + 3*segment1[0])
#define P2 (tet_points + 3*segment1[1])
#define P3 (tet_points + 3*segment2[0])
#define P4 (tet_points + 3*segment2[1])
#define C1 (tet_colors + 3*segment1[0])
#define C2 (tet_colors + 3*segment1[1])
#define C3 (tet_colors + 3*segment2[0])
#define C4 (tet_colors + 3*segment2[1])
#define T1 (tet_texcoords + 2*segment1[0])
#define T2 (tet_texcoords + 2*segment1[1])
#define T3 (tet_texcoords + 2*segment2[0])
#define T4 (tet_texcoords + 2*segment2[1])
      // Find the intersection of the projection of the two segments in the
      // XY plane.  This algorithm is based on that given in Graphics Gems
      // III, pg. 199-202.
      float A[3], B[3], C[3];
      // We can define the two lines parametrically as:
      //        P1 + alpha(A)
      //        P3 + beta(B)
      // where A = P2 - P1
      // and   B = P4 - P3.
      // alpha and beta are in the range [0,1] within the line segment.
      VEC3SUB(A, P2, P1);
      VEC3SUB(B, P4, P3);
      // The lines intersect when the values of the two parameteric equations
      // are equal.  Setting them equal and moving everything to one side:
      //        0 = C + beta(B) - alpha(A)
      // where C = P3 - P1.
      VEC3SUB(C, P3, P1);
      // When we project the lines to the xy plane (which we do by throwing
Kunda's avatar
Kunda committed
865
      // away the z value), we have two equations and two unknowns.  The
866 867 868 869 870 871 872
      // following are the solutions for alpha and beta.
      float denominator = (A[0]*B[1]-A[1]*B[0]);
      if (denominator == 0) continue;   // Must be degenerated tetrahedra.
      float alpha = (B[1]*C[0]-B[0]*C[1])/denominator;
      float beta = (A[1]*C[0]-A[0]*C[1])/denominator;

      if ((alpha >= 0) && (alpha <= 1))
873
      {
874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
        // The two segments intersect.  This corresponds to class 2 in
        // Shirley and Tuchman (or one of the degenerate cases).

        // Make new point at intersection.
        tet_points[3*4 + 0] = P1[0] + alpha*A[0];
        tet_points[3*4 + 1] = P1[1] + alpha*A[1];
        tet_points[3*4 + 2] = P1[2] + alpha*A[2];

        // Find depth at intersection.
        float depth = this->GetCorrectedDepth(
              tet_points[3*4 + 0],
              tet_points[3*4 + 1],
              tet_points[3*4 + 2],
              P3[2] + beta*B[2],
              inverse_projection_mat,
              use_linear_depth_correction,
              linear_depth_correction);

        // Find color at intersection.
        tet_colors[3*4 + 0] = static_cast<unsigned char>
              (0.5f*(C1[0] + alpha*(C2[0]-C1[0])
              + C3[0] + beta*(C4[0]-C3[0])));

        tet_colors[3*4 + 1] = static_cast<unsigned char>
              (0.5f*(C1[1] + alpha*(C2[1]-C1[1])
              + C3[1] + beta*(C4[1]-C3[1])));

        tet_colors[3*4 + 2] = static_cast<unsigned char>
              (0.5f*(C1[2] + alpha*(C2[2]-C1[2])
              + C3[2] + beta*(C4[2]-C3[2])));

//         tet_colors[3*0 + 0] = 255;
//         tet_colors[3*0 + 1] = 0;
//         tet_colors[3*0 + 2] = 0;
//         tet_colors[3*1 + 0] = 255;
//         tet_colors[3*1 + 1] = 0;
//         tet_colors[3*1 + 2] = 0;
//         tet_colors[3*2 + 0] = 255;
//         tet_colors[3*2 + 1] = 0;
//         tet_colors[3*2 + 2] = 0;
//         tet_colors[3*3 + 0] = 255;
//         tet_colors[3*3 + 1] = 0;
//         tet_colors[3*3 + 2] = 0;
//         tet_colors[3*4 + 0] = 255;
//         tet_colors[3*4 + 1] = 0;
//         tet_colors[3*4 + 2] = 0;

        // Find the opacity at intersection.
        tet_texcoords[2*4 + 0] = 0.5f*(  T1[0] + alpha*(T2[0]-T1[0])
                                       + T3[0] + alpha*(T4[0]-T3[0]));

        // Record the depth at the intersection.
926
        tet_texcoords[2*4 + 1] = depth/unit_distance;
927 928 929 930 931 932 933 934 935 936

        // Establish the order in which the points should be rendered.
        unsigned char indices[6];
        indices[0] = 4;
        indices[1] = segment1[0];
        indices[2] = segment2[0];
        indices[3] = segment1[1];
        indices[4] = segment2[1];
        indices[5] = segment1[0];
        // add the cells to the IBO
937
        for (int cellIdx = 0; cellIdx < 4; cellIdx++)
938
        {
939
          indexArray.push_back(indices[0]+numPts);
940 941
          indexArray.push_back(indices[cellIdx+1]+numPts);
          indexArray.push_back(indices[cellIdx+2]+numPts);
942
        }
943
      }
944
      else
945
      {
946 947 948
        // The two segments do not intersect.  This corresponds to class 1
        // in Shirley and Tuchman.
        if (alpha <= 0)
949
        {
950 951 952 953 954
          // Flip segment1 so that alpha is >= 1.  P1 and P2 are also
          // flipped as are C1-C2 and T1-T2.  Note that this will
          // invalidate A.  B and beta are unaffected.
          std::swap(segment1[0], segment1[1]);
          alpha = 1 - alpha;
955
        }
956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
        // From here on, we can assume P2 is the "thick" point.

        // Find the depth under the thick point.  Use the alpha and beta
        // from intersection to determine location of face under thick
        // point.
        float edgez = P3[2] + beta*B[2];
        float pointz = P1[2];
        float facez = (edgez + (alpha-1)*pointz)/alpha;
        float depth = GetCorrectedDepth(P2[0], P2[1], P2[2], facez,
                                        inverse_projection_mat,
                                        use_linear_depth_correction,
                                        linear_depth_correction);

        // Fix color at thick point.  Average color with color of opposite
        // face.
        for (j = 0; j < 3; j++)
972
        {
973 974 975 976
          float edgec = C3[j] + beta*(C4[j]-C3[j]);
          float pointc = C1[j];
          float facec = (edgec + (alpha-1)*pointc)/alpha;
          C2[j] = (unsigned char)(0.5f*(facec + C2[j]));
977
        }
978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999

//         tet_colors[3*segment1[0] + 0] = 0;
//         tet_colors[3*segment1[0] + 1] = 255;
//         tet_colors[3*segment1[0] + 2] = 0;
//         tet_colors[3*segment1[1] + 0] = 0;
//         tet_colors[3*segment1[1] + 1] = 255;
//         tet_colors[3*segment1[1] + 2] = 0;
//         tet_colors[3*segment2[0] + 0] = 0;
//         tet_colors[3*segment2[0] + 1] = 255;
//         tet_colors[3*segment2[0] + 2] = 0;
//         tet_colors[3*segment2[1] + 0] = 0;
//         tet_colors[3*segment2[1] + 1] = 255;
//         tet_colors[3*segment2[1] + 2] = 0;

        // Fix opacity at thick point.  Average opacity with opacity of
        // opposite face.
        float edgea = T3[0] + beta*(T4[0]-T3[0]);
        float pointa = T1[0];
        float facea = (edgea + (alpha-1)*pointa)/alpha;
        T2[0] = 0.5f*(facea + T2[0]);

        // Record thickness at thick point.
1000
        T2[1] = depth/unit_distance;
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010

        // Establish the order in which the points should be rendered.
        unsigned char indices[5];
        indices[0] = segment1[1];
        indices[1] = segment1[0];
        indices[2] = segment2[0];
        indices[3] = segment2[1];
        indices[4] = segment1[0];

        // add the cells to the IBO
1011
        for (int cellIdx = 0; cellIdx < 3; cellIdx++)
1012
        {
1013
          indexArray.push_back(indices[0]+numPts);
1014 1015
          indexArray.push_back(indices[cellIdx+1]+numPts);
          indexArray.push_back(indices[cellIdx+2]+numPts);
1016
        }
1017
      }
1018 1019

      // add the points to the VBO
1020
      union { unsigned char c[4]; float f; } v = { { 0, 0, 0, 255 } };
1021
      for (int ptIdx = 0; ptIdx < 5; ptIdx++)
1022
      {
1023 1024 1025
        *(it++) = tet_points[ptIdx*3];
        *(it++) = tet_points[ptIdx*3+1];
        *(it++) = tet_points[ptIdx*3+2];
1026 1027 1028 1029
        v.c[0] = tet_colors[ptIdx*3];
        v.c[1] = tet_colors[ptIdx*3+1];
        v.c[2] = tet_colors[ptIdx*3+2];
        *(it++) = v.f;
1030 1031
        *(it++) = tet_texcoords[ptIdx*2]; // attenuation
        *(it++) = tet_texcoords[ptIdx*2+1]; // depth
1032
      }
1033 1034
      numPts += 5;
    }
1035

Ken Martin's avatar
Ken Martin committed
1036 1037
    this->VBO->Upload(packedVBO, vtkOpenGLBufferObject::ArrayBuffer);
    this->VBO->Bind();
1038

Ken Martin's avatar
Ken Martin committed
1039
    this->Tris.VAO->Bind();
Ken Martin's avatar
Ken Martin committed
1040
    if (this->Tris.IBO->IndexCount && (
Ken Martin's avatar
Ken Martin committed
1041
        this->Tris.ShaderSourceTime > this->Tris.AttributeUpdateTime))
1042
    {
Ken Martin's avatar
Ken Martin committed
1043
      if (!this->Tris.VAO->AddAttributeArray(this->Tris.Program, this->VBO,
1044
                                      "vertexDC", 0,
1045
                                      this->VBO->GetStride(), VTK_FLOAT, 3, false))
1046
      {
1047
        vtkErrorMacro(<< "Error setting 'vertexDC' in shader VAO.");
1048
      }
Ken Martin's avatar
Ken Martin committed
1049
      if (!this->Tris.VAO->AddAttributeArray(this->Tris.Program, this->VBO,
1050
                                      "scalarColor", 3*sizeof(float),
1051
                                      this->VBO->GetStride(), VTK_UNSIGNED_CHAR,
1052
                                      3, true))
1053
      {
1054
        vtkErrorMacro(<< "Error setting 'scalarColor' in shader VAO.");
1055
      }
Ken Martin's avatar
Ken Martin committed
1056
      if (!this->Tris.VAO->AddAttributeArray(this->Tris.Program, this->VBO,
1057
                                      "attenuationArray", 4*sizeof(float),
1058
                                      this->VBO->GetStride(), VTK_FLOAT,
1059
                                      1, false))
1060
      {
1061
        vtkErrorMacro(<< "Error setting attenuation in shader VAO.");
1062
      }
Ken Martin's avatar
Ken Martin committed
1063
      if (!this->Tris.VAO->AddAttributeArray(this->Tris.Program, this->VBO,
1064
                                      "depthArray", 5*sizeof(float),
1065
                                      this->VBO->GetStride(), VTK_FLOAT,
1066
                                      1, false))
1067
      {
1068 1069
        vtkErrorMacro(<< "Error setting depth in shader VAO.");
      }
1070 1071
      this->Tris.AttributeUpdateTime.Modified();
    }
1072

Ken Martin's avatar
Ken Martin committed
1073
    this->Tris.IBO->Upload(indexArray, vtkOpenGLBufferObject::ElementArrayBuffer);
Ken Martin's avatar
Ken Martin committed
1074
    this->Tris.IBO->IndexCount = indexArray.size();
Ken Martin's avatar
Ken Martin committed
1075
    this->Tris.IBO->Bind();
1076
    glDrawRangeElements(GL_TRIANGLES, 0,
1077
                        static_cast<GLuint>(numPts - 1),
Ken Martin's avatar
Ken Martin committed
1078
                        static_cast<GLsizei>(this->Tris.IBO->IndexCount),
1079
                        GL_UNSIGNED_INT,
1080
                        nullptr);
Ken Martin's avatar
Ken Martin committed
1081 1082 1083
    this->Tris.IBO->Release();
    this->Tris.VAO->Release();
    this->VBO->Release();
1084
    numcellsrendered += num_cell_ids;
1085
  }
1086

1087
  if (fo)
1088
  {
1089
    // copy from our fbo to the default one
1090
    fo->Bind(fo->GetReadMode());
1091 1092

    // draw to default fbo
1093
    fo->RestorePreviousBindingsAndBuffers(fo->GetDrawMode());
1094

1095
    // Depth buffer has not changed so only copy color
1096
    glBlitFramebuffer(0, 0, this->CurrentFBOWidth, this->CurrentFBOHeight,
1097
                      0, 0, this->CurrentFBOWidth, this->CurrentFBOHeight,
1098
                      GL_COLOR_BUFFER_BIT, GL_NEAREST);
1099 1100 1101 1102

    vtkOpenGLCheckErrorMacro("failed at glBlitFramebuffer");

    // restore default fbo for both read+draw
1103
    fo->RestorePreviousBindingsAndBuffers(fo->GetReadMode());
1104
  }
1105 1106 1107 1108 1109

  // Restore the blend function.
  vtkOpenGLCheckErrorMacro("failed at glPopAttrib");

  glDepthMask(GL_TRUE);
1110
  glBlendFuncSeparate(blendSrcC, blendDstC, blendSrcA, blendDstA);
1111 1112

  vtkOpenGLCheckErrorMacro("failed after ProjectTetrahedra");
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
  this->GLSafeUpdateProgress(1.0, window);
}

//-----------------------------------------------------------------------------
void vtkOpenGLProjectedTetrahedraMapper::GLSafeUpdateProgress(
  double value, vtkOpenGLRenderWindow* window)
{
  scoped_annotate annotator("GLSafeUpdateProgress");
  vtkNew<vtkOpenGLFramebufferObject> fbo;
  fbo->SetContext(window);
  fbo->SaveCurrentBindingsAndBuffers();
  // since UpdateProgress may causes GL context changes, we save and restore
  // state.
  this->UpdateProgress(value);
  window->MakeCurrent();
  fbo->RestorePreviousBindingsAndBuffers();
1129
}