/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkProjectedTetrahedraMapper.cxx,v $ 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 "vtkProjectedTetrahedraMapper.h" #include "vtkObjectFactory.h" #include "vtkVisibilitySort.h" #include "vtkUnsignedCharArray.h" #include "vtkDoubleArray.h" #include "vtkFloatArray.h" #include "vtkIdTypeArray.h" #include "vtkCellCenterDepthSort.h" #include "vtkUnstructuredGrid.h" #include "vtkCellArray.h" #include "vtkVolume.h" #include "vtkVolumeProperty.h" #include "vtkPointData.h" #include "vtkCellData.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkCamera.h" #include "vtkMath.h" #include "vtkTimerLog.h" #include "vtkMatrix4x4.h" #include "vtkPiecewiseFunction.h" #include "vtkColorTransferFunction.h" #include "vtkGarbageCollector.h" #include "vtkOpenGL.h" #include #include //----------------------------------------------------------------------------- // static int tet_faces[4][3] = { {1,2,3}, {2,0,3}, {0,1,3}, {0,2,1} }; static int tet_edges[6][2] = { {0,1}, {1,2}, {2,0}, {0,3}, {1,3}, {2,3} }; //----------------------------------------------------------------------------- vtkCxxRevisionMacro(vtkProjectedTetrahedraMapper, "$Revision: 1.2 $"); vtkStandardNewMacro(vtkProjectedTetrahedraMapper); vtkCxxSetObjectMacro(vtkProjectedTetrahedraMapper, VisibilitySort, vtkVisibilitySort); vtkProjectedTetrahedraMapper::vtkProjectedTetrahedraMapper() { this->TransformedPoints = vtkFloatArray::New(); this->Colors = vtkUnsignedCharArray::New(); this->VisibilitySort = vtkCellCenterDepthSort::New(); this->ScalarMode = VTK_SCALAR_MODE_DEFAULT; this->ArrayName = new char[1]; this->ArrayName[0] = '\0'; this->ArrayId = -1; this->ArrayAccessMode = VTK_GET_ARRAY_BY_ID; this->LastVolume = NULL; this->OpacityTexture = 0; this->MaxCellSize = 0; this->GaveError = 0; } vtkProjectedTetrahedraMapper::~vtkProjectedTetrahedraMapper() { this->ReleaseGraphicsResources(NULL); this->TransformedPoints->Delete(); this->Colors->Delete(); if (this->VisibilitySort) this->VisibilitySort->UnRegister(this); delete[] this->ArrayName; } void vtkProjectedTetrahedraMapper::PrintSelf(ostream &os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "VisibilitySort: " << this->VisibilitySort << endl; os << indent << "ScalarMode: " << this->GetScalarModeAsString() << endl; if (this->ArrayAccessMode == VTK_GET_ARRAY_BY_ID) { os << indent << "ArrayId: " << this->ArrayId << endl; } else { os << indent << "ArrayName: " << this->ArrayName << endl; } } //----------------------------------------------------------------------------- void vtkProjectedTetrahedraMapper::ReleaseGraphicsResources(vtkWindow *win) { if (this->OpacityTexture) { GLuint texid = this->OpacityTexture; glDeleteTextures(1, &texid); this->OpacityTexture = 0; } this->Superclass::ReleaseGraphicsResources(win); } //----------------------------------------------------------------------------- void vtkProjectedTetrahedraMapper::ReportReferences(vtkGarbageCollector *collector) { this->Superclass::ReportReferences(collector); vtkGarbageCollectorReport(collector, this->VisibilitySort, "VisibilitySort"); } //----------------------------------------------------------------------------- void vtkProjectedTetrahedraMapper::SelectScalarArray(int arrayNum) { if ( (this->ArrayId == arrayNum) && (this->ArrayAccessMode == VTK_GET_ARRAY_BY_ID) ) { return; } this->Modified(); this->ArrayId = arrayNum; this->ArrayAccessMode = VTK_GET_ARRAY_BY_ID; } void vtkProjectedTetrahedraMapper::SelectScalarArray(const char *arrayName) { if ( !arrayName || ( (strcmp(this->ArrayName, arrayName) == 0) && (this->ArrayAccessMode == VTK_GET_ARRAY_BY_ID) ) ) { return; } this->Modified(); delete[] this->ArrayName; this->ArrayName = new char[strlen(arrayName) + 1]; strcpy(this->ArrayName, arrayName); this->ArrayAccessMode = VTK_GET_ARRAY_BY_NAME; } //----------------------------------------------------------------------------- // Return the method for obtaining scalar data. const char *vtkProjectedTetrahedraMapper::GetScalarModeAsString(void) { if ( this->ScalarMode == VTK_SCALAR_MODE_USE_CELL_DATA ) { return "UseCellData"; } else if ( this->ScalarMode == VTK_SCALAR_MODE_USE_POINT_DATA ) { return "UsePointData"; } else if ( this->ScalarMode == VTK_SCALAR_MODE_USE_POINT_FIELD_DATA ) { return "UsePointFieldData"; } else if ( this->ScalarMode == VTK_SCALAR_MODE_USE_CELL_FIELD_DATA ) { return "UseCellFieldData"; } else { return "Default"; } } //----------------------------------------------------------------------------- // Given a unit opacity, converts it to an opacity for the given depth. // Note that opacity is not the same as the attenuation. Specifically, // opacity = 1 - exp(-attenuation) // attenuation = -ln(1 - opacity) // The correction for opacity is simply depth * attenuation. Therefore, // the correction for opacity is: // 1 - exp(-depth * (-ln(1 - opacity))) // which resolves to // 1 - (1 - opacity)^depth // static inline float CorrectOpacityForDepth(float opacity, float depth) // { // return (1 - (float)pow(1 - opacity, depth)); // } void vtkProjectedTetrahedraMapper::Render(vtkRenderer *renderer, vtkVolume *volume) { vtkUnstructuredGrid *input = this->GetInput(); float last_max_cell_size = this->MaxCellSize; // Check to see if input changed. if ( (this->InputAnalyzedTime < this->MTime) || (this->InputAnalyzedTime < input->GetMTime()) ) { this->GaveError = 0; float max_cell_size2 = 0; vtkCellArray *cells = input->GetCells(); if (!cells) { // Apparently, the input has no cells. Just do nothing. return; } vtkIdType npts, *pts; cells->InitTraversal(); for (vtkIdType i = 0; cells->GetNextCell(npts, pts); i++) { int j; if (npts != 4) { if (!this->GaveError) { vtkErrorMacro("Encountered non-tetrahedra cell!"); this->GaveError = 1; } continue; } for (j = 0; j < 6; j++) { 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) max_cell_size2 = size2; } } this->MaxCellSize = (float)sqrt(max_cell_size2); this->InputAnalyzedTime.Modified(); } if (renderer->GetRenderWindow()->CheckAbortStatus() || this->GaveError) { return; } // Check to see if we need to rebuild opacity texture. if ( !this->OpacityTexture || (last_max_cell_size != this->MaxCellSize) || (this->LastVolume != volume) || (this->OpacityTextureTime < volume->GetMTime()) || (this->OpacityTextureTime < volume->GetProperty()->GetMTime()) ) { if (!this->OpacityTexture) { GLuint texid; glGenTextures(1, &texid); this->OpacityTexture = texid; } glBindTexture(GL_TEXTURE_2D, this->OpacityTexture); float unit_distance = volume->GetProperty()->GetScalarOpacityUnitDistance(); #define TEXRES 258 float *texture = new float[TEXRES*TEXRES]; for (int depthi = 0; depthi < TEXRES; depthi++) { if (renderer->GetRenderWindow()->CheckAbortStatus()) { delete[] texture; return; } float depth = depthi*this->MaxCellSize/(TEXRES); // for (int opacityi = 0; opacityi < TEXRES; opacityi++) // { // float opacity = (float)opacityi/(TEXRES-1); // float alpha = CorrectOpacityForDepth(opacity, depth/unit_distance); // texture[(depthi*TEXRES + opacityi)] = alpha; // } for (int attenuationi = 0; attenuationi < TEXRES; attenuationi++) { float attenuation = (float)attenuationi/(TEXRES); float alpha = 1 - (float)exp(-attenuation*depth/unit_distance); texture[(depthi*TEXRES + attenuationi)] = alpha; } } glTexImage2D(GL_TEXTURE_2D, 0, GL_INTENSITY, TEXRES, TEXRES, 1, GL_RED, GL_FLOAT, texture); delete[] texture; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glBindTexture(GL_TEXTURE_2D, 0); this->OpacityTextureTime.Modified(); } if (renderer->GetRenderWindow()->CheckAbortStatus()) { return; } // Check to see if we need to remap colors. if ( (this->ColorsMappedTime < this->MTime) || (this->ColorsMappedTime < input->GetMTime()) || (this->LastVolume != volume) || (this->ColorsMappedTime < volume->GetMTime()) || (this->ColorsMappedTime < volume->GetProperty()->GetMTime()) ) { vtkDataArray *scalars = this->GetScalars(input, this->ScalarMode, this->ArrayAccessMode, this->ArrayId, this->ArrayName, this->UsingCellColors); if (!scalars) { vtkErrorMacro(<< "Can't use projected tetrahedra without scalars!"); return; } vtkProjectedTetrahedraMapper::MapScalarsToColors(this->Colors, volume, scalars); this->ColorsMappedTime.Modified(); this->LastVolume = volume; } if (renderer->GetRenderWindow()->CheckAbortStatus()) { return; } this->Timer->StartTimer(); this->ProjectTetrahedra(renderer, volume); this->Timer->StopTimer(); this->TimeToDraw = this->Timer->GetElapsedTime(); } //----------------------------------------------------------------------------- template void vtkProjectedTetrahedraMapperTransformPoints(const point_type *in_points, vtkIdType num_points, const float projection_mat[16], const float modelview_mat[16], float *out_points); static inline float 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) { float depth = linear_depth_correction*(z1 - z2); if (depth < 0) depth = -depth; return depth; } else { float eye1[3], eye2[3], invw; invw = 1/( inverse_projection_mat[ 3]*x + inverse_projection_mat[ 7]*y + inverse_projection_mat[11]*z1 + inverse_projection_mat[15] ); eye1[0] = invw*( inverse_projection_mat[ 0]*x + inverse_projection_mat[ 4]*y + inverse_projection_mat[ 8]*z1 + inverse_projection_mat[12] ); eye1[1] = invw*( inverse_projection_mat[ 1]*x + inverse_projection_mat[ 5]*y + inverse_projection_mat[ 9]*z1 + inverse_projection_mat[13] ); eye1[2] = invw*( inverse_projection_mat[ 2]*x + inverse_projection_mat[ 6]*y + inverse_projection_mat[10]*z1 + inverse_projection_mat[14] ); invw = 1/( inverse_projection_mat[ 3]*x + inverse_projection_mat[ 7]*y + inverse_projection_mat[11]*z2 + inverse_projection_mat[15] ); eye2[0] = invw*( inverse_projection_mat[ 0]*x + inverse_projection_mat[ 4]*y + inverse_projection_mat[ 8]*z2 + inverse_projection_mat[12] ); eye2[1] = invw*( inverse_projection_mat[ 1]*x + inverse_projection_mat[ 5]*y + inverse_projection_mat[ 9]*z2 + inverse_projection_mat[13] ); eye2[2] = invw*( inverse_projection_mat[ 2]*x + inverse_projection_mat[ 6]*y + inverse_projection_mat[10]*z2 + inverse_projection_mat[14] ); return (float)sqrt(vtkMath::Distance2BetweenPoints(eye1, eye2)); } } void vtkProjectedTetrahedraMapper::ProjectTetrahedra(vtkRenderer *renderer, vtkVolume *volume) { vtkUnstructuredGrid *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()) { return; } float projection_mat[16]; float modelview_mat[16]; glGetFloatv(GL_PROJECTION_MATRIX, projection_mat); glGetFloatv(GL_MODELVIEW_MATRIX, modelview_mat); // 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. vtkstd::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. vtkstd::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) { 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)); } // Transform all the points. vtkIdType num_points = input->GetNumberOfPoints(); this->TransformedPoints->SetNumberOfComponents(3); this->TransformedPoints->SetNumberOfTuples(num_points); float *points = this->TransformedPoints->GetPointer(0); switch (input->GetPoints()->GetDataType()) { vtkTemplateMacro (vtkProjectedTetrahedraMapperTransformPoints( (const VTK_TT *)input->GetPoints()->GetVoidPointer(0), num_points, projection_mat, modelview_mat, points)); } if (renderer->GetRenderWindow()->CheckAbortStatus()) { return; } glDisable(GL_LIGHTING); glDepthMask(GL_FALSE); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, this->OpacityTexture); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // Establish vertex arrays. float tet_points[5*3]; glVertexPointer(3, GL_FLOAT, 0, tet_points); glEnableClientState(GL_VERTEX_ARRAY); unsigned char tet_colors[5*3]; glColorPointer(3, GL_UNSIGNED_BYTE, 0, tet_colors); glEnableClientState(GL_COLOR_ARRAY); float tet_texcoords[5*2]; glTexCoordPointer(2, GL_FLOAT, 0, tet_texcoords); glEnableClientState(GL_TEXTURE_COORD_ARRAY); // Since we had to transform the points on the CPU, replace the OpenGL // transforms with the identity matrix. glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); unsigned char *colors = this->Colors->GetPointer(0); vtkIdType *cells = input->GetCells()->GetPointer(); vtkIdType totalnumcells = input->GetNumberOfCells(); vtkIdType numcellsrendered = 0; // Let's do it! for (vtkIdTypeArray *sorted_cell_ids = this->VisibilitySort->GetNextCells(); sorted_cell_ids != NULL; sorted_cell_ids = this->VisibilitySort->GetNextCells()) { this->UpdateProgress((double)numcellsrendered/totalnumcells); if (renderer->GetRenderWindow()->CheckAbortStatus()) { break; } vtkIdType *cell_ids = sorted_cell_ids->GetPointer(0); vtkIdType num_cell_ids = sorted_cell_ids->GetNumberOfTuples(); for (vtkIdType i = 0; i < num_cell_ids; i++) { vtkIdType cell = cell_ids[i]; int j; // Get the data for the tetrahedra. for (j = 0; j < 4; j++) { // Assuming we only have tetrahedra, each entry in cells has 5 // components. const float *p = points + 3*cells[5*cell + j + 1]; 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) { c = colors + 4*cell; } else { c = colors + 4*cells[5*cell + j + 1]; } 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] = (float)c[3]/255; tet_texcoords[j*2 + 1] = 0; } // 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. { segment1[0] = 0; segment1[1] = 3; segment2[0] = 1; segment2[1] = 2; } else if (face_dir1 * face_dir3 >= 0) { segment1[0] = 0; segment1[1] = 2; segment2[0] = 1; segment2[1] = 3; } else // Unless the tet is degenerate, face_dir2*face_dir3 >= 0 { segment1[0] = 0; segment1[1] = 1; segment2[0] = 2; segment2[1] = 3; } #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 // away the z value), we have two equations and two unkowns. The // following are the solutions for alpha and beta. float alpha = (B[1]*C[0]-B[0]*C[1])/(A[0]*B[1]-A[1]*B[0]); float beta = (A[1]*C[0]-A[0]*C[1])/(A[0]*B[1]-A[1]*B[0]); if ((alpha >= 0) && (alpha <= 1)) { // 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 = 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] = (unsigned char)(0.5f*( C1[0] + alpha*(C2[0]-C1[0]) + C3[0] + beta*(C4[0]-C3[0]) )); tet_colors[3*4 + 1] = (unsigned char)(0.5f*( C1[1] + alpha*(C2[1]-C1[1]) + C3[1] + beta*(C4[1]-C3[1]) )); tet_colors[3*4 + 2] = (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. tet_texcoords[2*4 + 1] = depth/this->MaxCellSize; // Establish the order in which the points should be rendered. unsigned char gl_indices[6]; gl_indices[0] = 4; gl_indices[1] = segment1[0]; gl_indices[2] = segment2[0]; gl_indices[3] = segment1[1]; gl_indices[4] = segment2[1]; gl_indices[5] = segment1[0]; // Render glDrawElements(GL_TRIANGLE_FAN, 6, GL_UNSIGNED_BYTE, gl_indices); } else { // The two segments do not intersect. This corresponds to class 1 // in Shirley and Tuchman. if (alpha <= 0) { // 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. vtkstd::swap(segment1[0], segment1[1]); alpha = 1 - alpha; } // 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++) { 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])); } // 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. T2[1] = depth/this->MaxCellSize; // Establish the order in which the points should be rendered. unsigned char gl_indices[5]; gl_indices[0] = segment1[1]; gl_indices[1] = segment1[0]; gl_indices[2] = segment2[0]; gl_indices[3] = segment2[1]; gl_indices[4] = segment1[0]; // Render glDrawElements(GL_TRIANGLE_FAN, 5, GL_UNSIGNED_BYTE, gl_indices); } } numcellsrendered += num_cell_ids; } // Restore OpenGL state. glMatrixMode(GL_PROJECTION); glLoadMatrixf(projection_mat); glMatrixMode(GL_MODELVIEW); glLoadMatrixf(modelview_mat); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); glDepthMask(GL_TRUE); glEnable(GL_LIGHTING); this->UpdateProgress(1.0); } //----------------------------------------------------------------------------- template void vtkProjectedTetrahedraMapperTransformPoints(const point_type *in_points, vtkIdType num_points, const float projection_mat[16], const float modelview_mat[16], float *out_points) { float mat[16]; int row, col; vtkIdType i; const point_type *in_p; float *out_p; // Combine two transforms into one transform. for (col = 0; col < 4; col++) { for (row = 0; row < 4; row++) { mat[col*4+row] = ( projection_mat[0*4+row]*modelview_mat[col*4+0] + projection_mat[1*4+row]*modelview_mat[col*4+1] + projection_mat[2*4+row]*modelview_mat[col*4+2] + projection_mat[3*4+row]*modelview_mat[col*4+3]); } } // Transform all points. for (i = 0, in_p = in_points, out_p = out_points; i < num_points; i++, in_p += 3, out_p += 3) { for (row = 0; row < 3; row++) { out_p[row] = ( mat[0*4+row]*in_p[0] + mat[1*4+row]*in_p[1] + mat[2*4+row]*in_p[2] + mat[3*4+row]); } } // Check to see if we need to divide by w. if ( (mat[0*4+3] != 0) || (mat[1*4+3] != 0) || (mat[0*4+3] != 0) || (mat[1*4+3] != 1) ) { for (i = 0, in_p = in_points, out_p = out_points; i < num_points; i++, in_p += 3, out_p += 3) { float w = ( mat[0*4+3]*in_p[0] + mat[1*4+3]*in_p[1] + mat[2*4+3]*in_p[2] + mat[3*4+3]); out_p[0] /= w; out_p[1] /= w; out_p[2] /= w; } } } //----------------------------------------------------------------------------- namespace vtkProjectedTetrahedraMapperNamespace { template void MapScalarsToColors1(ColorType *colors, vtkVolumeProperty *property, vtkDataArray *scalars); template void MapScalarsToColors2(ColorType *colors, vtkVolumeProperty *property, ScalarType *scalars, int num_scalar_components, vtkIdType num_scalars); template void MapIndependentComponents(ColorType *colors, vtkVolumeProperty *property, ScalarType *scalars, int num_scalar_components, vtkIdType num_scalars); template void Map2DependentComponents(ColorType *colors, ScalarType *scalars, vtkIdType num_scalars); template void Map4DependentComponents(ColorType *colors, ScalarType *scalars, vtkIdType num_scalars); } void vtkProjectedTetrahedraMapper::MapScalarsToColors(vtkDataArray *colors, vtkVolume *volume, vtkDataArray *scalars) { using namespace vtkProjectedTetrahedraMapperNamespace; vtkDataArray *tmpColors; int castColors; if ( (colors->GetDataType() == VTK_UNSIGNED_CHAR) && ( (scalars->GetDataType() != VTK_UNSIGNED_CHAR) || (volume->GetProperty()->GetIndependentComponents()) ) ) { // Special case. Need to convert from range [0,1] to [0,255]. tmpColors = vtkDoubleArray::New(); castColors = 1; } else { tmpColors = colors; castColors = 0; } vtkIdType numscalars = scalars->GetNumberOfTuples(); tmpColors->Initialize(); tmpColors->SetNumberOfComponents(4); tmpColors->SetNumberOfTuples(numscalars); void *colorpointer = tmpColors->GetVoidPointer(0); switch (tmpColors->GetDataType()) { vtkTemplateMacro(MapScalarsToColors1(static_cast(colorpointer), volume->GetProperty(), scalars)); } if (castColors) { // Special case. Need to convert from range [0,1] to [0,255]. colors->Initialize(); colors->SetNumberOfComponents(4); colors->SetNumberOfTuples(scalars->GetNumberOfTuples()); unsigned char *c = static_cast(colors)->GetPointer(0); for (vtkIdType i = 0; i < numscalars; i++, c+= 4) { double *dc = tmpColors->GetTuple(i); c[0] = static_cast(dc[0]*255.9999); c[1] = static_cast(dc[1]*255.9999); c[2] = static_cast(dc[2]*255.9999); c[3] = static_cast(dc[3]*255.9999); } tmpColors->Delete(); } } namespace vtkProjectedTetrahedraMapperNamespace { template void MapScalarsToColors1(ColorType *colors, vtkVolumeProperty *property, vtkDataArray *scalars) { void *scalarpointer = scalars->GetVoidPointer(0); switch(scalars->GetDataType()) { vtkTemplateMacro(MapScalarsToColors2(colors, property, static_cast(scalarpointer), scalars->GetNumberOfComponents(), scalars->GetNumberOfTuples())); } } template void MapScalarsToColors2(ColorType *colors, vtkVolumeProperty *property, ScalarType *scalars, int num_scalar_components, vtkIdType num_scalars) { if (property->GetIndependentComponents()) { MapIndependentComponents(colors, property, scalars, num_scalar_components, num_scalars); } else { switch (num_scalar_components) { case 2: Map2DependentComponents(colors, scalars, num_scalars); break; case 4: Map4DependentComponents(colors, scalars, num_scalars); break; default: vtkGenericWarningMacro("Attempted to map scalar with " << num_scalar_components << " with dependent components"); break; } } } template void MapIndependentComponents(ColorType *colors, vtkVolumeProperty *property, ScalarType *scalars, int num_scalar_components, vtkIdType num_scalars) { // I don't really know what to do if there is more than one component. // How am I supposed to mix the resulting colors? Since I don't know // what to do, and the whole thing seems kinda pointless anyway, I'm just // going to punt and copy over the first scalar. ColorType *c = colors; ScalarType *s = scalars; vtkIdType i; if (property->GetColorChannels() == 1) { vtkPiecewiseFunction *gray = property->GetGrayTransferFunction(); vtkPiecewiseFunction *alpha = property->GetScalarOpacity(); for (i = 0; i < num_scalars; i++, c += 4, s += num_scalar_components) { c[0] = c[1] = c[2] = static_cast(gray->GetValue(s[0])); c[3] = static_cast(alpha->GetValue(s[0])); } } else { vtkColorTransferFunction *rgb = property->GetRGBTransferFunction(); vtkPiecewiseFunction *alpha = property->GetScalarOpacity(); for (i = 0; i < num_scalars; i++, c += 4, s += num_scalar_components) { double trgb[3]; rgb->GetColor(s[0], trgb); c[0] = static_cast(trgb[0]); c[1] = static_cast(trgb[1]); c[2] = static_cast(trgb[2]); c[3] = static_cast(alpha->GetValue(s[0])); } } } template void Map2DependentComponents(ColorType *colors, ScalarType *scalars, vtkIdType num_scalars) { for (vtkIdType i = 0; i < num_scalars; i++) { colors[0] = colors[1] = colors[2] = static_cast(scalars[0]); colors[3] = static_cast(scalars[3]); colors += 4; scalars += 2; } } template void Map4DependentComponents(ColorType *colors, ScalarType *scalars, vtkIdType num_scalars) { for (vtkIdType i = 0; i < num_scalars; i++) { colors[0] = static_cast(scalars[0]); colors[1] = static_cast(scalars[1]); colors[2] = static_cast(scalars[2]); colors[3] = static_cast(scalars[3]); colors += 4; scalars += 4; } } }