//============================================================================ // 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 detail { class FindSphereAABBs : public vtkm::worklet::WorkletMapField { public: VTKM_CONT FindSphereAABBs() {} 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& radius, vtkm::Float32& xmin, vtkm::Float32& ymin, vtkm::Float32& zmin, vtkm::Float32& xmax, vtkm::Float32& ymax, vtkm::Float32& zmax, const PointPortalType& points) const { // cast to Float32 vtkm::Vec3f_32 point; vtkm::Vec3f_32 temp; point = static_cast(points.Get(pointId)); temp[0] = radius; temp[1] = 0.f; temp[2] = 0.f; vtkm::Vec3f_32 p = point + temp; //set first point to max and min xmin = p[0]; xmax = p[0]; ymin = p[1]; ymax = p[1]; zmin = p[2]; zmax = p[2]; p = point - temp; xmin = vtkm::Min(xmin, p[0]); xmax = vtkm::Max(xmax, p[0]); ymin = vtkm::Min(ymin, p[1]); ymax = vtkm::Max(ymax, p[1]); zmin = vtkm::Min(zmin, p[2]); zmax = vtkm::Max(zmax, p[2]); temp[0] = 0.f; temp[1] = radius; temp[2] = 0.f; p = point + temp; xmin = vtkm::Min(xmin, p[0]); xmax = vtkm::Max(xmax, p[0]); ymin = vtkm::Min(ymin, p[1]); ymax = vtkm::Max(ymax, p[1]); zmin = vtkm::Min(zmin, p[2]); zmax = vtkm::Max(zmax, p[2]); p = point - temp; xmin = vtkm::Min(xmin, p[0]); xmax = vtkm::Max(xmax, p[0]); ymin = vtkm::Min(ymin, p[1]); ymax = vtkm::Max(ymax, p[1]); zmin = vtkm::Min(zmin, p[2]); zmax = vtkm::Max(zmax, p[2]); temp[0] = 0.f; temp[1] = 0.f; temp[2] = radius; p = point + temp; xmin = vtkm::Min(xmin, p[0]); xmax = vtkm::Max(xmax, p[0]); ymin = vtkm::Min(ymin, p[1]); ymax = vtkm::Max(ymax, p[1]); zmin = vtkm::Min(zmin, p[2]); zmax = vtkm::Max(zmax, p[2]); p = point - temp; xmin = vtkm::Min(xmin, p[0]); xmax = vtkm::Max(xmax, p[0]); ymin = vtkm::Min(ymin, p[1]); ymax = vtkm::Max(ymax, p[1]); zmin = vtkm::Min(zmin, p[2]); zmax = vtkm::Max(zmax, p[2]); } }; //class FindAABBs template class SphereLeafIntersector { public: using IdHandle = vtkm::cont::ArrayHandle; using IdArrayPortal = typename IdHandle::ReadPortalType; using FloatHandle = vtkm::cont::ArrayHandle; using FloatPortal = typename FloatHandle::ReadPortalType; IdArrayPortal PointIds; FloatPortal Radii; SphereLeafIntersector() {} SphereLeafIntersector(const IdHandle& pointIds, const FloatHandle& radii, vtkm::cont::Token& token) : PointIds(pointIds.PrepareForInput(Device(), token)) , Radii(radii.PrepareForInput(Device(), token)) { } 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, // closest distance in this set of primitives Precision& vtkmNotUsed(minU), Precision& vtkmNotUsed(minV), LeafPortalType leafs, const Precision& minDistance) const // report intesections past this distance { const vtkm::Id sphereCount = leafs.Get(currentNode); for (vtkm::Id i = 1; i <= sphereCount; ++i) { const vtkm::Id sphereIndex = leafs.Get(currentNode + i); vtkm::Id pointIndex = PointIds.Get(sphereIndex); vtkm::Float32 radius = Radii.Get(sphereIndex); vtkm::Vec center = vtkm::Vec(points.Get(pointIndex)); vtkm::Vec l = center - origin; Precision dot1 = vtkm::dot(l, dir); if (dot1 >= 0) { Precision d = vtkm::dot(l, l) - dot1 * dot1; Precision r2 = radius * radius; if (d <= r2) { Precision tch = vtkm::Sqrt(r2 - d); Precision t0 = dot1 - tch; //float t1 = dot1+tch; /* if t1 is > 0 and t0<0 then the ray is inside the sphere. if (t0 < closestDistance && t0 > minDistance) { hitIndex = pointIndex; closestDistance = t0; } } } } // for } }; class SphereLeafWrapper : public vtkm::cont::ExecutionObjectBase { protected: using IdHandle = vtkm::cont::ArrayHandle; using FloatHandle = vtkm::cont::ArrayHandle; IdHandle PointIds; FloatHandle Radii; public: SphereLeafWrapper(IdHandle& pointIds, FloatHandle radii) : PointIds(pointIds) , Radii(radii) { } template VTKM_CONT SphereLeafIntersector PrepareForExecution(Device, vtkm::cont::Token& token) const { return SphereLeafIntersector(this->PointIds, this->Radii, token); } }; class CalculateNormals : public vtkm::worklet::WorkletMapField { public: VTKM_CONT CalculateNormals() {} typedef void ControlSignature(FieldIn, FieldIn, FieldOut, FieldOut, FieldOut, WholeArrayIn, WholeArrayIn); typedef void ExecutionSignature(_1, _2, _3, _4, _5, _6, _7); template VTKM_EXEC inline void operator()(const vtkm::Id& hitIndex, const vtkm::Vec& intersection, Precision& normalX, Precision& normalY, Precision& normalZ, const PointPortalType& points, const IndicesPortalType& indicesPortal) const { if (hitIndex < 0) return; vtkm::Id pointId = indicesPortal.Get(hitIndex); vtkm::Vec center = points.Get(pointId); vtkm::Vec normal = intersection - center; vtkm::Normalize(normal); //flip the normal if its pointing the wrong way normalX = normal[0]; normalY = normal[1]; normalZ = normal[2]; } }; //class CalculateNormals template class GetScalar : public vtkm::worklet::WorkletMapField { private: Precision MinScalar; Precision InvDeltaScalar; bool Normalize; public: VTKM_CONT GetScalar(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 GetScalar } // namespace detail SphereIntersector::SphereIntersector() : ShapeIntersector() { } SphereIntersector::~SphereIntersector() {} void SphereIntersector::SetData(const vtkm::cont::CoordinateSystem& coords, vtkm::cont::ArrayHandle pointIds, vtkm::cont::ArrayHandle radii) { this->PointIds = pointIds; this->Radii = radii; this->CoordsHandle = coords; AABBs AABB; vtkm::worklet::DispatcherMapField(detail::FindSphereAABBs()) .Invoke(PointIds, Radii, AABB.xmins, AABB.ymins, AABB.zmins, AABB.xmaxs, AABB.ymaxs, AABB.zmaxs, CoordsHandle); this->SetAABBs(AABB); } void SphereIntersector::IntersectRays(Ray& rays, bool returnCellIndex) { IntersectRaysImp(rays, returnCellIndex); } void SphereIntersector::IntersectRays(Ray& rays, bool returnCellIndex) { IntersectRaysImp(rays, returnCellIndex); } template void SphereIntersector::IntersectRaysImp(Ray& rays, bool vtkmNotUsed(returnCellIndex)) { detail::SphereLeafWrapper leafIntersector(this->PointIds, Radii); BVHTraverser traverser; traverser.IntersectRays(rays, this->BVH, leafIntersector, this->CoordsHandle); RayOperations::UpdateRayStatus(rays); } template void SphereIntersector::IntersectionDataImp(Ray& rays, const vtkm::cont::Field scalarField, const vtkm::Range& scalarRange) { ShapeIntersector::IntersectionPoint(rays); const bool isSupportedField = scalarField.IsCellField() || scalarField.IsPointField(); if (!isSupportedField) { throw vtkm::cont::ErrorBadValue( "SphereIntersector: Field not accociated with a cell set or field"); } vtkm::worklet::DispatcherMapField(detail::CalculateNormals()) .Invoke(rays.HitIdx, rays.Intersection, rays.NormalX, rays.NormalY, rays.NormalZ, CoordsHandle, PointIds); vtkm::worklet::DispatcherMapField>( detail::GetScalar(vtkm::Float32(scalarRange.Min), vtkm::Float32(scalarRange.Max))) .Invoke(rays.HitIdx, rays.Scalar, vtkm::rendering::raytracing::GetScalarFieldArray(scalarField), PointIds); } void SphereIntersector::IntersectionData(Ray& rays, const vtkm::cont::Field scalarField, const vtkm::Range& scalarRange) { IntersectionDataImp(rays, scalarField, scalarRange); } void SphereIntersector::IntersectionData(Ray& rays, const vtkm::cont::Field scalarField, const vtkm::Range& scalarRange) { IntersectionDataImp(rays, scalarField, scalarRange); } vtkm::Id SphereIntersector::GetNumberOfShapes() const { return PointIds.GetNumberOfValues(); } } } } //namespace vtkm::rendering::raytracing