mirror of https://gitee.com/openkylin/vtk9.git
425 lines
15 KiB
C++
425 lines
15 KiB
C++
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: vtkUnstructuredGridPreIntegration.cxx
|
|
|
|
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 2004 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 "vtkUnstructuredGridPreIntegration.h"
|
|
|
|
#include "vtkAbstractVolumeMapper.h"
|
|
#include "vtkDoubleArray.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkUnstructuredGrid.h"
|
|
#include "vtkUnstructuredGridPartialPreIntegration.h"
|
|
#include "vtkVolume.h"
|
|
#include "vtkVolumeProperty.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
vtkStandardNewMacro(vtkUnstructuredGridPreIntegration);
|
|
|
|
vtkCxxSetObjectMacro(
|
|
vtkUnstructuredGridPreIntegration, Integrator, vtkUnstructuredGridVolumeRayIntegrator);
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
vtkUnstructuredGridPreIntegration::vtkUnstructuredGridPreIntegration()
|
|
{
|
|
this->Integrator = vtkUnstructuredGridPartialPreIntegration::New();
|
|
this->Property = nullptr;
|
|
|
|
this->NumComponents = 0;
|
|
this->IntegrationTable = nullptr;
|
|
this->IntegrationTableScalarShift = nullptr;
|
|
this->IntegrationTableScalarScale = nullptr;
|
|
|
|
this->IntegrationTableScalarResolution = 128;
|
|
this->IntegrationTableLengthResolution = 256;
|
|
|
|
this->IncrementalPreIntegration = 1;
|
|
this->IntegrationTableLengthScale = 0;
|
|
}
|
|
|
|
vtkUnstructuredGridPreIntegration::~vtkUnstructuredGridPreIntegration()
|
|
{
|
|
this->SetIntegrator(nullptr);
|
|
|
|
if (this->IntegrationTable)
|
|
{
|
|
for (int i = 0; i < this->NumComponents; i++)
|
|
{
|
|
delete[] this->IntegrationTable[i];
|
|
}
|
|
delete[] this->IntegrationTable;
|
|
}
|
|
delete[] this->IntegrationTableScalarShift;
|
|
delete[] this->IntegrationTableScalarScale;
|
|
}
|
|
|
|
void vtkUnstructuredGridPreIntegration::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os, indent);
|
|
|
|
os << indent << "Integrator: " << this->Integrator << endl;
|
|
|
|
os << indent << "IntegrationTableScalarResolution: " << this->IntegrationTableScalarResolution
|
|
<< endl;
|
|
os << indent << "IntegrationTableLengthResolution: " << this->IntegrationTableLengthResolution
|
|
<< endl;
|
|
|
|
os << indent << "IncrementalPreIntegration: " << this->IncrementalPreIntegration << endl;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
double vtkUnstructuredGridPreIntegration::GetIntegrationTableScalarShift(int component)
|
|
{
|
|
return this->IntegrationTableScalarShift[component];
|
|
}
|
|
|
|
double vtkUnstructuredGridPreIntegration::GetIntegrationTableScalarScale(int component)
|
|
{
|
|
return this->IntegrationTableScalarScale[component];
|
|
}
|
|
|
|
double vtkUnstructuredGridPreIntegration::GetIntegrationTableLengthScale()
|
|
{
|
|
return this->IntegrationTableLengthScale;
|
|
}
|
|
|
|
float* vtkUnstructuredGridPreIntegration::GetPreIntegrationTable(int component)
|
|
{
|
|
return this->IntegrationTable[component];
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void vtkUnstructuredGridPreIntegration::BuildPreIntegrationTables(vtkDataArray* scalars)
|
|
{
|
|
// Delete old tables.
|
|
if (this->IntegrationTable)
|
|
{
|
|
for (int i = 0; i < this->NumComponents; i++)
|
|
{
|
|
delete[] this->IntegrationTable[i];
|
|
}
|
|
delete[] this->IntegrationTable;
|
|
}
|
|
delete[] this->IntegrationTableScalarShift;
|
|
delete[] this->IntegrationTableScalarScale;
|
|
|
|
this->NumComponents = scalars->GetNumberOfComponents();
|
|
|
|
// Establish temporary inputs to integrator.
|
|
vtkVolume* tmpVolume = vtkVolume::New();
|
|
vtkVolumeProperty* tmpProperty = vtkVolumeProperty::New();
|
|
vtkDataArray* tmpScalars = vtkDataArray::CreateDataArray(scalars->GetDataType());
|
|
|
|
tmpVolume->SetMapper(this->Volume->GetMapper());
|
|
tmpVolume->SetProperty(tmpProperty);
|
|
|
|
tmpProperty->IndependentComponentsOn();
|
|
tmpProperty->SetInterpolationType(this->Property->GetInterpolationType());
|
|
|
|
tmpScalars->SetNumberOfComponents(1);
|
|
tmpScalars->SetNumberOfTuples(2);
|
|
|
|
vtkDoubleArray* tmpIntersectionLengths = vtkDoubleArray::New();
|
|
vtkDataArray* tmpNearIntersections = vtkDataArray::CreateDataArray(scalars->GetDataType());
|
|
vtkDataArray* tmpFarIntersections = vtkDataArray::CreateDataArray(scalars->GetDataType());
|
|
|
|
tmpIntersectionLengths->SetNumberOfComponents(1);
|
|
tmpIntersectionLengths->SetNumberOfTuples(1);
|
|
tmpNearIntersections->SetNumberOfComponents(1);
|
|
tmpNearIntersections->SetNumberOfTuples(1);
|
|
tmpFarIntersections->SetNumberOfComponents(1);
|
|
tmpFarIntersections->SetNumberOfTuples(1);
|
|
|
|
this->IntegrationTable = new float*[this->NumComponents];
|
|
this->IntegrationTableScalarShift = new double[this->NumComponents];
|
|
this->IntegrationTableScalarScale = new double[this->NumComponents];
|
|
// Note that the scale set up such that a length of (this->MaxLength +
|
|
// epsilon + 0.5) will scale to this->IntegrationTableLengthResolution-1.
|
|
// Similar scaling is performed for the other dimensions of the
|
|
// pre-integration table.
|
|
this->IntegrationTableLengthScale =
|
|
(this->IntegrationTableLengthResolution - 1) / this->MaxLength;
|
|
|
|
// We only do computations at one length.
|
|
float d_length = (float)(1.0 / this->IntegrationTableLengthScale);
|
|
|
|
for (int component = 0; component < this->NumComponents; component++)
|
|
{
|
|
int d_idx, sb_idx, sf_idx;
|
|
|
|
// Allocate table.
|
|
try
|
|
{
|
|
this->IntegrationTable[component] = new float[4 * this->IntegrationTableScalarResolution *
|
|
this->IntegrationTableScalarResolution * this->IntegrationTableLengthResolution];
|
|
}
|
|
catch (...)
|
|
{
|
|
this->IntegrationTable[component] = nullptr;
|
|
}
|
|
|
|
if (this->IntegrationTable[component] == nullptr)
|
|
{
|
|
// Could not allocate memory for table.
|
|
if ((this->IntegrationTableScalarResolution > 32) ||
|
|
(this->IntegrationTableLengthResolution > 64))
|
|
{
|
|
vtkWarningMacro("Could not allocate integration table.\n"
|
|
"Reducing the table size and trying again.");
|
|
for (int i = 0; i < component; i++)
|
|
{
|
|
delete[] this->IntegrationTable[i];
|
|
}
|
|
delete[] this->IntegrationTable;
|
|
this->IntegrationTable = nullptr;
|
|
|
|
this->IntegrationTableScalarResolution = 32;
|
|
this->IntegrationTableLengthResolution = 64;
|
|
this->BuildPreIntegrationTables(scalars);
|
|
}
|
|
else
|
|
{
|
|
vtkErrorMacro("Could not allocate integration table.");
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Determine scale and shift.
|
|
double* range = scalars->GetRange(component);
|
|
if (range[0] == range[1])
|
|
{
|
|
// Unusual case where the scalars are all the same.
|
|
this->IntegrationTableScalarScale[component] = 1.0;
|
|
}
|
|
else
|
|
{
|
|
this->IntegrationTableScalarScale[component] =
|
|
(this->IntegrationTableScalarResolution - 1) / (range[1] - range[0]);
|
|
}
|
|
this->IntegrationTableScalarShift[component] =
|
|
-range[0] * this->IntegrationTableScalarScale[component];
|
|
|
|
// Set values for d=0 (they are all zero).
|
|
float* c = this->IntegrationTable[component];
|
|
for (sb_idx = 0; sb_idx < this->IntegrationTableScalarResolution; sb_idx++)
|
|
{
|
|
for (sf_idx = 0; sf_idx < this->IntegrationTableScalarResolution; sf_idx++)
|
|
{
|
|
c[0] = c[1] = c[2] = c[3] = 0.0f;
|
|
c += 4;
|
|
}
|
|
}
|
|
|
|
// Initialize integrator.
|
|
if (this->Property->GetColorChannels(component) == 3)
|
|
{
|
|
tmpProperty->SetColor(this->Property->GetRGBTransferFunction(component));
|
|
}
|
|
else
|
|
{
|
|
tmpProperty->SetColor(this->Property->GetGrayTransferFunction(component));
|
|
}
|
|
tmpProperty->SetScalarOpacity(this->Property->GetScalarOpacity(component));
|
|
tmpProperty->SetScalarOpacityUnitDistance(
|
|
this->Property->GetScalarOpacityUnitDistance(component));
|
|
tmpProperty->SetShade(this->Property->GetShade(component));
|
|
tmpProperty->SetAmbient(this->Property->GetAmbient(component));
|
|
tmpProperty->SetDiffuse(this->Property->GetDiffuse(component));
|
|
tmpProperty->SetSpecular(this->Property->GetSpecular(component));
|
|
tmpProperty->SetSpecularPower(this->Property->GetSpecularPower(component));
|
|
tmpScalars->SetTuple1(0, range[0]);
|
|
tmpScalars->SetTuple1(1, range[1]);
|
|
this->Integrator->Initialize(tmpVolume, tmpScalars);
|
|
|
|
// Set values for next smallest d (the base values).
|
|
tmpIntersectionLengths->SetTuple1(0, d_length);
|
|
for (sb_idx = 0; sb_idx < this->IntegrationTableScalarResolution; sb_idx++)
|
|
{
|
|
double sb = (sb_idx - this->IntegrationTableScalarShift[component]) /
|
|
(this->IntegrationTableScalarScale[component]);
|
|
tmpFarIntersections->SetTuple1(0, sb);
|
|
for (sf_idx = 0; sf_idx < this->IntegrationTableScalarResolution; sf_idx++)
|
|
{
|
|
double sf = (sf_idx - this->IntegrationTableScalarShift[component]) /
|
|
(this->IntegrationTableScalarScale[component]);
|
|
tmpNearIntersections->SetTuple1(0, sf);
|
|
c[0] = c[1] = c[2] = c[3] = 0;
|
|
this->Integrator->Integrate(
|
|
tmpIntersectionLengths, tmpNearIntersections, tmpFarIntersections, c);
|
|
c += 4;
|
|
}
|
|
}
|
|
|
|
// Set rest of values using other values in table.
|
|
if (this->IncrementalPreIntegration)
|
|
{
|
|
for (d_idx = 2; d_idx < this->IntegrationTableLengthResolution; d_idx++)
|
|
{
|
|
for (sb_idx = 0; sb_idx < this->IntegrationTableScalarResolution; sb_idx++)
|
|
{
|
|
for (sf_idx = 0; sf_idx < this->IntegrationTableScalarResolution; sf_idx++)
|
|
{
|
|
// We are going to perform incremental pre-integration. To do
|
|
// this, we compute the integration of a ray from sf to sb of
|
|
// length d by combining two entries in the table. The first
|
|
// entry will be from sf to sm of length delta d (the smallest
|
|
// non-zero length stored in the table). The second entry will
|
|
// be from sm to sb of length d - delta d. See Weiler, et
|
|
// al. "Hardware-Based Ray Casting for Tetrahedral Meshes" for
|
|
// more details.
|
|
int sm_idx = ((d_idx - 1) * sf_idx + sb_idx + d_idx / 2) / d_idx;
|
|
|
|
float* colorf = this->GetIndexedTableEntry(sf_idx, sm_idx, 1, component);
|
|
float* colorb = this->GetIndexedTableEntry(sm_idx, sb_idx, d_idx - 1, component);
|
|
|
|
c[0] = colorf[0] + colorb[0] * (1.0f - colorf[3]);
|
|
c[1] = colorf[1] + colorb[1] * (1.0f - colorf[3]);
|
|
c[2] = colorf[2] + colorb[2] * (1.0f - colorf[3]);
|
|
c[3] = colorf[3] + colorb[3] * (1.0f - colorf[3]);
|
|
c += 4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (d_idx = 2; d_idx < this->IntegrationTableLengthResolution; d_idx++)
|
|
{
|
|
for (sb_idx = 0; sb_idx < this->IntegrationTableScalarResolution; sb_idx++)
|
|
{
|
|
for (sf_idx = 0; sf_idx < this->IntegrationTableScalarResolution; sf_idx++)
|
|
{
|
|
// Compute the integration table the old-fashioned slow way.
|
|
float length = d_idx * d_length;
|
|
float sb = (float)((sb_idx - this->IntegrationTableScalarShift[component]) /
|
|
(this->IntegrationTableScalarScale[component]));
|
|
float sf = (float)((sf_idx - this->IntegrationTableScalarShift[component]) /
|
|
(this->IntegrationTableScalarScale[component]));
|
|
tmpIntersectionLengths->SetTuple1(0, length);
|
|
tmpFarIntersections->SetTuple1(0, sb);
|
|
tmpNearIntersections->SetTuple1(0, sf);
|
|
c[0] = c[1] = c[2] = c[3] = 0.0f;
|
|
this->Integrator->Integrate(
|
|
tmpIntersectionLengths, tmpNearIntersections, tmpFarIntersections, c);
|
|
c += 4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get rid of temporary data.
|
|
tmpVolume->Delete();
|
|
tmpProperty->Delete();
|
|
tmpScalars->Delete();
|
|
|
|
tmpIntersectionLengths->Delete();
|
|
tmpNearIntersections->Delete();
|
|
tmpFarIntersections->Delete();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void vtkUnstructuredGridPreIntegration::Initialize(vtkVolume* volume, vtkDataArray* scalars)
|
|
{
|
|
vtkVolumeProperty* property = volume->GetProperty();
|
|
|
|
if ((property == this->Property) && (this->IntegrationTableBuilt > property->GetMTime()) &&
|
|
(this->IntegrationTableBuilt > this->MTime))
|
|
{
|
|
// Nothing changed from the last time Initialize was run.
|
|
return;
|
|
}
|
|
|
|
this->Property = property;
|
|
this->Volume = volume;
|
|
this->IntegrationTableBuilt.Modified();
|
|
|
|
if (!property->GetIndependentComponents())
|
|
{
|
|
vtkErrorMacro("Cannot store dependent components in pre-integration table.");
|
|
return;
|
|
}
|
|
|
|
// Determine the maximum possible length of a ray segment.
|
|
vtkDataSet* input = volume->GetMapper()->GetDataSetInput();
|
|
vtkIdType numcells = input->GetNumberOfCells();
|
|
this->MaxLength = 0;
|
|
for (vtkIdType i = 0; i < numcells; i++)
|
|
{
|
|
double cellbounds[6];
|
|
input->GetCellBounds(i, cellbounds);
|
|
#define SQR(x) ((x) * (x))
|
|
double diagonal_length = sqrt(SQR(cellbounds[1] - cellbounds[0]) +
|
|
SQR(cellbounds[3] - cellbounds[2]) + SQR(cellbounds[5] - cellbounds[4]));
|
|
#undef SQR
|
|
if (diagonal_length > this->MaxLength)
|
|
{
|
|
this->MaxLength = diagonal_length;
|
|
}
|
|
}
|
|
|
|
this->BuildPreIntegrationTables(scalars);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void vtkUnstructuredGridPreIntegration::Integrate(vtkDoubleArray* intersectionLengths,
|
|
vtkDataArray* nearIntersections, vtkDataArray* farIntersections, float color[4])
|
|
{
|
|
vtkIdType numIntersections = intersectionLengths->GetNumberOfTuples();
|
|
|
|
for (vtkIdType i = 0; i < numIntersections; i++)
|
|
{
|
|
float* c = this->GetTableEntry(nearIntersections->GetComponent(i, 0),
|
|
farIntersections->GetComponent(i, 0), intersectionLengths->GetComponent(i, 0), 0);
|
|
float newcolor[4] = { c[0], c[1], c[2], c[3] };
|
|
for (int component = 1; component < this->NumComponents; component++)
|
|
{
|
|
c = this->GetTableEntry(nearIntersections->GetComponent(i, component),
|
|
farIntersections->GetComponent(i, component), intersectionLengths->GetComponent(i, 0),
|
|
component);
|
|
// The blending I'm using is a combination of porter and duff xors
|
|
// and ins.
|
|
float coef1 = 1.0f - 0.5f * c[3];
|
|
float coef2 = 1.0f - 0.5f * newcolor[3];
|
|
newcolor[0] = newcolor[0] * coef1 + c[0] * coef2;
|
|
newcolor[1] = newcolor[1] * coef1 + c[1] * coef2;
|
|
newcolor[2] = newcolor[2] * coef1 + c[2] * coef2;
|
|
newcolor[3] = newcolor[3] * coef1 + c[3] * coef2;
|
|
}
|
|
|
|
float coef = 1.0f - color[3];
|
|
color[0] += newcolor[0] * coef;
|
|
color[1] += newcolor[1] * coef;
|
|
color[2] += newcolor[2] * coef;
|
|
color[3] += newcolor[3] * coef;
|
|
}
|
|
}
|