diff --git a/data/baseline/rendering/glyph_scalar/cells.png b/data/baseline/rendering/glyph_scalar/cells.png new file mode 100644 index 000000000..869ecc56e --- /dev/null +++ b/data/baseline/rendering/glyph_scalar/cells.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b70d68db818220a5c30d34b4dd14db000d6a2d7f4e117c5509f03e236186825b +size 20123 diff --git a/data/baseline/rendering/glyph_scalar/regular3D.png b/data/baseline/rendering/glyph_scalar/regular3D.png new file mode 100644 index 000000000..60302f038 --- /dev/null +++ b/data/baseline/rendering/glyph_scalar/regular3D.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8012ca572ca38b64fda0f617a9471a48d268e35c12a1352ce3725e8e27577a58 +size 16910 diff --git a/data/baseline/rendering/glyph_scalar/variable_axes_regular3D.png b/data/baseline/rendering/glyph_scalar/variable_axes_regular3D.png new file mode 100644 index 000000000..50c6caa21 --- /dev/null +++ b/data/baseline/rendering/glyph_scalar/variable_axes_regular3D.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ad37f93e562178c5491e06256083630ad6b5d500f9b19683a72ad91c0b4ab05 +size 95406 diff --git a/data/baseline/rendering/glyph_scalar/variable_quads_regular3D.png b/data/baseline/rendering/glyph_scalar/variable_quads_regular3D.png new file mode 100644 index 000000000..673c41b4a --- /dev/null +++ b/data/baseline/rendering/glyph_scalar/variable_quads_regular3D.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e500611307400dd0d618a37d4160722d471a971db4f4accb698c207259c38bb7 +size 25221 diff --git a/data/baseline/rendering/glyph_scalar/variable_regular3D.png b/data/baseline/rendering/glyph_scalar/variable_regular3D.png new file mode 100644 index 000000000..74c3e77b9 --- /dev/null +++ b/data/baseline/rendering/glyph_scalar/variable_regular3D.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91506c6a411f536fcf549ecdb74520d072b184d4b6e168c19f11b864db0f6bb4 +size 37260 diff --git a/data/baseline/rendering/glyph_scalar/variable_spheres_regular3D.png b/data/baseline/rendering/glyph_scalar/variable_spheres_regular3D.png new file mode 100644 index 000000000..477a71483 --- /dev/null +++ b/data/baseline/rendering/glyph_scalar/variable_spheres_regular3D.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb998a6d7ae70371a82e1de0778fcc0259996d0bb13bbf55bc14a9efd0f663e4 +size 131853 diff --git a/data/baseline/rendering/glyph_vector/points_arrows_cownose.png b/data/baseline/rendering/glyph_vector/points_arrows_cownose.png new file mode 100644 index 000000000..176859977 --- /dev/null +++ b/data/baseline/rendering/glyph_vector/points_arrows_cownose.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6658a13bb64a4921bda3ed49d2de7021cf2e31257e5b9e5bbbbfd2ad3c85c044 +size 21634 diff --git a/vtkm/rendering/Actor.cxx b/vtkm/rendering/Actor.cxx index c6ec807c1..349cf5244 100644 --- a/vtkm/rendering/Actor.cxx +++ b/vtkm/rendering/Actor.cxx @@ -83,8 +83,6 @@ Actor::Actor(const vtkm::cont::UnknownCellSet& cells, void Actor::Init(const vtkm::cont::CoordinateSystem& coordinates, const vtkm::cont::Field& scalarField) { - VTKM_ASSERT(scalarField.GetData().GetNumberOfComponentsFlat() == 1); - scalarField.GetRange(&this->Internals->ScalarRange); this->Internals->SpatialBounds = coordinates.GetBounds(); } diff --git a/vtkm/rendering/CMakeLists.txt b/vtkm/rendering/CMakeLists.txt index cf7a5e3c4..9fd4ff251 100644 --- a/vtkm/rendering/CMakeLists.txt +++ b/vtkm/rendering/CMakeLists.txt @@ -35,12 +35,16 @@ set(headers Cylinderizer.h DecodePNG.h # deprecated EncodePNG.h # deprecated + GlyphType.h LineRenderer.h LineRendererBatcher.h MatrixHelpers.h Scene.h Mapper.h MapperCylinder.h + MapperGlyphBase.h + MapperGlyphScalar.h + MapperGlyphVector.h MapperPoint.h MapperQuad.h MapperRayTracer.h @@ -103,6 +107,9 @@ set(device_sources ConnectivityProxy.cxx LineRendererBatcher.cxx MapperCylinder.cxx + MapperGlyphBase.cxx + MapperGlyphScalar.cxx + MapperGlyphVector.cxx MapperPoint.cxx MapperQuad.cxx MapperRayTracer.cxx @@ -119,6 +126,10 @@ set(device_sources raytracing/ConnectivityTracer.cxx raytracing/CylinderExtractor.cxx raytracing/CylinderIntersector.cxx + raytracing/GlyphExtractor.cxx + raytracing/GlyphExtractorVector.cxx + raytracing/GlyphIntersector.cxx + raytracing/GlyphIntersectorVector.cxx raytracing/MeshConnectivityBuilder.cxx raytracing/QuadExtractor.cxx raytracing/QuadIntersector.cxx diff --git a/vtkm/rendering/GlyphType.h b/vtkm/rendering/GlyphType.h new file mode 100644 index 000000000..f74ae5b2e --- /dev/null +++ b/vtkm/rendering/GlyphType.h @@ -0,0 +1,30 @@ +//============================================================================ +// 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. +//============================================================================ +#ifndef vtk_m_rendering_GlyphType_h +#define vtk_m_rendering_GlyphType_h + +namespace vtkm +{ +namespace rendering +{ + +enum struct GlyphType +{ + Arrow, + Axes, + Cube, + Quad, + Sphere, +}; + +} +} + +#endif diff --git a/vtkm/rendering/MapperGlyphBase.cxx b/vtkm/rendering/MapperGlyphBase.cxx new file mode 100644 index 000000000..4f0ee6c36 --- /dev/null +++ b/vtkm/rendering/MapperGlyphBase.cxx @@ -0,0 +1,161 @@ +//============================================================================ +// 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. +//============================================================================ + +#include + +#include +#include + +namespace vtkm +{ +namespace rendering +{ + +MapperGlyphBase::MapperGlyphBase() + : Canvas(nullptr) + , CompositeBackground(true) + , UseNodes(true) + , UseStride(false) + , Stride(1) + , ScaleByValue(false) + , BaseSize(-1.f) + , ScaleDelta(0.5f) +{ +} + +MapperGlyphBase::~MapperGlyphBase() {} + +void MapperGlyphBase::SetCanvas(vtkm::rendering::Canvas* canvas) +{ + vtkm::rendering::CanvasRayTracer* canvasRT = + dynamic_cast(canvas); + if (canvasRT == nullptr) + { + throw vtkm::cont::ErrorBadValue("MapperGlyphBase: bad canvas type. Must be CanvasRayTracer"); + } + + this->Canvas = canvasRT; +} + +vtkm::rendering::Canvas* MapperGlyphBase::GetCanvas() const +{ + return this->Canvas; +} + +bool MapperGlyphBase::GetUseCells() const +{ + return !this->UseNodes; +} + +void MapperGlyphBase::SetUseCells() +{ + this->UseNodes = false; +} + +bool MapperGlyphBase::GetUseNodes() const +{ + return this->UseNodes; +} + +void MapperGlyphBase::SetUseNodes() +{ + this->UseNodes = true; +} + +vtkm::Float32 MapperGlyphBase::GetBaseSize() const +{ + return this->BaseSize; +} + +void MapperGlyphBase::SetBaseSize(vtkm::Float32 size) +{ + if (size <= 0.f) + { + throw vtkm::cont::ErrorBadValue("MapperGlyphBase: base size must be positive"); + } + this->BaseSize = size; +} + +bool MapperGlyphBase::GetScaleByValue() const +{ + return this->ScaleByValue; +} + +void MapperGlyphBase::SetScaleByValue(bool on) +{ + this->ScaleByValue = on; +} + +vtkm::Float32 MapperGlyphBase::GetScaleDelta() const +{ + return this->ScaleDelta; +} + +void MapperGlyphBase::SetScaleDelta(vtkm::Float32 delta) +{ + if (delta < 0.0f) + { + throw vtkm::cont::ErrorBadValue("MapperGlyphBase: scale delta must be non-negative"); + } + + this->ScaleDelta = delta; +} + +bool MapperGlyphBase::GetUseStride() const +{ + return this->UseStride; +} + +void MapperGlyphBase::SetUseStride(bool on) +{ + this->UseStride = on; +} + +vtkm::Id MapperGlyphBase::GetStride() const +{ + return this->Stride; +} + +void MapperGlyphBase::SetStride(vtkm::Id stride) +{ + if (stride < 1) + { + throw vtkm::cont::ErrorBadValue("MapperGlyphBase: stride must be positive"); + } + this->Stride = stride; +} + +void MapperGlyphBase::SetCompositeBackground(bool on) +{ + this->CompositeBackground = on; +} + +vtkm::cont::DataSet MapperGlyphBase::FilterPoints(const vtkm::cont::UnknownCellSet& cellSet, + const vtkm::cont::CoordinateSystem& coords, + const vtkm::cont::Field& field) const +{ + vtkm::cont::DataSet result; + result.SetCellSet(cellSet); + result.AddCoordinateSystem(coords); + result.AddField(field); + + if (this->UseStride) + { + vtkm::filter::entity_extraction::MaskPoints pointMasker; + pointMasker.SetCompactPoints(true); + pointMasker.SetStride(this->Stride); + result = pointMasker.Execute(result); + } + + return result; +} + +} +} // namespace vtkm::rendering diff --git a/vtkm/rendering/MapperGlyphBase.h b/vtkm/rendering/MapperGlyphBase.h new file mode 100644 index 000000000..d44814218 --- /dev/null +++ b/vtkm/rendering/MapperGlyphBase.h @@ -0,0 +1,76 @@ +//============================================================================ +// 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. +//============================================================================ +#ifndef vtk_m_rendering_MapperGlyphBase_h +#define vtk_m_rendering_MapperGlyphBase_h + +#include +#include +#include + +#include + +namespace vtkm +{ +namespace rendering +{ + +class CanvasRayTracer; + +class VTKM_RENDERING_EXPORT MapperGlyphBase : public Mapper +{ +public: + MapperGlyphBase(); + + virtual ~MapperGlyphBase(); + + void SetCanvas(vtkm::rendering::Canvas* canvas) override; + virtual vtkm::rendering::Canvas* GetCanvas() const override; + + virtual bool GetUseCells() const; + virtual void SetUseCells(); + virtual bool GetUseNodes() const; + virtual void SetUseNodes(); + + virtual bool GetUseStride() const; + virtual void SetUseStride(bool on); + virtual vtkm::Id GetStride() const; + virtual void SetStride(vtkm::Id stride); + + virtual vtkm::Float32 GetBaseSize() const; + virtual void SetBaseSize(vtkm::Float32 size); + virtual bool GetScaleByValue() const; + virtual void SetScaleByValue(bool on); + virtual vtkm::Float32 GetScaleDelta() const; + virtual void SetScaleDelta(vtkm::Float32 delta); + + virtual void SetCompositeBackground(bool on); + +protected: + virtual vtkm::cont::DataSet FilterPoints(const vtkm::cont::UnknownCellSet& cellSet, + const vtkm::cont::CoordinateSystem& coords, + const vtkm::cont::Field& scalarField) const; + + + vtkm::rendering::CanvasRayTracer* Canvas; + bool CompositeBackground; + + bool UseNodes; + + bool UseStride; + vtkm::Id Stride; + + bool ScaleByValue; + vtkm::Float32 BaseSize; + vtkm::Float32 ScaleDelta; +}; +} +} //namespace vtkm::rendering + +#endif //vtk_m_rendering_MapperGlyphBase_h diff --git a/vtkm/rendering/MapperGlyphScalar.cxx b/vtkm/rendering/MapperGlyphScalar.cxx new file mode 100644 index 000000000..c175362d5 --- /dev/null +++ b/vtkm/rendering/MapperGlyphScalar.cxx @@ -0,0 +1,494 @@ +//============================================================================ +// 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. +//============================================================================ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace vtkm +{ +namespace rendering +{ +namespace +{ +// Packed frame buffer value with color set as black and depth as 1.0f +constexpr vtkm::Int64 ClearValue = 0x3F800000000000FF; + +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 + +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_EXEC_CONT +vtkm::UInt32 PackColor(vtkm::Float32 r, vtkm::Float32 g, vtkm::Float32 b, vtkm::Float32 a); + +VTKM_EXEC_CONT +vtkm::UInt32 PackColor(const vtkm::Vec4f_32& 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; +} + +VTKM_EXEC_CONT +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::Vec4f_32& 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; +} + +class PackIntoFrameBuffer : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void(FieldIn, FieldIn, FieldOut); + using ExecutionSignature = void(_1, _2, _3); + + VTKM_CONT + PackIntoFrameBuffer() {} + + VTKM_EXEC + void operator()(const vtkm::Vec4f_32& color, + const vtkm::Float32& depth, + vtkm::Int64& outValue) const + { + PackedValue packed; + packed.Ints.Color = PackColor(color); + packed.Floats.Depth = depth; + outValue = packed.Raw; + } +}; //class PackIntoFrameBuffer + +class UnpackFromFrameBuffer : public vtkm::worklet::WorkletMapField +{ +public: + VTKM_CONT + UnpackFromFrameBuffer() {} + + using ControlSignature = void(FieldIn, WholeArrayOut, WholeArrayOut); + using ExecutionSignature = void(_1, _2, _3, WorkIndex); + + template + VTKM_EXEC void operator()(const vtkm::Int64& packedValue, + ColorBufferPortal& colorBuffer, + DepthBufferPortal& depthBuffer, + const vtkm::Id& index) const + { + PackedValue packed; + packed.Raw = packedValue; + float depth = packed.Floats.Depth; + if (depth <= depthBuffer.Get(index)) + { + vtkm::Vec4f_32 color; + UnpackColor(packed.Ints.Color, color); + colorBuffer.Set(index, color); + depthBuffer.Set(index, depth); + } + } +}; // class UnpackFromFrameBuffer + +class GetNormalizedScalars : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void(FieldIn pointIds, FieldOut normalizedScalars, WholeArrayIn field); + using ExecutionSignature = void(_1, _2, _3); + + VTKM_CONT GetNormalizedScalars(vtkm::Float32 minScalar, vtkm::Float32 maxScalar) + : MinScalar(minScalar) + { + if (minScalar >= maxScalar) + { + this->InverseScalarDelta = 0.0f; + } + else + { + this->InverseScalarDelta = 1.0f / (maxScalar - minScalar); + } + } + + template + VTKM_EXEC void operator()(const vtkm::Id& pointId, + vtkm::Float32& normalizedScalar, + const FieldPortalType& field) const + { + normalizedScalar = static_cast(field.Get(pointId)); + normalizedScalar = (normalizedScalar - this->MinScalar) * this->InverseScalarDelta; + } + +private: + vtkm::Float32 MinScalar; + vtkm::Float32 InverseScalarDelta; +}; // class GetNormalizedScalars + +class BillboardGlyphPlotter : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void(FieldIn pointIds, + FieldIn sizes, + FieldIn normalizedScalar, + WholeArrayIn coords, + WholeArrayIn colorMap, + AtomicArrayInOut frameBuffer); + + using ExecutionSignature = void(_1, _2, _3, _4, _5, _6); + + VTKM_CONT + BillboardGlyphPlotter(const vtkm::Matrix& worldToProjection, + vtkm::Id width, + vtkm::Id height, + vtkm::Float32 projectionOffset) + : WorldToProjection(worldToProjection) + , Width(width) + , Height(height) + , ProjectionOffset(projectionOffset) + { + } + + template + VTKM_EXEC void operator()(const vtkm::Id& pointId, + const vtkm::Float32& size, + const vtkm::Float32& normalizedScalar, + const CoordinatesPortal& coordsPortal, + const ColorMapPortal& colorMap, + FrameBuffer& frameBuffer) const + { + vtkm::Vec3f_32 point = static_cast(coordsPortal.Get(pointId)); + point = this->TransformWorldToViewport(point); + vtkm::Vec4f_32 color = this->GetColor(normalizedScalar, colorMap); + + vtkm::Float32 halfSize = size / 2.0f; + vtkm::Id x1 = static_cast(vtkm::Round(point[0] - halfSize)); + vtkm::Id x2 = static_cast(vtkm::Round(point[0] + halfSize)); + vtkm::Id y1 = static_cast(vtkm::Round(point[1] - halfSize)); + vtkm::Id y2 = static_cast(vtkm::Round(point[1] + halfSize)); + vtkm::Float32 depth = point[2]; + + for (vtkm::Id x = x1; x <= x2; ++x) + { + for (vtkm::Id y = y1; y <= y2; ++y) + { + this->SetColor(x, y, depth, color, frameBuffer); + } + } + } + +private: + VTKM_EXEC + vtkm::Vec3f_32 TransformWorldToViewport(const vtkm::Vec3f_32& point) const + { + vtkm::Vec4f_32 temp(point[0], point[1], point[2], 1.0f); + vtkm::Vec3f_32 result; + temp = vtkm::MatrixMultiply(this->WorldToProjection, temp); + for (vtkm::IdComponent i = 0; i < 3; ++i) + { + result[i] = temp[i] / temp[3]; + } + result[0] = (result[0] * 0.5f + 0.5f) * vtkm::Float32(this->Width); + result[1] = (result[1] * 0.5f + 0.5f) * vtkm::Float32(this->Height); + result[2] = result[2] * 0.5f + 0.5f; + // Offset the point to a bit towards the camera. This is to ensure that the front faces of + // the wireframe wins the z-depth check against the surface render, and is in addition to the + // existing camera space offset. + result[2] -= this->ProjectionOffset; + return result; + } + + template + VTKM_EXEC vtkm::Vec4f_32 GetColor(vtkm::Float32 normalizedScalar, + const ColorMapPortal& colorMap) const + { + vtkm::Id colorMapSize = colorMap.GetNumberOfValues() - 1; + vtkm::Id colorIdx = static_cast(normalizedScalar * colorMapSize); + colorIdx = vtkm::Min(colorMapSize, vtkm::Max(vtkm::Id(0), colorIdx)); + return colorMap.Get(colorIdx); + } + + template + VTKM_EXEC void SetColor(vtkm::Id x, + vtkm::Id y, + vtkm::Float32 depth, + const vtkm::Vec4f_32& color, + FrameBuffer& frameBuffer) const + { + if (x < 0 || x >= this->Width || y < 0 || y >= this->Height) + { + return; + } + + vtkm::Id index = y * this->Width + x; + PackedValue current, next; + current.Raw = ClearValue; + next.Floats.Depth = depth; + + vtkm::Vec4f_32 currentColor; + do + { + UnpackColor(current.Ints.Color, currentColor); + next.Ints.Color = PackColor(color); + frameBuffer.CompareExchange(index, ¤t.Raw, next.Raw); + } while (current.Floats.Depth > next.Floats.Depth); + } + + const vtkm::Matrix WorldToProjection; + const vtkm::Id Width; + const vtkm::Id Height; + const vtkm::Float32 ProjectionOffset; +}; // class BillboardGlyphPlotter + +} + +MapperGlyphScalar::MapperGlyphScalar() + : MapperGlyphBase() + , GlyphType(vtkm::rendering::GlyphType::Sphere) +{ +} + +MapperGlyphScalar::~MapperGlyphScalar() {} + +vtkm::rendering::GlyphType MapperGlyphScalar::GetGlyphType() const +{ + return this->GlyphType; +} + +void MapperGlyphScalar::SetGlyphType(vtkm::rendering::GlyphType glyphType) +{ + if (!(glyphType == vtkm::rendering::GlyphType::Axes || + glyphType == vtkm::rendering::GlyphType::Cube || + glyphType == vtkm::rendering::GlyphType::Quad || + glyphType == vtkm::rendering::GlyphType::Sphere)) + { + throw vtkm::cont::ErrorBadValue("MapperGlyphScalar: bad glyph type"); + } + + this->GlyphType = glyphType; +} + +void MapperGlyphScalar::RenderCells(const vtkm::cont::UnknownCellSet& cellset, + const vtkm::cont::CoordinateSystem& coords, + const vtkm::cont::Field& scalarField, + const vtkm::cont::ColorTable& vtkmNotUsed(colorTable), + const vtkm::rendering::Camera& camera, + const vtkm::Range& scalarRange) +{ + raytracing::Logger* logger = raytracing::Logger::GetInstance(); + + vtkm::rendering::raytracing::RayTracer tracer; + tracer.Clear(); + + logger->OpenLogEntry("mapper_glyph_scalar"); + vtkm::cont::Timer tot_timer; + tot_timer.Start(); + vtkm::cont::Timer timer; + + vtkm::Bounds coordBounds = coords.GetBounds(); + vtkm::Float32 baseSize = this->BaseSize; + if (baseSize == -1.f) + { + // set a default size + vtkm::Float64 lx = coordBounds.X.Length(); + vtkm::Float64 ly = coordBounds.Y.Length(); + vtkm::Float64 lz = coordBounds.Z.Length(); + vtkm::Float64 mag = vtkm::Sqrt(lx * lx + ly * ly + lz * lz); + if (this->GlyphType == vtkm::rendering::GlyphType::Quad) + { + baseSize = 20.0f; + } + else + { + // same as used in vtk ospray + constexpr vtkm::Float64 heuristic = 500.; + baseSize = static_cast(mag / heuristic); + } + } + + vtkm::rendering::raytracing::GlyphExtractor glyphExtractor; + + vtkm::cont::DataSet processedDataSet = this->FilterPoints(cellset, coords, scalarField); + vtkm::cont::UnknownCellSet processedCellSet = processedDataSet.GetCellSet(); + vtkm::cont::CoordinateSystem processedCoords = processedDataSet.GetCoordinateSystem(); + vtkm::cont::Field processedField = processedDataSet.GetField(0); + + if (this->ScaleByValue) + { + vtkm::Float32 minSize = baseSize - baseSize * this->ScaleDelta; + vtkm::Float32 maxSize = baseSize + baseSize * this->ScaleDelta; + if (this->UseNodes) + { + glyphExtractor.ExtractCoordinates(processedCoords, processedField, minSize, maxSize); + } + else + { + glyphExtractor.ExtractCells(processedCellSet, processedField, minSize, maxSize); + } + } + else + { + if (this->UseNodes) + { + glyphExtractor.ExtractCoordinates(processedCoords, baseSize); + } + else + { + glyphExtractor.ExtractCells(processedCellSet, baseSize); + } + } + + if (this->GlyphType == vtkm::rendering::GlyphType::Quad) + { + vtkm::cont::ArrayHandle pointIds = glyphExtractor.GetPointIds(); + vtkm::cont::ArrayHandle sizes = glyphExtractor.GetSizes(); + + vtkm::cont::ArrayHandle normalizedScalars; + vtkm::Float32 rangeMin = static_cast(scalarRange.Min); + vtkm::Float32 rangeMax = static_cast(scalarRange.Max); + vtkm::cont::Invoker invoker; + invoker(GetNormalizedScalars{ rangeMin, rangeMax }, + pointIds, + normalizedScalars, + vtkm::rendering::raytracing::GetScalarFieldArray(scalarField)); + + vtkm::cont::ArrayHandle frameBuffer; + invoker(PackIntoFrameBuffer{}, + this->Canvas->GetColorBuffer(), + this->Canvas->GetDepthBuffer(), + frameBuffer); + + vtkm::Range clippingRange = camera.GetClippingRange(); + vtkm::Float64 offset1 = (clippingRange.Max - clippingRange.Min) / 1.0e4; + vtkm::Float64 offset2 = clippingRange.Min / 2.0; + vtkm::Float32 offset = static_cast(vtkm::Min(offset1, offset2)); + vtkm::Matrix modelMatrix; + vtkm::MatrixIdentity(modelMatrix); + modelMatrix[2][3] = offset; + vtkm::Matrix worldToCamera = + vtkm::MatrixMultiply(modelMatrix, camera.CreateViewMatrix()); + vtkm::Matrix worldToProjection = vtkm::MatrixMultiply( + camera.CreateProjectionMatrix(this->Canvas->GetWidth(), this->Canvas->GetHeight()), + worldToCamera); + vtkm::Float32 projectionOffset = + vtkm::Max(0.03f / static_cast(camera.GetClippingRange().Length()), 1e-4f); + invoker( + BillboardGlyphPlotter{ + worldToProjection, this->Canvas->GetWidth(), this->Canvas->GetHeight(), projectionOffset }, + pointIds, + sizes, + normalizedScalars, + coords, + this->ColorMap, + frameBuffer); + + timer.Start(); + invoker(UnpackFromFrameBuffer{}, + frameBuffer, + this->Canvas->GetColorBuffer(), + this->Canvas->GetDepthBuffer()); + } + else + { + vtkm::Bounds shapeBounds; + if (glyphExtractor.GetNumberOfGlyphs() > 0) + { + auto glyphIntersector = std::make_shared(this->GlyphType); + glyphIntersector->SetData( + processedCoords, glyphExtractor.GetPointIds(), glyphExtractor.GetSizes()); + tracer.AddShapeIntersector(glyphIntersector); + shapeBounds.Include(glyphIntersector->GetShapeBounds()); + } + + // + // Create rays + // + vtkm::Int32 width = (vtkm::Int32)this->Canvas->GetWidth(); + vtkm::Int32 height = (vtkm::Int32)this->Canvas->GetHeight(); + + vtkm::rendering::raytracing::Camera RayCamera; + vtkm::rendering::raytracing::Ray Rays; + + RayCamera.SetParameters(camera, width, height); + + RayCamera.CreateRays(Rays, shapeBounds); + Rays.Buffers.at(0).InitConst(0.f); + raytracing::RayOperations::MapCanvasToRays(Rays, camera, *this->Canvas); + + tracer.SetField(processedField, scalarRange); + tracer.GetCamera() = RayCamera; + tracer.SetColorMap(this->ColorMap); + tracer.Render(Rays); + + timer.Start(); + this->Canvas->WriteToCanvas(Rays, Rays.Buffers.at(0).Buffer, camera); + } + + if (this->CompositeBackground) + { + this->Canvas->BlendBackground(); + } + vtkm::Float64 time = timer.GetElapsedTime(); + logger->AddLogData("write_to_canvas", time); + time = tot_timer.GetElapsedTime(); + logger->CloseLogEntry(time); +} + +vtkm::rendering::Mapper* MapperGlyphScalar::NewCopy() const +{ + return new vtkm::rendering::MapperGlyphScalar(*this); +} +} +} // vtkm::rendering diff --git a/vtkm/rendering/MapperGlyphScalar.h b/vtkm/rendering/MapperGlyphScalar.h new file mode 100644 index 000000000..5d0be9135 --- /dev/null +++ b/vtkm/rendering/MapperGlyphScalar.h @@ -0,0 +1,46 @@ +//============================================================================ +// 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. +//============================================================================ +#ifndef vtk_m_rendering_MapperGlyphScalar_h +#define vtk_m_rendering_MapperGlyphScalar_h + +#include +#include + +namespace vtkm +{ +namespace rendering +{ + +class VTKM_RENDERING_EXPORT MapperGlyphScalar : public vtkm::rendering::MapperGlyphBase +{ +public: + MapperGlyphScalar(); + + ~MapperGlyphScalar(); + + vtkm::rendering::GlyphType GetGlyphType() const; + void SetGlyphType(vtkm::rendering::GlyphType glyphType); + + void RenderCells(const vtkm::cont::UnknownCellSet& cellset, + const vtkm::cont::CoordinateSystem& coords, + const vtkm::cont::Field& scalarField, + const vtkm::cont::ColorTable& colorTable, + const vtkm::rendering::Camera& camera, + const vtkm::Range& scalarRange) override; + + vtkm::rendering::Mapper* NewCopy() const override; + +protected: + vtkm::rendering::GlyphType GlyphType; +}; +} +} //namespace vtkm::rendering + +#endif //vtk_m_rendering_MapperGlyphScalar_h diff --git a/vtkm/rendering/MapperGlyphVector.cxx b/vtkm/rendering/MapperGlyphVector.cxx new file mode 100644 index 000000000..873d1eaec --- /dev/null +++ b/vtkm/rendering/MapperGlyphVector.cxx @@ -0,0 +1,174 @@ +//============================================================================ +// 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. +//============================================================================ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vtkm +{ +namespace rendering +{ + +MapperGlyphVector::MapperGlyphVector() + : MapperGlyphBase() + , GlyphType(vtkm::rendering::GlyphType::Arrow) +{ +} + +MapperGlyphVector::~MapperGlyphVector() {} + +vtkm::rendering::GlyphType MapperGlyphVector::GetGlyphType() const +{ + return this->GlyphType; +} + +void MapperGlyphVector::SetGlyphType(vtkm::rendering::GlyphType glyphType) +{ + if (!(glyphType == vtkm::rendering::GlyphType::Arrow)) + { + throw vtkm::cont::ErrorBadValue("MapperGlyphVector: bad glyph type"); + } + + this->GlyphType = glyphType; +} + +void MapperGlyphVector::RenderCells(const vtkm::cont::UnknownCellSet& cellset, + const vtkm::cont::CoordinateSystem& coords, + const vtkm::cont::Field& field, + const vtkm::cont::ColorTable& vtkmNotUsed(colorTable), + const vtkm::rendering::Camera& camera, + const vtkm::Range& vtkmNotUsed(fieldRange)) +{ + raytracing::Logger* logger = raytracing::Logger::GetInstance(); + + vtkm::rendering::raytracing::RayTracer tracer; + tracer.Clear(); + + logger->OpenLogEntry("mapper_glyph_vector"); + vtkm::cont::Timer tot_timer; + tot_timer.Start(); + vtkm::cont::Timer timer; + + vtkm::Bounds coordBounds = coords.GetBounds(); + vtkm::Float32 baseSize = this->BaseSize; + if (baseSize == -1.f) + { + // set a default size + vtkm::Float64 lx = coordBounds.X.Length(); + vtkm::Float64 ly = coordBounds.Y.Length(); + vtkm::Float64 lz = coordBounds.Z.Length(); + vtkm::Float64 mag = vtkm::Sqrt(lx * lx + ly * ly + lz * lz); + // same as used in vtk ospray + constexpr vtkm::Float64 heuristic = 500.; + baseSize = static_cast(mag / heuristic); + } + + vtkm::rendering::raytracing::GlyphExtractorVector glyphExtractor; + + vtkm::cont::DataSet processedDataSet = this->FilterPoints(cellset, coords, field); + vtkm::cont::UnknownCellSet processedCellSet = processedDataSet.GetCellSet(); + vtkm::cont::CoordinateSystem processedCoords = processedDataSet.GetCoordinateSystem(); + vtkm::cont::Field processedField = processedDataSet.GetField(0); + + if (this->ScaleByValue) + { + vtkm::Float32 minSize = baseSize - baseSize * this->ScaleDelta; + vtkm::Float32 maxSize = baseSize + baseSize * this->ScaleDelta; + if (this->UseNodes) + { + glyphExtractor.ExtractCoordinates(processedCoords, processedField, minSize, maxSize); + } + else + { + glyphExtractor.ExtractCells(processedCellSet, processedField, minSize, maxSize); + } + } + else + { + if (this->UseNodes) + { + glyphExtractor.ExtractCoordinates(processedCoords, processedField, baseSize); + } + else + { + glyphExtractor.ExtractCells(processedCellSet, processedField, baseSize); + } + } + + vtkm::Bounds shapeBounds; + if (glyphExtractor.GetNumberOfGlyphs() > 0) + { + auto glyphIntersector = std::make_shared(this->GlyphType); + if (this->GlyphType == vtkm::rendering::GlyphType::Arrow) + { + vtkm::Float32 arrowBodyRadius = 0.08f * baseSize; + vtkm::Float32 arrowHeadRadius = 0.16f * baseSize; + glyphIntersector->SetArrowRadii(arrowBodyRadius, arrowHeadRadius); + } + glyphIntersector->SetData( + processedCoords, glyphExtractor.GetPointIds(), glyphExtractor.GetSizes()); + + tracer.AddShapeIntersector(glyphIntersector); + shapeBounds.Include(glyphIntersector->GetShapeBounds()); + } + + // + // Create rays + // + vtkm::Int32 width = (vtkm::Int32)this->Canvas->GetWidth(); + vtkm::Int32 height = (vtkm::Int32)this->Canvas->GetHeight(); + + vtkm::rendering::raytracing::Camera RayCamera; + vtkm::rendering::raytracing::Ray Rays; + + RayCamera.SetParameters(camera, width, height); + + RayCamera.CreateRays(Rays, shapeBounds); + Rays.Buffers.at(0).InitConst(0.f); + raytracing::RayOperations::MapCanvasToRays(Rays, camera, *this->Canvas); + + auto magnitudeField = glyphExtractor.GetMagnitudeField(); + auto magnitudeFieldRange = magnitudeField.GetRange().ReadPortal().Get(0); + tracer.SetField(magnitudeField, magnitudeFieldRange); + tracer.GetCamera() = RayCamera; + tracer.SetColorMap(this->ColorMap); + tracer.Render(Rays); + + timer.Start(); + this->Canvas->WriteToCanvas(Rays, Rays.Buffers.at(0).Buffer, camera); + + if (this->CompositeBackground) + { + this->Canvas->BlendBackground(); + } + + vtkm::Float64 time = timer.GetElapsedTime(); + logger->AddLogData("write_to_canvas", time); + time = tot_timer.GetElapsedTime(); + logger->CloseLogEntry(time); +} + +vtkm::rendering::Mapper* MapperGlyphVector::NewCopy() const +{ + return new vtkm::rendering::MapperGlyphVector(*this); +} +} +} // vtkm::rendering diff --git a/vtkm/rendering/MapperGlyphVector.h b/vtkm/rendering/MapperGlyphVector.h new file mode 100644 index 000000000..9d9e6ade3 --- /dev/null +++ b/vtkm/rendering/MapperGlyphVector.h @@ -0,0 +1,46 @@ +//============================================================================ +// 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. +//============================================================================ +#ifndef vtk_m_rendering_MapperGlyphVector_h +#define vtk_m_rendering_MapperGlyphVector_h + +#include +#include + +namespace vtkm +{ +namespace rendering +{ + +class VTKM_RENDERING_EXPORT MapperGlyphVector : public vtkm::rendering::MapperGlyphBase +{ +public: + MapperGlyphVector(); + + ~MapperGlyphVector(); + + vtkm::rendering::GlyphType GetGlyphType() const; + void SetGlyphType(vtkm::rendering::GlyphType glyphType); + + void RenderCells(const vtkm::cont::UnknownCellSet& cellset, + const vtkm::cont::CoordinateSystem& coords, + const vtkm::cont::Field& scalarField, + const vtkm::cont::ColorTable& colorTable, + const vtkm::rendering::Camera& camera, + const vtkm::Range& scalarRange) override; + + vtkm::rendering::Mapper* NewCopy() const override; + +protected: + vtkm::rendering::GlyphType GlyphType; +}; +} +} //namespace vtkm::rendering + +#endif //vtk_m_rendering_MapperGlyphVector_h diff --git a/vtkm/rendering/raytracing/CMakeLists.txt b/vtkm/rendering/raytracing/CMakeLists.txt index 9ec3ad07f..a05b54189 100644 --- a/vtkm/rendering/raytracing/CMakeLists.txt +++ b/vtkm/rendering/raytracing/CMakeLists.txt @@ -20,6 +20,10 @@ set(headers ConnectivityTracer.h CylinderExtractor.h CylinderIntersector.h + GlyphExtractor.h + GlyphExtractorVector.h + GlyphIntersector.h + GlyphIntersectorVector.h Logger.h MeshConnectivityBuilder.h MeshConnectivityContainers.h diff --git a/vtkm/rendering/raytracing/GlyphExtractor.cxx b/vtkm/rendering/raytracing/GlyphExtractor.cxx new file mode 100644 index 000000000..d44178f8c --- /dev/null +++ b/vtkm/rendering/raytracing/GlyphExtractor.cxx @@ -0,0 +1,223 @@ +//============================================================================ +// 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. +//============================================================================ + +#include +#include +#include +#include +#include +#include + +namespace vtkm +{ +namespace rendering +{ +namespace raytracing +{ + +namespace +{ + +class CountPoints : public vtkm::worklet::WorkletVisitCellsWithPoints +{ +public: + VTKM_CONT + CountPoints() {} + typedef void ControlSignature(CellSetIn cellset, FieldOut); + typedef void ExecutionSignature(CellShape, _2); + + template + VTKM_EXEC void operator()(ShapeType shape, vtkm::Id& points) const + { + points = (shape.Id == vtkm::CELL_SHAPE_VERTEX) ? 1 : 0; + } +}; // class CountPoints + +class Pointify : public vtkm::worklet::WorkletVisitCellsWithPoints +{ + +public: + VTKM_CONT + Pointify() {} + typedef void ControlSignature(CellSetIn cellset, FieldInCell, WholeArrayOut); + typedef void ExecutionSignature(_2, CellShape, PointIndices, WorkIndex, _3); + + template + VTKM_EXEC void operator()(const vtkm::Id& pointOffset, + ShapeType shape, + const VecType& vtkmNotUsed(cellIndices), + const vtkm::Id& cellId, + OutputPortal& outputIndices) const + { + if (shape.Id == vtkm::CELL_SHAPE_VERTEX) + { + outputIndices.Set(pointOffset, cellId); + } + } +}; //class Pointify + +class GetFieldSize : public vtkm::worklet::WorkletMapField +{ +protected: + // vtkm::Float64 is used to handle field values that are very small or very large + // and could loose precision if vtkm::Float32 is used. + vtkm::Float64 MinSize; + vtkm::Float64 SizeDelta; + vtkm::Float64 MinValue; + vtkm::Float64 InverseDelta; + +public: + VTKM_CONT + GetFieldSize(vtkm::Float64 minSize, vtkm::Float64 maxSize, vtkm::Range scalarRange) + : MinSize(minSize) + , SizeDelta(maxSize - minSize) + , MinValue(scalarRange.Min) + { + vtkm::Float64 delta = scalarRange.Max - scalarRange.Min; + if (delta != 0.) + InverseDelta = 1. / (delta); + else + InverseDelta = 0.; // just map scalar to 0; + } + + typedef void ControlSignature(FieldIn, FieldOut, WholeArrayIn); + typedef void ExecutionSignature(_1, _2, _3); + + template + VTKM_EXEC void operator()(const vtkm::Id& pointId, + vtkm::Float32& size, + const ScalarPortalType& scalars) const + { + vtkm::Float64 scalar = vtkm::Float64(scalars.Get(pointId)); + vtkm::Float64 t = (scalar - this->MinValue) * this->InverseDelta; + size = static_cast(this->MinSize + t * this->SizeDelta); + } + +}; //class GetFieldSize + +} //namespace + +GlyphExtractor::GlyphExtractor() = default; + +void GlyphExtractor::ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords, + const vtkm::Float32 size) +{ + this->SetPointIdsFromCoords(coords); + this->SetUniformSize(size); +} + +void GlyphExtractor::ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords, + const vtkm::cont::Field& field, + const vtkm::Float32 minSize, + const vtkm::Float32 maxSize) +{ + this->SetPointIdsFromCoords(coords); + this->SetVaryingSize(minSize, maxSize, field); +} + +void GlyphExtractor::ExtractCells(const vtkm::cont::UnknownCellSet& cells, const vtkm::Float32 size) +{ + this->SetPointIdsFromCells(cells); + this->SetUniformSize(size); +} +void GlyphExtractor::ExtractCells(const vtkm::cont::UnknownCellSet& cells, + const vtkm::cont::Field& field, + const vtkm::Float32 minSize, + const vtkm::Float32 maxSize) +{ + this->SetPointIdsFromCells(cells); + this->SetVaryingSize(minSize, maxSize, field); +} + +void GlyphExtractor::SetUniformSize(const vtkm::Float32 size) +{ + const vtkm::Id numValues = this->PointIds.GetNumberOfValues(); + Sizes.AllocateAndFill(numValues, size); +} + +void GlyphExtractor::SetPointIdsFromCoords(const vtkm::cont::CoordinateSystem& coords) +{ + vtkm::Id size = coords.GetNumberOfPoints(); + vtkm::cont::ArrayCopy(vtkm::cont::ArrayHandleIndex(size), this->PointIds); +} + +void GlyphExtractor::SetPointIdsFromCells(const vtkm::cont::UnknownCellSet& cells) +{ + using SingleType = vtkm::cont::CellSetSingleType<>; + vtkm::Id numCells = cells.GetNumberOfCells(); + if (numCells == 0) + { + return; + } + // + // look for points in the cell set + // + if (cells.CanConvert>()) + { + auto cellsExplicit = cells.AsCellSet>(); + + vtkm::cont::ArrayHandle points; + vtkm::worklet::DispatcherMapTopology(CountPoints()).Invoke(cellsExplicit, points); + + vtkm::Id totalPoints = 0; + totalPoints = vtkm::cont::Algorithm::Reduce(points, vtkm::Id(0)); + + vtkm::cont::ArrayHandle cellOffsets; + vtkm::cont::Algorithm::ScanExclusive(points, cellOffsets); + PointIds.Allocate(totalPoints); + + vtkm::worklet::DispatcherMapTopology(Pointify()) + .Invoke(cellsExplicit, cellOffsets, this->PointIds); + } + else if (cells.CanConvert()) + { + SingleType pointCells = cells.AsCellSet(); + vtkm::UInt8 shape_id = pointCells.GetCellShape(0); + if (shape_id == vtkm::CELL_SHAPE_VERTEX) + { + vtkm::cont::ArrayCopy(vtkm::cont::ArrayHandleIndex(numCells), this->PointIds); + } + } +} + +void GlyphExtractor::SetVaryingSize(const vtkm::Float32 minSize, + const vtkm::Float32 maxSize, + const vtkm::cont::Field& field) +{ + vtkm::cont::ArrayHandle rangeArray = field.GetRange(); + if (rangeArray.GetNumberOfValues() != 1) + { + throw vtkm::cont::ErrorBadValue("Glyph Extractor: scalar field must have one component"); + } + + vtkm::Range range = rangeArray.ReadPortal().Get(0); + + Sizes.Allocate(this->PointIds.GetNumberOfValues()); + vtkm::worklet::DispatcherMapField(GetFieldSize(minSize, maxSize, range)) + .Invoke(this->PointIds, this->Sizes, vtkm::rendering::raytracing::GetScalarFieldArray(field)); +} + +vtkm::cont::ArrayHandle GlyphExtractor::GetPointIds() +{ + return this->PointIds; +} + +vtkm::cont::ArrayHandle GlyphExtractor::GetSizes() +{ + return this->Sizes; +} + +vtkm::Id GlyphExtractor::GetNumberOfGlyphs() const +{ + return this->PointIds.GetNumberOfValues(); +} +} +} +} //namespace vtkm::rendering::raytracing diff --git a/vtkm/rendering/raytracing/GlyphExtractor.h b/vtkm/rendering/raytracing/GlyphExtractor.h new file mode 100644 index 000000000..bf2070200 --- /dev/null +++ b/vtkm/rendering/raytracing/GlyphExtractor.h @@ -0,0 +1,74 @@ +//============================================================================ +// 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. +//============================================================================ +#ifndef vtk_m_rendering_raytracing_Glyph_Extractor_h +#define vtk_m_rendering_raytracing_Glyph_Extractor_h + +#include + +namespace vtkm +{ +namespace rendering +{ +namespace raytracing +{ + +class GlyphExtractor +{ +public: + GlyphExtractor(); + + // + // Extract all nodes using a constant size + // + void ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords, const vtkm::Float32 size); + + // + // Set size based on scalar field values. Each is interpolated from min to max + // + void ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords, + const vtkm::cont::Field& field, + const vtkm::Float32 minSize, + const vtkm::Float32 maxSize); + + // + // Extract all vertex shapes with constant size + // + void ExtractCells(const vtkm::cont::UnknownCellSet& cells, vtkm::Float32 size); + + // + // Extract all vertex elements with size based on scalar values + // + void ExtractCells(const vtkm::cont::UnknownCellSet& cells, + const vtkm::cont::Field& field, + const vtkm::Float32 minSize, + const vtkm::Float32 maxSize); + + + vtkm::cont::ArrayHandle GetPointIds(); + vtkm::cont::ArrayHandle GetSizes(); + + vtkm::Id GetNumberOfGlyphs() const; + +protected: + void SetUniformSize(const vtkm::Float32 size); + void SetVaryingSize(const vtkm::Float32 minSize, + const vtkm::Float32 maxSize, + const vtkm::cont::Field& field); + + void SetPointIdsFromCoords(const vtkm::cont::CoordinateSystem& coords); + void SetPointIdsFromCells(const vtkm::cont::UnknownCellSet& cells); + + vtkm::cont::ArrayHandle PointIds; + vtkm::cont::ArrayHandle Sizes; +}; // class GlyphExtractor +} +} +} //namespace vtkm::rendering::raytracing +#endif //vtk_m_rendering_raytracing_Glyph_Extractor_h diff --git a/vtkm/rendering/raytracing/GlyphExtractorVector.cxx b/vtkm/rendering/raytracing/GlyphExtractorVector.cxx new file mode 100644 index 000000000..bb1581825 --- /dev/null +++ b/vtkm/rendering/raytracing/GlyphExtractorVector.cxx @@ -0,0 +1,352 @@ +//============================================================================ +// 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. +//============================================================================ + +#include +#include +#include +#include +#include +#include +#include + +namespace vtkm +{ +namespace rendering +{ +namespace raytracing +{ + +namespace +{ + +class CountPoints : public vtkm::worklet::WorkletVisitCellsWithPoints +{ +public: + VTKM_CONT + CountPoints() {} + typedef void ControlSignature(CellSetIn cellset, FieldOut); + typedef void ExecutionSignature(CellShape, _2); + + template + VTKM_EXEC void operator()(ShapeType shape, vtkm::Id& points) const + { + points = (shape.Id == vtkm::CELL_SHAPE_VERTEX) ? 1 : 0; + } +}; // class CountPoints + +class Pointify : public vtkm::worklet::WorkletVisitCellsWithPoints +{ + +public: + VTKM_CONT + Pointify() {} + typedef void ControlSignature(CellSetIn cellset, FieldInCell, WholeArrayOut); + typedef void ExecutionSignature(_2, CellShape, PointIndices, WorkIndex, _3); + + template + VTKM_EXEC void operator()(const vtkm::Id& pointOffset, + ShapeType shape, + const VecType& vtkmNotUsed(cellIndices), + const vtkm::Id& cellId, + OutputPortal& outputIndices) const + { + if (shape.Id == vtkm::CELL_SHAPE_VERTEX) + { + outputIndices.Set(pointOffset, cellId); + } + } +}; //class Pointify + +struct MinFunctor +{ + template + VTKM_EXEC VecType operator()(const VecType& x, const VecType& y) const + { + return (vtkm::MagnitudeSquared(y) < vtkm::MagnitudeSquared(x)) ? y : x; + } +}; + +struct MaxFunctor +{ + template + VTKM_EXEC VecType operator()(const VecType& x, const VecType& y) const + { + return (vtkm::MagnitudeSquared(x) < vtkm::MagnitudeSquared(y)) ? y : x; + } +}; + +class GetFieldSize : public vtkm::worklet::WorkletMapField +{ +protected: + vtkm::Float64 MinSize; + vtkm::Float64 SizeDelta; + vtkm::Float64 MinValueMagnitude; + vtkm::Float64 InverseDelta; + +public: + VTKM_CONT + GetFieldSize(vtkm::Float64 minSize, + vtkm::Float64 maxSize, + vtkm::Vec3f_64 minValue, + vtkm::Vec3f_64 maxValue) + : MinSize(minSize) + , SizeDelta(maxSize - minSize) + { + MinValueMagnitude = vtkm::Magnitude(minValue); + vtkm::Float64 minMag = vtkm::Magnitude(minValue); + vtkm::Float64 maxMag = vtkm::Magnitude(maxValue); + vtkm::Float64 delta = maxMag - minMag; + if (delta != 0.) + InverseDelta = 1. / (delta); + else + InverseDelta = 0.; // just map scalar to 0; + } + + typedef void ControlSignature(FieldIn, FieldOut, WholeArrayIn); + typedef void ExecutionSignature(_1, _2, _3); + + template + VTKM_EXEC void operator()(const vtkm::Id& pointId, + vtkm::Vec3f_32& size, + const FieldPortalType& field) const + { + using ValueType = typename FieldPortalType::ValueType; + + ValueType fieldVal = field.Get(pointId); + vtkm::Float64 fieldValMag = vtkm::Magnitude(fieldVal); + vtkm::Normalize(fieldVal); + vtkm::Float64 t = (fieldValMag - MinValueMagnitude) * InverseDelta; + vtkm::Float64 sizeMag = MinSize + t * SizeDelta; + vtkm::Vec3f_64 tempSize = fieldVal * sizeMag; + + size[0] = static_cast(tempSize[0]); + size[1] = static_cast(tempSize[1]); + size[2] = static_cast(tempSize[2]); + } + +}; //class GetFieldSize + +class FieldMagnitude : public vtkm::worklet::WorkletMapField +{ +public: + VTKM_CONT + FieldMagnitude() {} + + typedef void ControlSignature(FieldIn, WholeArrayIn, WholeArrayInOut); + typedef void ExecutionSignature(_1, _2, _3); + + template + VTKM_EXEC void operator()(const vtkm::Id& pointId, + const FieldPortalType& field, + MagnitudeFieldPortalType& magnitudeField) const + { + using FieldValueType = typename FieldPortalType::ValueType; + + FieldValueType fieldVal = field.Get(pointId); + vtkm::Float32 fieldValMag = static_cast(vtkm::Magnitude(fieldVal)); + magnitudeField.Set(pointId, fieldValMag); + } +}; //class FieldMagnitude + +class UniformFieldMagnitude : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void(FieldIn, FieldOut, WholeArrayIn); + using ExecutionSignature = void(_1, _2, _3); + + VTKM_CONT + UniformFieldMagnitude(vtkm::Float32 uniformMagnitude) + : UniformMagnitude(uniformMagnitude) + { + } + + template + VTKM_EXEC void operator()(const vtkm::Id& pointId, + vtkm::Vec3f_32& size, + const FieldPortalType& field) const + { + vtkm::Vec3f_32 fieldVal = static_cast(field.Get(pointId)); + size = vtkm::Normal(fieldVal) * this->UniformMagnitude; + } + + vtkm::Float32 UniformMagnitude; +}; //class UniformFieldMagnitude + +} //namespace + +GlyphExtractorVector::GlyphExtractorVector() = default; + +void GlyphExtractorVector::ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords, + const vtkm::cont::Field& field, + const vtkm::Float32 size) +{ + this->SetPointIdsFromCoords(coords); + this->SetUniformSize(size, field); +} + +void GlyphExtractorVector::ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords, + const vtkm::cont::Field& field, + const vtkm::Float32 minSize, + const vtkm::Float32 maxSize) +{ + this->SetPointIdsFromCoords(coords); + this->SetVaryingSize(minSize, maxSize, field); +} + +void GlyphExtractorVector::ExtractCells(const vtkm::cont::UnknownCellSet& cells, + const vtkm::cont::Field& field, + const vtkm::Float32 size) +{ + this->SetPointIdsFromCells(cells); + this->SetUniformSize(size, field); +} +void GlyphExtractorVector::ExtractCells(const vtkm::cont::UnknownCellSet& cells, + const vtkm::cont::Field& field, + const vtkm::Float32 minSize, + const vtkm::Float32 maxSize) +{ + this->SetPointIdsFromCells(cells); + this->SetVaryingSize(minSize, maxSize, field); +} + +void GlyphExtractorVector::SetUniformSize(const vtkm::Float32 size, const vtkm::cont::Field& field) +{ + this->ExtractMagnitudeField(field); + + this->Sizes.Allocate(this->PointIds.GetNumberOfValues()); + vtkm::cont::Invoker invoker; + invoker(UniformFieldMagnitude(size), + this->PointIds, + this->Sizes, + vtkm::rendering::raytracing::GetVec3FieldArray(field)); +} + +void GlyphExtractorVector::ExtractMagnitudeField(const vtkm::cont::Field& field) +{ + vtkm::cont::ArrayHandle magnitudeArray; + magnitudeArray.Allocate(this->PointIds.GetNumberOfValues()); + vtkm::worklet::DispatcherMapField(FieldMagnitude()) + .Invoke(this->PointIds, vtkm::rendering::raytracing::GetVec3FieldArray(field), magnitudeArray); + this->MagnitudeField = vtkm::cont::Field(field); + this->MagnitudeField.SetData(magnitudeArray); +} + +void GlyphExtractorVector::SetPointIdsFromCoords(const vtkm::cont::CoordinateSystem& coords) +{ + vtkm::Id size = coords.GetNumberOfPoints(); + vtkm::cont::ArrayCopy(vtkm::cont::ArrayHandleIndex(size), this->PointIds); +} + +void GlyphExtractorVector::SetPointIdsFromCells(const vtkm::cont::UnknownCellSet& cells) +{ + using SingleType = vtkm::cont::CellSetSingleType<>; + vtkm::Id numCells = cells.GetNumberOfCells(); + if (numCells == 0) + { + return; + } + // + // look for points in the cell set + // + if (cells.CanConvert>()) + { + auto cellsExplicit = cells.AsCellSet>(); + + vtkm::cont::ArrayHandle points; + vtkm::worklet::DispatcherMapTopology(CountPoints()).Invoke(cellsExplicit, points); + + vtkm::Id totalPoints = 0; + totalPoints = vtkm::cont::Algorithm::Reduce(points, vtkm::Id(0)); + + vtkm::cont::ArrayHandle cellOffsets; + vtkm::cont::Algorithm::ScanExclusive(points, cellOffsets); + PointIds.Allocate(totalPoints); + + vtkm::worklet::DispatcherMapTopology(Pointify()) + .Invoke(cellsExplicit, cellOffsets, this->PointIds); + } + else if (cells.CanConvert()) + { + SingleType pointCells = cells.AsCellSet(); + vtkm::UInt8 shape_id = pointCells.GetCellShape(0); + if (shape_id == vtkm::CELL_SHAPE_VERTEX) + { + vtkm::cont::ArrayCopy(vtkm::cont::ArrayHandleIndex(numCells), this->PointIds); + } + } +} + +void GlyphExtractorVector::SetVaryingSize(const vtkm::Float32 minSize, + const vtkm::Float32 maxSize, + const vtkm::cont::Field& field) +{ + vtkm::cont::ArrayHandle rangeArray = field.GetRange(); + if (rangeArray.GetNumberOfValues() != 3) + { + throw vtkm::cont::ErrorBadValue( + "Glyph Extractor Vector: vector field must have three components"); + } + + using Vec3f_32Handle = vtkm::cont::ArrayHandle; + using Vec3f_64Handle = vtkm::cont::ArrayHandle; + vtkm::cont::UnknownArrayHandle fieldUnknownHandle = field.GetData(); + vtkm::Vec3f_32 minFieldValue, maxFieldValue; + + if (fieldUnknownHandle.CanConvert()) + { + Vec3f_64Handle fieldArray; + field.GetData().AsArrayHandle(fieldArray); + vtkm::Vec3f_64 initVal = vtkm::cont::ArrayGetValue(0, fieldArray); + minFieldValue = + static_cast(vtkm::cont::Algorithm::Reduce(fieldArray, initVal, MinFunctor())); + maxFieldValue = + static_cast(vtkm::cont::Algorithm::Reduce(fieldArray, initVal, MaxFunctor())); + } + else + { + Vec3f_32Handle fieldArray; + field.GetData().AsArrayHandle(fieldArray); + vtkm::Vec3f_32 initVal = vtkm::cont::ArrayGetValue(0, fieldArray); + minFieldValue = + static_cast(vtkm::cont::Algorithm::Reduce(fieldArray, initVal, MinFunctor())); + maxFieldValue = + static_cast(vtkm::cont::Algorithm::Reduce(fieldArray, initVal, MaxFunctor())); + } + + this->ExtractMagnitudeField(field); + + this->Sizes.Allocate(this->PointIds.GetNumberOfValues()); + vtkm::worklet::DispatcherMapField( + GetFieldSize(minSize, maxSize, minFieldValue, maxFieldValue)) + .Invoke(this->PointIds, this->Sizes, vtkm::rendering::raytracing::GetVec3FieldArray(field)); +} + +vtkm::cont::ArrayHandle GlyphExtractorVector::GetPointIds() +{ + return this->PointIds; +} + +vtkm::cont::ArrayHandle> GlyphExtractorVector::GetSizes() +{ + return this->Sizes; +} + +vtkm::cont::Field GlyphExtractorVector::GetMagnitudeField() +{ + return this->MagnitudeField; +} + +vtkm::Id GlyphExtractorVector::GetNumberOfGlyphs() const +{ + return this->PointIds.GetNumberOfValues(); +} +} +} +} //namespace vtkm::rendering::raytracing diff --git a/vtkm/rendering/raytracing/GlyphExtractorVector.h b/vtkm/rendering/raytracing/GlyphExtractorVector.h new file mode 100644 index 000000000..911f8adf6 --- /dev/null +++ b/vtkm/rendering/raytracing/GlyphExtractorVector.h @@ -0,0 +1,83 @@ +//============================================================================ +// 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. +//============================================================================ +#ifndef vtk_m_rendering_raytracing_Glyph_Extractor_Vector_h +#define vtk_m_rendering_raytracing_Glyph_Extractor_Vector_h + +#include + +namespace vtkm +{ +namespace rendering +{ +namespace raytracing +{ + +class GlyphExtractorVector +{ +public: + GlyphExtractorVector(); + + // + // Extract all nodes using a constant size + // + void ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords, + const vtkm::cont::Field& field, + const vtkm::Float32 size); + + // + // Set size based on scalar field values. Each is interpolated from min to max + // + void ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords, + const vtkm::cont::Field& field, + const vtkm::Float32 minSize, + const vtkm::Float32 maxSize); + + // + // Extract all vertex shapes with constant size + // + void ExtractCells(const vtkm::cont::UnknownCellSet& cells, + const vtkm::cont::Field& field, + vtkm::Float32 size); + + // + // Extract all vertex elements with size based on scalar values + // + void ExtractCells(const vtkm::cont::UnknownCellSet& cells, + const vtkm::cont::Field& field, + const vtkm::Float32 minSize, + const vtkm::Float32 maxSize); + + + vtkm::cont::ArrayHandle GetPointIds(); + vtkm::cont::ArrayHandle> GetSizes(); + vtkm::cont::Field GetMagnitudeField(); + + vtkm::Id GetNumberOfGlyphs() const; + +protected: + void SetUniformSize(const vtkm::Float32 size, const vtkm::cont::Field& field); + void SetVaryingSize(const vtkm::Float32 minSize, + const vtkm::Float32 maxSize, + const vtkm::cont::Field& field); + + void SetPointIdsFromCoords(const vtkm::cont::CoordinateSystem& coords); + void SetPointIdsFromCells(const vtkm::cont::UnknownCellSet& cells); + + void ExtractMagnitudeField(const vtkm::cont::Field& field); + + vtkm::cont::ArrayHandle PointIds; + vtkm::cont::ArrayHandle> Sizes; + vtkm::cont::Field MagnitudeField; + +}; // class GlyphExtractorVector +} +} +} //namespace vtkm::rendering::raytracing +#endif //vtk_m_rendering_raytracing_Glyph_Extractor_Vector_h diff --git a/vtkm/rendering/raytracing/GlyphIntersector.cxx b/vtkm/rendering/raytracing/GlyphIntersector.cxx new file mode 100644 index 000000000..f0545e98d --- /dev/null +++ b/vtkm/rendering/raytracing/GlyphIntersector.cxx @@ -0,0 +1,637 @@ +//============================================================================ +// 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. +//============================================================================ + +#include +#include +#include +#include +#include +#include +#include + +// This line is at the end to prevent warnings when building for CUDA +#include + +namespace vtkm +{ +namespace rendering +{ +namespace raytracing +{ + +namespace detail +{ + +class FindGlyphAABBs : public vtkm::worklet::WorkletMapField +{ +public: + VTKM_CONT + FindGlyphAABBs() {} + typedef void ControlSignature(FieldIn, + FieldIn, + FieldOut, + FieldOut, + FieldOut, + FieldOut, + FieldOut, + FieldOut, + WholeArrayIn); + typedef void ExecutionSignature(_1, _2, _3, _4, _5, _6, _7, _8, _9); + template + VTKM_EXEC void operator()(const vtkm::Id& pointId, + const vtkm::Float32& size, + vtkm::Float32& xmin, + vtkm::Float32& ymin, + vtkm::Float32& zmin, + vtkm::Float32& xmax, + vtkm::Float32& ymax, + vtkm::Float32& zmax, + const PointPortalType& points) const + { + vtkm::Vec3f_32 point; + point = static_cast(points.Get(pointId)); + vtkm::Float32 absSize = vtkm::Abs(size); + + xmin = point[0] - absSize; + xmax = point[0] + absSize; + ymin = point[1] - absSize; + ymax = point[1] + absSize; + zmin = point[2] - absSize; + zmax = point[2] + absSize; + } +}; //class FindGlyphAABBs + +template +class GlyphLeafIntersector +{ +public: + using IdHandle = vtkm::cont::ArrayHandle; + using IdArrayPortal = typename IdHandle::ReadPortalType; + using FloatHandle = vtkm::cont::ArrayHandle; + using VecHandle = vtkm::cont::ArrayHandle>; + using FloatPortal = typename FloatHandle::ReadPortalType; + using VecPortal = typename VecHandle::ReadPortalType; + IdArrayPortal PointIds; + FloatPortal Sizes; + vtkm::rendering::GlyphType GlyphType; + + GlyphLeafIntersector() {} + + GlyphLeafIntersector(const IdHandle& pointIds, + const FloatHandle& sizes, + vtkm::rendering::GlyphType glyphType, + vtkm::cont::Token& token) + : PointIds(pointIds.PrepareForInput(Device(), token)) + , Sizes(sizes.PrepareForInput(Device(), token)) + , GlyphType(glyphType) + { + } + + template + VTKM_EXEC inline void IntersectLeaf(const vtkm::Int32& currentNode, + const vtkm::Vec& origin, + const vtkm::Vec& dir, + const PointPortalType& points, + vtkm::Id& hitIndex, + Precision& closestDistance, + Precision& minU, + Precision& minV, + LeafPortalType leafs, + const Precision& minDistance) const + { + const vtkm::Id glyphCount = leafs.Get(currentNode); + + for (vtkm::Id i = 1; i <= glyphCount; ++i) + { + const vtkm::Id idx = leafs.Get(currentNode + i); + vtkm::Id pointIndex = PointIds.Get(idx); + Precision size = Sizes.Get(idx); + vtkm::Vec point = vtkm::Vec(points.Get(pointIndex)); + + if (this->GlyphType == vtkm::rendering::GlyphType::Sphere) + { + this->IntersectSphere( + origin, dir, point, size, pointIndex, hitIndex, closestDistance, minU, minV, minDistance); + } + else if (this->GlyphType == vtkm::rendering::GlyphType::Cube) + { + this->IntersectCube( + origin, dir, point, size, pointIndex, hitIndex, closestDistance, minU, minV, minDistance); + } + else if (this->GlyphType == vtkm::rendering::GlyphType::Axes) + { + this->IntersectAxes( + origin, dir, point, size, pointIndex, hitIndex, closestDistance, minU, minV, minDistance); + } + } + } + + template + VTKM_EXEC inline void IntersectSphere(const vtkm::Vec& origin, + const vtkm::Vec& dir, + const vtkm::Vec& point, + const Precision& size, + const vtkm::Id& pointIndex, + vtkm::Id& hitIndex, + Precision& closestDistance, + Precision& vtkmNotUsed(minU), + Precision& vtkmNotUsed(minV), + const Precision& minDistance) const + { + vtkm::Vec l = point - origin; + Precision dot1 = vtkm::dot(l, dir); + if (dot1 >= 0) + { + Precision d = vtkm::dot(l, l) - dot1 * dot1; + Precision r2 = size * size; + if (d <= r2) + { + Precision tch = vtkm::Sqrt(r2 - d); + Precision t0 = dot1 - tch; + + if (t0 < closestDistance && t0 > minDistance) + { + hitIndex = pointIndex; + closestDistance = t0; + } + } + } + } + + template + VTKM_EXEC inline void IntersectCube(const vtkm::Vec& origin, + const vtkm::Vec& dir, + const vtkm ::Vec& point, + const Precision& size, + const vtkm::Id& pointIndex, + vtkm::Id& hitIndex, + Precision& closestDistance, + Precision& vtkmNotUsed(minU), + Precision& vtkmNotUsed(minV), + const Precision& minDistance) const + { + Precision xmin, xmax, ymin, ymax, zmin, zmax; + this->CalculateAABB(point, size, xmin, ymin, zmin, xmax, ymax, zmax); + + Precision tmin = (xmin - origin[0]) / dir[0]; + Precision tmax = (xmax - origin[0]) / dir[0]; + + if (tmin > tmax) + vtkm::Swap(tmin, tmax); + + Precision tymin = (ymin - origin[1]) / dir[1]; + Precision tymax = (ymax - origin[1]) / dir[1]; + if (tymin > tymax) + vtkm::Swap(tymin, tymax); + + if ((tmin > tymax) || (tymin > tmax)) + return; + + if (tymin > tmin) + tmin = tymin; + + if (tymax < tmax) + tmax = tymax; + + Precision tzmin = (zmin - origin[2]) / dir[2]; + Precision tzmax = (zmax - origin[2]) / dir[2]; + + if (tzmin > tzmax) + vtkm::Swap(tzmin, tzmax); + + if ((tmin > tzmax) || (tzmin > tmax)) + return; + + if (tzmin > tmin) + tmin = tzmin; + + if (tzmax < tmax) + tmax = tzmax; + + if (tmin < closestDistance && tmin > minDistance) + { + hitIndex = pointIndex; + closestDistance = tmin; + } + } + + template + VTKM_EXEC inline void IntersectAxes(const vtkm::Vec& origin, + const vtkm::Vec& dir, + const vtkm::Vec& point, + const Precision& size, + const vtkm::Id& pointIndex, + vtkm::Id& hitIndex, + Precision& closestDistance, + Precision& vtkmNotUsed(minU), + Precision& vtkmNotUsed(minV), + const Precision& minDistance) const + { + Precision xmin, xmax, ymin, ymax, zmin, zmax; + this->CalculateAABB(point, size, xmin, ymin, zmin, xmax, ymax, zmax); + + Precision t = (point[0] - origin[0]) / dir[0]; + vtkm::Vec intersection = origin + t * dir; + + if ((intersection[1] >= ymin && intersection[1] <= ymax) && + (intersection[2] >= zmin && intersection[2] <= zmax)) + { + if (t < closestDistance && t > minDistance) + { + hitIndex = pointIndex; + closestDistance = t; + } + } + + t = (point[1] - origin[1]) / dir[1]; + intersection = origin + t * dir; + if ((intersection[0] >= xmin && intersection[0] <= xmax) && + (intersection[2] >= zmin && intersection[2] <= zmax)) + { + if (t < closestDistance && t > minDistance) + { + hitIndex = pointIndex; + closestDistance = t; + } + } + + t = (point[2] - origin[2]) / dir[2]; + intersection = origin + t * dir; + if ((intersection[0] >= xmin && intersection[0] <= xmax) && + (intersection[1] >= ymin && intersection[1] <= ymax)) + { + if (t < closestDistance && t > minDistance) + { + hitIndex = pointIndex; + closestDistance = t; + } + } + } + + template + VTKM_EXEC void CalculateAABB(const vtkm::Vec& point, + const Precision& size, + Precision& xmin, + Precision& ymin, + Precision& zmin, + Precision& xmax, + Precision& ymax, + Precision& zmax) const + { + Precision absSize = vtkm::Abs(size); + xmin = point[0] - absSize; + xmax = point[0] + absSize; + ymin = point[1] - absSize; + ymax = point[1] + absSize; + zmin = point[2] - absSize; + zmax = point[2] + absSize; + } +}; + +class GlyphLeafWrapper : public vtkm::cont::ExecutionObjectBase +{ +protected: + using IdHandle = vtkm::cont::ArrayHandle; + using FloatHandle = vtkm::cont::ArrayHandle; + using VecHandle = vtkm::cont::ArrayHandle>; + IdHandle PointIds; + FloatHandle Sizes; + vtkm::rendering::GlyphType GlyphType; + +public: + GlyphLeafWrapper(IdHandle& pointIds, FloatHandle sizes, vtkm::rendering::GlyphType glyphType) + : PointIds(pointIds) + , Sizes(sizes) + , GlyphType(glyphType) + { + } + + template + VTKM_CONT GlyphLeafIntersector PrepareForExecution(Device, vtkm::cont::Token& token) const + { + return GlyphLeafIntersector(this->PointIds, this->Sizes, this->GlyphType, token); + } +}; // class GlyphLeafWrapper + +class CalculateGlyphNormals : public vtkm::worklet::WorkletMapField +{ +public: + VTKM_CONT + CalculateGlyphNormals(vtkm::rendering::GlyphType glyphType) + : GlyphType(glyphType) + { + } + + typedef void ControlSignature(FieldIn, + FieldIn, + FieldIn, + FieldOut, + FieldOut, + FieldOut, + WholeArrayIn, + WholeArrayIn, + WholeArrayIn); + typedef void ExecutionSignature(_1, _2, _3, _4, _5, _6, _7, _8, _9); + + template + VTKM_EXEC inline void operator()(const vtkm::Id& hitIndex, + const vtkm::Vec& rayDir, + const vtkm::Vec& intersection, + Precision& normalX, + Precision& normalY, + Precision& normalZ, + const PointPortalType& points, + const IndicesPortalType& indicesPortal, + const SizesPortalType& sizesPortal) const + { + if (hitIndex < 0) + return; + + vtkm::Id pointId = indicesPortal.Get(hitIndex); + vtkm::Vec point = points.Get(pointId); + Precision size = sizesPortal.Get(hitIndex); + + if (this->GlyphType == vtkm::rendering::GlyphType::Sphere) + { + this->CalculateNormalForSphere(rayDir, intersection, point, size, normalX, normalY, normalZ); + } + else if (this->GlyphType == vtkm::rendering::GlyphType::Cube) + { + this->CalculateNormalForCube(rayDir, intersection, point, size, normalX, normalY, normalZ); + } + else if (this->GlyphType == vtkm::rendering::GlyphType::Axes) + { + this->CalculateNormalForAxes(rayDir, intersection, point, size, normalX, normalY, normalZ); + } + } + + template + VTKM_EXEC inline void CalculateNormalForSphere(const vtkm::Vec& rayDir, + const vtkm::Vec& intersection, + const vtkm::Vec& point, + const Precision& vtkmNotUsed(size), + Precision& normalX, + Precision& normalY, + Precision& normalZ) const + { + vtkm::Vec normal = intersection - point; + vtkm::Normalize(normal); + + // Flip normal if it is pointing the wrong way + if (vtkm::Dot(normal, rayDir) > 0.0f) + { + normal = -normal; + } + + normalX = normal[0]; + normalY = normal[1]; + normalZ = normal[2]; + } + + template + VTKM_EXEC inline void CalculateNormalForCube(const vtkm::Vec& rayDir, + const vtkm::Vec& intersection, + const vtkm::Vec& point, + const Precision& size, + Precision& normalX, + Precision& normalY, + Precision& normalZ) const + { + vtkm::Vec lp = intersection - point; + + // Localize the intersection point to the surface of the cube. + // One of the components will be 1 or -1 based on the face it lies on + lp = lp * (1.0f / size); + + Precision eps = 1e-4f; + vtkm::Vec normal{ 0.0f, 0.0f, 0.0f }; + normal[0] = (vtkm::Abs(vtkm::Abs(lp[0]) - 1.0f) <= eps) ? lp[0] : 0.0f; + normal[1] = (vtkm::Abs(vtkm::Abs(lp[1]) - 1.0f) <= eps) ? lp[1] : 0.0f; + normal[2] = (vtkm::Abs(vtkm::Abs(lp[2]) - 1.0f) <= eps) ? lp[2] : 0.0f; + vtkm::Normalize(normal); + + // Flip normal if it is pointing the wrong way + if (vtkm::Dot(normal, rayDir) > 0.0f) + { + normal = -normal; + } + + normalX = normal[0]; + normalY = normal[1]; + normalZ = normal[2]; + } + + template + VTKM_EXEC inline void CalculateNormalForAxes(const vtkm::Vec& rayDir, + const vtkm::Vec& intersection, + const vtkm::Vec& point, + const Precision& vtkmNotUsed(size), + Precision& normalX, + Precision& normalY, + Precision& normalZ) const + { + vtkm::Vec normal{ 0.0f, 0.0f, 0.0f }; + + if (this->ApproxEquals(point[0], intersection[0])) + { + normal[0] = 1.0f; + } + else if (this->ApproxEquals(point[1], intersection[1])) + { + normal[1] = 1.0f; + } + else + { + normal[2] = 1.0f; + } + + // Flip normal if it is pointing the wrong way + if (vtkm::Dot(normal, rayDir) > 0.0f) + { + normal = -normal; + } + + normalX = normal[0]; + normalY = normal[1]; + normalZ = normal[2]; + } + + template + VTKM_EXEC inline Precision ApproxEquals(Precision x, Precision y, Precision eps = 1e-5f) const + { + return vtkm::Abs(x - y) <= eps; + } + + vtkm::rendering::GlyphType GlyphType; +}; //class CalculateGlyphNormals + +template +class GetScalars : public vtkm::worklet::WorkletMapField +{ +private: + Precision MinScalar; + Precision InvDeltaScalar; + bool Normalize; + +public: + VTKM_CONT + GetScalars(const vtkm::Float32& minScalar, const vtkm::Float32& maxScalar) + : MinScalar(minScalar) + { + Normalize = true; + if (minScalar >= maxScalar) + { + // support the scalar renderer + Normalize = false; + this->InvDeltaScalar = Precision(0.f); + } + else + { + //Make sure the we don't divide by zero on + //something like an iso-surface + this->InvDeltaScalar = 1.f / (maxScalar - this->MinScalar); + } + } + typedef void ControlSignature(FieldIn, FieldOut, WholeArrayIn, WholeArrayIn); + typedef void ExecutionSignature(_1, _2, _3, _4); + template + VTKM_EXEC void operator()(const vtkm::Id& hitIndex, + Precision& scalar, + const ScalarPortalType& scalars, + const IndicesPortalType& indicesPortal) const + { + if (hitIndex < 0) + return; + + vtkm::Id pointId = indicesPortal.Get(hitIndex); + + scalar = Precision(scalars.Get(pointId)); + if (Normalize) + { + scalar = (scalar - this->MinScalar) * this->InvDeltaScalar; + } + } +}; //class GetScalars +} + +GlyphIntersector::GlyphIntersector(vtkm::rendering::GlyphType glyphType) + : ShapeIntersector() +{ + this->SetGlyphType(glyphType); +} + +GlyphIntersector::~GlyphIntersector() {} + +void GlyphIntersector::SetGlyphType(vtkm::rendering::GlyphType glyphType) +{ + this->GlyphType = glyphType; +} + +void GlyphIntersector::SetData(const vtkm::cont::CoordinateSystem& coords, + vtkm::cont::ArrayHandle pointIds, + vtkm::cont::ArrayHandle sizes) +{ + this->PointIds = pointIds; + this->Sizes = sizes; + this->CoordsHandle = coords; + AABBs AABB; + vtkm::worklet::DispatcherMapField(detail::FindGlyphAABBs()) + .Invoke(PointIds, + Sizes, + AABB.xmins, + AABB.ymins, + AABB.zmins, + AABB.xmaxs, + AABB.ymaxs, + AABB.zmaxs, + CoordsHandle); + + this->SetAABBs(AABB); +} + +void GlyphIntersector::IntersectRays(Ray& rays, bool returnCellIndex) +{ + IntersectRaysImp(rays, returnCellIndex); +} + +void GlyphIntersector::IntersectRays(Ray& rays, bool returnCellIndex) +{ + IntersectRaysImp(rays, returnCellIndex); +} + +template +void GlyphIntersector::IntersectRaysImp(Ray& rays, bool vtkmNotUsed(returnCellIndex)) +{ + detail::GlyphLeafWrapper leafIntersector(this->PointIds, Sizes, this->GlyphType); + + BVHTraverser traverser; + traverser.IntersectRays(rays, this->BVH, leafIntersector, this->CoordsHandle); + + RayOperations::UpdateRayStatus(rays); +} + +template +void GlyphIntersector::IntersectionDataImp(Ray& rays, + const vtkm::cont::Field scalarField, + const vtkm::Range& scalarRange) +{ + ShapeIntersector::IntersectionPoint(rays); + + const bool isSupportedField = scalarField.IsFieldCell() || scalarField.IsFieldPoint(); + if (!isSupportedField) + { + throw vtkm::cont::ErrorBadValue( + "GlyphIntersector: Field not accociated with a cell set or field"); + } + + vtkm::worklet::DispatcherMapField( + detail::CalculateGlyphNormals(this->GlyphType)) + .Invoke(rays.HitIdx, + rays.Dir, + rays.Intersection, + rays.NormalX, + rays.NormalY, + rays.NormalZ, + CoordsHandle, + PointIds, + Sizes); + + vtkm::worklet::DispatcherMapField>( + detail::GetScalars(vtkm::Float32(scalarRange.Min), vtkm::Float32(scalarRange.Max))) + .Invoke(rays.HitIdx, + rays.Scalar, + vtkm::rendering::raytracing::GetScalarFieldArray(scalarField), + PointIds); +} + +void GlyphIntersector::IntersectionData(Ray& rays, + const vtkm::cont::Field scalarField, + const vtkm::Range& scalarRange) +{ + IntersectionDataImp(rays, scalarField, scalarRange); +} + +void GlyphIntersector::IntersectionData(Ray& rays, + const vtkm::cont::Field scalarField, + const vtkm::Range& scalarRange) +{ + IntersectionDataImp(rays, scalarField, scalarRange); +} + +vtkm::Id GlyphIntersector::GetNumberOfShapes() const +{ + return PointIds.GetNumberOfValues(); +} +} +} +} //namespace vtkm::rendering::raytracing diff --git a/vtkm/rendering/raytracing/GlyphIntersector.h b/vtkm/rendering/raytracing/GlyphIntersector.h new file mode 100644 index 000000000..770120816 --- /dev/null +++ b/vtkm/rendering/raytracing/GlyphIntersector.h @@ -0,0 +1,69 @@ +//============================================================================ +// 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. +//============================================================================ +#ifndef vtk_m_rendering_raytracing_Glyph_Intersector_h +#define vtk_m_rendering_raytracing_Glyph_Intersector_h + +#include +#include + +namespace vtkm +{ +namespace rendering +{ +namespace raytracing +{ + +class GlyphIntersector : public ShapeIntersector +{ +public: + GlyphIntersector(vtkm::rendering::GlyphType glyphType); + virtual ~GlyphIntersector() override; + + void SetGlyphType(vtkm::rendering::GlyphType glyphType); + + void SetData(const vtkm::cont::CoordinateSystem& coords, + vtkm::cont::ArrayHandle pointIds, + vtkm::cont::ArrayHandle sizes); + + void IntersectRays(Ray& rays, bool returnCellIndex = false) override; + + + void IntersectRays(Ray& rays, bool returnCellIndex = false) override; + + template + void IntersectRaysImp(Ray& rays, bool returnCellIndex); + + + template + void IntersectionDataImp(Ray& rays, + const vtkm::cont::Field scalarField, + const vtkm::Range& scalarRange); + + void IntersectionData(Ray& rays, + const vtkm::cont::Field scalarField, + const vtkm::Range& scalarRange) override; + + void IntersectionData(Ray& rays, + const vtkm::cont::Field scalarField, + const vtkm::Range& scalarRange) override; + + vtkm::Id GetNumberOfShapes() const override; + +protected: + vtkm::cont::ArrayHandle PointIds; + vtkm::cont::ArrayHandle Sizes; + vtkm::rendering::GlyphType GlyphType; + +}; // class GlyphIntersector + +} +} +} //namespace vtkm::rendering::raytracing +#endif //vtk_m_rendering_raytracing_Glyph_Intersector_h diff --git a/vtkm/rendering/raytracing/GlyphIntersectorVector.cxx b/vtkm/rendering/raytracing/GlyphIntersectorVector.cxx new file mode 100644 index 000000000..38f94b421 --- /dev/null +++ b/vtkm/rendering/raytracing/GlyphIntersectorVector.cxx @@ -0,0 +1,721 @@ +//============================================================================ +// 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. +//============================================================================ + +#include +#include +#include +#include +#include +#include +#include + +// This line is at the end to prevent warnings when building for CUDA +#include + +namespace vtkm +{ +namespace rendering +{ +namespace raytracing +{ + +namespace detail +{ +static constexpr vtkm::Float32 ARROW_BODY_SIZE = 0.75f; + +class FindGlyphVectorAABBs : public vtkm::worklet::WorkletMapField +{ + vtkm::rendering::GlyphType GlyphType; + vtkm::Float32 ArrowBodyRadius; + vtkm::Float32 ArrowHeadRadius; + +public: + using ControlSignature = void(FieldIn, + FieldIn, + FieldOut, + FieldOut, + FieldOut, + FieldOut, + FieldOut, + FieldOut, + WholeArrayIn); + using ExecutionSignature = void(_1, _2, _3, _4, _5, _6, _7, _8, _9); + + VTKM_CONT + FindGlyphVectorAABBs(vtkm::rendering::GlyphType glyphType, + vtkm::Float32 bodyRadius, + vtkm::Float32 headRadius) + : GlyphType(glyphType) + , ArrowBodyRadius(bodyRadius) + , ArrowHeadRadius(headRadius) + { + } + + template + VTKM_EXEC void operator()(const vtkm::Id& pointId, + const vtkm::Vec3f_32& size, + vtkm::Float32& xmin, + vtkm::Float32& ymin, + vtkm::Float32& zmin, + vtkm::Float32& xmax, + vtkm::Float32& ymax, + vtkm::Float32& zmax, + const PointPortalType& points) const + { + vtkm::Vec3f_32 point = static_cast(points.Get(pointId)); + xmin = point[0]; + xmax = point[0]; + ymin = point[1]; + ymax = point[1]; + zmin = point[2]; + zmax = point[2]; + + if (this->GlyphType == vtkm::rendering::GlyphType::Arrow) + { + this->CalculateArrowAABB(point, size, xmin, ymin, zmin, xmax, ymax, zmax); + } + } + + VTKM_EXEC inline void CalculateArrowAABB(const vtkm::Vec3f_32& point, + const vtkm::Vec3f_32& size, + vtkm::Float32& xmin, + vtkm::Float32& ymin, + vtkm::Float32& zmin, + vtkm::Float32& xmax, + vtkm::Float32& ymax, + vtkm::Float32& zmax) const + { + vtkm::Vec3f_32 body_pa = point; + vtkm::Vec3f_32 body_pb = body_pa + ARROW_BODY_SIZE * size; + vtkm::Vec3f_32 head_pa = body_pb; + vtkm::Vec3f_32 head_pb = point + size; + + this->CylinderAABB(body_pa, body_pb, this->ArrowBodyRadius, xmin, ymin, zmin, xmax, ymax, zmax); + this->ConeAABB( + head_pa, head_pb, this->ArrowHeadRadius, 0.0f, xmin, ymin, zmin, xmax, ymax, zmax); + } + + VTKM_EXEC inline void CylinderAABB(const vtkm::Vec3f_32& pa, + const vtkm::Vec3f_32& pb, + const vtkm::Float32& ra, + vtkm::Float32& xmin, + vtkm::Float32& ymin, + vtkm::Float32& zmin, + vtkm::Float32& xmax, + vtkm::Float32& ymax, + vtkm::Float32& zmax) const + { + vtkm::Vec3f_32 a = pb - pa; + vtkm::Vec3f_32 e_prime = a * a / vtkm::Dot(a, a); + vtkm::Vec3f_32 e = ra * vtkm::Sqrt(1.0f - e_prime); + + vtkm::Vec3f_32 pa1 = pa - e; + vtkm::Vec3f_32 pa2 = pa + e; + vtkm::Vec3f_32 pb1 = pb - e; + vtkm::Vec3f_32 pb2 = pb + e; + + xmin = vtkm::Min(xmin, vtkm::Min(pa1[0], pb1[0])); + ymin = vtkm::Min(ymin, vtkm::Min(pa1[1], pb1[1])); + zmin = vtkm::Min(zmin, vtkm::Min(pa1[2], pb1[2])); + xmax = vtkm::Max(xmax, vtkm::Max(pa2[0], pb2[0])); + ymax = vtkm::Max(ymax, vtkm::Max(pa2[1], pb2[1])); + zmax = vtkm::Max(zmax, vtkm::Max(pa2[2], pb2[2])); + } + + VTKM_EXEC inline void ConeAABB(const vtkm::Vec3f_32& pa, + const vtkm::Vec3f_32& pb, + const vtkm::Float32& ra, + const vtkm::Float32& rb, + vtkm::Float32& xmin, + vtkm::Float32& ymin, + vtkm::Float32& zmin, + vtkm::Float32& xmax, + vtkm::Float32& ymax, + vtkm::Float32& zmax) const + { + vtkm::Vec3f_32 a = pb - pa; + vtkm::Vec3f_32 e_prime = a * a / vtkm::Dot(a, a); + vtkm::Vec3f_32 e = vtkm::Sqrt(1.0f - e_prime); + + vtkm::Vec3f_32 pa1 = pa - e * ra; + vtkm::Vec3f_32 pa2 = pa + e * ra; + vtkm::Vec3f_32 pb1 = pb - e * rb; + vtkm::Vec3f_32 pb2 = pb + e * rb; + + xmin = vtkm::Min(xmin, vtkm::Min(pa1[0], pb1[0])); + ymin = vtkm::Min(ymin, vtkm::Min(pa1[1], pb1[1])); + zmin = vtkm::Min(zmin, vtkm::Min(pa1[2], pb1[2])); + xmax = vtkm::Max(xmax, vtkm::Max(pa2[0], pb2[0])); + ymax = vtkm::Max(ymax, vtkm::Max(pa2[1], pb2[1])); + zmax = vtkm::Max(zmax, vtkm::Max(pa2[2], pb2[2])); + } +}; //class FindGlyphVectorAABBs + +template +class GlyphVectorLeafIntersector +{ +public: + using IdHandle = vtkm::cont::ArrayHandle; + using IdArrayPortal = typename IdHandle::ReadPortalType; + using Vec3f_32Handle = vtkm::cont::ArrayHandle; + using Vec3f_32Portal = typename Vec3f_32Handle::ReadPortalType; + + vtkm::rendering::GlyphType GlyphType; + IdArrayPortal PointIds; + Vec3f_32Portal Sizes; + vtkm::Float32 ArrowBodyRadius; + vtkm::Float32 ArrowHeadRadius; + + GlyphVectorLeafIntersector() = default; + + GlyphVectorLeafIntersector(vtkm::rendering::GlyphType glyphType, + const IdHandle& pointIds, + const Vec3f_32Handle& sizes, + vtkm::Float32 bodyRadius, + vtkm::Float32 headRadius, + vtkm::cont::Token& token) + : GlyphType(glyphType) + , PointIds(pointIds.PrepareForInput(Device(), token)) + , Sizes(sizes.PrepareForInput(Device(), token)) + , ArrowBodyRadius(bodyRadius) + , ArrowHeadRadius(headRadius) + { + } + + template + VTKM_EXEC inline void IntersectLeaf(const vtkm::Int32& currentNode, + const vtkm::Vec& origin, + const vtkm::Vec& dir, + const PointPortalType& points, + vtkm::Id& hitIndex, + Precision& closestDistance, + Precision& minU, + Precision& minV, + LeafPortalType leafs, + const Precision& minDistance) const + { + const vtkm::Id glyphCount = leafs.Get(currentNode); + + for (vtkm::Id i = 1; i <= glyphCount; ++i) + { + const vtkm::Id idx = leafs.Get(currentNode + i); + vtkm::Id pointIndex = PointIds.Get(idx); + vtkm::Vec size = Sizes.Get(idx); + vtkm::Vec point = vtkm::Vec(points.Get(pointIndex)); + + if (this->GlyphType == vtkm::rendering::GlyphType::Arrow) + { + this->IntersectArrow( + origin, dir, point, size, pointIndex, hitIndex, closestDistance, minU, minV, minDistance); + } + } + } + + template + VTKM_EXEC inline void IntersectArrow(const vtkm::Vec& origin, + const vtkm::Vec& dir, + const vtkm::Vec& point, + const vtkm::Vec& size, + const vtkm::Id& pointIndex, + vtkm::Id& hitIndex, + Precision& closestDistance, + Precision& minU, + Precision& minV, + const Precision& minDistance) const + { + using Vec2 = vtkm::Vec; + using Vec3 = vtkm::Vec; + using Vec4 = vtkm::Vec; + + Vec3 body_pa = point; + Vec3 body_pb = body_pa + ARROW_BODY_SIZE * size; + Vec3 head_pa = body_pb; + Vec3 head_pb = point + size; + + Vec4 bodyIntersection = + this->IntersectCylinder(origin, dir, body_pa, body_pb, Precision(this->ArrowBodyRadius)); + Vec4 headIntersection = this->IntersectCone( + origin, dir, head_pa, head_pb, Precision(this->ArrowHeadRadius), Precision(0.0f)); + + bool bodyHit = bodyIntersection[0] >= minDistance; + bool headHit = headIntersection[0] >= minDistance; + if (bodyHit && !headHit) + { + Precision t = bodyIntersection[0]; + if (t < closestDistance) + { + hitIndex = pointIndex; + closestDistance = t; + minU = bodyIntersection[1]; + minV = bodyIntersection[2]; + } + } + else if (!bodyHit && headHit) + { + Precision t = headIntersection[0]; + if (t < closestDistance) + { + hitIndex = pointIndex; + closestDistance = t; + minU = headIntersection[1]; + minV = headIntersection[2]; + } + } + else if (bodyHit || headHit) + { + Precision t1 = bodyIntersection[0]; + Precision t2 = headIntersection[0]; + + Precision t = t1; + Vec2 partialNormal = { bodyIntersection[1], bodyIntersection[2] }; + if (t2 < t) + { + t = t2; + partialNormal[0] = headIntersection[1]; + partialNormal[1] = headIntersection[2]; + } + + if (t < closestDistance) + { + hitIndex = pointIndex; + closestDistance = t; + minU = partialNormal[0]; + minV = partialNormal[1]; + } + } + } + + template + VTKM_EXEC vtkm::Vec4f_32 IntersectCylinder(const vtkm::Vec& ro, + const vtkm::Vec& rd, + const vtkm::Vec& pa, + const vtkm::Vec& pb, + const Precision& ra) const + { + using Vec3 = vtkm::Vec; + using Vec4 = vtkm::Vec; + + const Vec4 NO_HIT{ -1.0f, -1.0f, -1.0f, -1.0f }; + + Vec3 cc = 0.5f * (pa + pb); + Precision ch = vtkm::Magnitude(pb - pa); + Vec3 ca = (pb - pa) / ch; + ch *= 0.5f; + + Vec3 oc = ro - cc; + + Precision card = vtkm::Dot(ca, rd); + Precision caoc = vtkm::Dot(ca, oc); + + Precision a = 1.0f - card * card; + Precision b = vtkm::Dot(oc, rd) - caoc * card; + Precision c = vtkm::Dot(oc, oc) - caoc * caoc - ra * ra; + Precision h = b * b - a * c; + if (h < 0.0f) + { + return NO_HIT; + } + + h = vtkm::Sqrt(h); + Precision t1 = (-b - h) / a; + /* Precision t2 = (-b + h) / a; // exit point */ + + Precision y = caoc + t1 * card; + + // body + if (vtkm::Abs(y) < ch) + { + vtkm::Vec3f_32 normal = vtkm::Normal(oc + t1 * rd - ca * y); + return vtkm::Vec4f_32(static_cast(t1), normal[0], normal[1], normal[2]); + } + + // bottom cap + Precision sy = -1; + Precision tp = (sy * ch - caoc) / card; + if (vtkm::Abs(b + a * tp) < h) + { + vtkm::Vec3f_32 normal = vtkm::Normal(ca * sy); + return vtkm::Vec4f_32(static_cast(tp), normal[0], normal[1], normal[2]); + } + + // top cap + sy = 1; + tp = (sy * ch - caoc) / card; + if (vtkm::Abs(b + a * tp) < h) + { + vtkm::Vec3f_32 normal = vtkm::Normal(ca * sy); + return vtkm::Vec4f_32(static_cast(tp), normal[0], normal[1], normal[2]); + } + + return NO_HIT; + } + + template + VTKM_EXEC vtkm::Vec4f_32 IntersectCone(const vtkm::Vec& ro, + const vtkm::Vec& rd, + const vtkm::Vec& pa, + const vtkm::Vec& pb, + const Precision& ra, + const Precision& rb) const + { + using Vec3 = vtkm::Vec; + using Vec4 = vtkm::Vec; + + const Vec4 NO_HIT{ -1.0f, -1.0f, -1.0f, -1.0f }; + + Vec3 ba = pb - pa; + Vec3 oa = ro - pa; + Vec3 ob = ro - pb; + + Precision m0 = vtkm::Dot(ba, ba); + Precision m1 = vtkm::Dot(oa, ba); + Precision m2 = vtkm::Dot(ob, ba); + Precision m3 = vtkm::Dot(rd, ba); + + //caps + if (m1 < 0.0) + { + Vec3 m11 = oa * m3 - rd * m1; + Precision m12 = ra * ra * m3 * m3; + if (vtkm::Dot(m11, m11) < m12) + { + Precision t = -m1 / m3; + Vec3 normal = -ba * 1.0f / vtkm::Sqrt(m0); + return Vec4(t, normal[0], normal[1], normal[2]); + } + } + else if (m2 > 0.0) + { + Vec3 m21 = ob * m3 - rd * m2; + Precision m22 = rb * rb * m3 * m3; + if (vtkm::Dot(m21, m21) < m22) + { + Precision t = -m2 / m3; + Vec3 normal = ba * 1.0f / vtkm::Sqrt(m0); + return Vec4(t, normal[0], normal[1], normal[2]); + } + } + + // body + Precision rr = ra - rb; + Precision hy = m0 + rr * rr; + Precision m4 = vtkm::Dot(rd, oa); + Precision m5 = vtkm::Dot(oa, oa); + + Precision k2 = m0 * m0 - m3 * m3 * hy; + Precision k1 = m0 * m0 * m4 - m1 * m3 * hy + m0 * ra * (rr * m3 * 1.0f); + Precision k0 = m0 * m0 * m5 - m1 * m1 * hy + m0 * ra * (rr * m1 * 2.0f - m0 * ra); + + Precision h = k1 * k1 - k2 * k0; + if (h < 0.0) + { + return NO_HIT; + } + + Precision t = (-k1 - sqrt(h)) / k2; + Precision y = m1 + t * m3; + + if (y > 0.0 && y < m0) + { + Vec3 normal = vtkm::Normal(m0 * (m0 * (oa + t * rd) + rr * ba * ra) - ba * hy * y); + return Vec4(t, normal[0], normal[1], normal[2]); + } + + return NO_HIT; + } +}; + +class GlyphVectorLeafWrapper : public vtkm::cont::ExecutionObjectBase +{ +protected: + using IdHandle = vtkm::cont::ArrayHandle; + using Vec3f_32Handle = vtkm::cont::ArrayHandle; + vtkm::rendering::GlyphType GlyphType; + IdHandle PointIds; + Vec3f_32Handle Sizes; + vtkm::Float32 ArrowBodyRadius; + vtkm::Float32 ArrowHeadRadius; + +public: + GlyphVectorLeafWrapper(vtkm::rendering::GlyphType glyphType, + IdHandle& pointIds, + Vec3f_32Handle& sizes, + vtkm::Float32 bodyRadius, + vtkm::Float32 headRadius) + : GlyphType(glyphType) + , PointIds(pointIds) + , Sizes(sizes) + , ArrowBodyRadius(bodyRadius) + , ArrowHeadRadius(headRadius) + { + } + + template + VTKM_CONT GlyphVectorLeafIntersector PrepareForExecution(Device, + vtkm::cont::Token& token) const + { + return GlyphVectorLeafIntersector(this->GlyphType, + this->PointIds, + this->Sizes, + this->ArrowBodyRadius, + this->ArrowHeadRadius, + token); + } +}; + +class CalculateGlyphVectorNormals : public vtkm::worklet::WorkletMapField +{ +public: + VTKM_CONT + CalculateGlyphVectorNormals(vtkm::rendering::GlyphType glyphType) + : GlyphType(glyphType) + { + } + + typedef void ControlSignature(FieldIn, + FieldIn, + FieldIn, + FieldIn, + FieldIn, + FieldOut, + FieldOut, + FieldOut, + WholeArrayIn, + WholeArrayIn, + WholeArrayIn); + typedef void ExecutionSignature(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11); + + template + VTKM_EXEC inline void operator()(const vtkm::Id& hitIndex, + const vtkm::Vec& rayDir, + const vtkm::Vec& intersection, + const Precision& u, + const Precision& v, + Precision& normalX, + Precision& normalY, + Precision& normalZ, + const PointPortalType& vtkmNotUsed(points), + const IndicesPortalType& vtkmNotUsed(indicesPortal), + const SizesPortalType& vtkmNotUsed(sizesPortal)) const + { + if (hitIndex < 0) + return; + + if (this->GlyphType == vtkm::rendering::GlyphType::Arrow) + { + this->CalculateArrowNormal(rayDir, intersection, u, v, normalX, normalY, normalZ); + } + } + + template + VTKM_EXEC inline void CalculateArrowNormal( + const vtkm::Vec& rayDir, + const vtkm::Vec& vtkmNotUsed(intersection), + const Precision& u, + const Precision& v, + Precision& normalX, + Precision& normalY, + Precision& normalZ) const + { + vtkm::Vec normal; + normal[0] = u; + normal[1] = v; + normal[2] = 1.0f - (normalX * normalX) - (normalY * normalY); + + if (vtkm::Dot(normal, rayDir) > 0.0f) + { + normal = -normal; + } + + normalX = normal[0]; + normalY = normal[1]; + normalZ = normal[2]; + } + + vtkm::rendering::GlyphType GlyphType; +}; //class CalculateGlyphVectorNormals + +template +class GetScalars : public vtkm::worklet::WorkletMapField +{ +private: + Precision MinScalar; + Precision InvDeltaScalar; + bool Normalize; + +public: + VTKM_CONT + GetScalars(const vtkm::Float32& minScalar, const vtkm::Float32& maxScalar) + : MinScalar(minScalar) + { + Normalize = true; + if (minScalar >= maxScalar) + { + // support the scalar renderer + Normalize = false; + this->InvDeltaScalar = Precision(0.f); + } + else + { + //Make sure the we don't divide by zero on + //something like an iso-surface + this->InvDeltaScalar = 1.f / (maxScalar - this->MinScalar); + } + } + typedef void ControlSignature(FieldIn, FieldOut, WholeArrayIn, WholeArrayIn); + typedef void ExecutionSignature(_1, _2, _3, _4); + template + VTKM_EXEC void operator()(const vtkm::Id& hitIndex, + Precision& scalar, + const FieldPortalType& scalars, + const IndicesPortalType& indicesPortal) const + { + if (hitIndex < 0) + return; + + vtkm::Id pointId = indicesPortal.Get(hitIndex); + + scalar = Precision(scalars.Get(pointId)); + if (Normalize) + { + scalar = (scalar - this->MinScalar) * this->InvDeltaScalar; + } + } +}; //class GetScalar + +} // namespace + +GlyphIntersectorVector::GlyphIntersectorVector(vtkm::rendering::GlyphType glyphType) + : ShapeIntersector() + , ArrowBodyRadius(0.004f) + , ArrowHeadRadius(0.008f) +{ + this->SetGlyphType(glyphType); +} + +GlyphIntersectorVector::~GlyphIntersectorVector() {} + +void GlyphIntersectorVector::SetGlyphType(vtkm::rendering::GlyphType glyphType) +{ + this->GlyphType = glyphType; +} + +void GlyphIntersectorVector::SetData(const vtkm::cont::CoordinateSystem& coords, + vtkm::cont::ArrayHandle pointIds, + vtkm::cont::ArrayHandle sizes) +{ + this->PointIds = pointIds; + this->Sizes = sizes; + this->CoordsHandle = coords; + AABBs AABB; + vtkm::cont::Invoker invoker; + invoker( + detail::FindGlyphVectorAABBs{ this->GlyphType, this->ArrowBodyRadius, this->ArrowHeadRadius }, + PointIds, + Sizes, + AABB.xmins, + AABB.ymins, + AABB.zmins, + AABB.xmaxs, + AABB.ymaxs, + AABB.zmaxs, + CoordsHandle); + + this->SetAABBs(AABB); +} + +void GlyphIntersectorVector::IntersectRays(Ray& rays, bool returnCellIndex) +{ + IntersectRaysImp(rays, returnCellIndex); +} + +void GlyphIntersectorVector::IntersectRays(Ray& rays, bool returnCellIndex) +{ + IntersectRaysImp(rays, returnCellIndex); +} + +template +void GlyphIntersectorVector::IntersectRaysImp(Ray& rays, + bool vtkmNotUsed(returnCellIndex)) +{ + detail::GlyphVectorLeafWrapper leafIntersector( + this->GlyphType, this->PointIds, this->Sizes, this->ArrowBodyRadius, this->ArrowHeadRadius); + + BVHTraverser traverser; + traverser.IntersectRays(rays, this->BVH, leafIntersector, this->CoordsHandle); + + RayOperations::UpdateRayStatus(rays); +} + +template +void GlyphIntersectorVector::IntersectionDataImp(Ray& rays, + const vtkm::cont::Field field, + const vtkm::Range& range) +{ + ShapeIntersector::IntersectionPoint(rays); + + const bool isSupportedField = field.IsFieldCell() || field.IsFieldPoint(); + if (!isSupportedField) + { + throw vtkm::cont::ErrorBadValue( + "GlyphIntersectorVector: Field not accociated with a cell set or field"); + } + + vtkm::worklet::DispatcherMapField( + detail::CalculateGlyphVectorNormals(this->GlyphType)) + .Invoke(rays.HitIdx, + rays.Dir, + rays.Intersection, + rays.U, + rays.V, + rays.NormalX, + rays.NormalY, + rays.NormalZ, + CoordsHandle, + PointIds, + Sizes); + + vtkm::worklet::DispatcherMapField>( + detail::GetScalars(vtkm::Float32(range.Min), vtkm::Float32(range.Max))) + .Invoke( + rays.HitIdx, rays.Scalar, vtkm::rendering::raytracing::GetScalarFieldArray(field), PointIds); +} + +void GlyphIntersectorVector::IntersectionData(Ray& rays, + const vtkm::cont::Field field, + const vtkm::Range& range) +{ + IntersectionDataImp(rays, field, range); +} + +void GlyphIntersectorVector::IntersectionData(Ray& rays, + const vtkm::cont::Field field, + const vtkm::Range& range) +{ + IntersectionDataImp(rays, field, range); +} + +vtkm::Id GlyphIntersectorVector::GetNumberOfShapes() const +{ + return PointIds.GetNumberOfValues(); +} + +void GlyphIntersectorVector::SetArrowRadii(vtkm::Float32 bodyRadius, vtkm::Float32 headRadius) +{ + this->ArrowHeadRadius = headRadius; + this->ArrowBodyRadius = bodyRadius; +} + +} +} +} //namespace vtkm::rendering::raytracing diff --git a/vtkm/rendering/raytracing/GlyphIntersectorVector.h b/vtkm/rendering/raytracing/GlyphIntersectorVector.h new file mode 100644 index 000000000..28197860a --- /dev/null +++ b/vtkm/rendering/raytracing/GlyphIntersectorVector.h @@ -0,0 +1,74 @@ +//============================================================================ +// 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. +//============================================================================ +#ifndef vtk_m_rendering_raytracing_Glyph_Intersector_Vector_h +#define vtk_m_rendering_raytracing_Glyph_Intersector_Vector_h + +#include +#include + +namespace vtkm +{ +namespace rendering +{ +namespace raytracing +{ + +class GlyphIntersectorVector : public ShapeIntersector +{ +public: + GlyphIntersectorVector(vtkm::rendering::GlyphType glyphType); + virtual ~GlyphIntersectorVector() override; + + void SetGlyphType(vtkm::rendering::GlyphType glyphType); + + void SetData(const vtkm::cont::CoordinateSystem& coords, + vtkm::cont::ArrayHandle pointIds, + vtkm::cont::ArrayHandle> sizes); + + void IntersectRays(Ray& rays, bool returnCellIndex = false) override; + + + void IntersectRays(Ray& rays, bool returnCellIndex = false) override; + + template + void IntersectRaysImp(Ray& rays, bool returnCellIndex); + + + template + void IntersectionDataImp(Ray& rays, + const vtkm::cont::Field field, + const vtkm::Range& range); + + void IntersectionData(Ray& rays, + const vtkm::cont::Field field, + const vtkm::Range& range) override; + + void IntersectionData(Ray& rays, + const vtkm::cont::Field field, + const vtkm::Range& range) override; + + vtkm::Id GetNumberOfShapes() const override; + + void SetArrowRadii(vtkm::Float32 bodyRadius, vtkm::Float32 headRadius); + +protected: + vtkm::cont::ArrayHandle PointIds; + vtkm::cont::ArrayHandle Sizes; + vtkm::cont::ArrayHandle Normals; + vtkm::rendering::GlyphType GlyphType; + + vtkm::Float32 ArrowBodyRadius; + vtkm::Float32 ArrowHeadRadius; +}; // class GlyphIntersectorVector + +} +} +} //namespace vtkm::rendering::raytracing +#endif //vtk_m_rendering_raytracing_Glyph_Intersector_Vector_h diff --git a/vtkm/rendering/raytracing/RayTracingTypeDefs.h b/vtkm/rendering/raytracing/RayTracingTypeDefs.h index 5ff6260ff..9a23c8115 100644 --- a/vtkm/rendering/raytracing/RayTracingTypeDefs.h +++ b/vtkm/rendering/raytracing/RayTracingTypeDefs.h @@ -144,6 +144,12 @@ GetScalarFieldArray(const vtkm::cont::Field& field) { return field.GetData().ResetTypes(ScalarRenderingTypes{}, VTKM_DEFAULT_STORAGE_LIST{}); } + +VTKM_CONT inline vtkm::cont::UncertainArrayHandle +GetVec3FieldArray(const vtkm::cont::Field& field) +{ + return field.GetData().ResetTypes(Vec3RenderingTypes{}, VTKM_DEFAULT_STORAGE_LIST{}); +} } } } //namespace vtkm::rendering::raytracing diff --git a/vtkm/rendering/testing/CMakeLists.txt b/vtkm/rendering/testing/CMakeLists.txt index f23bba083..110797d21 100644 --- a/vtkm/rendering/testing/CMakeLists.txt +++ b/vtkm/rendering/testing/CMakeLists.txt @@ -26,6 +26,8 @@ set(unit_tests UnitTestMapperWireframer.cxx UnitTestMapperVolume.cxx UnitTestScalarRenderer.cxx + UnitTestMapperGlyphScalar.cxx + UnitTestMapperGlyphVector.cxx ) set(library_sources diff --git a/vtkm/rendering/testing/RenderTest.cxx b/vtkm/rendering/testing/RenderTest.cxx index 744052896..894d9d999 100644 --- a/vtkm/rendering/testing/RenderTest.cxx +++ b/vtkm/rendering/testing/RenderTest.cxx @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -116,6 +118,38 @@ void SetupMapper(vtkm::rendering::MapperPoint& mapper, } } +void SetupMapper(vtkm::rendering::MapperGlyphScalar& mapper, + const vtkm::rendering::testing::RenderTestOptions& options) +{ + mapper.SetGlyphType(options.GlyphType); + mapper.SetScaleByValue(options.UseVariableRadius); + if (options.Radius >= 0) + { + mapper.SetBaseSize(options.Radius); + } + mapper.SetScaleDelta(0.5); + if (options.RenderCells) + { + mapper.SetUseCells(); + } +} + +void SetupMapper(vtkm::rendering::MapperGlyphVector& mapper, + const vtkm::rendering::testing::RenderTestOptions& options) +{ + mapper.SetGlyphType(options.GlyphType); + mapper.SetScaleByValue(options.UseVariableRadius); + if (options.Radius >= 0) + { + mapper.SetBaseSize(options.Radius); + } + mapper.SetScaleDelta(0.5); + if (options.RenderCells) + { + mapper.SetUseCells(); + } +} + template std::unique_ptr MakeMapper( const vtkm::rendering::testing::RenderTestOptions& options) @@ -222,6 +256,12 @@ void DoRenderTest(vtkm::rendering::CanvasRayTracer& canvas, case vtkm::rendering::testing::MapperType::Wireframer: mapper = MakeMapper(options); break; + case vtkm::rendering::testing::MapperType::GlyphScalar: + mapper = MakeMapper(options); + break; + case vtkm::rendering::testing::MapperType::GlyphVector: + mapper = MakeMapper(options); + break; } DoRenderTest(canvas, *mapper, dataSetsFields, outputFile, options); } diff --git a/vtkm/rendering/testing/RenderTest.h b/vtkm/rendering/testing/RenderTest.h index 1b79c5fdd..57417f338 100644 --- a/vtkm/rendering/testing/RenderTest.h +++ b/vtkm/rendering/testing/RenderTest.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -49,7 +50,9 @@ enum struct MapperType Point, Quad, Volume, - Wireframer + Wireframer, + GlyphScalar, + GlyphVector }; struct RenderTestOptions @@ -102,7 +105,8 @@ struct RenderTestOptions // use the best available device for rendering. bool AllowAnyDevice = true; - // Special options for some glyph-like mappers + // Special options for some glyph and glyph-like mappers + vtkm::rendering::GlyphType GlyphType = vtkm::rendering::GlyphType::Cube; bool UseVariableRadius = false; vtkm::Float32 Radius = -1.0f; vtkm::Float32 RadiusDelta = 0.5f; diff --git a/vtkm/rendering/testing/UnitTestMapperGlyphScalar.cxx b/vtkm/rendering/testing/UnitTestMapperGlyphScalar.cxx new file mode 100644 index 000000000..e91ed79f2 --- /dev/null +++ b/vtkm/rendering/testing/UnitTestMapperGlyphScalar.cxx @@ -0,0 +1,81 @@ +//============================================================================ +// 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. +//============================================================================ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + +void RenderTests() +{ + vtkm::cont::testing::MakeTestDataSet maker; + + vtkm::rendering::testing::RenderTestOptions options; + options.Mapper = vtkm::rendering::testing::MapperType::GlyphScalar; + options.AllowAnyDevice = false; + options.ColorTable = vtkm::cont::ColorTable::Preset::Inferno; + + options.GlyphType = vtkm::rendering::GlyphType::Cube; + vtkm::rendering::testing::RenderTest( + maker.Make3DUniformDataSet1(), "pointvar", "rendering/glyph_scalar/regular3D.png", options); + + options.UseVariableRadius = true; + options.RadiusDelta = 4.0f; + options.Radius = 0.25f; + vtkm::rendering::testing::RenderTest(maker.Make3DUniformDataSet1(), + "pointvar", + "rendering/glyph_scalar/variable_regular3D.png", + options); + + options.GlyphType = vtkm::rendering::GlyphType::Sphere; + vtkm::rendering::testing::RenderTest(maker.Make3DUniformDataSet3({ 7 }), + "pointvar", + "rendering/glyph_scalar/variable_spheres_regular3D.png", + options); + + options.GlyphType = vtkm::rendering::GlyphType::Axes; + vtkm::rendering::testing::RenderTest(maker.Make3DUniformDataSet3({ 7 }), + "pointvar", + "rendering/glyph_scalar/variable_axes_regular3D.png", + options); + + options.GlyphType = vtkm::rendering::GlyphType::Quad; + options.Radius = 5.0f; + options.RadiusDelta = 0.75f; + vtkm::rendering::testing::RenderTest(maker.Make3DUniformDataSet3({ 7 }), + "pointvar", + "rendering/glyph_scalar/variable_quads_regular3D.png", + options); + + // restore defaults + options.RadiusDelta = 0.5f; + options.UseVariableRadius = false; + options.GlyphType = vtkm::rendering::GlyphType::Cube; + + options.RenderCells = true; + options.Radius = 1.f; + vtkm::rendering::testing::RenderTest( + maker.Make3DExplicitDataSet7(), "cellvar", "rendering/glyph_scalar/cells.png", options); +} + +} //namespace + +int UnitTestMapperGlyphScalar(int argc, char* argv[]) +{ + return vtkm::cont::testing::Testing::Run(RenderTests, argc, argv); +} diff --git a/vtkm/rendering/testing/UnitTestMapperGlyphVector.cxx b/vtkm/rendering/testing/UnitTestMapperGlyphVector.cxx new file mode 100644 index 000000000..43d944bb3 --- /dev/null +++ b/vtkm/rendering/testing/UnitTestMapperGlyphVector.cxx @@ -0,0 +1,49 @@ +//============================================================================ +// 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. +//============================================================================ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + +void RenderTests() +{ + vtkm::cont::testing::MakeTestDataSet maker; + + vtkm::rendering::testing::RenderTestOptions options; + options.Mapper = vtkm::rendering::testing::MapperType::GlyphVector; + options.AllowAnyDevice = false; + options.ColorTable = vtkm::cont::ColorTable::Preset::Inferno; + options.GlyphType = vtkm::rendering::GlyphType::Arrow; + options.UseVariableRadius = true; + options.RadiusDelta = 4.0f; + options.Radius = 0.02f; + + vtkm::rendering::testing::RenderTest(maker.Make3DExplicitDataSetCowNose(), + "point_vectors", + "rendering/glyph_vector/points_arrows_cownose.png", + options); +} + +} //namespace + +int UnitTestMapperGlyphVector(int argc, char* argv[]) +{ + return vtkm::cont::testing::Testing::Run(RenderTests, argc, argv); +}