1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkOpenGLImageAlgorithmHelper.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 
16 #include "vtkOpenGLImageAlgorithmHelper.h"
17 #include "vtkObjectFactory.h"
18 #include "vtkTextureObject.h"
19 #include "vtkOpenGLRenderWindow.h"
20 #include "vtkDataArray.h"
21 #include "vtkImageData.h"
22 #include "vtkNew.h"
23 #include "vtkOpenGLFramebufferObject.h"
24 #include "vtkOpenGLShaderCache.h"
25 #include "vtkOpenGLState.h"
26 #include "vtk_glew.h"
27 #include "vtkPixelTransfer.h"
28 #include "vtkPointData.h"
29 #include "vtkPixelBufferObject.h"
30 #include "vtkShaderProgram.h"
31 #include "vtkOpenGLVertexArrayObject.h"
32 
33 vtkStandardNewMacro(vtkOpenGLImageAlgorithmHelper);
34 
35 // ----------------------------------------------------------------------------
vtkOpenGLImageAlgorithmHelper()36 vtkOpenGLImageAlgorithmHelper::vtkOpenGLImageAlgorithmHelper()
37 {
38   this->RenderWindow = nullptr;
39 }
40 
41 // ----------------------------------------------------------------------------
~vtkOpenGLImageAlgorithmHelper()42 vtkOpenGLImageAlgorithmHelper::~vtkOpenGLImageAlgorithmHelper()
43 {
44   this->SetRenderWindow(nullptr);
45 }
46 
SetRenderWindow(vtkRenderWindow * renWin)47 void vtkOpenGLImageAlgorithmHelper::SetRenderWindow(vtkRenderWindow *renWin)
48 {
49   if (renWin == this->RenderWindow.GetPointer())
50   {
51     return;
52   }
53 
54   vtkOpenGLRenderWindow *orw  = nullptr;
55   if (renWin)
56   {
57     orw = vtkOpenGLRenderWindow::SafeDownCast(renWin);
58   }
59 
60   this->RenderWindow = orw;
61   this->Modified();
62 }
63 
Execute(vtkOpenGLImageAlgorithmCallback * cb,vtkImageData * inImage,vtkDataArray * inArray,vtkImageData * outImage,int outExt[6],const char * vertexCode,const char * fragmentCode,const char * geometryCode)64 void vtkOpenGLImageAlgorithmHelper::Execute(
65   vtkOpenGLImageAlgorithmCallback *cb,
66   vtkImageData *inImage, vtkDataArray *inArray,
67   vtkImageData *outImage, int outExt[6],
68   const char *vertexCode,
69   const char *fragmentCode,
70   const char *geometryCode
71   )
72 {
73   // make sure it is initialized
74   if (!this->RenderWindow)
75   {
76     this->SetRenderWindow(vtkRenderWindow::New());
77     this->RenderWindow->SetOffScreenRendering(true);
78     this->RenderWindow->UnRegister(this);
79   }
80   this->RenderWindow->Initialize();
81 
82   // Is it a 2D or 3D image
83   int dims[3];
84   inImage->GetDimensions(dims);
85   int dimensions = 0;
86   for (int i = 0; i < 3; i ++)
87   {
88     if (dims[i] > 1)
89     {
90       dimensions++;
91     }
92   }
93 
94   // no 1D or 2D support yet
95   if (dimensions < 3)
96   {
97     vtkErrorMacro("no 1D or 2D processing support yet");
98     return;
99   }
100 
101   // send vector data to a texture
102   int inputExt[6];
103   inImage->GetExtent(inputExt);
104   void *inPtr = inArray->GetVoidPointer(0);
105 
106   // could do shortcut here if the input volume is
107   // exactly what we want (updateExtent == wholeExtent)
108   // vtkIdType incX, incY, incZ;
109   // inImage->GetContinuousIncrements(inArray, extent, incX, incY, incZ);
110   //  tmpImage->CopyAndCastFrom(inImage, inUpdateExtent)
111 
112   vtkNew<vtkTextureObject> inputTex;
113   inputTex->SetContext(this->RenderWindow);
114   inputTex->Create3DFromRaw(
115     dims[0], dims[1], dims[2],
116     inArray->GetNumberOfComponents(),
117     inArray->GetDataType(), inPtr);
118 
119   float shift = 0.0;
120   float scale = 1.0;
121   inputTex->GetShiftAndScale(shift, scale);
122 
123   // now create the framebuffer for the output
124   int outDims[3];
125   outDims[0] = outExt[1] - outExt[0] + 1;
126   outDims[1] = outExt[3] - outExt[2] + 1;
127   outDims[2] = outExt[5] - outExt[4] + 1;
128 
129   vtkNew<vtkTextureObject> outputTex;
130   outputTex->SetContext(this->RenderWindow);
131 
132   vtkNew<vtkOpenGLFramebufferObject> fbo;
133   fbo->SetContext(this->RenderWindow);
134   vtkOpenGLState *ostate = this->RenderWindow->GetState();
135 
136   outputTex->Create2D(outDims[0], outDims[1], 4, VTK_FLOAT, false);
137   fbo->AddColorAttachment(fbo->GetDrawMode(), 0, outputTex);
138 
139   // because the same FBO can be used in another pass but with several color
140   // buffers, force this pass to use 1, to avoid side effects from the
141   // render of the previous frame.
142   fbo->ActivateDrawBuffer(0);
143 
144   fbo->StartNonOrtho(outDims[0], outDims[1]);
145   ostate->vtkglViewport(0, 0, outDims[0], outDims[1]);
146   ostate->vtkglScissor(0, 0, outDims[0], outDims[1]);
147   ostate->vtkglDisable(GL_DEPTH_TEST);
148 
149   vtkShaderProgram *prog =
150     this->RenderWindow->GetShaderCache()->ReadyShaderProgram(
151       vertexCode, fragmentCode, geometryCode);
152   if (prog != this->Quad.Program)
153   {
154     this->Quad.Program = prog;
155     this->Quad.VAO->ShaderProgramChanged();
156   }
157   cb->InitializeShaderUniforms(prog);
158 
159   inputTex->Activate();
160   int inputTexId = inputTex->GetTextureUnit();
161   this->Quad.Program->SetUniformi("inputTex1", inputTexId);
162   // shift and scale to get the data backing into its original units
163   this->Quad.Program->SetUniformf("inputShift", shift);
164   this->Quad.Program->SetUniformf("inputScale", scale);
165   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
166   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
167 
168   // for each zslice in the output
169   vtkPixelExtent outputPixelExt(outExt);
170   for (int i = outExt[4]; i <= outExt[5]; i++)
171   {
172     cb->UpdateShaderUniforms(prog, i);
173     this->Quad.Program->SetUniformf("zPos", (i - outExt[4] + 0.5) / (outDims[2]));
174 
175     fbo->RenderQuad(
176       0, outDims[0] - 1,
177       0, outDims[1] - 1,
178       this->Quad.Program, this->Quad.VAO);
179 
180     vtkPixelBufferObject *outPBO = outputTex->Download();
181 
182     vtkPixelTransfer::Blit<float, double>(
183       outputPixelExt,
184       outputPixelExt,
185       outputPixelExt,
186       outputPixelExt,
187       4,
188       (float*)outPBO->MapPackedBuffer(),
189       outImage->GetPointData()->GetScalars()->GetNumberOfComponents(),
190       static_cast<double *>(outImage->GetScalarPointer(outExt[0], outExt[2], i)));
191 
192     outPBO->UnmapPackedBuffer();
193     outPBO->Delete();
194   }
195 }
196 
197 // ----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)198 void vtkOpenGLImageAlgorithmHelper::PrintSelf(ostream& os, vtkIndent indent)
199 {
200   this->Superclass::PrintSelf(os,indent);
201 
202   os << indent << "RenderWindow:";
203   if(this->RenderWindow != nullptr)
204   {
205     this->RenderWindow->PrintSelf(os,indent);
206   }
207   else
208   {
209     os << "(none)" <<endl;
210   }
211 }
212