// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #if WITH_EDITOR #include "SparseVolumeTextureOpenVDB.h" #if OPENVDB_AVAILABLE #include "CoreMinimal.h" template constexpr uint32 GetOpenVDBValueNumComponents() { return (uint32)ValueType::size; } template<> constexpr uint32 GetOpenVDBValueNumComponents() { return 1; } template<> constexpr uint32 GetOpenVDBValueNumComponents() { return 1; } template<> constexpr uint32 GetOpenVDBValueNumComponents() { return 1; } template float GetOpenVDBValueComponent(const ValueType& Value, uint32 ComponentIndex) { return (float)Value[FMath::Min(ComponentIndex, (uint32)ValueType::size)]; } template<> float GetOpenVDBValueComponent(const FOpenVDBHalf& Value, uint32 ComponentIndex) { return (float)Value; } template<> float GetOpenVDBValueComponent(const float& Value, uint32 ComponentIndex) { return Value; } template<> float GetOpenVDBValueComponent(const double& Value, uint32 ComponentIndex) { return (float)Value; } template ValueType GetOpenVDBMaxComponent(const ValueType& ValueA, const ValueType& ValueB) { return openvdb::math::maxComponent(ValueA, ValueB); } template<> FOpenVDBHalf GetOpenVDBMaxComponent(const FOpenVDBHalf& ValueA, const FOpenVDBHalf& ValueB) { return FOpenVDBHalf(FMath::Max((float)ValueA, (float)ValueB)); } template<> float GetOpenVDBMaxComponent(const float& ValueA, const float& ValueB) { return FMath::Max(ValueA, ValueB); } template<> double GetOpenVDBMaxComponent(const double& ValueA, const double& ValueB) { return FMath::Max(ValueA, ValueB); } template ValueType GetOpenVDBMinComponent(const ValueType& ValueA, const ValueType& ValueB) { return openvdb::math::minComponent(ValueA, ValueB); } template<> FOpenVDBHalf GetOpenVDBMinComponent(const FOpenVDBHalf& ValueA, const FOpenVDBHalf& ValueB) { return FOpenVDBHalf(FMath::Min((float)ValueA, (float)ValueB)); } template<> float GetOpenVDBMinComponent(const float& ValueA, const float& ValueB) { return FMath::Min(ValueA, ValueB); } template<> double GetOpenVDBMinComponent(const double& ValueA, const double& ValueB) { return FMath::Min(ValueA, ValueB); } // Interface for sampling and getting other values from an openVDB grid, abstracting away the actual type/format of the underlying grid. class IOpenVDBGridAdapterBase { public: virtual void IteratePhysical(TFunctionRef OnVisit) const = 0; virtual float Sample(const FIntVector3& VoxelCoord, uint32 ComponentIndex) const = 0; virtual void GetMinMaxValue(uint32 ComponentIndex, float* OutMinVal, float* OutMaxVal) const = 0; virtual float GetBackgroundValue(uint32 ComponentIndex) const = 0; virtual ~IOpenVDBGridAdapterBase() = default; }; template class TOpenVDBGridAdapter : public IOpenVDBGridAdapterBase { using ValueType = typename GridType::ValueType; public: explicit TOpenVDBGridAdapter(openvdb::SharedPtr Grid) : Grid(Grid), Accessor(Grid->getConstAccessor()) { } void IteratePhysical(TFunctionRef OnVisit) const override { constexpr uint32 NumComponents = GetOpenVDBValueNumComponents(); static_assert(NumComponents <= 4); // Iterate over both voxels and tiles. for (auto ValueIt = Grid->cbeginValueOn(); ValueIt; ++ValueIt) { const ValueType VoxelValue = ValueIt.getValue(); const openvdb::Coord CoordVDB = ValueIt.getCoord(); float VoxelValueComponents[4]{}; for (uint32 ComponentIdx = 0; ComponentIdx < NumComponents; ++ComponentIdx) { VoxelValueComponents[ComponentIdx] = GetOpenVDBValueComponent(VoxelValue, ComponentIdx); } const FIntVector3 Coord(CoordVDB[0], CoordVDB[1], CoordVDB[2]); if (ValueIt.isVoxelValue()) { // A single voxel OnVisit(Coord, NumComponents, VoxelValueComponents); } else { // Tile, a higher level node specifying a single value for all finer level nodes/children without actually allocating memory for them. openvdb::CoordBBox TileAABB; ValueIt.getBoundingBox(TileAABB); check(TileAABB.min().x() == Coord.X && TileAABB.min().y() == Coord.Y && TileAABB.min().z() == Coord.Z); const openvdb::Coord AABBDimVDB = TileAABB.dim(); const FIntVector3 AABBDim = FIntVector3(AABBDimVDB.x(), AABBDimVDB.y(), AABBDimVDB.z()); // Iterate over tile AABB and invoke callback for every (virtual) voxel. for (int32 Z = 0; Z < AABBDim.Z; ++Z) { for (int32 Y = 0; Y < AABBDim.Y; ++Y) { for (int32 X = 0; X < AABBDim.X; ++X) { const FIntVector3 TileLocalCoord(Coord.X + X, Coord.Y + Y, Coord.Z + Z); OnVisit(TileLocalCoord, NumComponents, VoxelValueComponents); } } } } } } float Sample(const FIntVector3& VoxelCoord, uint32 ComponentIndex) const override { ValueType VoxelValue = Accessor.getValue(openvdb::Coord(VoxelCoord.X, VoxelCoord.Y, VoxelCoord.Z)); return GetOpenVDBValueComponent(VoxelValue, ComponentIndex); } void GetMinMaxValue(uint32 ComponentIndex, float* OutMinVal, float* OutMaxVal) const override { *OutMinVal = FLT_MAX; *OutMaxVal = -FLT_MAX; if (auto Iter = Grid->tree().cbeginValueOn()) { ValueType MinVal = *Iter; ValueType MaxVal = MinVal; for (++Iter; Iter; ++Iter) { ValueType Value = *Iter; MinVal = GetOpenVDBMinComponent(MinVal, Value); MaxVal = GetOpenVDBMaxComponent(MaxVal, Value); } *OutMinVal = GetOpenVDBValueComponent(MinVal, ComponentIndex); *OutMaxVal = GetOpenVDBValueComponent(MaxVal, ComponentIndex); } } float GetBackgroundValue(uint32 ComponentIndex) const override { return GetOpenVDBValueComponent(Grid->background(), ComponentIndex); } private: typename GridType::Ptr Grid; typename GridType::ConstAccessor Accessor; }; // This is just to not have to repeat the GridType multiple times per MakeShared<>() call. template TSharedPtr CreateOpenVDBGridAdapterInternal(openvdb::GridBase::Ptr Grid) { return MakeShared>(TOpenVDBGridAdapter(openvdb::gridPtrCast(Grid))); } // Creates an adapter suitable for the type of the given grid or nullptr if the type is not supported. TSharedPtr CreateOpenVDBGridAdapter(openvdb::GridBase::Ptr Grid) { if (Grid->isType()) { return CreateOpenVDBGridAdapterInternal(Grid); } else if (Grid->isType()) { return CreateOpenVDBGridAdapterInternal(Grid); } else if (Grid->isType()) { return CreateOpenVDBGridAdapterInternal(Grid); } else if (Grid->isType()) { return CreateOpenVDBGridAdapterInternal(Grid); } else if (Grid->isType()) { return CreateOpenVDBGridAdapterInternal(Grid); } else if (Grid->isType()) { return CreateOpenVDBGridAdapterInternal(Grid); } else if (Grid->isType()) { return CreateOpenVDBGridAdapterInternal(Grid); } else if (Grid->isType()) { return CreateOpenVDBGridAdapterInternal(Grid); } else if (Grid->isType()) { return CreateOpenVDBGridAdapterInternal(Grid); } else if (Grid->isType()) { return CreateOpenVDBGridAdapterInternal(Grid); } else if (Grid->isType()) { return CreateOpenVDBGridAdapterInternal(Grid); } else if (Grid->isType()) { return CreateOpenVDBGridAdapterInternal(Grid); } return nullptr; } #endif //OPENVDB_AVAILABLE #endif // WITH_EDITOR