1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4 
5   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
6   All rights reserved.
7   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
8 
9      This software is distributed WITHOUT ANY WARRANTY; without even
10      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11      PURPOSE.  See the above copyright notice for more information.
12 
13 =========================================================================*/
14 #include "vtkSurfaceLICHelper.h"
15 
16 #include "vtkBoundingBox.h"
17 #include "vtkCompositeDataIterator.h"
18 #include "vtkCompositeDataSet.h"
19 #include "vtkDataSet.h"
20 #include "vtkImageData.h"
21 #include "vtkLineIntegralConvolution2D.h"
22 #include "vtkMath.h"
23 #include "vtkMatrix4x4.h"
24 #include "vtkNew.h"
25 #include "vtkOpenGLActor.h"
26 #include "vtkOpenGLCamera.h"
27 #include "vtkOpenGLError.h"
28 #include "vtkOpenGLFramebufferObject.h"
29 #include "vtkOpenGLRenderUtilities.h"
30 #include "vtkOpenGLRenderWindow.h"
31 #include "vtkPainterCommunicator.h"
32 #include "vtkPixelBufferObject.h"
33 #include "vtkRenderer.h"
34 #include "vtkSurfaceLICComposite.h"
35 #include "vtkTextureObject.h"
36 #include "vtk_glew.h"
37 
38 #include <vector>
39 
40 #define vtkSurfaceLICHelperDEBUG 0
41 
42 // Description
43 // find min/max of unmasked fragments across all regions
44 // download each search each region individually
StreamingFindMinMax(vtkOpenGLFramebufferObject * fbo,std::deque<vtkPixelExtent> & blockExts,float & min,float & max)45 void vtkSurfaceLICHelper::StreamingFindMinMax(
46   vtkOpenGLFramebufferObject* fbo, std::deque<vtkPixelExtent>& blockExts, float& min, float& max)
47 {
48   size_t nBlocks = blockExts.size();
49   // initiate download
50   fbo->ActivateReadBuffer(1U);
51   vtkStaticCheckFrameBufferStatusMacro(GL_FRAMEBUFFER);
52   std::vector<vtkPixelBufferObject*> pbos(nBlocks, nullptr);
53   for (size_t e = 0; e < nBlocks; ++e)
54   {
55     pbos[e] = fbo->Download(blockExts[e].GetData(), VTK_FLOAT, 4, GL_FLOAT, GL_RGBA);
56   }
57   fbo->RemoveColorAttachment(0U);
58   fbo->RemoveColorAttachment(1U);
59   fbo->DeactivateDrawBuffers();
60   fbo->DeactivateReadBuffer();
61   // map search and release each region
62   for (size_t e = 0; e < nBlocks; ++e)
63   {
64     vtkPixelBufferObject*& pbo = pbos[e];
65     float* pColors = (float*)pbo->MapPackedBuffer();
66 
67     size_t n = blockExts[e].Size();
68     for (size_t i = 0; i < n; ++i)
69     {
70       if (pColors[4 * i + 3] != 0.0f)
71       {
72         float L = pColors[4 * i + 2];
73         min = min > L ? L : min;
74         max = max < L ? L : max;
75       }
76     }
77     pbo->UnmapPackedBuffer();
78     pbo->Delete();
79     pbo = nullptr;
80   }
81 #if vtkSurfaceLICHelperDEBUG >= 1
82   cerr << "min=" << min << " max=" << max << endl;
83 #endif
84 }
85 
86 // Description:
87 // Constructor
vtkSurfaceLICHelper()88 vtkSurfaceLICHelper::vtkSurfaceLICHelper()
89 {
90   this->Viewsize[0] = this->Viewsize[1] = 0;
91 
92   this->ContextNeedsUpdate = true;
93   this->CommunicatorNeedsUpdate = true;
94 
95   this->Communicator = new vtkPainterCommunicator;
96 
97   this->HasVectors = false;
98 
99   this->ColorPass = nullptr;
100   this->ColorEnhancePass = nullptr;
101   this->CopyPass = nullptr;
102 }
103 
104 // Description:
105 // Destructor
~vtkSurfaceLICHelper()106 vtkSurfaceLICHelper::~vtkSurfaceLICHelper()
107 {
108   this->ReleaseGraphicsResources(nullptr);
109 
110   delete this->ColorPass;
111   delete this->ColorEnhancePass;
112   delete this->CopyPass;
113   this->ColorPass = nullptr;
114   this->ColorEnhancePass = nullptr;
115   this->CopyPass = nullptr;
116 
117   delete this->Communicator;
118 }
119 
120 // Description:
121 // Check for OpenGL support
IsSupported(vtkOpenGLRenderWindow * context)122 bool vtkSurfaceLICHelper::IsSupported(vtkOpenGLRenderWindow* context)
123 {
124   if (context == nullptr)
125   {
126     vtkGenericWarningMacro("OpenGL render window required");
127     return false;
128   }
129 
130   bool lic2d = vtkLineIntegralConvolution2D::IsSupported(context);
131 
132   bool floatFormats = vtkTextureObject::IsSupported(context, true, true, false);
133 
134   bool support = lic2d && floatFormats;
135 
136   if (!support)
137   {
138     vtkGenericWarningMacro(<< "SurfaceLIC is not supported" << endl
139                            << context->GetClassName() << endl
140                            << "LIC support = " << lic2d << endl
141                            << "floating point texture formats = " << floatFormats);
142     return false;
143   }
144   return true;
145 }
146 
147 // Description:
148 // Free textures and shader programs we're holding a reference to.
ReleaseGraphicsResources(vtkWindow * win)149 void vtkSurfaceLICHelper::ReleaseGraphicsResources(vtkWindow* win)
150 {
151   if (this->ColorEnhancePass)
152   {
153     this->ColorEnhancePass->ReleaseGraphicsResources(win);
154   }
155   if (this->ColorPass)
156   {
157     this->ColorPass->ReleaseGraphicsResources(win);
158   }
159   if (this->CopyPass)
160   {
161     this->CopyPass->ReleaseGraphicsResources(win);
162   }
163 
164   this->ClearTextures();
165 
166   this->Compositor = nullptr;
167   this->LICer = nullptr;
168   this->FBO = nullptr;
169 }
170 
171 // Description:
172 // Free textures we're holding a reference to.
ClearTextures()173 void vtkSurfaceLICHelper::ClearTextures()
174 {
175   this->DepthImage = nullptr;
176   this->GeometryImage = nullptr;
177   this->VectorImage = nullptr;
178   this->MaskVectorImage = nullptr;
179   this->CompositeVectorImage = nullptr;
180   this->CompositeMaskVectorImage = nullptr;
181   this->NoiseImage = nullptr;
182   this->LICImage = nullptr;
183   this->RGBColorImage = nullptr;
184   this->HSLColorImage = nullptr;
185 }
186 
187 // Description:
188 // Allocate textures.
AllocateTextures(vtkOpenGLRenderWindow * context,int * viewsize)189 void vtkSurfaceLICHelper::AllocateTextures(vtkOpenGLRenderWindow* context, int* viewsize)
190 {
191   this->AllocateDepthTexture(context, viewsize, this->DepthImage);
192   this->AllocateTexture(context, viewsize, this->GeometryImage, vtkTextureObject::Nearest);
193   this->AllocateTexture(context, viewsize, this->VectorImage, vtkTextureObject::Linear);
194   this->AllocateTexture(context, viewsize, this->MaskVectorImage, vtkTextureObject::Linear);
195   this->AllocateTexture(context, viewsize, this->CompositeVectorImage, vtkTextureObject::Linear);
196   this->AllocateTexture(
197     context, viewsize, this->CompositeMaskVectorImage, vtkTextureObject::Linear);
198   this->AllocateTexture(context, viewsize, this->LICImage, vtkTextureObject::Nearest);
199   this->AllocateTexture(context, viewsize, this->RGBColorImage, vtkTextureObject::Nearest);
200   this->AllocateTexture(context, viewsize, this->HSLColorImage, vtkTextureObject::Nearest);
201 }
202 
203 // Description:
204 // Allocate a size texture, store in the given smart pointer.
AllocateTexture(vtkOpenGLRenderWindow * context,int * viewsize,vtkSmartPointer<vtkTextureObject> & tex,int filter)205 void vtkSurfaceLICHelper::AllocateTexture(
206   vtkOpenGLRenderWindow* context, int* viewsize, vtkSmartPointer<vtkTextureObject>& tex, int filter)
207 {
208   if (!tex)
209   {
210     vtkTextureObject* newTex = vtkTextureObject::New();
211     newTex->SetContext(context);
212     newTex->SetBaseLevel(0);
213     newTex->SetMaxLevel(0);
214     newTex->SetWrapS(vtkTextureObject::ClampToEdge);
215     newTex->SetWrapT(vtkTextureObject::ClampToEdge);
216     newTex->SetMinificationFilter(filter);
217     newTex->SetMagnificationFilter(filter);
218     newTex->SetBorderColor(0.0f, 0.0f, 0.0f, 0.0f);
219     newTex->Create2D(viewsize[0], viewsize[1], 4, VTK_FLOAT, false);
220     newTex->SetAutoParameters(0);
221     tex = newTex;
222     newTex->Delete();
223   }
224 }
225 
226 // Description:
227 // Allocate a size texture, store in the given smart pointer.
AllocateDepthTexture(vtkOpenGLRenderWindow * context,int * viewsize,vtkSmartPointer<vtkTextureObject> & tex)228 void vtkSurfaceLICHelper::AllocateDepthTexture(
229   vtkOpenGLRenderWindow* context, int* viewsize, vtkSmartPointer<vtkTextureObject>& tex)
230 {
231   if (!tex)
232   {
233     vtkTextureObject* newTex = vtkTextureObject::New();
234     newTex->SetContext(context);
235     newTex->AllocateDepth(viewsize[0], viewsize[1], vtkTextureObject::Float32);
236     newTex->SetAutoParameters(0);
237     tex = newTex;
238     newTex->Delete();
239   }
240 }
241 
242 // Description:
243 // After LIC has been computed reset/clean internal state
Updated()244 void vtkSurfaceLICHelper::Updated()
245 {
246   this->ContextNeedsUpdate = false;
247   this->CommunicatorNeedsUpdate = false;
248 }
249 
250 // Description:
251 // Force all stages to re-execute. Necessary if the
252 // context or communicator changes.
UpdateAll()253 void vtkSurfaceLICHelper::UpdateAll()
254 {
255   this->ContextNeedsUpdate = true;
256   this->CommunicatorNeedsUpdate = true;
257 }
258 
259 // Description:
260 // Convert a viewport to a bounding box and it's texture coordinates for a
261 // screen size texture.
ViewportQuadTextureCoords(const vtkPixelExtent & viewExt,const vtkPixelExtent & viewportExt,GLfloat * tcoords)262 void vtkSurfaceLICHelper::ViewportQuadTextureCoords(
263   const vtkPixelExtent& viewExt, const vtkPixelExtent& viewportExt, GLfloat* tcoords)
264 {
265   GLfloat viewsize[2];
266   viewExt.Size(viewsize);
267 
268   // cell to node
269   vtkPixelExtent next(viewportExt);
270   next.CellToNode();
271   next.GetData(tcoords);
272 
273   tcoords[0] = tcoords[0] / viewsize[0];
274   tcoords[1] = tcoords[1] / viewsize[0];
275   tcoords[2] = tcoords[2] / viewsize[1];
276   tcoords[3] = tcoords[3] / viewsize[1];
277 }
278 
279 // Description:
280 // Render a quad (to trigger a shader to run)
RenderQuad(const vtkPixelExtent & viewExt,const vtkPixelExtent & viewportExt,vtkOpenGLHelper * cbo)281 void vtkSurfaceLICHelper::RenderQuad(
282   const vtkPixelExtent& viewExt, const vtkPixelExtent& viewportExt, vtkOpenGLHelper* cbo)
283 {
284   vtkOpenGLStaticCheckErrorMacro("failed at RenderQuad");
285 
286   // cell to node
287   vtkPixelExtent next(viewportExt);
288   next.CellToNode();
289 
290   GLfloat quadPts[4];
291   next.GetData(quadPts);
292 
293   GLfloat quadTCoords[4];
294   this->ViewportQuadTextureCoords(viewExt, viewportExt, quadTCoords);
295 
296   float tcoords[] = { quadTCoords[0], quadTCoords[2], quadTCoords[1], quadTCoords[2],
297     quadTCoords[1], quadTCoords[3], quadTCoords[0], quadTCoords[3] };
298 
299   float verts[] = { quadTCoords[0] * 2.0f - 1.0f, quadTCoords[2] * 2.0f - 1.0f, 0.0f,
300     quadTCoords[1] * 2.0f - 1.0f, quadTCoords[2] * 2.0f - 1.0f, 0.0f, quadTCoords[1] * 2.0f - 1.0f,
301     quadTCoords[3] * 2.0f - 1.0f, 0.0f, quadTCoords[0] * 2.0f - 1.0f, quadTCoords[3] * 2.0f - 1.0f,
302     0.0f };
303 
304   vtkOpenGLRenderUtilities::RenderQuad(verts, tcoords, cbo->Program, cbo->VAO);
305   vtkOpenGLStaticCheckErrorMacro("failed at RenderQuad");
306 }
307 
308 // Description:
309 // given a axes aligned bounding box in
310 // normalized device coordinates test for
311 // view frustum visibility.
312 // if all points are outside one of the
313 // view frustum planes then this box
314 // is not visible. we might have false
315 // positive where more than one clip
316 // plane intersects the box.
VisibilityTest(double ndcBBox[24])317 bool vtkSurfaceLICHelper::VisibilityTest(double ndcBBox[24])
318 {
319   // check all points in the direction d
320   // at the same time.
321   for (int d = 0; d < 3; ++d)
322   {
323     if (((ndcBBox[d] < -1.0) && (ndcBBox[3 + d] < -1.0) && (ndcBBox[6 + d] < -1.0) &&
324           (ndcBBox[9 + d] < -1.0) && (ndcBBox[12 + d] < -1.0) && (ndcBBox[15 + d] < -1.0) &&
325           (ndcBBox[18 + d] < -1.0) && (ndcBBox[21 + d] < -1.0)) ||
326       ((ndcBBox[d] > 1.0) && (ndcBBox[3 + d] > 1.0) && (ndcBBox[6 + d] > 1.0) &&
327         (ndcBBox[9 + d] > 1.0) && (ndcBBox[12 + d] > 1.0) && (ndcBBox[15 + d] > 1.0) &&
328         (ndcBBox[18 + d] > 1.0) && (ndcBBox[21 + d] > 1.0)))
329     {
330       return false;
331     }
332   }
333   return true;
334 }
335 
336 // Description:
337 // Given world space bounds,
338 // compute bounding boxes in clip and normalized device
339 // coordinates and perform view frustum visibility test.
340 // return true if the bounds are visible. If so the passed
341 // in extent object is initialized with the corresponding
342 // screen space extents.
ProjectBounds(double PMV[16],int viewsize[2],double bounds[6],vtkPixelExtent & screenExt)343 bool vtkSurfaceLICHelper::ProjectBounds(
344   double PMV[16], int viewsize[2], double bounds[6], vtkPixelExtent& screenExt)
345 {
346   // this is how to get the 8 corners of a bounding
347   // box from the VTK bounds
348   int bbIds[24] = { 0, 2, 4, 1, 2, 4, 1, 3, 4, 0, 3, 4, 0, 2, 5, 1, 2, 5, 1, 3, 5, 0, 3, 5 };
349 
350   // normalized device coordinate bounding box
351   double ndcBBox[24];
352   for (int q = 0; q < 8; ++q)
353   {
354     int qq = 3 * q;
355     // bounding box corner
356     double wx = bounds[bbIds[qq]];
357     double wy = bounds[bbIds[qq + 1]];
358     double wz = bounds[bbIds[qq + 2]];
359     // to clip coordinates
360     ndcBBox[qq] = wx * PMV[idx(0, 0)] + wy * PMV[idx(0, 1)] + wz * PMV[idx(0, 2)] + PMV[idx(0, 3)];
361     ndcBBox[qq + 1] =
362       wx * PMV[idx(1, 0)] + wy * PMV[idx(1, 1)] + wz * PMV[idx(1, 2)] + PMV[idx(1, 3)];
363     ndcBBox[qq + 2] =
364       wx * PMV[idx(2, 0)] + wy * PMV[idx(2, 1)] + wz * PMV[idx(2, 2)] + PMV[idx(2, 3)];
365     double ndcw = wx * PMV[idx(3, 0)] + wy * PMV[idx(3, 1)] + wz * PMV[idx(3, 2)] + PMV[idx(3, 3)];
366 
367     // TODO
368     // if the point is past the near clipping plane
369     // we need to do something more robust. this ensures
370     // the correct result but its inefficient
371     if (ndcw < 0.0)
372     {
373       screenExt = vtkPixelExtent(viewsize[0], viewsize[1]);
374       // cerr << "W<0!!!!!!!!!!!!!" << endl;
375       return true;
376     }
377 
378     // to normalized device coordinates
379     ndcw = (ndcw == 0.0 ? 1.0 : 1.0 / ndcw);
380     ndcBBox[qq] *= ndcw;
381     ndcBBox[qq + 1] *= ndcw;
382     ndcBBox[qq + 2] *= ndcw;
383   }
384 
385   // compute screen extent only if the object
386   // is inside the view frustum.
387   if (VisibilityTest(ndcBBox))
388   {
389     // these bounds are visible. compute screen
390     // space exents
391     double vx = viewsize[0] - 1.0;
392     double vy = viewsize[1] - 1.0;
393     double vx2 = viewsize[0] * 0.5;
394     double vy2 = viewsize[1] * 0.5;
395     vtkBoundingBox box;
396     for (int q = 0; q < 8; ++q)
397     {
398       int qq = 3 * q;
399       double sx = (ndcBBox[qq] + 1.0) * vx2;
400       double sy = (ndcBBox[qq + 1] + 1.0) * vy2;
401       box.AddPoint(vtkMath::ClampValue(sx, 0.0, vx), vtkMath::ClampValue(sy, 0.0, vy), 0.0);
402     }
403     // to screen extent
404     const double* s0 = box.GetMinPoint();
405     const double* s1 = box.GetMaxPoint();
406     screenExt[0] = static_cast<int>(s0[0]);
407     screenExt[1] = static_cast<int>(s1[0]);
408     screenExt[2] = static_cast<int>(s0[1]);
409     screenExt[3] = static_cast<int>(s1[1]);
410     return true;
411   }
412 
413   // these bounds aren't visible
414   return false;
415 }
416 
417 // Description:
418 // Compute screen space extents for each block in the input
419 // dataset and for the entire dataset. Only visible blocks
420 // are used in the computations.
ProjectBounds(vtkRenderer * ren,vtkActor * actor,vtkDataObject * dobj,int viewsize[2],vtkPixelExtent & dataExt,std::deque<vtkPixelExtent> & blockExts)421 int vtkSurfaceLICHelper::ProjectBounds(vtkRenderer* ren, vtkActor* actor, vtkDataObject* dobj,
422   int viewsize[2], vtkPixelExtent& dataExt, std::deque<vtkPixelExtent>& blockExts)
423 {
424   // get the modelview projection matrix
425   vtkNew<vtkMatrix4x4> tmpMatrix;
426 
427   vtkOpenGLCamera* oglCam = vtkOpenGLCamera::SafeDownCast(ren->GetActiveCamera());
428   vtkMatrix4x4* wcdc;
429   vtkMatrix4x4* wcvc;
430   vtkMatrix3x3* norms;
431   vtkMatrix4x4* vcdc;
432   oglCam->GetKeyMatrices(ren, wcvc, norms, vcdc, wcdc);
433 
434   if (!actor->GetIsIdentity())
435   {
436     vtkMatrix4x4* mcwc;
437     vtkMatrix3x3* anorms;
438     ((vtkOpenGLActor*)actor)->GetKeyMatrices(mcwc, anorms);
439     vtkMatrix4x4::Multiply4x4(mcwc, wcdc, tmpMatrix);
440   }
441   else
442   {
443     tmpMatrix->DeepCopy(wcdc);
444   }
445   /*
446     for ( int c = 0; c < 4; c ++ )
447       {
448       for ( int r = 0; r < 4; r ++ )
449         {
450         PMV[c*4+r]
451           = P[idx(r,0)] * MV[idx(0,c)]
452           + P[idx(r,1)] * MV[idx(1,c)]
453           + P[idx(r,2)] * MV[idx(2,c)]
454           + P[idx(r,3)] * MV[idx(3,c)];
455         }
456       }
457   */
458   // dataset case
459   vtkDataSet* ds = dynamic_cast<vtkDataSet*>(dobj);
460   if (ds && ds->GetNumberOfCells())
461   {
462     double bounds[6];
463     ds->GetBounds(bounds);
464     if (vtkBoundingBox::IsValid(bounds) &&
465       this->ProjectBounds(tmpMatrix->Element[0], viewsize, bounds, dataExt))
466     {
467       // the dataset is visible
468       // add its extent
469       blockExts.push_back(dataExt);
470       return 1;
471     }
472     // cerr << "ds " << ds << " not visible " << endl;
473     return 0;
474   }
475   // composite dataset case
476   vtkCompositeDataSet* cd = dynamic_cast<vtkCompositeDataSet*>(dobj);
477   if (cd)
478   {
479     // process each block's bounds
480     vtkBoundingBox bbox;
481     vtkCompositeDataIterator* iter = cd->NewIterator();
482     for (iter->InitTraversal(); !iter->IsDoneWithTraversal(); iter->GoToNextItem())
483     {
484       ds = dynamic_cast<vtkDataSet*>(iter->GetCurrentDataObject());
485       if (ds && ds->GetNumberOfCells())
486       {
487         double bounds[6];
488         ds->GetBounds(bounds);
489         vtkPixelExtent screenExt;
490         if (vtkBoundingBox::IsValid(bounds) &&
491           this->ProjectBounds(tmpMatrix->Element[0], viewsize, bounds, screenExt))
492         {
493           // this block is visible
494           // save it's screen extent
495           // and accumulate its bounds
496           blockExts.push_back(screenExt);
497           bbox.AddBounds(bounds);
498         }
499         // else { cerr << "leaf " << ds << " not visible " << endl << endl;}
500       }
501     }
502     iter->Delete();
503     // process accumulated dataset bounds
504     double bounds[6];
505     bbox.GetBounds(bounds);
506     if (vtkBoundingBox::IsValid(bounds) &&
507       this->ProjectBounds(tmpMatrix->Element[0], viewsize, bounds, dataExt))
508     {
509       return 1;
510     }
511     return 0;
512   }
513   // cerr << "ds " << ds << " no cells " << endl;
514   return 0;
515 }
516 
517 // Description:
518 // Shrink an extent to tightly bound non-zero values
GetPixelBounds(float * rgba,int ni,vtkPixelExtent & ext)519 void vtkSurfaceLICHelper::GetPixelBounds(float* rgba, int ni, vtkPixelExtent& ext)
520 {
521   vtkPixelExtent text;
522   for (int j = ext[2]; j <= ext[3]; ++j)
523   {
524     for (int i = ext[0]; i <= ext[1]; ++i)
525     {
526       if (rgba[4 * (j * ni + i) + 3] > 0.0f)
527       {
528         text[0] = text[0] > i ? i : text[0];
529         text[1] = text[1] < i ? i : text[1];
530         text[2] = text[2] > j ? j : text[2];
531         text[3] = text[3] < j ? j : text[3];
532       }
533     }
534   }
535   ext = text;
536 }
537 
538 // Description:
539 // Shrink a set of extents to tightly bound non-zero values
540 // cull extent if it's empty
GetPixelBounds(float * rgba,int ni,std::deque<vtkPixelExtent> & blockExts)541 void vtkSurfaceLICHelper::GetPixelBounds(float* rgba, int ni, std::deque<vtkPixelExtent>& blockExts)
542 {
543   std::vector<vtkPixelExtent> tmpExts(blockExts.begin(), blockExts.end());
544   blockExts.clear();
545   size_t nBlocks = tmpExts.size();
546   for (size_t b = 0; b < nBlocks; ++b)
547   {
548     vtkPixelExtent& tmpExt = tmpExts[b];
549     GetPixelBounds(rgba, ni, tmpExt);
550     if (!tmpExt.Empty())
551     {
552       blockExts.push_back(tmpExt);
553     }
554   }
555 }
556