Visualization Library 2.0.0

A lightweight C++ OpenGL middleware for 2D/3D graphics

VL     Star     Watch     Fork     Issue

[Download] [Tutorials] [All Classes] [Grouped Classes]
SlicedVolume.cpp
Go to the documentation of this file.
1 /**************************************************************************************/
2 /* */
3 /* Visualization Library */
4 /* http://visualizationlibrary.org */
5 /* */
6 /* Copyright (c) 2005-2020, Michele Bosi */
7 /* All rights reserved. */
8 /* */
9 /* Redistribution and use in source and binary forms, with or without modification, */
10 /* are permitted provided that the following conditions are met: */
11 /* */
12 /* - Redistributions of source code must retain the above copyright notice, this */
13 /* list of conditions and the following disclaimer. */
14 /* */
15 /* - Redistributions in binary form must reproduce the above copyright notice, this */
16 /* list of conditions and the following disclaimer in the documentation and/or */
17 /* other materials provided with the distribution. */
18 /* */
19 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND */
20 /* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */
21 /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
22 /* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR */
23 /* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
24 /* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
25 /* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
26 /* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
27 /* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
28 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
29 /* */
30 /**************************************************************************************/
31 
33 #include <vlGraphics/GLSL.hpp>
34 #include <vlGraphics/Camera.hpp>
35 #include <vlCore/Time.hpp>
36 
37 using namespace vl;
38 
77 //-----------------------------------------------------------------------------
80 {
81  VL_DEBUG_SET_OBJECT_NAME()
82  mActor = NULL;
83  mSliceCount = 1024;
84  mGeometry = new Geometry;
85  mGeometry->setObjectName("vl::SlicedVolume");
86 
87  fvec3 texc[] =
88  {
89  fvec3(0,0,0), fvec3(1,0,0), fvec3(1,1,0), fvec3(0,1,0),
90  fvec3(0,0,1), fvec3(1,0,1), fvec3(1,1,1), fvec3(0,1,1)
91  };
92  memcpy(mTexCoord, texc, sizeof(texc));
93 }
94 //-----------------------------------------------------------------------------
103 void SlicedVolume::updateUniforms(Actor*actor, real, const Camera* camera, Renderable*, const Shader* shader)
104 {
105  // FIXME: move all computation to eye coordinates.
106 
107  const GLSLProgram* glsl = shader->getGLSLProgram();
108 
109  if (glsl->getUniformLocation("light_position[0]") != -1 && glsl->getUniformLocation("light_enable[0]") != -1)
110  {
111  // computes up to 4 light positions (in object space) and enables
112 
113  int light_enable[4] = { 0,0,0,0 };
114  fvec3 light_position[4];
115 
116  for(int i=0; i<4; ++i)
117  {
118  const Light* light = shader->getLight(i);
119  light_enable[i] = light != NULL;
120  if (light)
121  {
122  // light position following transform
123  if (light->boundTransform())
124  light_position[i] = (fmat4)light->boundTransform()->worldMatrix() * light->position().xyz();
125  // light position following camera
126  else
127  light_position[i] = ((fmat4)camera->modelingMatrix() * light->position()).xyz();
128 
129  // light position in object space
130  if (actor->transform())
131  light_position[i] = (fmat4)actor->transform()->worldMatrix().getInverse() * light_position[i];
132  }
133  }
134 
135  actor->gocUniform("light_position")->setUniform(4, light_position);
136  actor->gocUniform("light_enable")->setUniform1i(4, light_enable);
137  }
138 
139  if (glsl->getUniformLocation("eye_position") != -1)
140  {
141  // pass the eye position in object space
142 
143  // eye postion
144  fvec3 eye = (fvec3)camera->modelingMatrix().getT();
145  // world to object space
146  if (actor->transform())
147  eye = (fmat4)actor->transform()->worldMatrix().getInverse() * eye;
148  actor->gocUniform("eye_position")->setUniform(eye);
149  }
150 
152  //actor->gocUniform( "gradientDelta" )->setUniform( fvec3( 0.25f / tex->width(), 0.25f / tex->height(), 0.25f / tex->depth() ) );
153 }
154 //-----------------------------------------------------------------------------
155 namespace
156 {
157  class Edge
158  {
159  public:
160  int v0, v1, intersection, flags;
161  bool operator<(const Edge& other) const
162  {
163  return intersection > other.intersection;
164  }
165  };
166 }
167 //-----------------------------------------------------------------------------
169 {
170  actor->actorEventCallbacks()->push_back( this );
171  actor->setLod( 0, mGeometry.get() );
172 }
173 //-----------------------------------------------------------------------------
174 void SlicedVolume::onActorRenderStarted(Actor* actor, real clock, const Camera* camera, Renderable* rend, const Shader* shader, int pass)
175 {
176  if ( pass > 0 ) {
177  return;
178  }
179 
180  // setup uniform variables
181 
182  if (shader->getGLSLProgram()) {
183  updateUniforms(actor, clock, camera, rend, shader);
184  }
185 
186  // setup geometry: generate viewport aligned slices
187 
188  // skip generation is actor and camera did not move
189  fmat4 mat;
190  if (actor->transform())
191  mat = (fmat4)(camera->viewMatrix() * actor->transform()->worldMatrix());
192  else
193  mat = (fmat4)camera->viewMatrix();
194 
195  if (mCache == mat)
196  return;
197  else
198  mCache = mat;
199 
200  fmat4 imat = mat.getInverse();
201 
202  fvec3 cube_verts[] =
203  {
204  fvec3((float)box().minCorner().x(), (float)box().minCorner().y(), (float)box().minCorner().z()),
205  fvec3((float)box().maxCorner().x(), (float)box().minCorner().y(), (float)box().minCorner().z()),
206  fvec3((float)box().maxCorner().x(), (float)box().maxCorner().y(), (float)box().minCorner().z()),
207  fvec3((float)box().minCorner().x(), (float)box().maxCorner().y(), (float)box().minCorner().z()),
208  fvec3((float)box().minCorner().x(), (float)box().minCorner().y(), (float)box().maxCorner().z()),
209  fvec3((float)box().maxCorner().x(), (float)box().minCorner().y(), (float)box().maxCorner().z()),
210  fvec3((float)box().maxCorner().x(), (float)box().maxCorner().y(), (float)box().maxCorner().z()),
211  fvec3((float)box().minCorner().x(), (float)box().maxCorner().y(), (float)box().maxCorner().z())
212  };
213 
214  int min_idx = 0;
215  int max_idx = 0;
216  for(int i=0; i<8; ++i)
217  {
218  cube_verts[i] = mat * cube_verts[i];
219  if (fabs(cube_verts[i].z()) < fabs(cube_verts[min_idx].z())) min_idx = i;
220  if (fabs(cube_verts[i].z()) > fabs(cube_verts[max_idx].z())) max_idx = i;
221  }
222 
223  if (cube_verts[min_idx].z() > 0)
224  {
225  // fixme?
226  // the actor is not visible: remove the geometry or disable the actor?
227  // return;
228  }
229 
230  const int TOP = 1;
231  const int BOTTOM = 2;
232  const int LEFT = 4;
233  const int RIGHT = 8;
234  const int FRONT = 16;
235  const int BACK = 32;
236 
237  Edge edges[] =
238  {
239  {0,1,-1,FRONT |BOTTOM}, {1,2,-1,FRONT|RIGHT}, {2,3,-1,FRONT|TOP}, {3,0,-1,FRONT |LEFT},
240  {4,5,-1,BACK |BOTTOM}, {5,6,-1,BACK |RIGHT}, {6,7,-1,BACK |TOP}, {7,4,-1,BACK |LEFT},
241  {1,5,-1,BOTTOM|RIGHT}, {2,6,-1,TOP |RIGHT}, {3,7,-1,TOP |LEFT}, {0,4,-1,BOTTOM|LEFT}
242  };
243 
244  std::vector<fvec3> points;
245  std::vector<fvec3> points_t;
246  std::vector<fvec3> polygons;
247  std::vector<fvec3> polygons_t;
248 
249  int slice_count = sliceCount() ? sliceCount() : vl::max( camera->viewport()->width(), camera->viewport()->height() );
250 
251  polygons.reserve(slice_count*5);
252  polygons_t.reserve(slice_count*5);
253  float zrange = cube_verts[max_idx].z() - cube_verts[min_idx].z();
254  float zstep = zrange/(slice_count+1);
255  int vert_idx[12];
256  for(int islice=0; islice<slice_count; ++islice)
257  {
258  float z = cube_verts[max_idx].z() - zstep*(islice+1);
259  fvec3 plane_o(0,0,z);
260  fvec3 plane_n(0,0,1.0f);
261  points.clear();
262  points_t.clear();
263  for(int iedge=0; iedge<12; ++iedge)
264  {
265  edges[iedge].intersection = -1;
266  fvec3 vi = cube_verts[ edges[iedge].v0 ];
267  fvec3 eij = cube_verts[ edges[iedge].v1 ] - cube_verts[ edges[iedge].v0 ];
268  float denom = dot(plane_n,eij);
269  if (denom == 0)
270  continue;
271  float lambda = (z - dot(plane_n,vi))/denom;
272  if (lambda<0 || lambda>1)
273  continue;
274  fvec3 v = vi + eij*lambda;
275  edges[iedge].intersection = (int)points.size();
276  points.push_back(v);
277  fvec3 a = texCoords()[ edges[iedge].v0 ];
278  fvec3 b = texCoords()[ edges[iedge].v1 ] - texCoords()[ edges[iedge].v0 ];
279  fvec3 vt = a + b*lambda;
280  points_t.push_back(vt);
281  }
282  std::sort(edges, edges+12);
283  int vert_idx_c = 0;
284  for(int ie0=0; ie0<12-1; ++ie0)
285  {
286  if (edges[ie0].intersection == -1)
287  break;
288  vert_idx[vert_idx_c++] = edges[ie0].intersection;
289  for(int ie1=ie0+1; ie1<12; ++ie1)
290  {
291  if (edges[ie1].intersection == -1)
292  continue;
293  if( (edges[ie0].flags & edges[ie1].flags) )
294  {
295  Edge t = edges[ie0+1];
296  edges[ie0+1] = edges[ie1];
297  edges[ie1] = t;
298  break;
299  }
300  }
301  }
302  for(int vc=0; vc<vert_idx_c-2; ++vc)
303  {
304  polygons.push_back(imat*points [vert_idx[0]]);
305  polygons.push_back(imat*points [vert_idx[vc+1]]);
306  polygons.push_back(imat*points [vert_idx[vc+2]]);
307  polygons_t.push_back(points_t[vert_idx[0]]);
308  polygons_t.push_back(points_t[vert_idx[vc+1]]);
309  polygons_t.push_back(points_t[vert_idx[vc+2]]);
310  }
311  #ifndef NDEBUG
312  for(int ie0=0; ie0<12-1; ++ie0)
313  {
314  if (edges[ie0].intersection == -1)
315  break;
316  if (edges[ie0+1].intersection == -1)
317  break;
318  VL_CHECK(edges[ie0].flags & edges[ie0+1].flags)
319  }
320  #endif
321  }
322 
323  mGeometry->drawCalls().clear();
324  ref<DrawArrays> da = new DrawArrays(PT_TRIANGLES, 0, (int)polygons.size());
325  mGeometry->drawCalls().push_back( da.get() );
326  ref<ArrayFloat3> vertex_array = new ArrayFloat3;
327  ref<ArrayFloat3> texcoo_array = new ArrayFloat3;
328  vertex_array->resize(polygons.size());
329  texcoo_array->resize(polygons_t.size());
330  VL_CHECK((size_t)vertex_array->bufferObject()->bytesUsed() == sizeof(polygons [0])*polygons. size());
331  VL_CHECK((size_t)texcoo_array->bufferObject()->bytesUsed() == sizeof(polygons_t[0])*polygons_t.size());
332  memcpy(vertex_array->ptr(), &polygons [0], vertex_array->bufferObject()->bytesUsed());
333  memcpy(texcoo_array->ptr(), &polygons_t[0], texcoo_array->bufferObject()->bytesUsed());
334  mGeometry->setVertexArray(vertex_array.get());
335  mGeometry->setTexCoordArray(0,texcoo_array.get());
336 
337  mGeometry->setDisplayListDirty(true);
338  mGeometry->setBufferObjectDirty(true);
339 
340  // fixme:
341  // it seems we have some problems with camera clipping/culling when the camera is close to the volume: the slices disappear or degenerate.
342  // it does not seem to depend from camera clipping plane optimization.
343 }
344 //-----------------------------------------------------------------------------
346 {
347  if (!img_size.x() || !img_size.y() || !img_size.z())
348  {
349  Log::error("SlicedVolume::generateTextureCoordinates(): failed! The img_size passed does not represent a 3D image.\n");
350  return;
351  }
352 
353  float dx = 0.5f/img_size.x();
354  float dy = 0.5f/img_size.y();
355  float dz = 0.5f/img_size.z();
356 
357  float x0 = 0.0f + dx;
358  float x1 = 1.0f - dx;
359  float y0 = 0.0f + dy;
360  float y1 = 1.0f - dy;
361  float z0 = 0.0f + dz;
362  float z1 = 1.0f - dz;
363 
364  fvec3 texc[] =
365  {
366  fvec3(x0,y0,z0), fvec3(x1,y0,z0), fvec3(x1,y1,z0), fvec3(x0,y1,z0),
367  fvec3(x0,y0,z1), fvec3(x1,y0,z1), fvec3(x1,y1,z1), fvec3(x0,y1,z1),
368  };
369  memcpy(mTexCoord, texc, sizeof(texc));
370 }
371 //-----------------------------------------------------------------------------
372 void SlicedVolume::generateTextureCoordinates(const ivec3& img_size, const ivec3& min_corner, const ivec3& max_corner)
373 {
374  if (!img_size.x() || !img_size.y() || !img_size.z())
375  {
376  Log::error("SlicedVolume::setDisplayRegion(): failed! The size passed does not represent a 3D image.\n");
377  return;
378  }
379 
380  float dx = 0.5f/img_size.x();
381  float dy = 0.5f/img_size.y();
382  float dz = 0.5f/img_size.z();
383 
384  float x0 = min_corner.x()/(float)img_size.x() + dx;
385  float x1 = max_corner.x()/(float)img_size.x() - dx;
386  float y0 = min_corner.y()/(float)img_size.y() + dy;
387  float y1 = max_corner.y()/(float)img_size.y() - dy;
388  float z0 = min_corner.z()/(float)img_size.z() + dz;
389  float z1 = max_corner.z()/(float)img_size.z() - dz;
390 
391  fvec3 texc[] =
392  {
393  fvec3(x0,y0,z0), fvec3(x1,y0,z0), fvec3(x1,y1,z0), fvec3(x0,y1,z0),
394  fvec3(x0,y0,z1), fvec3(x1,y0,z1), fvec3(x1,y1,z1), fvec3(x0,y1,z1)
395  };
396  memcpy(mTexCoord, texc, sizeof(texc));
397 }
398 //-----------------------------------------------------------------------------
400 {
401  mBox = box;
402  mCache.fill(0);
403  mGeometry->setBoundingBox( box );
404  mGeometry->setBoundingSphere( box );
405  mGeometry->setBoundsDirty(true);
406 }
407 //-----------------------------------------------------------------------------
Associates a Renderable object to an Effect and Transform.
Definition: Actor.hpp:130
const mat4 & viewMatrix() const
Returns the Camera&#39;s view matrix (inverse of the modeling matrix).
Definition: Camera.hpp:161
void setUniform1i(int count, const int *value)
Definition: Uniform.hpp:99
Vector3< float > fvec3
A 3 components vector with float precision.
Definition: Vector3.hpp:252
Transform * transform()
Returns the Transform bound tho an Actor.
Definition: Actor.hpp:190
const T * get() const
Definition: Object.hpp:128
int getUniformLocation(const char *name) const
Returns the binding index of the given uniform.
Definition: GLSL.hpp:405
const T_Scalar & z() const
Definition: Vector3.hpp:91
ref< Geometry > mGeometry
int sliceCount() const
Returns the number of slices used to render the volume.
static void error(const String &message)
Use this function to provide information about run-time errors: file not found, out of memory...
Definition: Log.cpp:165
Vector3< T_Scalar > xyz() const
Definition: Vector4.hpp:131
void generateTextureCoordinates(const ivec3 &size)
Generates a default set of texture coordinates for the 8 box corners of the volume based on the given...
Wraps a GLSL program to which you can bind vertex, fragment and geometry shaders. ...
Definition: GLSL.hpp:233
const Collection< ActorEventCallback > * actorEventCallbacks() const
Returns the list of ActorEventCallback bound to an Actor.
Definition: Actor.hpp:370
void resize(size_t dim)
Definition: Array.hpp:233
size_t bytesUsed() const
Definition: Buffer.hpp:197
SlicedVolume()
Constructor.
const Light * getLight(int light_index) const
Definition: Shader.cpp:161
The Geometry class is a Renderable that implements a polygonal mesh made of polygons, lines and points.
Definition: Geometry.hpp:66
Viewport * viewport()
The viewport bound to a camera.
Definition: Camera.hpp:141
Visualization Library main namespace.
const BufferObject * bufferObject() const
Definition: Array.hpp:97
const fvec3 * texCoords() const
Returns the texture coordinates assigned to each of the 8 box corners of the volume.
float dot(float a, float b)
Definition: glsl_math.hpp:1111
const unsigned char * ptr() const
Returns the pointer to the first element of the local buffer. Equivalent to bufferObject()->ptr() ...
Definition: Array.hpp:103
T_Scalar getInverse(Matrix4 &dest) const
Definition: Matrix4.hpp:1009
int height() const
Definition: Viewport.hpp:71
int width() const
Definition: Viewport.hpp:69
The AABB class implements an axis-aligned bounding box using vl::real precision.
Definition: AABB.hpp:44
float max(float a, float b)
Definition: Vector2.hpp:311
void bindActor(Actor *)
Binds a SlicedVolume to an Actor so that the SlicedVolume can generate the viewport aligned slices&#39; g...
An abstract class that represents all the objects that can be rendered.
Definition: Renderable.hpp:58
Matrix4 & fill(T_Scalar val)
Definition: Matrix4.hpp:89
const mat4 & modelingMatrix() const
Returns the Camera&#39;s modelingMatrix() (inverse of the view matrix).
Definition: Camera.hpp:169
Transform * boundTransform()
Definition: Light.cpp:119
const T_Scalar & y() const
Definition: Vector3.hpp:90
void setBox(const AABB &box)
Defines the dimensions of the box enclosing the volume.
#define NULL
Definition: OpenGLDefs.hpp:81
const mat4 & worldMatrix() const
Returns the world matrix used for rendering.
Definition: Transform.hpp:168
Manages most of the OpenGL rendering states responsible of the final aspect of the rendered objects...
Definition: Shader.hpp:1830
Uniform * gocUniform(const char *name)
Equivalent to getUniformSet()->getUniform(name, get_mode)
Definition: Actor.cpp:131
const GLSLProgram * getGLSLProgram() const
Returns a GLSLProgram if it exists or NULL otherwise.
Definition: Shader.cpp:59
Vector3< T_Scalar > getT() const
Definition: Matrix4.hpp:131
const fvec4 & position() const
The position or direction of a light.
Definition: Light.hpp:79
void setLod(int lod_index, Renderable *renderable)
Sets the Renderable object representing the LOD level specifed by lod_index.
Definition: Actor.hpp:159
void setUniform(int count, const int *value)
Definition: Uniform.hpp:145
Wraps the OpenGL function glLight().
Definition: Light.hpp:51
const T_Scalar & x() const
Definition: Vector3.hpp:89
The ref<> class is used to reference-count an Object.
Definition: Object.hpp:55
Matrix4< float > fmat4
A 4x4 matrix using float precision.
Definition: Matrix4.hpp:1229
An array of vl::fvec3.
Definition: Array.hpp:414
Represents a virtual camera defining, among other things, the point of view from which scenes can be ...
Definition: Camera.hpp:50
void onActorRenderStarted(Actor *actor, real frame_clock, const Camera *cam, Renderable *renderable, const Shader *shader, int pass)
Event generated just before an Actor is rendered but after the render states are ready and setup...
const AABB & box() const
The dimensions of the box enclosing the volume.
Wraps the OpenGL function glDrawArrays().
Definition: DrawArrays.hpp:57
#define VL_CHECK(expr)
Definition: checks.hpp:73
const Actor * actor() const
Returns the currently bound actor.
virtual void updateUniforms(Actor *actor, real clock, const Camera *camera, Renderable *rend, const Shader *shader)
Updates the uniforms used by the GLSLProgram to render the volume each time the onActorRenderStarted(...