mirror of https://gitee.com/openkylin/vtk9.git
372 lines
11 KiB
C++
372 lines
11 KiB
C++
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: vtkQuadricLODActor.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.
|
|
|
|
=========================================================================*/
|
|
#include "vtkQuadricLODActor.h"
|
|
|
|
#include "vtkCellArray.h"
|
|
#include "vtkCellData.h"
|
|
#include "vtkFollower.h"
|
|
#include "vtkMatrix4x4.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkPolyData.h"
|
|
#include "vtkPolyDataAlgorithm.h"
|
|
#include "vtkPolyDataMapper.h"
|
|
#include "vtkProperty.h"
|
|
#include "vtkQuadricClustering.h"
|
|
#include "vtkRenderWindow.h"
|
|
#include "vtkRenderWindowInteractor.h"
|
|
#include "vtkRenderer.h"
|
|
#include "vtkTexture.h"
|
|
|
|
vtkStandardNewMacro(vtkQuadricLODActor);
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Specify the quadric clustering algorithm for decimating the geometry.
|
|
vtkCxxSetObjectMacro(vtkQuadricLODActor, LODFilter, vtkQuadricClustering);
|
|
|
|
//------------------------------------------------------------------------------
|
|
vtkQuadricLODActor::vtkQuadricLODActor()
|
|
{
|
|
// Configure the decimation (quadric clustering) filter
|
|
this->LODFilter = vtkQuadricClustering::New();
|
|
this->LODFilter->UseInputPointsOn();
|
|
this->LODFilter->CopyCellDataOn();
|
|
this->LODFilter->UseInternalTrianglesOff();
|
|
|
|
this->Static = 0;
|
|
this->DeferLODConstruction = 0;
|
|
this->CollapseDimensionRatio = 0.05;
|
|
this->DataConfiguration = UNKNOWN;
|
|
this->PropType = ACTOR;
|
|
this->Camera = nullptr;
|
|
|
|
// Internal data members
|
|
this->CachedInteractiveFrameRate = 0.0;
|
|
|
|
// By default create an actor
|
|
this->LODActor = vtkActor::New();
|
|
|
|
// mapper for LOD actor
|
|
this->LODMapper = vtkPolyDataMapper::New();
|
|
|
|
// A internal matrix for performance
|
|
vtkMatrix4x4* m = vtkMatrix4x4::New();
|
|
this->LODActor->SetUserMatrix(m);
|
|
m->Delete();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
vtkQuadricLODActor::~vtkQuadricLODActor()
|
|
{
|
|
this->LODFilter->Delete();
|
|
this->LODActor->Delete();
|
|
this->LODActor = nullptr;
|
|
this->LODMapper->Delete();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkQuadricLODActor::Render(vtkRenderer* ren, vtkMapper* vtkNotUsed(m))
|
|
{
|
|
if (!this->Mapper)
|
|
{
|
|
vtkErrorMacro("No mapper for actor.");
|
|
return;
|
|
}
|
|
|
|
// determine out how much time we have to render
|
|
float allowedTime = this->AllocatedRenderTime;
|
|
double frameRate = ren->GetRenderWindow()->GetInteractor()->GetDesiredUpdateRate();
|
|
frameRate = (frameRate < 1.0 ? 1.0 : (frameRate > 75 ? 75.0 : frameRate));
|
|
int interactiveRender = 0;
|
|
// interactive renders are defined when compared with the desired update rate. Here we use
|
|
// a generous fudge factor to ensure that the LOD kicks in.
|
|
if (allowedTime <= (1.1 / frameRate))
|
|
{
|
|
interactiveRender = 1;
|
|
}
|
|
|
|
vtkMatrix4x4* matrix;
|
|
|
|
// Build LOD only if necessary
|
|
if ((interactiveRender || !this->DeferLODConstruction) &&
|
|
(this->GetMTime() > this->BuildTime || (this->Mapper->GetMTime() > this->BuildTime) ||
|
|
(this->CachedInteractiveFrameRate < 0.9 * frameRate) ||
|
|
(this->CachedInteractiveFrameRate > 1.1 * frameRate)))
|
|
{
|
|
vtkDebugMacro(">>>>>>>>>>>>>>>Building LOD");
|
|
this->CachedInteractiveFrameRate = frameRate;
|
|
|
|
// The mapper must be updated the first time prior to going static
|
|
this->Mapper->Update();
|
|
this->Mapper->SetStatic(this->Static);
|
|
|
|
// Make sure LOD mapper is consistent with mapper
|
|
this->LODMapper->ShallowCopy(this->Mapper);
|
|
this->LODActor->SetProperty(this->GetProperty());
|
|
this->LODActor->SetBackfaceProperty(this->BackfaceProperty);
|
|
|
|
// This table has been empirically defined. It specifies a quadric
|
|
// clustering bin size go along with a desired frame rate.
|
|
static const int NumTableEntries = 7;
|
|
static const double FPSTable[] = { 0.0, 5.0, 10.0, 17.5, 25.0, 50.0, 75.0 };
|
|
static const double DIMTable[] = { 75.0, 60.0, 50.0, 35.0, 25.0, 20.0, 15.0 };
|
|
int dim = 15;
|
|
for (int i = 0; i < (NumTableEntries - 1); i++)
|
|
{
|
|
if (frameRate >= FPSTable[i] && frameRate <= FPSTable[i + 1])
|
|
{
|
|
dim = static_cast<int>((DIMTable[i] +
|
|
(frameRate - FPSTable[i]) / (FPSTable[i + 1] - FPSTable[i]) *
|
|
(DIMTable[i + 1] - DIMTable[i])));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// TODO: When the 'TestQuadricLODActor' test gets here frameRate=15.0
|
|
// and dim=40. This causes vtkQuadricClustering::AddTriangle()'s computations
|
|
// to overflow. If you set dim=35 there's no overflow, if you set it to 36 there is.
|
|
|
|
// Construct the LOD
|
|
vtkPolyData* pd = vtkPolyData::SafeDownCast(this->Mapper->GetInput());
|
|
|
|
// First see if there is an explicit description of the data configuration.
|
|
if (this->DataConfiguration == XLINE)
|
|
{
|
|
this->LODFilter->SetNumberOfDivisions(dim, 1, 1);
|
|
}
|
|
else if (this->DataConfiguration == YLINE)
|
|
{
|
|
this->LODFilter->SetNumberOfDivisions(1, dim, 1);
|
|
}
|
|
else if (this->DataConfiguration == ZLINE)
|
|
{
|
|
this->LODFilter->SetNumberOfDivisions(1, 1, dim);
|
|
}
|
|
else if (this->DataConfiguration == XYPLANE)
|
|
{
|
|
this->LODFilter->SetNumberOfDivisions(dim, dim, 1);
|
|
}
|
|
else if (this->DataConfiguration == YZPLANE)
|
|
{
|
|
this->LODFilter->SetNumberOfDivisions(1, dim, dim);
|
|
}
|
|
else if (this->DataConfiguration == XZPLANE)
|
|
{
|
|
this->LODFilter->SetNumberOfDivisions(dim, 1, dim);
|
|
}
|
|
else if (this->DataConfiguration == XYZVOLUME)
|
|
{
|
|
this->LODFilter->SetNumberOfDivisions(dim, dim, dim);
|
|
}
|
|
else // no explicit description
|
|
{
|
|
// If here, we analyze the data to see if we can optimize binning. The
|
|
// binning is optimized depending on data dimension and data aspect
|
|
// ratio.
|
|
double bounds[6], h[3];
|
|
pd->GetBounds(bounds);
|
|
h[0] = bounds[1] - bounds[0];
|
|
h[1] = bounds[3] - bounds[2];
|
|
h[2] = bounds[5] - bounds[4];
|
|
double hMax = (h[0] > h[1]) ? (h[0] > h[2] ? h[0] : h[2]) : (h[1] > h[2] ? h[1] : h[2]);
|
|
int nDivs[3], numSmallDims = 0;
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
if (h[i] <= (this->CollapseDimensionRatio * hMax))
|
|
{
|
|
nDivs[i] = 1;
|
|
numSmallDims++;
|
|
}
|
|
else
|
|
{
|
|
nDivs[i] = dim;
|
|
}
|
|
}
|
|
this->LODFilter->SetNumberOfDivisions(nDivs);
|
|
} // data configuration not explicitly specified
|
|
|
|
vtkDebugMacro("QC bin size: " << dim);
|
|
this->LODFilter->AutoAdjustNumberOfDivisionsOff();
|
|
this->LODFilter->SetInputConnection(this->Mapper->GetInputConnection(0, 0));
|
|
this->LODFilter->Update();
|
|
this->LODMapper->SetInputConnection(this->LODFilter->GetOutputPort());
|
|
|
|
// Make sure the device has the same matrix. Only update when still update
|
|
// rate is requested.
|
|
matrix = this->LODActor->GetUserMatrix();
|
|
this->GetMatrix(matrix);
|
|
|
|
this->LODMapper->Update();
|
|
if (this->Static)
|
|
{
|
|
this->LODMapper->StaticOn();
|
|
}
|
|
this->BuildTime.Modified();
|
|
}
|
|
|
|
// Figure out which resolution to use. We want the highest resolution that
|
|
// fits under the time allowed. There is no order to the list, so it is
|
|
// assumed that mappers that take longer to render are better quality.
|
|
// Timings might become out of date, but we rely on them to be consistent
|
|
// across renders.
|
|
vtkMapper* bestMapper = this->Mapper;
|
|
#ifndef NDEBUG
|
|
float bestTime = bestMapper->GetTimeToDraw();
|
|
#endif
|
|
if (interactiveRender)
|
|
{ // use lod
|
|
bestMapper = this->LODMapper;
|
|
#ifndef NDEBUG
|
|
bestTime = bestMapper->GetTimeToDraw();
|
|
#endif
|
|
vtkDebugMacro("LOD render (best,allowed): " << bestTime << "," << allowedTime);
|
|
}
|
|
else
|
|
{ // use full resolution
|
|
// Only update when still update rate is requested.
|
|
matrix = this->LODActor->GetUserMatrix();
|
|
this->GetMatrix(matrix);
|
|
vtkDebugMacro("----Full render (best,allowed): " << bestTime << "," << allowedTime);
|
|
}
|
|
|
|
// render the property
|
|
if (!this->Property)
|
|
{
|
|
// force creation of a property
|
|
this->GetProperty();
|
|
}
|
|
this->Property->Render(this, ren);
|
|
|
|
if (this->BackfaceProperty)
|
|
{
|
|
this->BackfaceProperty->BackfaceRender(this, ren);
|
|
this->LODActor->SetBackfaceProperty(this->BackfaceProperty);
|
|
}
|
|
this->LODActor->SetProperty(this->Property);
|
|
|
|
// render the texture
|
|
if (this->Texture)
|
|
{
|
|
this->Texture->Render(ren);
|
|
}
|
|
|
|
// The internal actor needs to share property keys. This allows depth peeling
|
|
// etc to work.
|
|
this->LODActor->SetPropertyKeys(this->GetPropertyKeys());
|
|
|
|
// copy current translucent pass setting
|
|
this->LODActor->SetIsRenderingTranslucentPolygonalGeometry(
|
|
this->IsRenderingTranslucentPolygonalGeometry());
|
|
|
|
// Store information on time it takes to render.
|
|
// We might want to estimate time from the number of polygons in mapper.
|
|
this->LODActor->Render(ren, bestMapper);
|
|
this->EstimatedRenderTime = bestMapper->GetTimeToDraw();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkQuadricLODActor::ReleaseGraphicsResources(vtkWindow* renWin)
|
|
{
|
|
vtkActor::ReleaseGraphicsResources(renWin);
|
|
this->LODActor->ReleaseGraphicsResources(renWin);
|
|
this->Mapper->ReleaseGraphicsResources(renWin);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkQuadricLODActor::ShallowCopy(vtkProp* prop)
|
|
{
|
|
// Now do superclass
|
|
this->vtkActor::ShallowCopy(prop);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkQuadricLODActor::SetCamera(vtkCamera* camera)
|
|
{
|
|
vtkFollower* follower = vtkFollower::SafeDownCast(this->LODActor);
|
|
if (follower)
|
|
{
|
|
follower->SetCamera(camera);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkQuadricLODActor::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os, indent);
|
|
|
|
os << indent << "Defer LOD Construction: " << (this->DeferLODConstruction ? "On\n" : "Off\n");
|
|
|
|
os << indent << "Static : " << (this->Static ? "On\n" : "Off\n");
|
|
|
|
os << indent << "Collapse Dimension Ratio: " << this->CollapseDimensionRatio << "\n";
|
|
|
|
os << indent << "Data Configuration: ";
|
|
switch (this->DataConfiguration)
|
|
{
|
|
case XYZVOLUME:
|
|
os << "XYZ Volume\n";
|
|
break;
|
|
case XLINE:
|
|
os << "X Line\n";
|
|
break;
|
|
case YLINE:
|
|
os << "Y Line\n";
|
|
break;
|
|
case ZLINE:
|
|
os << "Z Line\n";
|
|
break;
|
|
case XYPLANE:
|
|
os << "XY Plane\n";
|
|
break;
|
|
case YZPLANE:
|
|
os << "YZ Plane\n";
|
|
break;
|
|
case XZPLANE:
|
|
os << "XZ Plane\n";
|
|
break;
|
|
default: // XLINE
|
|
os << "Unknown\n";
|
|
}
|
|
|
|
os << indent << "LOD Filter: ";
|
|
if (this->LODFilter)
|
|
{
|
|
os << this->LODFilter << "\n";
|
|
}
|
|
else
|
|
{
|
|
os << "(none)\n";
|
|
}
|
|
|
|
os << indent << "Prop Type: ";
|
|
if (this->PropType == FOLLOWER)
|
|
{
|
|
os << "Follower\n";
|
|
}
|
|
else
|
|
{
|
|
os << "Actor\n";
|
|
}
|
|
|
|
os << indent << "Camera: ";
|
|
if (this->Camera)
|
|
{
|
|
os << this->Camera << "\n";
|
|
}
|
|
else
|
|
{
|
|
os << "(none)\n";
|
|
}
|
|
}
|