Commit 93aa6410 authored by Matt Larsen's avatar Matt Larsen Committed by Kitware Robot
Browse files

Merge topic 'line_rendering'

8e0a0308 Handles the case when the passed canvas has rendered data.
b727377f Refactoring
44357d51 Apply external faces filter on cellset before wireframing
7e95269c Refactoring wireframing code
5738e895 Wireframe rendering
657b0b94 Applies `clang-format` and minor refactoring
af2acc57

 Implements a simple line renderer using Xialin Wu's algorithm

Acked-by: Kitware Robot's avatarKitware Robot <kwrobot@kitware.com>
Acked-by: default avatarMatt Larsen <mlarsen@cs.uoregon.edu>
Merge-request: !895
parents 2e7d9e3c 8e0a0308
......@@ -41,6 +41,7 @@ set(headers
MapperRayTracer.h
MapperVolume.h
MapperConnectivity.h
MapperWireframer.h
TextAnnotation.h
TextAnnotationBillboard.h
TextAnnotationScreen.h
......@@ -73,6 +74,7 @@ set(sources
MapperRayTracer.cxx
MapperVolume.cxx
MapperConnectivity.cxx
MapperWireframer.cxx
Scene.cxx
TextAnnotation.cxx
TextAnnotationBillboard.cxx
......@@ -126,6 +128,8 @@ set(osmesa_sources
# This list of sources has code that uses devices and so might need to be
# compiled with a device-specific compiler (like CUDA).
set(device_sources
Mapper.cxx
MapperWireframer.cxx
CanvasRayTracer.cxx
ConnectivityProxy.cxx
raytracing/BoundingVolumeHierarchy.cxx
......
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt 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 2016 Sandia Corporation.
// Copyright 2016 UT-Battelle, LLC.
// Copyright 2016 Los Alamos National Security.
//
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
//
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//============================================================================
#include <vtkm/Assert.h>
#include <vtkm/cont/DeviceAdapterAlgorithm.h>
#include <vtkm/cont/TryExecute.h>
#include <vtkm/exec/CellEdge.h>
#include <vtkm/filter/ExternalFaces.h>
#include <vtkm/rendering/CanvasRayTracer.h>
#include <vtkm/rendering/MapperRayTracer.h>
#include <vtkm/rendering/MapperWireframer.h>
#include <vtkm/rendering/Wireframer.h>
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/DispatcherMapTopology.h>
#include <vtkm/worklet/ScatterCounting.h>
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/WorkletMapTopology.h>
namespace vtkm
{
namespace rendering
{
namespace
{
struct EdgesCounter : public vtkm::worklet::WorkletMapPointToCell
{
typedef void ControlSignature(CellSetIn cellSet, FieldOutCell<> numEdges);
typedef _2 ExecutionSignature(CellShape shape, PointCount numPoints);
using InputDomain = _1;
template <typename CellShapeTag>
VTKM_EXEC vtkm::IdComponent operator()(CellShapeTag shape, vtkm::IdComponent numPoints) const
{
if (shape.Id == vtkm::CELL_SHAPE_LINE)
{
return 1;
}
else
{
return vtkm::exec::CellEdgeNumberOfEdges(numPoints, shape, *this);
}
}
}; // struct EdgesCounter
struct EdgesExtracter : public vtkm::worklet::WorkletMapPointToCell
{
typedef void ControlSignature(CellSetIn cellSet, FieldOutCell<> edgeIndices);
typedef void ExecutionSignature(CellShape, PointIndices, VisitIndex, _2);
using InputDomain = _1;
using ScatterType = vtkm::worklet::ScatterCounting;
VTKM_CONT
template <typename CountArrayType, typename DeviceTag>
EdgesExtracter(const CountArrayType& counts, DeviceTag device)
: Scatter(counts, device)
{
}
VTKM_CONT ScatterType GetScatter() const { return this->Scatter; }
template <typename CellShapeTag, typename PointIndexVecType, typename EdgeIndexVecType>
VTKM_EXEC void operator()(CellShapeTag shape,
const PointIndexVecType& pointIndices,
vtkm::IdComponent visitIndex,
EdgeIndexVecType& edgeIndices) const
{
vtkm::Id p1, p2;
if (shape.Id == vtkm::CELL_SHAPE_LINE)
{
p1 = pointIndices[0];
p2 = pointIndices[1];
}
else
{
vtkm::Vec<vtkm::IdComponent, 2> localEdgeIndices = vtkm::exec::CellEdgeLocalIndices(
pointIndices.GetNumberOfComponents(), visitIndex, shape, *this);
p1 = pointIndices[localEdgeIndices[0]];
p2 = pointIndices[localEdgeIndices[1]];
}
// These indices need to be arranged in a definite order, as they will later be sorted to
// detect duplicates
edgeIndices[0] = p1 < p2 ? p1 : p2;
edgeIndices[1] = p1 < p2 ? p2 : p1;
}
private:
ScatterType Scatter;
}; // struct EdgesExtracter
struct ExtractUniqueEdges
{
vtkm::cont::DynamicCellSet CellSet;
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Id, 2>> EdgeIndices;
VTKM_CONT
ExtractUniqueEdges(const vtkm::cont::DynamicCellSet& cellSet)
: CellSet(cellSet)
{
}
template <typename DeviceTag>
VTKM_CONT bool operator()(DeviceTag)
{
VTKM_IS_DEVICE_ADAPTER_TAG(DeviceTag);
vtkm::cont::ArrayHandle<vtkm::IdComponent> counts;
vtkm::worklet::DispatcherMapTopology<EdgesCounter, DeviceTag>().Invoke(CellSet, counts);
EdgesExtracter extractWorklet(counts, DeviceTag());
vtkm::worklet::DispatcherMapTopology<EdgesExtracter, DeviceTag> extractDispatcher(
extractWorklet);
extractDispatcher.Invoke(CellSet, EdgeIndices);
vtkm::cont::DeviceAdapterAlgorithm<DeviceTag>::template Sort<vtkm::Id2>(EdgeIndices);
vtkm::cont::DeviceAdapterAlgorithm<DeviceTag>::template Unique<vtkm::Id2>(EdgeIndices);
return true;
}
}; // struct ExtractUniqueEdges
} // namespace
struct MapperWireframer::InternalsType
{
InternalsType()
: InternalsType(nullptr, false, false)
{
}
InternalsType(vtkm::rendering::Canvas* canvas, bool showInternalZones, bool isOverlay)
: Canvas(canvas)
, ShowInternalZones(showInternalZones)
, IsOverlay(isOverlay)
{
}
vtkm::rendering::Canvas* Canvas;
bool ShowInternalZones;
bool IsOverlay;
}; // struct MapperWireframer::InternalsType
MapperWireframer::MapperWireframer()
: Internals(new InternalsType(nullptr, false, false))
{
}
MapperWireframer::~MapperWireframer()
{
}
vtkm::rendering::Canvas* MapperWireframer::GetCanvas() const
{
return this->Internals->Canvas;
}
void MapperWireframer::SetCanvas(vtkm::rendering::Canvas* canvas)
{
this->Internals->Canvas = canvas;
}
bool MapperWireframer::GetShowInternalZones() const
{
return this->Internals->ShowInternalZones;
}
void MapperWireframer::SetShowInternalZones(bool showInternalZones)
{
this->Internals->ShowInternalZones = showInternalZones;
}
bool MapperWireframer::GetIsOverlay() const
{
return this->Internals->IsOverlay;
}
void MapperWireframer::SetIsOverlay(bool isOverlay)
{
this->Internals->IsOverlay = isOverlay;
}
void MapperWireframer::StartScene()
{
// Nothing needs to be done.
}
void MapperWireframer::EndScene()
{
// Nothing needs to be done.
}
void MapperWireframer::RenderCells(const vtkm::cont::DynamicCellSet& inCellSet,
const vtkm::cont::CoordinateSystem& coords,
const vtkm::cont::Field& inScalarField,
const vtkm::rendering::ColorTable& colorTable,
const vtkm::rendering::Camera& camera,
const vtkm::Range& scalarRange)
{
vtkm::cont::DynamicCellSet cellSet = inCellSet;
vtkm::cont::Field field = inScalarField;
if (!(this->Internals->ShowInternalZones))
{
// If internal zones are to be hidden, the number of edges processed can be reduced by
// running the external faces filter on the input cell set.
vtkm::cont::DataSet dataSet;
dataSet.AddCoordinateSystem(coords);
dataSet.AddCellSet(inCellSet);
vtkm::filter::ExternalFaces externalFaces;
externalFaces.SetCompactPoints(false);
externalFaces.SetPassPolyData(true);
vtkm::filter::Result result = externalFaces.Execute(dataSet);
externalFaces.MapFieldOntoOutput(result, inScalarField);
cellSet = result.GetDataSet().GetCellSet();
field = result.GetDataSet().GetField(0);
}
// Extract unique edges from the cell set.
ExtractUniqueEdges extracter(cellSet);
vtkm::cont::TryExecute(extracter);
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Id, 2>> edgeIndices = extracter.EdgeIndices;
Wireframer renderer(
this->Internals->Canvas, this->Internals->ShowInternalZones, this->Internals->IsOverlay);
// Render the cell set using a raytracer, on a separate canvas, and use the generated depth
// buffer, which represents the solid mesh, to avoid drawing on the internal zones
if (!(this->Internals->ShowInternalZones) && !(this->Internals->IsOverlay))
{
CanvasRayTracer canvas(this->Internals->Canvas->GetWidth(),
this->Internals->Canvas->GetHeight());
canvas.SetBackgroundColor(vtkm::rendering::Color::white);
canvas.Initialize();
canvas.Activate();
canvas.Clear();
MapperRayTracer raytracer;
raytracer.SetCanvas(&canvas);
raytracer.SetActiveColorTable(colorTable);
raytracer.RenderCells(cellSet, coords, field, colorTable, camera, scalarRange);
renderer.SetSolidDepthBuffer(canvas.GetDepthBuffer());
}
else if (this->Internals->IsOverlay)
{
renderer.SetSolidDepthBuffer(this->Internals->Canvas->GetDepthBuffer());
}
renderer.SetCamera(camera);
renderer.SetColorMap(this->ColorMap);
renderer.SetData(coords, edgeIndices, field, scalarRange);
renderer.Render();
}
vtkm::rendering::Mapper* MapperWireframer::NewCopy() const
{
return new vtkm::rendering::MapperWireframer(*this);
}
}
} // namespace vtkm::rendering
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt 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 2015 Sandia Corporation.
// Copyright 2015 UT-Battelle, LLC.
// Copyright 2015 Los Alamos National Security.
//
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
//
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//============================================================================
#ifndef vtk_m_rendering_MapperWireframer_h
#define vtk_m_rendering_MapperWireframer_h
#include <memory>
#include <vtkm/cont/CoordinateSystem.h>
#include <vtkm/cont/DynamicCellSet.h>
#include <vtkm/cont/Field.h>
#include <vtkm/rendering/Camera.h>
#include <vtkm/rendering/Canvas.h>
#include <vtkm/rendering/ColorTable.h>
#include <vtkm/rendering/Mapper.h>
namespace vtkm
{
namespace rendering
{
class VTKM_RENDERING_EXPORT MapperWireframer : public Mapper
{
public:
VTKM_CONT
MapperWireframer();
virtual ~MapperWireframer();
virtual vtkm::rendering::Canvas* GetCanvas() const VTKM_OVERRIDE;
virtual void SetCanvas(vtkm::rendering::Canvas* canvas) VTKM_OVERRIDE;
bool GetShowInternalZones() const;
void SetShowInternalZones(bool showInternalZones);
bool GetIsOverlay() const;
void SetIsOverlay(bool isOverlay);
virtual void StartScene() VTKM_OVERRIDE;
virtual void EndScene() VTKM_OVERRIDE;
virtual void RenderCells(const vtkm::cont::DynamicCellSet& cellset,
const vtkm::cont::CoordinateSystem& coords,
const vtkm::cont::Field& scalarField,
const vtkm::rendering::ColorTable& colorTable,
const vtkm::rendering::Camera& camera,
const vtkm::Range& scalarRange) VTKM_OVERRIDE;
virtual vtkm::rendering::Mapper* NewCopy() const VTKM_OVERRIDE;
private:
struct InternalsType;
std::shared_ptr<InternalsType> Internals;
}; // class MapperWireframer
}
} // namespace vtkm::rendering
#endif // vtk_m_rendering_MapperWireframer_h
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt 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 2016 Sandia Corporation.
// Copyright 2016 UT-Battelle, LLC.
// Copyright 2016 Los Alamos National Security.
//
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
//
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//============================================================================
#ifndef vtk_m_rendering_Wireframer_h
#define vtk_m_rendering_Wireframer_h
#include <vtkm/Assert.h>
#include <vtkm/Math.h>
#include <vtkm/Types.h>
#include <vtkm/VectorAnalysis.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/DynamicArrayHandle.h>
#include <vtkm/exec/AtomicArray.h>
#include <vtkm/rendering/MatrixHelpers.h>
#include <vtkm/rendering/Triangulator.h>
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/WorkletMapField.h>
namespace vtkm
{
namespace rendering
{
namespace
{
using ColorMapHandle = vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Float32, 4>>;
using IndicesHandle = vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Id, 2>>;
using PackedFrameBufferHandle = vtkm::cont::ArrayHandle<vtkm::Int64>;
// Depth value of 1.0f
const vtkm::Int64 ClearDepth = 0x3F800000;
// Packed frame buffer value with color set as white and depth as 1.0f
const vtkm::Int64 ClearValue = 0x3F800000FFFFFFFF;
VTKM_EXEC_CONT
vtkm::Float32 IntegerPart(vtkm::Float32 x)
{
return vtkm::Floor(x);
}
VTKM_EXEC_CONT
vtkm::Float32 FractionalPart(vtkm::Float32 x)
{
return x - vtkm::Floor(x);
}
VTKM_EXEC_CONT
vtkm::Float32 ReverseFractionalPart(vtkm::Float32 x)
{
return 1.0f - FractionalPart(x);
}
VTKM_EXEC_CONT
vtkm::UInt32 ScaleColorComponent(vtkm::Float32 c)
{
vtkm::Int32 t = vtkm::Int32(c * 256.0f);
return vtkm::UInt32(t < 0 ? 0 : (t > 255 ? 255 : t));
}
vtkm::UInt32 PackColor(vtkm::Float32 r, vtkm::Float32 g, vtkm::Float32 b, vtkm::Float32 a);
VTKM_EXEC_CONT
vtkm::UInt32 PackColor(const vtkm::Vec<vtkm::Float32, 4>& color)
{
return PackColor(color[0], color[1], color[2], color[3]);
}
VTKM_EXEC_CONT
vtkm::UInt32 PackColor(vtkm::Float32 r, vtkm::Float32 g, vtkm::Float32 b, vtkm::Float32 a)
{
vtkm::UInt32 packed = (ScaleColorComponent(r) << 24);
packed |= (ScaleColorComponent(g) << 16);
packed |= (ScaleColorComponent(b) << 8);
packed |= ScaleColorComponent(a);
return packed;
}
void UnpackColor(vtkm::UInt32 color,
vtkm::Float32& r,
vtkm::Float32& g,
vtkm::Float32& b,
vtkm::Float32& a);
VTKM_EXEC_CONT
void UnpackColor(vtkm::UInt32 packedColor, vtkm::Vec<vtkm::Float32, 4>& color)
{
UnpackColor(packedColor, color[0], color[1], color[2], color[3]);
}
VTKM_EXEC_CONT
void UnpackColor(vtkm::UInt32 color,
vtkm::Float32& r,
vtkm::Float32& g,
vtkm::Float32& b,
vtkm::Float32& a)
{
r = vtkm::Float32((color & 0xFF000000) >> 24) / 255.0f;
g = vtkm::Float32((color & 0x00FF0000) >> 16) / 255.0f;
b = vtkm::Float32((color & 0x0000FF00) >> 8) / 255.0f;
a = vtkm::Float32((color & 0x000000FF)) / 255.0f;
}
union PackedValue {
struct PackedFloats
{
vtkm::Float32 Color;
vtkm::Float32 Depth;
} Floats;
struct PackedInts
{
vtkm::UInt32 Color;
vtkm::UInt32 Depth;
} Ints;
vtkm::Int64 Raw;
}; // union PackedValue
struct CopyIntoFrameBuffer : public vtkm::worklet::WorkletMapField
{
typedef void ControlSignature(FieldIn<>, FieldIn<>, FieldOut<>);
typedef void ExecutionSignature(_1, _2, _3);
VTKM_CONT
CopyIntoFrameBuffer() {}
VTKM_EXEC
void operator()(const vtkm::Vec<vtkm::Float32, 4>& color,
const vtkm::Float32& depth,
vtkm::Int64& outValue) const
{
PackedValue packed;
packed.Ints.Color = PackColor(color);
packed.Floats.Depth = depth;
outValue = packed.Raw;
}
}; //struct CopyIntoFrameBuffer
template <typename DeviceTag>
class EdgePlotter : public vtkm::worklet::WorkletMapField
{
public:
using AtomicPackedFrameBufferHandle = vtkm::exec::AtomicArray<vtkm::Int64, DeviceTag>;
typedef void ControlSignature(FieldIn<>, WholeArrayIn<>, WholeArrayIn<Scalar>);
typedef void ExecutionSignature(_1, _2, _3);
using InputDomain = _1;
VTKM_CONT
EdgePlotter(const vtkm::Matrix<vtkm::Float32, 4, 4>& worldToProjection,
vtkm::Id width,
vtkm::Id height,
const vtkm::Range& fieldRange,
const ColorMapHandle& colorMap,
const AtomicPackedFrameBufferHandle& frameBuffer,
const vtkm::Range& clippingRange)
: WorldToProjection(worldToProjection)
, Width(width)
, Height(height)
, ColorMap(colorMap.PrepareForInput(DeviceTag()))
, ColorMapSize(vtkm::Float32(colorMap.GetNumberOfValues() - 1))
, FrameBuffer(frameBuffer)
, FieldMin(vtkm::Float32(fieldRange.Min))
{
InverseFieldDelta = 1.0f / vtkm::Float32(fieldRange.Length());
Offset = vtkm::Max(0.03f / vtkm::Float32(clippingRange.Length()), 0.000001f);
}
template <typename CoordinatesPortalType, typename ScalarFieldPortalType>
VTKM_EXEC void operator()(const vtkm::Vec<vtkm::Id, 2>& edgeIndices,
const CoordinatesPortalType& coordsPortal,
const ScalarFieldPortalType& fieldPortal) const
{
vtkm::Id point1Idx = edgeIndices[0];
vtkm::Id point2Idx = edgeIndices[1];
vtkm::Vec<vtkm::Float32, 3> point1 = coordsPortal.Get(edgeIndices[0]);
vtkm::Vec<vtkm::Float32, 3> point2 = coordsPortal.Get(edgeIndices[1]);
TransformWorldToViewport(point1);
TransformWorldToViewport(point2);
vtkm::Float32 x1 = point1[0];
vtkm::Float32 y1 = point1[1];
vtkm::Float32 z1 = point1[2];
vtkm::Float32 x2 = point2[0];
vtkm::Float32 y2 = point2[1];
vtkm::Float32 z2 = point2[2];
// If the line is steep, i.e., the height is greater than the width, then
// transpose the co-ordinates to prevent "holes" in the line. This ensures
// that we pick the co-ordinate which grows at a lesser rate than the other.
bool transposed = vtkm::Abs(y2 - y1) > vtkm::Abs(x2 - x1);
if (transposed)
{
std::swap(x1, y1);
std::swap(x2, y2);
}
// Ensure we are always going from left to right
if (x1 > x2)
{
std::swap(x1, x2);
std::swap(y1, y2);
std::swap(z1, z2);
}
vtkm::Float32 dx = x2 - x1;
vtkm::Float32 dy = y2 - y1;
vtkm::Float32 gradient = (dx == 0.0) ? 1.0f : (dy / dx);
vtkm::Float32 xEnd = vtkm::Round(x1);
vtkm::Float32 yEnd = y1 + gradient * (xEnd - x1);
vtkm::Float32 xGap = ReverseFractionalPart(x1 + 0.5f);
vtkm::Float32 xPxl1 = xEnd, yPxl1 = IntegerPart(yEnd);
vtkm::Float64 point1Field = fieldPortal.Get(point1Idx);
vtkm::Float64 point2Field = fieldPortal.Get(point2Idx);
// Plot first endpoint
vtkm::Vec<vtkm::Float32, 4> color = GetColor(point1Field);
if (transposed)
{
Plot(yPxl1, xPxl1, z1, color, ReverseFractionalPart(yEnd) * xGap);
Plot(yPxl1 + 1, xPxl1, z1, color, FractionalPart(yEnd) * xGap);
}
else