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