diff --git a/misc/computeutils/CMakeLists.txt b/misc/computeutils/CMakeLists.txt new file mode 100644 index 00000000..9f123a1b --- /dev/null +++ b/misc/computeutils/CMakeLists.txt @@ -0,0 +1,50 @@ +ivw_module(ComputeUtils) + +set(HEADER_FILES + include/inviwo/computeutils/algorithm/volumechannelsplitgl.h + include/inviwo/computeutils/algorithm/volumeminmaxgl.h + include/inviwo/computeutils/algorithm/volumenormalizationgl.h + include/inviwo/computeutils/algorithm/volumereductiongl.h + include/inviwo/computeutils/algorithm/volumeshrinktonormalrangegl.h + include/inviwo/computeutils/computeutilsmodule.h + include/inviwo/computeutils/computeutilsmoduledefine.h + include/inviwo/computeutils/processors/volumechannelsplitglprocessor.h + include/inviwo/computeutils/processors/volumeminmaxglprocessor.h + include/inviwo/computeutils/processors/volumenormalizationglprocessor.h + include/inviwo/computeutils/processors/volumereductionglprocessor.h + include/inviwo/computeutils/processors/volumeshrinktonormalrangeglprocessor.h +) +ivw_group("Header Files" ${HEADER_FILES}) + +set(SOURCE_FILES + src/algorithm/volumechannelsplitgl.cpp + src/algorithm/volumeminmaxgl.cpp + src/algorithm/volumenormalizationgl.cpp + src/algorithm/volumereductiongl.cpp + src/algorithm/volumeshrinktonormalrangegl.cpp + src/computeutilsmodule.cpp + src/processors/volumechannelsplitglprocessor.cpp + src/processors/volumeminmaxglprocessor.cpp + src/processors/volumenormalizationglprocessor.cpp + src/processors/volumereductionglprocessor.cpp + src/processors/volumeshrinktonormalrangeglprocessor.cpp +) +ivw_group("Source Files" ${SOURCE_FILES}) + +set(SHADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/glsl/volumesplit.comp + ${CMAKE_CURRENT_SOURCE_DIR}/glsl/volumereduction.comp + ${CMAKE_CURRENT_SOURCE_DIR}/glsl/volumenormalization.comp + ${CMAKE_CURRENT_SOURCE_DIR}/glsl/volumeshrinktonormalrange.comp +) +ivw_group("Shader Files" ${SHADER_FILES}) + +set(TEST_FILES + tests/unittests/computeutils-unittest-main.cpp +) +ivw_add_unittest(${TEST_FILES}) + +ivw_create_module(${SOURCE_FILES} ${HEADER_FILES} ${SHADER_FILES}) + +# Add shader directory to install package +#ivw_add_to_module_pack(${CMAKE_CURRENT_SOURCE_DIR}/glsl) diff --git a/misc/computeutils/depends.cmake b/misc/computeutils/depends.cmake new file mode 100644 index 00000000..83062bff --- /dev/null +++ b/misc/computeutils/depends.cmake @@ -0,0 +1,3 @@ +set(dependencies + InviwoOpenGLModule +) diff --git a/misc/computeutils/glsl/volumenormalization.comp b/misc/computeutils/glsl/volumenormalization.comp new file mode 100644 index 00000000..b51c464e --- /dev/null +++ b/misc/computeutils/glsl/volumenormalization.comp @@ -0,0 +1,80 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2014-2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#include "utils/structs.glsl" + +uniform sampler3D inputTexture; +uniform VolumeParameters inputTextureParameters; +writeonly uniform image3D outputTexture; + +layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +vec4 getNormalizedVoxel(sampler3D volume, VolumeParameters volumeParams, ivec3 samplePos) { + return (texelFetch(volume, samplePos, 0) + volumeParams.formatOffset) + * (1.0 - volumeParams.formatScaling); +} + +float getNormalizedVoxelChannel(sampler3D volume, VolumeParameters volumeParams, ivec3 samplePos, int channel) { + vec4 v = getNormalizedVoxel(volume, volumeParams, samplePos); + return v[channel]; +} + +void main() { + ivec3 samplePos = ivec3(gl_GlobalInvocationID); + + vec4 original = texelFetch(inputTexture, samplePos, 0); + + float l, m, n, o; + +#ifdef NORMALIZE_CHANNEL_0 + l = getNormalizedVoxelChannel(inputTexture, inputTextureParameters, samplePos, 0); +#else + l = original[0]; +#endif + +#ifdef NORMALIZE_CHANNEL_1 + m = getNormalizedVoxelChannel(inputTexture, inputTextureParameters, samplePos, 1); +#else + m = original[1]; +#endif + +#ifdef NORMALIZE_CHANNEL_2 + n = getNormalizedVoxelChannel(inputTexture, inputTextureParameters, samplePos, 2); +#else + n = original[2]; +#endif + +#ifdef NORMALIZE_CHANNEL_3 + o = getNormalizedVoxelChannel(inputTexture, inputTextureParameters, samplePos, 3); +#else + o = original[3]; +#endif + + imageStore(outputTexture, samplePos, vec4(l, m, n, o)); +} diff --git a/misc/computeutils/glsl/volumereduction.comp b/misc/computeutils/glsl/volumereduction.comp new file mode 100644 index 00000000..f4478151 --- /dev/null +++ b/misc/computeutils/glsl/volumereduction.comp @@ -0,0 +1,218 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +uniform +#if defined REDUCTION_SAMPLER_TYPE_FLOAT + sampler3D +#elif defined REDUCTION_SAMPLER_TYPE_INTEGER + isampler3D +#elif defined REDUCTION_SAMPLER_TYPE_UNSIGNED + usampler3D +#endif +inputTexture; + +uniform vec2 disregardingRange; + +writeonly uniform image3D outputTexture; + +layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void main() { + ivec3 outputPosition = ivec3(gl_GlobalInvocationID); + ivec3 samplePosition000 = outputPosition * ivec3(2, 2, 2); + ivec3 samplePosition001 = samplePosition000 + ivec3(0, 0, 1); + ivec3 samplePosition010 = samplePosition000 + ivec3(0, 1, 0); + ivec3 samplePosition011 = samplePosition000 + ivec3(0, 1, 1); + ivec3 samplePosition100 = samplePosition000 + ivec3(1, 0, 0); + ivec3 samplePosition101 = samplePosition000 + ivec3(1, 0, 1); + ivec3 samplePosition110 = samplePosition000 + ivec3(1, 1, 0); + ivec3 samplePosition111 = samplePosition000 + ivec3(1, 1, 1); + + vec4 val = vec4(0); + vec4 sampledVal = vec4(0); + bool less = false; + bool greater = false; + vec4 disregardingRangeMin = vec4(disregardingRange.x); + vec4 disregardingRangeMax = vec4(disregardingRange.y); + +// Min operator +#if OPERATOR == 0 + // Init with infinity + val = vec4(1.0 / 0.0); + + + sampledVal = texelFetch(inputTexture, samplePosition000, 0); +#if DISREGARD == 1 + less = any(lessThan(sampledVal, disregardingRangeMin)); + val = less ? val : min(val, sampledVal); +#else + val = min(val, sampledVal); +#endif + + sampledVal = texelFetch(inputTexture, samplePosition001, 0); +#if DISREGARD == 1 + less = any(lessThan(sampledVal, disregardingRangeMin)); + val = less ? val : min(val, sampledVal); +#else + val = min(val, sampledVal); +#endif + + sampledVal = texelFetch(inputTexture, samplePosition010, 0); +#if DISREGARD == 1 + less = any(lessThan(sampledVal, disregardingRangeMin)); + val = less ? val : min(val, sampledVal); +#else + val = min(val, sampledVal); +#endif + + sampledVal = texelFetch(inputTexture, samplePosition011, 0); +#if DISREGARD == 1 + less = any(lessThan(sampledVal, disregardingRangeMin)); + val = less ? val : min(val, sampledVal); +#else + val = min(val, sampledVal); +#endif + + sampledVal = texelFetch(inputTexture, samplePosition100, 0); +#if DISREGARD == 1 + less = any(lessThan(sampledVal, disregardingRangeMin)); + val = less ? val : min(val, sampledVal); +#else + val = min(val, sampledVal); +#endif + + sampledVal = texelFetch(inputTexture, samplePosition101, 0); +#if DISREGARD == 1 + less = any(lessThan(sampledVal, disregardingRangeMin)); + val = less ? val : min(val, sampledVal); +#else + val = min(val, sampledVal); +#endif + + sampledVal = texelFetch(inputTexture, samplePosition110, 0); +#if DISREGARD == 1 + less = any(lessThan(sampledVal, disregardingRangeMin)); + val = less ? val : min(val, sampledVal); +#else + val = min(val, sampledVal); +#endif + + sampledVal = texelFetch(inputTexture, samplePosition111, 0); +#if DISREGARD == 1 + less = any(lessThan(sampledVal, disregardingRangeMin)); + val = less ? val : min(val, sampledVal); +#else + val = min(val, sampledVal); +#endif +#endif + +// Max operator +#if OPERATOR == 1 + // Init with -infinity + val = vec4(-1.0 / 0.0); + + sampledVal = texelFetch(inputTexture, samplePosition000, 0); +#if DISREGARD == 1 + greater = any(greaterThan(sampledVal, disregardingRangeMax)); + val = greater ? val : max(val, sampledVal); +#else + val = max(val, sampledVal); +#endif + + sampledVal = texelFetch(inputTexture, samplePosition001, 0); +#if DISREGARD == 1 + greater = any(greaterThan(sampledVal, disregardingRangeMax)); + val = greater ? val : max(val, sampledVal); +#else + val = max(val, sampledVal); +#endif + + sampledVal = texelFetch(inputTexture, samplePosition010, 0); +#if DISREGARD == 1 + greater = any(greaterThan(sampledVal, disregardingRangeMax)); + val = greater ? val : max(val, sampledVal); +#else + val = max(val, sampledVal); +#endif + + sampledVal = texelFetch(inputTexture, samplePosition011, 0); +#if DISREGARD == 1 + greater = any(greaterThan(sampledVal, disregardingRangeMax)); + val = greater ? val : max(val, sampledVal); +#else + val = max(val, sampledVal); +#endif + + sampledVal = texelFetch(inputTexture, samplePosition100, 0); +#if DISREGARD == 1 + greater = any(greaterThan(sampledVal, disregardingRangeMax)); + val = greater ? val : max(val, sampledVal); +#else + val = max(val, sampledVal); +#endif + + sampledVal = texelFetch(inputTexture, samplePosition101, 0); +#if DISREGARD == 1 + greater = any(greaterThan(sampledVal, disregardingRangeMax)); + val = greater ? val : max(val, sampledVal); +#else + val = max(val, sampledVal); +#endif + + sampledVal = texelFetch(inputTexture, samplePosition110, 0); +#if DISREGARD == 1 + greater = any(greaterThan(sampledVal, disregardingRangeMax)); + val = greater ? val : max(val, sampledVal); +#else + val = max(val, sampledVal); +#endif + + sampledVal = texelFetch(inputTexture, samplePosition111, 0); +#if DISREGARD == 1 + greater = any(greaterThan(sampledVal, disregardingRangeMax)); + val = greater ? val : max(val, sampledVal); +#else + val = max(val, sampledVal); +#endif +#endif + +// Sum operator +#if OPERATOR == 2 + val = texelFetch(inputTexture, samplePosition000, 0); + val = val + texelFetch(inputTexture, samplePosition001, 0); + val = val + texelFetch(inputTexture, samplePosition010, 0); + val = val + texelFetch(inputTexture, samplePosition011, 0); + val = val + texelFetch(inputTexture, samplePosition100, 0); + val = val + texelFetch(inputTexture, samplePosition101, 0); + val = val + texelFetch(inputTexture, samplePosition110, 0); + val = val + texelFetch(inputTexture, samplePosition111, 0); +#endif + + imageStore (outputTexture, outputPosition, val); +} diff --git a/misc/computeutils/glsl/volumeshrinktonormalrange.comp b/misc/computeutils/glsl/volumeshrinktonormalrange.comp new file mode 100644 index 00000000..a311963a --- /dev/null +++ b/misc/computeutils/glsl/volumeshrinktonormalrange.comp @@ -0,0 +1,82 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2014-2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#include "utils/structs.glsl" + +uniform sampler3D inputTexture; +uniform VolumeParameters inputTextureParameters; +writeonly uniform image3D outputTexture; +uniform float offset; + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +vec4 getNormalizedVoxel(sampler3D volume, VolumeParameters volumeParams, ivec3 samplePos) { + return (texelFetch(volume, samplePos, 0) + volumeParams.formatOffset) * + (1.0 - volumeParams.formatScaling); +} + +float getNormalizedVoxelChannel(sampler3D volume, VolumeParameters volumeParams, ivec3 samplePos, + int channel) { + vec4 v = getNormalizedVoxel(volume, volumeParams, samplePos); + return v[channel]; +} + +void main() { + ivec3 samplePos = ivec3(gl_GlobalInvocationID); + + vec4 original = texelFetch(inputTexture, samplePos, 0); + + float l, m, n, o; + +#ifdef SHRINK_CHANNEL_0 + l = getNormalizedVoxelChannel(inputTexture, inputTextureParameters, samplePos, 0) + offset; +#else + l = original[0]; +#endif + +#ifdef SHRINK_CHANNEL_1 + m = getNormalizedVoxelChannel(inputTexture, inputTextureParameters, samplePos, 1) + offset; +#else + m = original[1]; +#endif + +#ifdef SHRINK_CHANNEL_2 + n = getNormalizedVoxelChannel(inputTexture, inputTextureParameters, samplePos, 2) + offset; +#else + n = original[2]; +#endif + +#ifdef SHRINK_CHANNEL_3 + o = getNormalizedVoxelChannel(inputTexture, inputTextureParameters, samplePos, 3) + offset; +#else + o = original[3]; +#endif + + imageStore(outputTexture, samplePos, vec4(l, m, n, o)); +} diff --git a/misc/computeutils/glsl/volumesplit.comp b/misc/computeutils/glsl/volumesplit.comp new file mode 100644 index 00000000..01a4ba48 --- /dev/null +++ b/misc/computeutils/glsl/volumesplit.comp @@ -0,0 +1,56 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +uniform sampler3D inputTexture; + +writeonly uniform image3D outputTexture; + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void main() { + ivec3 pos = ivec3(gl_GlobalInvocationID); + + vec4 val = texelFetch(inputTexture, pos, 0); + +#if CHANNEL == 0 + imageStore(outputTexture, pos, vec4(val.x)); +#endif + +#if CHANNEL == 1 + imageStore(outputTexture, pos, vec4(val.y)); +#endif + +#if CHANNEL == 2 + imageStore(outputTexture, pos, vec4(val.z)); +#endif + +#if CHANNEL == 3 + imageStore(outputTexture, pos, vec4(val.w)); +#endif +} diff --git a/misc/computeutils/include/inviwo/computeutils/algorithm/volumechannelsplitgl.h b/misc/computeutils/include/inviwo/computeutils/algorithm/volumechannelsplitgl.h new file mode 100644 index 00000000..72c19b3d --- /dev/null +++ b/misc/computeutils/include/inviwo/computeutils/algorithm/volumechannelsplitgl.h @@ -0,0 +1,67 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2016-2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace inviwo { +/** \class VolumeChannelSplitGL + * + * Splits multi-channel volumes into several single-channel volumes. + */ +class IVW_MODULE_COMPUTEUTILS_API VolumeChannelSplitGL { +public: + template + VolumeChannelSplitGL(Callback C) : VolumeChannelSplitGL() { + shader_.onReload(C); + } + + VolumeChannelSplitGL(); + + virtual ~VolumeChannelSplitGL() = default; + + /** + * Splits multi-channel volumes into several single-channel volumes + * + * @param volume Volume to be split. + * @return Set of single-channel volumes + */ + std::vector> split(std::shared_ptr volume); + +protected: + Shader shader_; + + VolumeReductionGL volumeReductionGl_; +}; + +} // namespace inviwo diff --git a/misc/computeutils/include/inviwo/computeutils/algorithm/volumeminmaxgl.h b/misc/computeutils/include/inviwo/computeutils/algorithm/volumeminmaxgl.h new file mode 100644 index 00000000..195b117f --- /dev/null +++ b/misc/computeutils/include/inviwo/computeutils/algorithm/volumeminmaxgl.h @@ -0,0 +1,74 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2016-2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace inviwo { + +/** \class VolumeMinMaxGL + * + * GL implementation of min-max computation for inviwo volumes. + */ +class IVW_MODULE_COMPUTEUTILS_API VolumeMinMaxGL { +public: + VolumeMinMaxGL() = default; + + virtual ~VolumeMinMaxGL() = default; + + /** + * This method calculates min and max values of a volume. If you need min/max per + * channel you should look at VolumeReductionGL. + * + * @param volume Input volume. + * @param disregardingStatus Indicating whether or not the calculation of the min/max value + * should disregard a certain value range. This is for example handy for volumes where special + * regions are marked with voxel values of INT_MAX or the like. + * * Example: Your data array consists of {0, 1, 2, 3, INT_MAX} and you would like to compute + * the max value. In addition, you know that outliers are marked with a value of INT_MAX so you + * would like those values to not be considered. In that case, you call + * minmax(myVolume, DisregardingStatus::On, + * vec2{myVolume->dataMap_.dataRange.x, std::numeric_limits::max() - 1}); + * @param range The range that should be disregarded. + * + * @returns Aggregated min/max values. + */ + dvec2 minmax(std::shared_ptr volume, + DisregardingStatus disregardingStatus = DisregardingStatus::Off, + const vec2& range = vec2{0}); + +protected: + VolumeReductionGL volumeReductionGL_; +}; + +} // namespace inviwo diff --git a/misc/computeutils/include/inviwo/computeutils/algorithm/volumenormalizationgl.h b/misc/computeutils/include/inviwo/computeutils/algorithm/volumenormalizationgl.h new file mode 100644 index 00000000..b9200981 --- /dev/null +++ b/misc/computeutils/include/inviwo/computeutils/algorithm/volumenormalizationgl.h @@ -0,0 +1,93 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2016-2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#pragma once + +#include +#include +#include + +namespace inviwo { +/** \class VolumeNormalizationGL + * + * GL implementation of volume normalization. The algorithm takes in a volume and normalized its + * data in the selected channels to range [0,1]. + * Note that this algorithm normalizes channels independently, it does not normalize a multi-channel + * volume in terms of vector norms! + */ +class IVW_MODULE_COMPUTEUTILS_API VolumeNormalizationGL { +public: + template + VolumeNormalizationGL(Callback C) : VolumeNormalizationGL() { + shader_.onReload(C); + } + + VolumeNormalizationGL(); + + virtual ~VolumeNormalizationGL() = default; + + /** + * Performs the normalization on the GPU. + * + * @param volume Input volume to be normalized. + * @return A volume whose selected channels have been normalized. + */ + std::shared_ptr normalize(const Volume& volume); + + /** + * Sets the channel that are to be normalized. In practice, this method in-/ejects shader + * defines for every channel. + * + * @param channel Channel for which the normalization should be set to true or false. + * @param normalize Boolean value indicating whether or not the selected channel should be + * normalized. + */ + void setNormalizeChannel(size_t channel, bool normalize); + + /** + * Sets the channels that are to be normalized. In practice, this method in-/ejects shader + * defines for every channel. + * + * @param normalize Set of boolean values indicating which channels to normalize. + */ + void setNormalizeChannels(bvec4 normalize); + + /** + * Resets the normalization settings. Channel 0 is set to true, rest to false. + */ + void reset(); + +protected: + Shader shader_; + +private: + bool needsCompilation_; +}; + +} // namespace inviwo diff --git a/misc/computeutils/include/inviwo/computeutils/algorithm/volumereductiongl.h b/misc/computeutils/include/inviwo/computeutils/algorithm/volumereductiongl.h new file mode 100644 index 00000000..de744cdf --- /dev/null +++ b/misc/computeutils/include/inviwo/computeutils/algorithm/volumereductiongl.h @@ -0,0 +1,113 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2016-2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#pragma once + +#include +#include +#include + +namespace inviwo { +enum class ReductionOperator { Min = 0, Max = 1, Sum = 2, None = 3 }; +enum class DisregardingStatus { Off = 0, On = 1, Unset = 2 }; + +/** \class VolumeReductionGL + * + * GL implementation of add, min, and max reductions for 3D textures (volumes). + */ +class IVW_MODULE_COMPUTEUTILS_API VolumeReductionGL { +public: + template + VolumeReductionGL(Callback C) : VolumeReductionGL() { + shader_.onReload(C); + } + + VolumeReductionGL(); + + virtual ~VolumeReductionGL() = default; + + /** + * This method calculates and returns the reduced volume (dimensions 1x1x1) according to the + * operator, i.e. min/max/sum. + * + * @param volume Input volume. + * @param op Reduction operator that is applied to calculate the reduced value, i.e. + * min/max/sum. + * @param disregardingStatus Indicating whether or not the calculation of the min/max/sum value + * should disregard a certain value range. This is for example handy for volumes where special + * regions are marked with voxel values of INT_MAX or the like. + * Example: Your data array consists of {0, 1, 2, 3, INT_MAX} and you would like to compute the + * max value. In addition, you know that outliers are marked with a value of INT_MAX so you + * would like those values to not be considered. In that case, you call + * reduce(myVolume, ReductionOperator::Max, DisregardingStatus::On, + * vec2{myVolume->dataMap_.dataRange.x, std::numeric_limits::max() - 1}); + * @param range The range that should be disregarded. + * + * @returns Reduced volume (dimensions 1x1x1) according to selected operator. + */ + std::shared_ptr reduce(std::shared_ptr volume, ReductionOperator op, + DisregardingStatus disregardingStatus = DisregardingStatus::Off, + const vec2& range = vec2{0}); + + /** + * This method calculates and returns the reduced value according to the operator, i.e. + * min/max/sum. + * + * @param volume Input volume. + * @param op Reduction operator that is applied to calculate the reduced value, i.e. + * min/max/sum. + * @param disregardingStatus Indicating whether or not the calculation of the min/max/sum value + * should be clamped to a certain range. This is for example handy for volumes where special + * regions are marked with voxel values of INT_MAX or the like. + * Example: Your data array consists of {0, 1, 2, 3, INT_MAX} and you would like to compute the + * max value. In addition, you know that outliers are marked with a value of INT_MAX so you + * would like those values to not be considered. In that case, you call + * reduce(myVolume, ReductionOperator::Max, DisregardingStatus::On, + * vec2{myVolume->dataMap_.dataRange.x, std::numeric_limits::max() - 1}); + * @param range The range that should be disregarded. + * + * @returns Reduced value according to selected operator. + */ + double reduce_v(std::shared_ptr volume, ReductionOperator op, + DisregardingStatus disregardingStatus = DisregardingStatus::Off, + const vec2& range = vec2{0}); + +protected: + Shader shader_; + ReductionOperator activeReductionOperator_; + DisregardingStatus activeDisregardingStatus_; + +private: + void setReductionOperator(ReductionOperator op); + void setDisregarding(DisregardingStatus disregardingStatus); + void setSamplerType(std::shared_ptr volume); + static void gpuDispatch(GLuint x, GLuint y, GLuint z); +}; + +} // namespace inviwo diff --git a/misc/computeutils/include/inviwo/computeutils/algorithm/volumeshrinktonormalrangegl.h b/misc/computeutils/include/inviwo/computeutils/algorithm/volumeshrinktonormalrangegl.h new file mode 100644 index 00000000..01315127 --- /dev/null +++ b/misc/computeutils/include/inviwo/computeutils/algorithm/volumeshrinktonormalrangegl.h @@ -0,0 +1,93 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2016-2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#pragma once + +#include +#include +#include + +namespace inviwo { +/** \class VolumeShrinkToNormalRangeGL + * + * GL implementation of volume shrink operation. The algorithm takes in a volume and shrinks its + * data in the selected channels to range [0,1] + offset where offset is the percentual deviation of + * the minimum from 0. For example, range [-0.5, 1.0] will be shrunk to [-0.33, 0.66]. + */ +class IVW_MODULE_COMPUTEUTILS_API VolumeShrinkToNormalRangeGL { +public: + template + VolumeShrinkToNormalRangeGL(Callback C) : VolumeShrinkToNormalRangeGL() { + shader_.onReload(C); + } + + VolumeShrinkToNormalRangeGL(); + + virtual ~VolumeShrinkToNormalRangeGL() = default; + + /** + * Shrinks volume data in the selected channels to range [0,1] + offset where offset is the + * percentual deviaton of the minimum from 0. For example, range [-0.5, 1.0] will be shrunk to + * [-0.33, 0.66]. + * + * @param volume Input volume to be shrunk. + * @return A volume whose selected channels have been shrunk. + */ + std::shared_ptr shrink(const Volume& volume); + + /** + * Sets the channel that are to be shrunk. In practice, this method in-/ejects shader + * defines for every channel. + * + * @param channel Channel for which the shrinking should be set to true or false. + * @param shrink Boolean value indicating whether or not the selected channel should be shrunk. + */ + void setShrinkChannel(size_t channel, bool shrink); + + /** + * Sets the channels that are to be shrunk. In practice, this method in-/ejects shader + * defines for every channel. + * + * @param shrink Set of boolean values indicating which channels to shrink. + */ + void setShrinkChannels(bvec4 shrink); + + /** + * Resets the skrinking settings. Channel 0 is set to true, rest to false. + */ + void reset(); + +protected: + Shader shader_; + +private: + bool needsCompilation_; +}; + +} // namespace inviwo diff --git a/misc/computeutils/include/inviwo/computeutils/computeutilsmodule.h b/misc/computeutils/include/inviwo/computeutils/computeutilsmodule.h new file mode 100644 index 00000000..acf2aa82 --- /dev/null +++ b/misc/computeutils/include/inviwo/computeutils/computeutilsmodule.h @@ -0,0 +1,42 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ +#pragma once + +#include +#include + +namespace inviwo { + +class IVW_MODULE_COMPUTEUTILS_API ComputeUtilsModule : public InviwoModule { +public: + ComputeUtilsModule(InviwoApplication* app); + virtual ~ComputeUtilsModule() = default; +}; + +} // namespace inviwo diff --git a/misc/computeutils/include/inviwo/computeutils/computeutilsmoduledefine.h b/misc/computeutils/include/inviwo/computeutils/computeutilsmoduledefine.h new file mode 100644 index 00000000..e3923f60 --- /dev/null +++ b/misc/computeutils/include/inviwo/computeutils/computeutilsmoduledefine.h @@ -0,0 +1,22 @@ +#pragma once + +// clang-format off +#ifdef INVIWO_ALL_DYN_LINK //DYNAMIC + // If we are building DLL files we must declare dllexport/dllimport + #ifdef IVW_MODULE_COMPUTEUTILS_EXPORTS + #ifdef _WIN32 + #define IVW_MODULE_COMPUTEUTILS_API __declspec(dllexport) + #else //UNIX (GCC) + #define IVW_MODULE_COMPUTEUTILS_API __attribute__ ((visibility ("default"))) + #endif + #else + #ifdef _WIN32 + #define IVW_MODULE_COMPUTEUTILS_API __declspec(dllimport) + #else + #define IVW_MODULE_COMPUTEUTILS_API + #endif + #endif +#else //STATIC + #define IVW_MODULE_COMPUTEUTILS_API +#endif +// clang-format on diff --git a/misc/computeutils/include/inviwo/computeutils/processors/volumechannelsplitglprocessor.h b/misc/computeutils/include/inviwo/computeutils/processors/volumechannelsplitglprocessor.h new file mode 100644 index 00000000..4cb67e06 --- /dev/null +++ b/misc/computeutils/include/inviwo/computeutils/processors/volumechannelsplitglprocessor.h @@ -0,0 +1,69 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +namespace inviwo { + +/** \docpage{org.inviwo.VolumeChannelSplitGLProcessor, Volume Channel Split Processor} + * ![](org.inviwo.VolumeChannelSplitGLProcessor.png?classIdentifier=org.inviwo.VolumeChannelSplitGLProcessor) + * + * Splits a multi-channel volume into a set of single-channel volumes. + * + * ### Inports + * * __Volume inport__ Input volume. + * + * ### Outports + * * __Volume outport__ Set of single-channel volumes. + */ +class IVW_MODULE_COMPUTEUTILS_API VolumeChannelSplitGLProcessor : public Processor { +public: + VolumeChannelSplitGLProcessor(); + virtual ~VolumeChannelSplitGLProcessor() = default; + + virtual void process() override; + + virtual const ProcessorInfo getProcessorInfo() const override; + static const ProcessorInfo processorInfo_; + +private: + VolumeInport volumeInport_; + + VolumeSequenceOutport volumeOutport_; + + VolumeChannelSplitGL volumeChannelSplitGl_; +}; + +} // namespace inviwo diff --git a/misc/computeutils/include/inviwo/computeutils/processors/volumeminmaxglprocessor.h b/misc/computeutils/include/inviwo/computeutils/processors/volumeminmaxglprocessor.h new file mode 100644 index 00000000..163d5368 --- /dev/null +++ b/misc/computeutils/include/inviwo/computeutils/processors/volumeminmaxglprocessor.h @@ -0,0 +1,82 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace inviwo { + +/** \docpage{org.inviwo.VolumeMinMaxGLProcessor, Volume Min Max GLProcessor} + * ![](org.inviwo.VolumeMinMaxGLProcessor.png?classIdentifier=org.inviwo.VolumeMinMaxGLProcessor) + * + * This method calculates min and max values of a volume. If you need min/max per channel you should + * look at VolumeReductionGL. The disregarding status indicates whether or not the calculation of + * the min/max values should disregard a certain value range. This is for example handy for volumes + * where special regions are marked with voxel values of INT_MAX or the like. Example: Your data + * array consists of {0, 1, 2, 3, INT_MAX} and you would like to compute the max value. In addition, + * you know that outliers are marked with a value of INT_MAX so you would like those values to not + * be considered. + * + * ### Inputs + * * __Volume inport__ Input volume. + * + * ### Outports + * * __Volume outport__ Reduced volume. + * + * ### Properties + * * __Disregarding status__ Enable/disable value discarding + * * __Disregarding range__ Value range to be discarded when computing minmax values. + */ +class IVW_MODULE_COMPUTEUTILS_API VolumeMinMaxGLProcessor : public Processor { +public: + VolumeMinMaxGLProcessor(); + virtual ~VolumeMinMaxGLProcessor() = default; + + virtual void process() override; + + virtual const ProcessorInfo getProcessorInfo() const override; + static const ProcessorInfo processorInfo_; + +private: + VolumeInport volumeInport_; + VolumeOutport volumeOutport_; + + VolumeMinMaxGL volumeMinMaxGl_; + + TemplateOptionProperty disregardingStatus_; + FloatMinMaxProperty disregardingRange_; +}; + +} // namespace inviwo diff --git a/misc/computeutils/include/inviwo/computeutils/processors/volumenormalizationglprocessor.h b/misc/computeutils/include/inviwo/computeutils/processors/volumenormalizationglprocessor.h new file mode 100644 index 00000000..99470cf0 --- /dev/null +++ b/misc/computeutils/include/inviwo/computeutils/processors/volumenormalizationglprocessor.h @@ -0,0 +1,89 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace inviwo { + +/** \docpage{org.inviwo.VolumeNormalizationGLProcessor, Volume Normalization Processor} + * ![](org.inviwo.VolumeNormalizationGLProcessor.png?classIdentifier=org.inviwo.VolumeNormalizationGLProcessor) + * + * Normalizes the selected channels of the input volume to range [0,1]. + * Note that this algorithm normalizes channels independently, it does not normalize a multi-channel + * volume in terms of vector norms! + * + * ### Inputs + * * __Volume inport__ Input Volume + * + * ### Outports + * * __Volume outport__ Normalized volume (if so selected) + * + * ### Properties + * * __Channels__ Check the boxes for those channels you wish to normalize to range [0,1] + */ + +/** + * \class VolumeNormalizationGLProcessor + * + * Enables the usage of the %VolumeNormalization algorithm. For details about the algorithm, + * please see VolumeNormalization. + * Note that this algorithm normalizes channels independently, it does not normalize a multi-channel + * volume in terms of vector norms! + */ +class IVW_MODULE_COMPUTEUTILS_API VolumeNormalizationGLProcessor : public Processor { +public: + VolumeNormalizationGLProcessor(); + virtual ~VolumeNormalizationGLProcessor() = default; + + virtual void process() override; + + virtual const ProcessorInfo getProcessorInfo() const override; + static const ProcessorInfo processorInfo_; + +private: + VolumeInport volumeInport_; + VolumeOutport volumeOutport_; + + CompositeProperty channels_; + BoolProperty normalizeChannel0_; + BoolProperty normalizeChannel1_; + BoolProperty normalizeChannel2_; + BoolProperty normalizeChannel3_; + + VolumeNormalizationGL volumeNormalization_; +}; + +} // namespace inviwo diff --git a/misc/computeutils/include/inviwo/computeutils/processors/volumereductionglprocessor.h b/misc/computeutils/include/inviwo/computeutils/processors/volumereductionglprocessor.h new file mode 100644 index 00000000..ff3831d0 --- /dev/null +++ b/misc/computeutils/include/inviwo/computeutils/processors/volumereductionglprocessor.h @@ -0,0 +1,74 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace inviwo { + +/** \docpage{org.inviwo.VolumeReductionGLProcessor, Volume Reduction Processor} + * ![](org.inviwo.VolumeReductionGLProcessor.png?classIdentifier=org.inviwo.VolumeReductionGLProcessor) + * + * GL implementation of add, min, and max reductions for 3D textures (volumes). + * + * ### Inputs + * * __Volume inport__ Input volume. + * + * ### Outports + * * __Volume outport__ Reduced volume. + * + * ### Properties + * * __Reduction operator__ Operator for reduction. + * + */ +class IVW_MODULE_COMPUTEUTILS_API VolumeReductionGLProcessor : public Processor { +public: + VolumeReductionGLProcessor(); + virtual ~VolumeReductionGLProcessor() = default; + + virtual void process() override; + + virtual const ProcessorInfo getProcessorInfo() const override; + static const ProcessorInfo processorInfo_; + +private: + VolumeInport volumeInport_; + VolumeOutport volumeOutport_; + + TemplateOptionProperty reductionOperator_; + + VolumeReductionGL gpuReduction_; +}; + +} // namespace inviwo diff --git a/misc/computeutils/include/inviwo/computeutils/processors/volumeshrinktonormalrangeglprocessor.h b/misc/computeutils/include/inviwo/computeutils/processors/volumeshrinktonormalrangeglprocessor.h new file mode 100644 index 00000000..8227673d --- /dev/null +++ b/misc/computeutils/include/inviwo/computeutils/processors/volumeshrinktonormalrangeglprocessor.h @@ -0,0 +1,91 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace inviwo { + +/** \docpage{org.inviwo.VolumeShrinkToNormalRangeGLProcessor, Volume Shrink To Normal Range + * Processor} + * ![](org.inviwo.VolumeShrinkToNormalRangeGLProcessor.png?classIdentifier=org.inviwo.VolumeShrinkToNormalRangeGLProcessor) + * + * Shrinks the selected channels of the input volume to range [0 + offset, 1 + offset] where offset + * is the percentual deviaton of the minimum from 0. For example, range [-0.5, 1.0] will be shrunk + * to [-0.33, 0.66]. + * + * ### Inputs + * * __Volume inport__ Input Volume + * + * ### Outports + * * __Volume outport__ Shrunk volume (if so selected) + * + * ### Properties + * * __Channels__ Check the boxes for those channels you wish to shrink to range [0 + offset, 1 + + * offset] + */ + +/** + * \class VolumeShrinkToNormalRangeGLProcessor + * + * Enables the usage of the %VolumeNormalization algorithm. For details about the algorithm, + * please see VolumeNormalization. + * Note that this algorithm normalizes channels independently, it does not normalize a multi-channel + * volume in terms of vector norms! + */ +class IVW_MODULE_COMPUTEUTILS_API VolumeShrinkToNormalRangeGLProcessor : public Processor { +public: + VolumeShrinkToNormalRangeGLProcessor(); + virtual ~VolumeShrinkToNormalRangeGLProcessor() = default; + + virtual void process() override; + + virtual const ProcessorInfo getProcessorInfo() const override; + static const ProcessorInfo processorInfo_; + +private: + VolumeInport volumeInport_; + VolumeOutport volumeOutport_; + + CompositeProperty channels_; + BoolProperty shrinkChannel0_; + BoolProperty shrinkChannel1_; + BoolProperty shrinkChannel2_; + BoolProperty shrinkChannel3_; + + VolumeShrinkToNormalRangeGL volumeShrinkToNormalRangeGl_; +}; + +} // namespace inviwo diff --git a/misc/computeutils/readme.md b/misc/computeutils/readme.md new file mode 100644 index 00000000..0694131c --- /dev/null +++ b/misc/computeutils/readme.md @@ -0,0 +1,3 @@ +# ComputeUtils Module + +Description of the ComputeUtils module diff --git a/misc/computeutils/src/algorithm/volumechannelsplitgl.cpp b/misc/computeutils/src/algorithm/volumechannelsplitgl.cpp new file mode 100644 index 00000000..127b952d --- /dev/null +++ b/misc/computeutils/src/algorithm/volumechannelsplitgl.cpp @@ -0,0 +1,115 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2016-2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +namespace inviwo { + +VolumeChannelSplitGL::VolumeChannelSplitGL() + : shader_({{ShaderType::Compute, utilgl::findShaderResource("volumesplit.comp")}}, + Shader::Build::No) {} + +std::vector> VolumeChannelSplitGL::split( + std::shared_ptr volume) { + std::vector> outVolumes; + + const auto oldDataFormat = volume->getDataFormat(); + + const auto numericType = oldDataFormat->getNumericType(); + const auto numberOfComponents = 1; + const auto precision = oldDataFormat->getPrecision(); + + auto dataFormat = DataFormatBase::get(numericType, numberOfComponents, precision); + + const auto dimensions = volume->getDimensions(); + + for (auto i{0}; i < 4; ++i) { + shader_.getShaderObject(ShaderType::Compute) + ->removeShaderDefine(StrBuffer("CHANNEL {}", i)); + } + + for (size_t i{0}; i < volume->getDataFormat()->getComponents(); ++i) { + auto outVolume = std::make_shared( + volume->getDimensions(), dataFormat, swizzlemasks::rgba, volume->getInterpolation(), + Wrapping3D{Wrapping::Clamp, Wrapping::Clamp, Wrapping::Clamp}); + + outVolume->setModelMatrix(volume->getModelMatrix()); + outVolume->setWorldMatrix(volume->getWorldMatrix()); + outVolume->copyMetaDataFrom(*volume); + + outVolumes.push_back(outVolume); + + shader_.getShaderObject(ShaderType::Compute) + ->removeShaderDefine(StrBuffer("CHANNEL {}", i - 1)); + shader_.getShaderObject(ShaderType::Compute)->addShaderDefine(StrBuffer("CHANNEL {}", i)); + shader_.build(); + + shader_.activate(); + + TextureUnitContainer cont; + utilgl::bindAndSetUniforms(shader_, cont, *volume, "inputTexture"); + + shader_.setUniform("outputTexture", 0); + + auto outVolumeGL = outVolume->getEditableRepresentation(); + + glActiveTexture(GL_TEXTURE0); + + const auto texture = outVolumeGL->getTexture(); + const auto texHandle = texture->getID(); + glBindImageTexture(0, texHandle, 0, GL_FALSE, 0, GL_WRITE_ONLY, + texture->getInternalFormat()); + + outVolumeGL->getTexture()->bind(); + + glDispatchCompute(static_cast(dimensions.x), static_cast(dimensions.y), + static_cast(dimensions.z)); + + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + shader_.deactivate(); + } + + for (auto outVolume : outVolumes) { + outVolume->dataMap_.dataRange = outVolume->dataMap_.valueRange = + dvec2{volumeReductionGl_.reduce_v(outVolume, ReductionOperator::Min), + volumeReductionGl_.reduce_v(outVolume, ReductionOperator::Max)}; + } + + return outVolumes; +} + +} // namespace inviwo diff --git a/misc/computeutils/src/algorithm/volumeminmaxgl.cpp b/misc/computeutils/src/algorithm/volumeminmaxgl.cpp new file mode 100644 index 00000000..9a7d6eee --- /dev/null +++ b/misc/computeutils/src/algorithm/volumeminmaxgl.cpp @@ -0,0 +1,43 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2016-2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#include + +namespace inviwo { +dvec2 VolumeMinMaxGL::minmax(std::shared_ptr volume, + DisregardingStatus disregardingStatus, const vec2& range) { + const auto min = + volumeReductionGL_.reduce_v(volume, ReductionOperator::Min, disregardingStatus, range); + const auto max = + volumeReductionGL_.reduce_v(volume, ReductionOperator::Max, disregardingStatus, range); + + return dvec2{min, max}; +} + +} // namespace inviwo \ No newline at end of file diff --git a/misc/computeutils/src/algorithm/volumenormalizationgl.cpp b/misc/computeutils/src/algorithm/volumenormalizationgl.cpp new file mode 100644 index 00000000..29e3c0f7 --- /dev/null +++ b/misc/computeutils/src/algorithm/volumenormalizationgl.cpp @@ -0,0 +1,139 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2016-2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include + +namespace inviwo { + +VolumeNormalizationGL::VolumeNormalizationGL() + : shader_({{ShaderType::Compute, utilgl::findShaderResource("volumenormalization.comp")}}, + Shader::Build::No) + , needsCompilation_(false) { + shader_.getShaderObject(ShaderType::Compute)->addShaderDefine("NORMALIZE_CHANNEL_0"); + shader_.build(); +} + +void VolumeNormalizationGL::setNormalizeChannel(const size_t channel, const bool normalize) { + needsCompilation_ = true; + + if (normalize) { + shader_.getShaderObject(ShaderType::Compute) + ->addShaderDefine(StrBuffer{"NORMALIZE_CHANNEL_{}", channel}); + } else { + shader_.getShaderObject(ShaderType::Compute) + ->removeShaderDefine(StrBuffer{"NORMALIZE_CHANNEL_{}", channel}); + } +} + +void VolumeNormalizationGL::setNormalizeChannels(bvec4 normalize) { + setNormalizeChannel(0, normalize[0]); + setNormalizeChannel(1, normalize[1]); + setNormalizeChannel(2, normalize[2]); + setNormalizeChannel(3, normalize[3]); +} + +void VolumeNormalizationGL::reset() { setNormalizeChannels({true, false, false, false}); } + +std::shared_ptr VolumeNormalizationGL::normalize(const Volume& volume) { + std::shared_ptr outVolume; + + // Don't dispatch if we don't have to + if (volume.getDataFormat()->getNumericType() == NumericType::Float) { + outVolume = std::make_shared(volume.getDimensions(), volume.getDataFormat(), + volume.getSwizzleMask(), volume.getInterpolation(), + volume.getWrapping()); + } else { + outVolume = volume.getRepresentation() + ->dispatch, dispatching::filter::Integers>( + [](auto vrprecision) { + using ValueType = util::PrecisionValueType; + + using P = typename util::same_extent::type; + + return std::make_shared( + vrprecision->getDimensions(), DataFormat

::get(), + vrprecision->getSwizzleMask(), vrprecision->getInterpolation(), + vrprecision->getWrapping()); + }); + } + + outVolume->setModelMatrix(volume.getModelMatrix()); + outVolume->setWorldMatrix(volume.getWorldMatrix()); + outVolume->copyMetaDataFrom(volume); + + if (needsCompilation_) { + needsCompilation_ = false; + shader_.build(); + } + + const auto dimensions = volume.getDimensions(); + + shader_.activate(); + + TextureUnitContainer cont; + utilgl::bindAndSetUniforms(shader_, cont, volume, "inputTexture"); + shader_.setUniform("outputTexture", 0); + + auto outVolumeGL = outVolume->getEditableRepresentation(); + outVolumeGL->setWrapping({Wrapping::Clamp, Wrapping::Clamp, Wrapping::Clamp}); + + glActiveTexture(GL_TEXTURE0); + + const auto texture = outVolumeGL->getTexture(); + const auto texHandle = texture->getID(); + glBindImageTexture(0, texHandle, 0, GL_FALSE, 0, GL_WRITE_ONLY, texture->getInternalFormat()); + + outVolumeGL->setSwizzleMask(swizzlemasks::rgba); + + outVolumeGL->getTexture()->bind(); + + /* + * Run normalization. + */ + glDispatchCompute(static_cast(dimensions.x), static_cast(dimensions.y), + static_cast(dimensions.z)); + + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + shader_.deactivate(); + + outVolume->dataMap_.dataRange = outVolume->dataMap_.valueRange = dvec2{0, 1}; + + return outVolume; +} + +} // namespace inviwo diff --git a/misc/computeutils/src/algorithm/volumereductiongl.cpp b/misc/computeutils/src/algorithm/volumereductiongl.cpp new file mode 100644 index 00000000..c4a757e1 --- /dev/null +++ b/misc/computeutils/src/algorithm/volumereductiongl.cpp @@ -0,0 +1,190 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2016-2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include + +namespace inviwo { + +VolumeReductionGL::VolumeReductionGL() + : shader_({{ShaderType::Compute, utilgl::findShaderResource("volumereduction.comp")}}, + Shader::Build::No) + , activeReductionOperator_(ReductionOperator::None) + , activeDisregardingStatus_(DisregardingStatus::Unset) {} + +std::shared_ptr VolumeReductionGL::reduce(std::shared_ptr volume, + const ReductionOperator op, + DisregardingStatus disregardingStatus, + const vec2& range) { + setReductionOperator(op); + setDisregarding(disregardingStatus); + setSamplerType(volume); + + shader_.build(); + + shader_.activate(); + + const auto dimensions = volume->getDimensions(); + + auto input = std::shared_ptr(volume->clone()); + auto dataFormat = volume->getDataFormat(); + std::shared_ptr output; + + auto m = static_cast(dimensions.x) / GLuint{2}; + auto n = static_cast(dimensions.y) / GLuint{2}; + auto o = static_cast(dimensions.z) / GLuint{2}; + + while (true) { + /* + * Bind textures and set uniforms. + */ + TextureUnitContainer cont; + utilgl::bindAndSetUniforms(shader_, cont, *input, "inputTexture"); + shader_.setUniform("outputTexture", 0); + + /* + * Set value disregarding parameters + */ + shader_.setUniform("disregardingRange", range); + + /* + * Update output texture. + */ + output = std::make_shared(size3_t{m, n, o}, dataFormat); + auto outputGL = output->getEditableRepresentation(); + outputGL->setWrapping({Wrapping::Clamp, Wrapping::Clamp, Wrapping::Clamp}); + + glActiveTexture(GL_TEXTURE0); + + auto texture = outputGL->getTexture(); + auto texHandle = texture->getID(); + glBindImageTexture(0, texHandle, 0, GL_FALSE, 0, GL_WRITE_ONLY, + texture->getInternalFormat()); + + outputGL->setSwizzleMask(swizzlemasks::rgba); + + outputGL->getTexture()->bind(); + + /* + * Run reduction. + */ + gpuDispatch(m, n, o); + + /* + * When we have reached an output texture size of 1, the reduction is complete. + */ + if (m == 1 && n == 1 && o == 1) break; + + /* + * Swap textures. + */ + std::swap(input, output); + + /* + * Update output texture size + */ + m = std::max(m / 2, GLuint{1}); + n = std::max(n / 2, GLuint{1}); + o = std::max(o / 2, GLuint{1}); + } + + shader_.deactivate(); + + return output; +} + +double VolumeReductionGL::reduce_v(std::shared_ptr volume, const ReductionOperator op, + DisregardingStatus disregardingStatus, const vec2& range) { + auto res = reduce(volume, op, disregardingStatus, range); + + return res->getRepresentation()->getAsDouble(size3_t{0, 0, 0}); +} + +void VolumeReductionGL::setReductionOperator(ReductionOperator op) { + if (op == activeReductionOperator_) return; + + auto computeShader = shader_.getComputeShaderObject(); + computeShader->removeShaderDefine(StrBuffer{"OPERATOR {}", activeReductionOperator_}); + + activeReductionOperator_ = op; + + computeShader->addShaderDefine(StrBuffer{"OPERATOR {}", activeReductionOperator_}); +} + +void VolumeReductionGL::setDisregarding(DisregardingStatus disregardingStatus) { + if (disregardingStatus == activeDisregardingStatus_) return; + + auto computeShader = shader_.getComputeShaderObject(); + computeShader->removeShaderDefine(StrBuffer{"DISREGARD {}", activeDisregardingStatus_}); + + activeDisregardingStatus_ = disregardingStatus; + + computeShader->addShaderDefine(StrBuffer{"DISREGARD {}", activeDisregardingStatus_}); +} + +void VolumeReductionGL::setSamplerType(std::shared_ptr volume) { + auto computeShader = shader_.getComputeShaderObject(); + + /* + * Reset just to be sure. + */ + computeShader->removeShaderDefine("REDUCTION_SAMPLER_TYPE_FLOAT"); + computeShader->removeShaderDefine("REDUCTION_SAMPLER_TYPE_INTEGER"); + computeShader->removeShaderDefine("REDUCTION_SAMPLER_TYPE_UNSIGNED"); + + volume->getRepresentation()->dispatch( + [&computeShader](auto vrprecision) { + using VectorType = util::PrecisionValueType; + using ValueType = util::value_type_t; + + if constexpr (std::is_floating_point_v) { + computeShader->addShaderDefine("REDUCTION_SAMPLER_TYPE_FLOAT"); + } + if constexpr (std::is_integral_v) { + computeShader->addShaderDefine("REDUCTION_SAMPLER_TYPE_SIGNED"); + } + if constexpr (std::is_unsigned_v) { + computeShader->addShaderDefine("REDUCTION_SAMPLER_TYPE_UNSIGNED"); + } + }); +} + +void VolumeReductionGL::gpuDispatch(const GLuint x, const GLuint y, const GLuint z) { + glDispatchCompute(x, y, z); + + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); +} +} // namespace inviwo diff --git a/misc/computeutils/src/algorithm/volumeshrinktonormalrangegl.cpp b/misc/computeutils/src/algorithm/volumeshrinktonormalrangegl.cpp new file mode 100644 index 00000000..b7836200 --- /dev/null +++ b/misc/computeutils/src/algorithm/volumeshrinktonormalrangegl.cpp @@ -0,0 +1,145 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2016-2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include + +namespace inviwo { + +VolumeShrinkToNormalRangeGL::VolumeShrinkToNormalRangeGL() + : shader_({{ShaderType::Compute, utilgl::findShaderResource("volumeshrinktonormalrange.comp")}}, + Shader::Build::No) + , needsCompilation_(false) { + shader_.getShaderObject(ShaderType::Compute)->addShaderDefine("SHRINK_CHANNEL_0"); + shader_.build(); +} + +void VolumeShrinkToNormalRangeGL::setShrinkChannel(const size_t channel, const bool shrink) { + needsCompilation_ = true; + + if (shrink) { + shader_.getShaderObject(ShaderType::Compute) + ->addShaderDefine(StrBuffer{"SHRINK_CHANNEL_{}", channel}); + } else { + shader_.getShaderObject(ShaderType::Compute) + ->removeShaderDefine(StrBuffer{"SHRINK_CHANNEL_{}", channel}); + } +} + +void VolumeShrinkToNormalRangeGL::setShrinkChannels(bvec4 normalize) { + setShrinkChannel(0, normalize[0]); + setShrinkChannel(1, normalize[1]); + setShrinkChannel(2, normalize[2]); + setShrinkChannel(3, normalize[3]); +} + +void VolumeShrinkToNormalRangeGL::reset() { setShrinkChannels({true, false, false, false}); } + +std::shared_ptr VolumeShrinkToNormalRangeGL::shrink(const Volume& volume) { + std::shared_ptr outVolume; + + const auto offset = (volume.dataMap_.dataRange.x < 0.0 && volume.dataMap_.dataRange.y > 0.0) + ? volume.dataMap_.dataRange.x / + (volume.dataMap_.dataRange.y - volume.dataMap_.dataRange.x) + : 0.0; + + // Don't dispatch if we don't have to + if (volume.getDataFormat()->getNumericType() == NumericType::Float) { + outVolume = std::make_shared(volume.getDimensions(), volume.getDataFormat(), + volume.getSwizzleMask(), volume.getInterpolation(), + volume.getWrapping()); + } else { + outVolume = volume.getRepresentation() + ->dispatch, dispatching::filter::Integers>( + [](auto vrprecision) { + using ValueType = util::PrecisionValueType; + + using P = typename util::same_extent::type; + + return std::make_shared( + vrprecision->getDimensions(), DataFormat

::get(), + vrprecision->getSwizzleMask(), vrprecision->getInterpolation(), + vrprecision->getWrapping()); + }); + } + + outVolume->setModelMatrix(volume.getModelMatrix()); + outVolume->setWorldMatrix(volume.getWorldMatrix()); + outVolume->copyMetaDataFrom(volume); + + if (needsCompilation_) { + needsCompilation_ = false; + shader_.build(); + } + + const auto dimensions = volume.getDimensions(); + + shader_.activate(); + + TextureUnitContainer cont; + utilgl::bindAndSetUniforms(shader_, cont, volume, "inputTexture"); + shader_.setUniform("outputTexture", 0); + shader_.setUniform("offset", static_cast(offset)); + + auto outVolumeGL = outVolume->getEditableRepresentation(); + outVolumeGL->setWrapping({Wrapping::Clamp, Wrapping::Clamp, Wrapping::Clamp}); + + glActiveTexture(GL_TEXTURE0); + + const auto texture = outVolumeGL->getTexture(); + const auto texHandle = texture->getID(); + glBindImageTexture(0, texHandle, 0, GL_FALSE, 0, GL_WRITE_ONLY, texture->getInternalFormat()); + + outVolumeGL->setSwizzleMask(swizzlemasks::rgba); + + outVolumeGL->getTexture()->bind(); + + /* + * Run normalization. + */ + glDispatchCompute(static_cast(dimensions.x), static_cast(dimensions.y), + static_cast(dimensions.z)); + + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + shader_.deactivate(); + + outVolume->dataMap_.dataRange = outVolume->dataMap_.valueRange = dvec2{0 + offset, 1 + offset}; + + return outVolume; +} + +} // namespace inviwo diff --git a/misc/computeutils/src/computeutilsmodule.cpp b/misc/computeutils/src/computeutilsmodule.cpp new file mode 100644 index 00000000..35d9640a --- /dev/null +++ b/misc/computeutils/src/computeutilsmodule.cpp @@ -0,0 +1,50 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +namespace inviwo { + +ComputeUtilsModule::ComputeUtilsModule(InviwoApplication* app) : InviwoModule(app, "ComputeUtils") { + ShaderManager::getPtr()->addShaderSearchPath(getPath(ModulePath::GLSL)); + + registerProcessor(); + registerProcessor(); + registerProcessor(); + registerProcessor(); + registerProcessor(); +} + +} // namespace inviwo diff --git a/misc/computeutils/src/processors/volumechannelsplitglprocessor.cpp b/misc/computeutils/src/processors/volumechannelsplitglprocessor.cpp new file mode 100644 index 00000000..0002d5cb --- /dev/null +++ b/misc/computeutils/src/processors/volumechannelsplitglprocessor.cpp @@ -0,0 +1,60 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#include + +namespace inviwo { + +// The Class Identifier has to be globally unique. Use a reverse DNS naming scheme +const ProcessorInfo VolumeChannelSplitGLProcessor::processorInfo_{ + "org.inviwo.VolumeChannelSplitGLProcessor", // Class identifier + "Volume Channel Split Processor", // Display name + "Volume Operation", // Category + CodeState::Experimental, // Code state + Tags::GL, // Tags +}; +const ProcessorInfo VolumeChannelSplitGLProcessor::getProcessorInfo() const { + return processorInfo_; +} + +VolumeChannelSplitGLProcessor::VolumeChannelSplitGLProcessor() + : Processor(), volumeInport_("volumeInport"), volumeOutport_("volumeOutport") { + + addPorts(volumeInport_, volumeOutport_); +} + +void VolumeChannelSplitGLProcessor::process() { + if (!volumeInport_.hasData() || !volumeInport_.getData()) { + return; + } + + volumeOutport_.setData(volumeChannelSplitGl_.split(volumeInport_.getData())); +} + +} // namespace inviwo diff --git a/misc/computeutils/src/processors/volumeminmaxglprocessor.cpp b/misc/computeutils/src/processors/volumeminmaxglprocessor.cpp new file mode 100644 index 00000000..621e84c3 --- /dev/null +++ b/misc/computeutils/src/processors/volumeminmaxglprocessor.cpp @@ -0,0 +1,87 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#include +#include + +namespace inviwo { + +// The Class Identifier has to be globally unique. Use a reverse DNS naming scheme +const ProcessorInfo VolumeMinMaxGLProcessor::processorInfo_{ + "org.inviwo.VolumeMinMaxGLProcessor", // Class identifier + "Volume Min Max", // Display name + "Volume Operation", // Category + CodeState::Experimental, // Code state + "range, minmax", // Tags +}; +const ProcessorInfo VolumeMinMaxGLProcessor::getProcessorInfo() const { return processorInfo_; } + +VolumeMinMaxGLProcessor::VolumeMinMaxGLProcessor() + : Processor() + , volumeInport_("volumeInport") + , volumeOutport_("volumeOutport") + , disregardingStatus_( + "disregardingStatus", "Disregarding status", + {{"off", "Off", DisregardingStatus::Off}, {"on", "On", DisregardingStatus::On}}) + , disregardingRange_("range", "Range") { + + addPorts(volumeInport_, volumeOutport_); + + disregardingRange_.visibilityDependsOn(disregardingStatus_, [](Property& prop) { + const auto status = dynamic_cast&>(prop).get(); + + return status == DisregardingStatus::On; + }); + + addProperties(disregardingStatus_, disregardingRange_); + + volumeInport_.onChange([this]() { + /* + * Here we call the volume reduction without any disregarding so we get the real min and max + * values. + */ + const auto range = vec2{volumeMinMaxGl_.minmax(volumeInport_.getData())}; + + NetworkLock l; + + disregardingRange_.setRangeMin(range.x); + disregardingRange_.setRangeMax(range.y); + disregardingRange_.set(range); + }); +} + +void VolumeMinMaxGLProcessor::process() { + auto outVolume = std::shared_ptr(volumeInport_.getData()->clone()); + outVolume->dataMap_.dataRange = outVolume->dataMap_.valueRange = volumeMinMaxGl_.minmax( + volumeInport_.getData(), disregardingStatus_.get(), disregardingRange_.get()); + + volumeOutport_.setData(outVolume); +} + +} // namespace inviwo diff --git a/misc/computeutils/src/processors/volumenormalizationglprocessor.cpp b/misc/computeutils/src/processors/volumenormalizationglprocessor.cpp new file mode 100644 index 00000000..02e1f361 --- /dev/null +++ b/misc/computeutils/src/processors/volumenormalizationglprocessor.cpp @@ -0,0 +1,118 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ +#include +#include + +namespace inviwo { + +// The Class Identifier has to be globally unique. Use a reverse DNS naming scheme +const ProcessorInfo VolumeNormalizationGLProcessor::processorInfo_{ + "org.inviwo.VolumeNormalizationGLProcessor", // Class identifier + "Volume Normalization", // Display name + "Volume Operation", // Category + CodeState::Stable, // Code state + Tags::GL, // Tags +}; +const ProcessorInfo VolumeNormalizationGLProcessor::getProcessorInfo() const { + return processorInfo_; +} + +VolumeNormalizationGLProcessor::VolumeNormalizationGLProcessor() + : Processor() + , volumeInport_("volumeInport") + , volumeOutport_("volumeOutport") + , channels_("channels", "Channels") + , normalizeChannel0_("normalizeChannel0", "Channel 1", true) + , normalizeChannel1_("normalizeChannel1", "Channel 2", false) + , normalizeChannel2_("normalizeChannel2", "Channel 3", false) + , normalizeChannel3_("normalizeChannel3", "Channel 4", false) + , volumeNormalization_([&]() { this->invalidate(InvalidationLevel::InvalidOutput); }) { + + addPorts(volumeInport_, volumeOutport_); + + normalizeChannel1_.setVisible(false); + normalizeChannel2_.setVisible(false); + normalizeChannel3_.setVisible(false); + + channels_.addProperties(normalizeChannel0_, normalizeChannel1_, normalizeChannel2_, + normalizeChannel3_); + + addProperties(channels_); + + volumeInport_.onChange([this]() { + if (volumeInport_.hasData()) { + auto volume = volumeInport_.getData(); + + const auto channels = static_cast(volume->getDataFormat()->getComponents()); + if (channels == static_cast(channels_.getProperties().size())) return; + + auto properties = channels_.getProperties(); + + dynamic_cast(properties[0])->set(true); + + for (int i = 1; i < 4; i++) { + auto boolProp = dynamic_cast(properties[i]); + boolProp->set(i < channels); + boolProp->setVisible(i < channels); + } + + volumeNormalization_.reset(); + } + }); + + normalizeChannel0_.onChange( + [this]() { volumeNormalization_.setNormalizeChannel(0, normalizeChannel0_.get()); }); + normalizeChannel1_.onChange( + [this]() { volumeNormalization_.setNormalizeChannel(1, normalizeChannel1_.get()); }); + normalizeChannel2_.onChange( + [this]() { volumeNormalization_.setNormalizeChannel(2, normalizeChannel2_.get()); }); + normalizeChannel3_.onChange( + [this]() { volumeNormalization_.setNormalizeChannel(3, normalizeChannel3_.get()); }); +} + +void VolumeNormalizationGLProcessor::process() { + auto inputVolume = volumeInport_.getData(); + auto channelProperties = channels_.getProperties(); + + bool apply = false; + for (size_t i{0}; i < channelProperties.size(); ++i) { + apply = apply || dynamic_cast(channelProperties[i])->get(); + } + if (inputVolume->getDataFormat()->getNumericType() != NumericType::Float) { + LogWarn("Numeric type of input volume is not floating point."); + } + + if (!apply) { + volumeOutport_.setData(inputVolume); + } else { + volumeOutport_.setData(volumeNormalization_.normalize(*inputVolume)); + } +} + +} // namespace inviwo diff --git a/misc/computeutils/src/processors/volumereductionglprocessor.cpp b/misc/computeutils/src/processors/volumereductionglprocessor.cpp new file mode 100644 index 00000000..ba28d3d3 --- /dev/null +++ b/misc/computeutils/src/processors/volumereductionglprocessor.cpp @@ -0,0 +1,64 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#include + +namespace inviwo { + +// The Class Identifier has to be globally unique. Use a reverse DNS naming scheme +const ProcessorInfo VolumeReductionGLProcessor::processorInfo_{ + "org.inviwo.VolumeReductionGLProcessor", // Class identifier + "Volume Reduction", // Display name + "Volume Operation", // Category + CodeState::Experimental, // Code state + Tags::GL, // Tags +}; +const ProcessorInfo VolumeReductionGLProcessor::getProcessorInfo() const { return processorInfo_; } + +VolumeReductionGLProcessor::VolumeReductionGLProcessor() + : Processor() + , volumeInport_("volumeInport") + , volumeOutport_("volumeOutport") + , reductionOperator_("reductionOperator", "Reduction operator", + {{"min", "Min", ReductionOperator::Min}, + {"max", "Max", ReductionOperator::Max}, + {"sum", "Sum", ReductionOperator::Sum}}) { + + addPorts(volumeInport_, volumeOutport_); + + addProperties(reductionOperator_); +} + +void VolumeReductionGLProcessor::process() { + const auto reduced = gpuReduction_.reduce(volumeInport_.getData(), reductionOperator_.get()); + + volumeOutport_.setData(reduced); +} + +} // namespace inviwo diff --git a/misc/computeutils/src/processors/volumeshrinktonormalrangeglprocessor.cpp b/misc/computeutils/src/processors/volumeshrinktonormalrangeglprocessor.cpp new file mode 100644 index 00000000..d36b909e --- /dev/null +++ b/misc/computeutils/src/processors/volumeshrinktonormalrangeglprocessor.cpp @@ -0,0 +1,117 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ +#include +#include + +namespace inviwo { + +// The Class Identifier has to be globally unique. Use a reverse DNS naming scheme +const ProcessorInfo VolumeShrinkToNormalRangeGLProcessor::processorInfo_{ + "org.inviwo.VolumeShrinkToNormalRangeGLProcessor", // Class identifier + "Volume Shrink to Normal Range", // Display name + "Volume Operation", // Category + CodeState::Stable, // Code state + "range, normal, shrink, feature", // Tags +}; +const ProcessorInfo VolumeShrinkToNormalRangeGLProcessor::getProcessorInfo() const { + return processorInfo_; +} + +VolumeShrinkToNormalRangeGLProcessor::VolumeShrinkToNormalRangeGLProcessor() + : Processor() + , volumeInport_("volumeInport") + , volumeOutport_("volumeOutport") + , channels_("channels", "Channels") + , shrinkChannel0_("shrinkChannel0", "Channel 1", true) + , shrinkChannel1_("shrinkChannel1", "Channel 2", false) + , shrinkChannel2_("shrinkChannel2", "Channel 3", false) + , shrinkChannel3_("shrinkChannel3", "Channel 4", false) + , volumeShrinkToNormalRangeGl_([&]() { this->invalidate(InvalidationLevel::InvalidOutput); }) { + + addPorts(volumeInport_, volumeOutport_); + + shrinkChannel1_.setVisible(false); + shrinkChannel2_.setVisible(false); + shrinkChannel3_.setVisible(false); + + channels_.addProperties(shrinkChannel0_, shrinkChannel1_, shrinkChannel2_, shrinkChannel3_); + + addProperties(channels_); + + volumeInport_.onChange([this]() { + if (volumeInport_.hasData()) { + auto volume = volumeInport_.getData(); + + const auto channels = static_cast(volume->getDataFormat()->getComponents()); + if (channels == static_cast(channels_.getProperties().size())) return; + + auto properties = channels_.getProperties(); + + dynamic_cast(properties[0])->set(true); + + for (int i = 1; i < 4; i++) { + auto boolProp = dynamic_cast(properties[i]); + boolProp->set(i < channels); + boolProp->setVisible(i < channels); + } + + volumeShrinkToNormalRangeGl_.reset(); + } + }); + + shrinkChannel0_.onChange( + [this]() { volumeShrinkToNormalRangeGl_.setShrinkChannel(0, shrinkChannel0_.get()); }); + shrinkChannel1_.onChange( + [this]() { volumeShrinkToNormalRangeGl_.setShrinkChannel(1, shrinkChannel1_.get()); }); + shrinkChannel2_.onChange( + [this]() { volumeShrinkToNormalRangeGl_.setShrinkChannel(2, shrinkChannel2_.get()); }); + shrinkChannel3_.onChange( + [this]() { volumeShrinkToNormalRangeGl_.setShrinkChannel(3, shrinkChannel3_.get()); }); +} + +void VolumeShrinkToNormalRangeGLProcessor::process() { + auto inputVolume = volumeInport_.getData(); + auto channelProperties = channels_.getProperties(); + + bool apply = false; + for (size_t i{0}; i < channelProperties.size(); ++i) { + apply = apply || dynamic_cast(channelProperties[i])->get(); + } + if (inputVolume->getDataFormat()->getNumericType() != NumericType::Float) { + LogWarn("Numeric type of input volume is not floating point."); + } + + if (!apply) { + volumeOutport_.setData(inputVolume); + } else { + volumeOutport_.setData(volumeShrinkToNormalRangeGl_.shrink(*inputVolume)); + } +} + +} // namespace inviwo diff --git a/multivis/featurelevelsetsgl/CMakeLists.txt b/multivis/featurelevelsetsgl/CMakeLists.txt new file mode 100644 index 00000000..ddbd5a80 --- /dev/null +++ b/multivis/featurelevelsetsgl/CMakeLists.txt @@ -0,0 +1,40 @@ +ivw_module(FeatureLevelSetsGL) + +set(HEADER_FILES + include/inviwo/featurelevelsetsgl/featurelevelsetsglmodule.h + include/inviwo/featurelevelsetsgl/featurelevelsetsglmoduledefine.h + include/inviwo/featurelevelsetsgl/processors/featurelevelsetprocessorgl.h + include/inviwo/featurelevelsetsgl/properties/traitproperty.h + include/inviwo/featurelevelsetsgl/properties/implicitfunctiontraitproperty.h + include/inviwo/featurelevelsetsgl/properties/pointtraitproperty.h + include/inviwo/featurelevelsetsgl/properties/rangetraitproperty.h + include/inviwo/featurelevelsetsgl/util/util.h +) +ivw_group("Header Files" ${HEADER_FILES}) + +set(SOURCE_FILES + src/featurelevelsetsglmodule.cpp + src/processors/featurelevelsetprocessorgl.cpp + src/properties/traitproperty.cpp + src/properties/implicitfunctiontraitproperty.cpp + src/properties/pointtraitproperty.cpp + src/properties/rangetraitproperty.cpp +) +ivw_group("Source Files" ${SOURCE_FILES}) + +set(SHADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/glsl/featurelevelsets.comp + ${CMAKE_CURRENT_SOURCE_DIR}/glsl/pointtrait.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/glsl/rangetrait.glsl +) +ivw_group("Shader Files" ${SHADER_FILES}) + +set(TEST_FILES + tests/unittests/featurelevelsetsgl-unittest-main.cpp +) +ivw_add_unittest(${TEST_FILES}) + +ivw_create_module(${SOURCE_FILES} ${HEADER_FILES} ${SHADER_FILES}) + +# Add shader directory to install package +#ivw_add_to_module_pack(${CMAKE_CURRENT_SOURCE_DIR}/glsl) diff --git a/multivis/featurelevelsetsgl/depends.cmake b/multivis/featurelevelsetsgl/depends.cmake new file mode 100644 index 00000000..ad407f4f --- /dev/null +++ b/multivis/featurelevelsetsgl/depends.cmake @@ -0,0 +1,5 @@ +set(dependencies + InviwoBaseGLModule + InviwoOpenGLModule # Example dependency + InviwoComputeUtilsModule +) diff --git a/multivis/featurelevelsetsgl/glsl/featurelevelsets.comp b/multivis/featurelevelsetsgl/glsl/featurelevelsets.comp new file mode 100644 index 00000000..b4840a35 --- /dev/null +++ b/multivis/featurelevelsetsgl/glsl/featurelevelsets.comp @@ -0,0 +1,102 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#include "utils/structs.glsl" +#include "pointtrait.glsl" +#include "rangetrait.glsl" + +uniform VolumeParameters volumeParameters[NUM_VOLUMES]; + +uniform sampler3D volume[NUM_VOLUMES]; + +writeonly uniform image3D dest; + +uniform vec4 pointTraits[TRAIT_ALLOCATION]; +uniform mat4 rangeTraits[TRAIT_ALLOCATION]; + +uniform vec2 volumeRanges[NUM_VOLUMES]; + +uniform int numPointTraits; +uniform int numRangeTraits; + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +// #define ENABLE_IMPLICIT_FUNCTION_1 +void implicitFunction1(in float point[NUM_VOLUMES], in vec2[NUM_VOLUMES] ranges, out float result) { + // #IMPLICIT_FUNCTION_1 +} + +// #define ENABLE_IMPLICIT_FUNCTION_2 +void implicitFunction2(in float point[NUM_VOLUMES], in vec2[NUM_VOLUMES] ranges, out float result) { + // #IMPLICIT_FUNCTION_2 +} + +// #define ENABLE_IMPLICIT_FUNCTION_3 +void implicitFunction3(in float point[NUM_VOLUMES], in vec2[NUM_VOLUMES] ranges, out float result) { + // #IMPLICIT_FUNCTION_3 +} + +void main() { + ivec3 storePos = ivec3(gl_GlobalInvocationID); + + ivec3 dims = textureSize(volume[0], 0); + + float point[NUM_VOLUMES]; + + for (int i = 0; i < NUM_VOLUMES; i++) { + vec4 dimensionValue = texelFetch(volume[i], storePos, 0); + + point[i] = dimensionValue.r; + } + + float pointDist = minDistanceToSetOfPointTraits(point, pointTraits, numPointTraits); + float rangeDist = minDistanceToSetOfRangeTraits(point, rangeTraits, numRangeTraits); + + float result = min(pointDist, rangeDist); + +#ifdef ENABLE_IMPLICIT_FUNCTION_1 + float res = 1.0 / 0.0; + implicitFunction1(point, volumeRanges, res); + result = min(result, res); +#endif + +#ifdef ENABLE_IMPLICIT_FUNCTION_2 + float res = 1.0 / 0.0; + implicitFunction2(point, volumeRanges, res); + result = min(result, res); +#endif + +#ifdef ENABLE_IMPLICIT_FUNCTION_3 + float res = 1.0 / 0.0; + implicitFunction3(point, volumeRanges, res); + result = min(result, res); +#endif + + imageStore(dest, storePos, vec4(result)); +} diff --git a/multivis/featurelevelsetsgl/glsl/pointtrait.glsl b/multivis/featurelevelsetsgl/glsl/pointtrait.glsl new file mode 100644 index 00000000..0305be4d --- /dev/null +++ b/multivis/featurelevelsetsgl/glsl/pointtrait.glsl @@ -0,0 +1,34 @@ +/* +Available macros: + NUM_VOLUMES: The number of input volumes, corresponds to the dimensionality of the attribute space + TRAIT_ALLOCATION: Maximum number of traits +*/ + +/* +* Calculates the euclidean distance of an n-dimensional point to an n-dimensional point trait. +*/ +float euclideanDistanceToPointTrait(float point[NUM_VOLUMES], vec4 trait) { + float dist = 0.0f; + + for(int i = 0; i < NUM_VOLUMES; i++) { + float diff = point[i] - trait[i]; + dist += diff * diff; + } + +#ifdef SQUARED_DISTANCE + return dist; +#else + return sqrt(dist); +#endif +} + +float minDistanceToSetOfPointTraits(float point[NUM_VOLUMES], vec4 traits[TRAIT_ALLOCATION], int numPointTraits) { + float minDistance = 1.0f / 0.0f; + + for (int i = 0; i < numPointTraits; i++) { + vec4 trait = traits[i]; + minDistance = min(euclideanDistanceToPointTrait(point, trait), minDistance); + } + + return minDistance; +} diff --git a/multivis/featurelevelsetsgl/glsl/rangetrait.glsl b/multivis/featurelevelsetsgl/glsl/rangetrait.glsl new file mode 100644 index 00000000..92944d5f --- /dev/null +++ b/multivis/featurelevelsetsgl/glsl/rangetrait.glsl @@ -0,0 +1,37 @@ +/* +Available macros: + NUM_VOLUMES: The number of input volumes, corresponds to the dimensionality of the attribute space + TRAIT_ALLOCATION: Maximum number of traits +*/ + +/* +* Calculates the euclidean distance of an n-dimensional point to an n-dimensional range trait. +*/ +float euclideanDistanceToRangeTrait(float point[NUM_VOLUMES], mat4 trait) { + /* + float dist = 0.0f; + + for(int i = 0; i < NUM_VOLUMES; i++) { + float diff = point[i] - trait[i]; + dist += diff * diff; + } + +#ifdef SQUARED_DISTANCE + return dist; +#else + return sqrt(dist); +#endif +*/ + return 0; +} + +float minDistanceToSetOfRangeTraits(float point[NUM_VOLUMES], mat4 traits[TRAIT_ALLOCATION], int numPointTraits) { + float minDistance = 1.0f / 0.0f; + + for (int i = 0; i < numPointTraits; i++) { + mat4 trait = traits[i]; + minDistance = min(euclideanDistanceToRangeTrait(point, trait), minDistance); + } + + return minDistance; +} diff --git a/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/featurelevelsetsglmodule.h b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/featurelevelsetsglmodule.h new file mode 100644 index 00000000..bfb601b9 --- /dev/null +++ b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/featurelevelsetsglmodule.h @@ -0,0 +1,42 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ +#pragma once + +#include +#include + +namespace inviwo { + +class IVW_MODULE_FEATURELEVELSETSGL_API FeatureLevelSetsGLModule : public InviwoModule { +public: + FeatureLevelSetsGLModule(InviwoApplication* app); + virtual ~FeatureLevelSetsGLModule() = default; +}; + +} // namespace inviwo diff --git a/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/featurelevelsetsglmoduledefine.h b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/featurelevelsetsglmoduledefine.h new file mode 100644 index 00000000..aff1e6f6 --- /dev/null +++ b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/featurelevelsetsglmoduledefine.h @@ -0,0 +1,22 @@ +#pragma once + +// clang-format off +#ifdef INVIWO_ALL_DYN_LINK //DYNAMIC + // If we are building DLL files we must declare dllexport/dllimport + #ifdef IVW_MODULE_FEATURELEVELSETSGL_EXPORTS + #ifdef _WIN32 + #define IVW_MODULE_FEATURELEVELSETSGL_API __declspec(dllexport) + #else //UNIX (GCC) + #define IVW_MODULE_FEATURELEVELSETSGL_API __attribute__ ((visibility ("default"))) + #endif + #else + #ifdef _WIN32 + #define IVW_MODULE_FEATURELEVELSETSGL_API __declspec(dllimport) + #else + #define IVW_MODULE_FEATURELEVELSETSGL_API + #endif + #endif +#else //STATIC + #define IVW_MODULE_FEATURELEVELSETSGL_API +#endif +// clang-format on diff --git a/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/processors/featurelevelsetprocessorgl.h b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/processors/featurelevelsetprocessorgl.h new file mode 100644 index 00000000..79c13d23 --- /dev/null +++ b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/processors/featurelevelsetprocessorgl.h @@ -0,0 +1,132 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace inviwo { + +/** \docpage{org.inviwo.FeatureLevelSetProcessorGL, Feature Level Set Processor} + * ![](org.inviwo.FeatureLevelSetProcessorGL.png?classIdentifier=org.inviwo.FeatureLevelSetProcessorGL) + */ +class IVW_MODULE_FEATURELEVELSETSGL_API FeatureLevelSetProcessorGL : public Processor, + public PropertyOwnerObserver + +{ +public: + FeatureLevelSetProcessorGL(); + virtual ~FeatureLevelSetProcessorGL() = default; + + virtual void process() override; + + virtual const ProcessorInfo getProcessorInfo() const override; + static const ProcessorInfo processorInfo_; + + virtual void serialize(Serializer& s) const final; + virtual void deserialize(Deserializer& s) final; + +private: + // Ports + DataInport volumes_; + VolumeOutport distanceVolumeOutport_; + + // Properties + BoolProperty squaredDistance_; + BoolProperty useVolumesDataMap_; + BoolProperty useNormalizedValues_; + BoolProperty capMaxDistance_; + ListProperty traitPropertiesContainer_; + ButtonProperty injectButton_; + + // Caches + size_t prevNumberOfVolumes_; + std::array dataRangesCache_; + size_t traitAllocation_{8}; + std::vector volumeNameCache_{}; + double maxDist_; + std::vector>> normalizedVolumesCache_; + + // Members + Shader shader_; + ShaderSegment implicitFunctionSegment_; + const size_t maxVolumes_{4}; + VolumeNormalizationGL normalization_; + VolumeReductionGL reduction_; + + // Helpers + std::vector gatherPointTraits() const; + void normalizePointTraits(std::vector& pointTraits) const; + + /* + * Gathers the point traits to upload to the GPU. + * The layout is as follows: + * Column defines the dimension in the attribute space + * 1st row is the min value + * 2nd row is the max value + */ + std::vector gatherRangeTraits() const; + void normalizeRangeTraits(std::vector& rangeTraits) const; + + std::vector gatherVolumeRanges() const; + void normalizeVolumeRanges(std::vector& volumeRanges) const; + + // Code fragments + void checkInput() const; + void setUniforms(); + std::shared_ptr bindOutputTexture(); + void gpuDispatch(); + + // Updates + void updateDataRangesCache(); + void updateMaximumDistance(); + void updateNormalizedVolumesCache(); + void updateVolumeNamesCache(); + + /* + * Returns true if input volumes have same identifiers as name cache + */ + bool compareInputsToCache() const; + + void resizeTraitAllocation(); + void initializeAllProperties(); + + virtual void onWillAddProperty(Property* property, size_t index) final; +}; + +} // namespace inviwo diff --git a/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/properties/implicitfunctiontraitproperty.h b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/properties/implicitfunctiontraitproperty.h new file mode 100644 index 00000000..792fbde7 --- /dev/null +++ b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/properties/implicitfunctiontraitproperty.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace inviwo { +class IVW_MODULE_FEATURELEVELSETSGL_API ImplicitFunctionTraitProperty : public TraitProperty { +public: + static constexpr std::string_view stub{ + "// in: point[NUM_VOLUMES]\n//in: ranges[NUM_VOLUMES]\n// out: " + "result\nresult = point[0];"}; + + ImplicitFunctionTraitProperty() = delete; + ImplicitFunctionTraitProperty(const ImplicitFunctionTraitProperty& p) + : ImplicitFunctionTraitProperty(p.getIdentifier(), p.getDisplayName()) {} + + ImplicitFunctionTraitProperty(const std::string& identifier, const std::string& displayName) + : TraitProperty(identifier, displayName) + , shaderInjection_("shaderInjection", "Implicit function", std::string(stub), + InvalidationLevel::InvalidOutput, PropertySemantics::ShaderEditor) { + addProperties(shaderInjection_); + } + + virtual ImplicitFunctionTraitProperty* clone() const final { + return new ImplicitFunctionTraitProperty(*this); + } + + virtual std::string getClassIdentifier() const override; + static const std::string classIdentifier; + + virtual ~ImplicitFunctionTraitProperty() = default; + + void addAttribute(const std::string& name, std::shared_ptr volume, + bool useVolumeDataMap) final; + + void inject(Shader& shader); + +private: + StringProperty shaderInjection_; + std::string originalShader_; +}; + +} // namespace inviwo diff --git a/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/properties/pointtraitproperty.h b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/properties/pointtraitproperty.h new file mode 100644 index 00000000..16c109a6 --- /dev/null +++ b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/properties/pointtraitproperty.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include + +namespace inviwo { +class IVW_MODULE_FEATURELEVELSETSGL_API PointTraitProperty : public TraitProperty { +public: + PointTraitProperty() = delete; + PointTraitProperty(const PointTraitProperty& p) + : TraitProperty(p) + , attribute1_(p.attribute1_) + , attribute2_(p.attribute2_) + , attribute3_(p.attribute3_) + , attribute4_(p.attribute4_) { + + attributes_ = + std::array{&attribute1_, &attribute2_, &attribute3_, &attribute4_}; + + addProperties(attribute1_, attribute2_, attribute3_, attribute4_); + } + + PointTraitProperty(const std::string& identifier, const std::string& displayName) + : TraitProperty(identifier, displayName) + , attribute1_("attribute1", "") + , attribute2_("attribute2", "") + , attribute3_("attribute3", "") + , attribute4_("attribute4", "") { + + attributes_ = + std::array{&attribute1_, &attribute2_, &attribute3_, &attribute4_}; + + for (auto attribute : attributes_) { + attribute->setVisible(false); + attribute->setSerializationMode(PropertySerializationMode::All); + } + + addProperties(attribute1_, attribute2_, attribute3_, attribute4_); + } + + virtual PointTraitProperty* clone() const final { return new PointTraitProperty(*this); } + + virtual std::string getClassIdentifier() const override; + static const std::string classIdentifier; + + virtual ~PointTraitProperty() final = default; + + vec4 getAsVec4() const; + + void addAttribute(const std::string& name, std::shared_ptr volume, + bool useVolumeDataMap) final; + +private: + FloatProperty attribute1_; + FloatProperty attribute2_; + FloatProperty attribute3_; + FloatProperty attribute4_; + + std::array attributes_; +}; + +} // namespace inviwo diff --git a/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/properties/rangetraitproperty.h b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/properties/rangetraitproperty.h new file mode 100644 index 00000000..d426f38d --- /dev/null +++ b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/properties/rangetraitproperty.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +namespace inviwo { +class IVW_MODULE_FEATURELEVELSETSGL_API RangeTraitProperty : public TraitProperty { +public: + RangeTraitProperty() = delete; + RangeTraitProperty(const RangeTraitProperty& p) + : TraitProperty(p) + , attribute1_(p.attribute1_) + , attribute2_(p.attribute2_) + , attribute3_(p.attribute3_) + , attribute4_(p.attribute4_) { + attributes_ = std::array{&attribute1_, &attribute2_, &attribute3_, + &attribute4_}; + + addProperties(attribute1_, attribute2_, attribute3_, attribute4_); + } + + RangeTraitProperty(const std::string& identifier, const std::string& displayName) + : TraitProperty(identifier, displayName) + , attribute1_("attribute1", "") + , attribute2_("attribute2", "") + , attribute3_("attribute3", "") + , attribute4_("attribute4", "") { + attributes_ = std::array{&attribute1_, &attribute2_, &attribute3_, + &attribute4_}; + + for (auto attribute : attributes_) { + attribute->setVisible(false); + attribute->setSerializationMode(PropertySerializationMode::All); + } + + addProperties(attribute1_, attribute2_, attribute3_, attribute4_); + } + + virtual RangeTraitProperty* clone() const final { return new RangeTraitProperty(*this); } + + virtual std::string getClassIdentifier() const override; + static const std::string classIdentifier; + + virtual ~RangeTraitProperty() = default; + + mat4 getAsMat4() const; + + void addAttribute(const std::string& name, std::shared_ptr volume, + bool useVolumeDataMap) final; + +private: + FloatMinMaxProperty attribute1_; + FloatMinMaxProperty attribute2_; + FloatMinMaxProperty attribute3_; + FloatMinMaxProperty attribute4_; + + std::array attributes_; +}; + +} // namespace inviwo diff --git a/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/properties/traitproperty.h b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/properties/traitproperty.h new file mode 100644 index 00000000..fcb7dd82 --- /dev/null +++ b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/properties/traitproperty.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +namespace inviwo { +class IVW_MODULE_FEATURELEVELSETSGL_API TraitProperty : public CompositeProperty { +public: + TraitProperty() = delete; + TraitProperty(const std::string& identifier, const std::string& displayName) + : CompositeProperty(identifier, displayName), numInitializedAttributes_(0) {} + TraitProperty(const TraitProperty& p) : CompositeProperty(p), numInitializedAttributes_(0) {} + + virtual ~TraitProperty() override = default; + + /* + * Enforce clone method to enable usage as prefab in list property + */ + TraitProperty* clone() const override = 0; + + std::string getClassIdentifier() const override; + static const std::string classIdentifier; + + virtual void addAttribute(const std::string& name, std::shared_ptr volume, + bool useVolumeDataMap) = 0; + + size_t numInitializedAttributes() const { return numInitializedAttributes_; } + void inc() { numInitializedAttributes_++; } + void reset() { numInitializedAttributes_ = 0; } + +private: + size_t numInitializedAttributes_; +}; + +} // namespace inviwo diff --git a/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/util/util.h b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/util/util.h new file mode 100644 index 00000000..35c716e2 --- /dev/null +++ b/multivis/featurelevelsetsgl/include/inviwo/featurelevelsetsgl/util/util.h @@ -0,0 +1,82 @@ +namespace inviwo { +namespace util { +enum class DistanceMetric { Euclidean, Manhattan, Minkowski, SquaredSum }; + +/** + * \brief Calculates Minkowski distance of order n + * Input: Two vectors, each of which represent one point in an + * n-dimensional space Output: Minkowski distance of order n between the two + * points + * + * \param a Point a + * \param b Point b + * \param order Order of the Minkowski distance + * \return Minkowski distance of order n between points a and b + */ +template +T minkowskiDistance(const std::vector& a, const std::vector& b, const T order) { + IVW_ASSERT(a.size() == b.size(), "Minkowski distance requires equal length vectors"); + return std::pow(std::inner_product(a.begin(), a.end(), b.begin(), T(0), std::plus<>(), + [order](T x, T y) { return std::pow(y - x, order); }), + T(1) / order); +} + +/** + * \brief Calculates Manhattan distance + * Input: Two vectors, each of which represent one point in an + * n-dimensional space Output: Manhattan distance between the two points + * + * \param a Point a + * \param b Point b + * \return Manhattan distance between points a and b + */ +template +T manhattanDistance(const std::vector& a, const std::vector& b) { + return minkowskiDistance(a, b, T(1)); +} + +/** + * \brief Calculates Euclidean distance + * Input: Two vectors, each of which represent one point in an + * n-dimensional space Output: Euclidean distance between the two points + * + * \param a Point a + * \param b Point b + * \return Euclidean distance between points a and b + */ +template +T euclideanDistance(const std::vector& a, const std::vector& b) { + return minkowskiDistance(a, b, T(2)); +} + +/** + * \brief Calculates squared sum distance + * Input: Two vectors, each of which represent one point in an + * n-dimensional space Output: Squared sum distance between the two points + * + * \param a Point a + * \param b Point b + * \return Squared sum distance between points a and b + */ +template +T squaredSumDistance(const std::vector& a, const std::vector& b) { + IVW_ASSERT(a.size() == b.size(), "Squared sum distance requires equal length vectors"); + return std::inner_product(a.begin(), a.end(), b.begin(), T(0), std::plus<>(), + [](T x, T y) { return std::pow(y - x, T(2)); }); +} + +template +T normalizeValue(const T& val, const T& minVal, const T& maxVal) { + if (val >= maxVal) return T(1); + if (val <= minVal) return T(0); + return (val - minVal) / (maxVal - minVal); +} + +template +T denormalizeValue(const T& val, const T& minVal, const T& maxVal) { + if (val <= T(0)) return minVal; + if (val >= T(1)) return maxVal; + return minVal + val * (maxVal - minVal); +} +} // namespace util +} // namespace inviwo \ No newline at end of file diff --git a/multivis/featurelevelsetsgl/readme.md b/multivis/featurelevelsetsgl/readme.md new file mode 100644 index 00000000..68894388 --- /dev/null +++ b/multivis/featurelevelsetsgl/readme.md @@ -0,0 +1,3 @@ +# FeatureLevelSetsGL Module + +Description of the FeatureLevelSetsGL module diff --git a/multivis/featurelevelsetsgl/src/featurelevelsetsglmodule.cpp b/multivis/featurelevelsetsgl/src/featurelevelsetsglmodule.cpp new file mode 100644 index 00000000..1c284a19 --- /dev/null +++ b/multivis/featurelevelsetsgl/src/featurelevelsetsglmodule.cpp @@ -0,0 +1,49 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#include +#include +#include +#include +#include +#include + +namespace inviwo { + +FeatureLevelSetsGLModule::FeatureLevelSetsGLModule(InviwoApplication* app) + : InviwoModule(app, "FeatureLevelSetsGL") { + ShaderManager::getPtr()->addShaderSearchPath(getPath(ModulePath::GLSL)); + + registerProcessor(); + registerProperty(); + registerProperty(); + registerProperty(); +} + +} // namespace inviwo diff --git a/multivis/featurelevelsetsgl/src/processors/featurelevelsetprocessorgl.cpp b/multivis/featurelevelsetsgl/src/processors/featurelevelsetprocessorgl.cpp new file mode 100644 index 00000000..92add717 --- /dev/null +++ b/multivis/featurelevelsetsgl/src/processors/featurelevelsetprocessorgl.cpp @@ -0,0 +1,626 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace inviwo { + +// The Class Identifier has to be globally unique. Use a reverse DNS naming scheme +const ProcessorInfo FeatureLevelSetProcessorGL::processorInfo_{ + "org.inviwo.FeatureLevelSetProcessorGL", // Class identifier + "Feature Level Sets", // Display name + "Volume Operation", // Category + CodeState::Experimental, // Code state + Tags::GL, // Tags +}; + +const ProcessorInfo FeatureLevelSetProcessorGL::getProcessorInfo() const { return processorInfo_; } + +void FeatureLevelSetProcessorGL::serialize(Serializer& s) const { + Processor::serialize(s); + s.serialize("volumeNameCache", volumeNameCache_); +} + +void FeatureLevelSetProcessorGL::deserialize(Deserializer& s) { + Processor::deserialize(s); + s.deserialize("volumeNameCache", volumeNameCache_); +} + +FeatureLevelSetProcessorGL::FeatureLevelSetProcessorGL() + : Processor() + , PropertyOwnerObserver() + , volumes_("volume") + , distanceVolumeOutport_("distanceVolumeOutport") + , squaredDistance_("squaredDistance", "Use squared distance", false) + , useVolumesDataMap_("useVolumesDataMap", "Use volume's data map", true) + , useNormalizedValues_("useNormalizedValues", "Use normalized values", false) + , capMaxDistance_("capMaxDistance", "Cap max distance", true) + , traitPropertiesContainer_("traitProperties", "Traits") + , injectButton_("injectButton", "Inject") + , prevNumberOfVolumes_(1) + , dataRangesCache_(std::array{vec2(0), vec2(0), vec2(0), vec2(0)}) + , shader_({{ + ShaderType::Compute, + "featurelevelsets.comp", + }}, + Shader::Build::No) { + traitPropertiesContainer_.addPrefab( + std::make_unique("pointTrait", "Point Trait")); + traitPropertiesContainer_.addPrefab( + std::make_unique("rangeTrait", "Range Trait")); + traitPropertiesContainer_.addPrefab( + std::make_unique("implicitFunction", "Implicit function")); + + addPorts(volumes_, distanceVolumeOutport_); + addProperties(squaredDistance_, useVolumesDataMap_, useNormalizedValues_, capMaxDistance_, + traitPropertiesContainer_, injectButton_); + + traitPropertiesContainer_.PropertyOwnerObservable::addObserver(this); + + shader_.onReload([this]() { invalidate(InvalidationLevel::InvalidOutput); }); + { + auto computeShader = shader_.getShaderObject(ShaderType::Compute); + + computeShader->addShaderDefine("NUM_VOLUMES", StrBuffer{"{}", prevNumberOfVolumes_}); + computeShader->addShaderDefine("TRAIT_ALLOCATION", StrBuffer{"{}", traitAllocation_}); + } + shader_.build(); + + squaredDistance_.onChange([this]() { + if (auto computeShader = shader_.getShaderObject(ShaderType::Compute); + squaredDistance_.get()) { + computeShader->addShaderDefine("SQUARED_DISTANCE"); + } else { + computeShader->removeShaderDefine("SQUARED_DISTANCE"); + } + + shader_.build(); + }); + + volumes_.onConnect([this]() { + if (compareInputsToCache()) { + return; + } + + auto sourceVectorData = volumes_.getSourceVectorData(); + + /* + * At this point we know that we are not deserializing and that one volume has been + * connected. If more that 4 volumes are connected now, just ignore the new volume. + * Otherwise, update the attribute properties of all trait properties. + */ + const auto numVolumes = sourceVectorData.size(); + if (numVolumes > maxVolumes_) { + LogWarn(fmt::format("More than {} volumes connected ({}). Ignoring remaining ones.", + maxVolumes_, numVolumes)); + return; + } + + /* + * There is a possibility that there are more than 1 newly connected volumes. Therefore, + * check if there is only one new volume and then update properties. + */ + + /* + * Return difference sets. + */ + auto [namesToAdd, namesToRemove] = [&inputVolumes = sourceVectorData, + &volumeNameCache = volumeNameCache_]() { + auto sortedCache = std::vector(volumeNameCache); + auto inputNames = std::vector{}; + auto namesToAdd = std::vector{}; + auto namesToRemove = std::vector{}; + + // auto inputVolumes = volumes_.getSourceVectorData(); + for (const auto& [outport, volume] : inputVolumes) { + inputNames.push_back(outport->getProcessor()->getDisplayName()); + } + + std::sort(std::begin(sortedCache), std::end(sortedCache)); + std::sort(std::begin(inputNames), std::end(inputNames)); + + std::set_difference(std::begin(inputNames), std::end(inputNames), + std::begin(sortedCache), std::end(sortedCache), + std::back_inserter(namesToAdd)); + + std::set_difference(std::begin(sortedCache), std::end(sortedCache), + std::begin(inputNames), std::end(inputNames), + std::back_inserter(namesToRemove)); + + return std::pair(namesToAdd, namesToRemove); + }(); + + /* + * This is not production code, for now just handle case of +1, otherwise reset everything. + */ + if (!namesToRemove.empty() || namesToAdd.size() != 1) { + initializeAllProperties(); + return; + } + + /* + * Find volume with corresponding name + */ + const auto pair = + std::find_if(std::begin(sourceVectorData), std::end(sourceVectorData), + [&name = namesToAdd.front()](const auto& pair) { + return pair.first->getProcessor()->getDisplayName() == name; + }); + + const auto& [outport, volume] = *pair; + + for (const auto property : traitPropertiesContainer_.getProperties()) { + if (auto traitProperty = dynamic_cast(property)) { + + traitProperty->addAttribute(outport->getProcessor()->getDisplayName(), volume, + useVolumesDataMap_.get()); + + traitProperty->setSerializationMode(PropertySerializationMode::All); + } + } + + updateNormalizedVolumesCache(); + }); + + volumes_.onChange([this]() { + updateVolumeNamesCache(); + + auto computeShader = shader_.getShaderObject(ShaderType::Compute); + + computeShader->removeShaderDefine(StrBuffer{"NUM_VOLUMES {}", prevNumberOfVolumes_}); + + prevNumberOfVolumes_ = volumes_.getVectorData().size(); + + computeShader->addShaderDefine("NUM_VOLUMES", StrBuffer{"{}", prevNumberOfVolumes_}); + + checkInput(); + + shader_.build(); + + updateDataRangesCache(); + + updateNormalizedVolumesCache(); + + updateMaximumDistance(); + }); + + volumes_.onDisconnect([this]() { + const auto sourceVectorData = volumes_.getSourceVectorData(); + + if (const auto numVolumes = sourceVectorData.size(); numVolumes > maxVolumes_) { + LogWarn(fmt::format("More than {} volumes connected ({}). Ignoring remaining ones.", + maxVolumes_, numVolumes)); + return; + } + /* + * Return difference sets. + */ + auto [namesToAdd, namesToRemove] = [inputVolumes = volumes_.getSourceVectorData(), + &volumeNameCache = volumeNameCache_]() { + auto sortedCache = std::vector(volumeNameCache); + auto inputNames = std::vector{}; + auto namesToAdd = std::vector{}; + auto namesToRemove = std::vector{}; + + // auto inputVolumes = volumes_.getSourceVectorData(); + for (const auto& [outport, volume] : inputVolumes) { + inputNames.push_back(outport->getProcessor()->getDisplayName()); + } + + std::sort(std::begin(sortedCache), std::end(sortedCache)); + std::sort(std::begin(inputNames), std::end(inputNames)); + + std::set_difference(std::begin(inputNames), std::end(inputNames), + std::begin(sortedCache), std::end(sortedCache), + std::back_inserter(namesToAdd)); + + std::set_difference(std::begin(sortedCache), std::end(sortedCache), + std::begin(inputNames), std::end(inputNames), + std::back_inserter(namesToRemove)); + + return std::pair(namesToAdd, namesToRemove); + }(); + + /* + * For now, reset everything, research must go on. + */ + initializeAllProperties(); + + updateNormalizedVolumesCache(); + }); + + injectButton_.onChange([this]() { + for (auto property : traitPropertiesContainer_.getProperties()) { + if (auto implicitFunctionTraitProperty = + dynamic_cast(property)) { + implicitFunctionTraitProperty->inject(shader_); + } + } + }); + + capMaxDistance_.onChange([this]() { + if (capMaxDistance_.get()) { + LogInfoCustom("Feature Level Sets", + fmt::format("Capping max distance at {}.", maxDist_)); + } else { + LogInfoCustom("Feature Level Sets", "Capping disabled."); + } + }); +} + +void FeatureLevelSetProcessorGL::process() { + if (!volumes_.hasData() || volumes_.getVectorData().empty()) return; + if (traitPropertiesContainer_.getProperties().empty()) return; + + checkInput(); + + shader_.activate(); + + setUniforms(); + + const auto outputTexture = bindOutputTexture(); + + gpuDispatch(); + + shader_.deactivate(); + + auto min = reduction_.reduce_v(outputTexture, ReductionOperator::Min); + const auto max = reduction_.reduce_v(outputTexture, ReductionOperator::Max); + + /*if (std::abs(max - min) < std::numeric_limits::epsilon()) { + min = min - max; + }*/ + + outputTexture->dataMap_.valueRange = outputTexture->dataMap_.dataRange = + dvec2(min, capMaxDistance_.get() ? std::min(max, maxDist_) : max); + + distanceVolumeOutport_.setData(outputTexture); +} + +void FeatureLevelSetProcessorGL::checkInput() const { + const size3_t dims = volumes_.getData()->getDimensions(); + for (const auto vol : volumes_) { + if (dims != vol->getDimensions()) { + throw Exception(fmt::format("Different volume dimensions: {}, expected {}", + glm::to_string(vol->getDimensions()), glm::to_string(dims)), + IVW_CONTEXT); + } + + if (vol->getDataFormat()->getNumericType() != NumericType::Float) { + LogWarn("Sampling non-float volume, shader will normalize values."); + } + } +} + +std::vector FeatureLevelSetProcessorGL::gatherPointTraits() const { + auto properties = traitPropertiesContainer_.getProperties(); + std::vector pointTraits; + + for (auto property : properties) { + if (const auto pointTraitProperty = dynamic_cast(property)) { + pointTraits.push_back(pointTraitProperty->getAsVec4()); + } + } + + return pointTraits; +} + +void FeatureLevelSetProcessorGL::normalizePointTraits(std::vector& pointTraits) const { + for (auto& pointTrait : pointTraits) { + for (size_t i{0}; i < 4; i++) { + pointTrait[i] = + util::normalizeValue(pointTrait[i], dataRangesCache_[i].x, dataRangesCache_[i].y); + } + } +} + +std::vector FeatureLevelSetProcessorGL::gatherRangeTraits() const { + auto properties = traitPropertiesContainer_.getProperties(); + std::vector rangeTraits{}; + + for (auto property : properties) { + if (const auto rangeTraitProperty = dynamic_cast(property)) { + rangeTraits.push_back(rangeTraitProperty->getAsMat4()); + } + } + + return rangeTraits; +} + +void FeatureLevelSetProcessorGL::normalizeRangeTraits(std::vector& rangeTraits) const { + for (auto& rangeTrait : rangeTraits) { + for (size_t i{0}; i < 4; i++) { + auto minVal = rangeTrait[i][0]; + auto maxVal = rangeTrait[i][1]; + + rangeTrait[i][0] = + util::normalizeValue(minVal, dataRangesCache_[i].x, dataRangesCache_[i].y); + rangeTrait[i][1] = + util::normalizeValue(maxVal, dataRangesCache_[i].x, dataRangesCache_[i].y); + } + } +} + +std::vector FeatureLevelSetProcessorGL::gatherVolumeRanges() const { + std::vector ranges{}; + + for (const auto volume : volumes_.getVectorData()) { + ranges.emplace_back(volume->dataMap_.dataRange); + } + + return ranges; +} + +void FeatureLevelSetProcessorGL::normalizeVolumeRanges(std::vector& volumeRanges) const { + std::transform(std::begin(volumeRanges), std::end(volumeRanges), std::begin(volumeRanges), + [](const auto&) { + return vec2{0.f, 1.f}; + }); +} + +void FeatureLevelSetProcessorGL::setUniforms() { + const size3_t dims = volumes_.getData()->getDimensions(); + + TextureUnitContainer cont; + + if (!useNormalizedValues_.get()) { + size_t idx{0}; + + for (auto& [outport, volume] : volumes_.getSourceVectorData()) { + if (idx >= maxVolumes_) break; + LogInfo("Binding " + outport->getProcessor()->getDisplayName() + " at index " + + std::to_string(idx)); + TextureUnit unit; + utilgl::bindTexture(*volume, unit); + shader_.setUniform(StrBuffer{"volume[{}]", idx}, unit.getUnitNumber()); + utilgl::setShaderUniforms(shader_, *volume, StrBuffer{"volumeParameters[{}]", idx++}); + cont.push_back(std::move(unit)); + } + } else { + for (auto&& [index, volume] : util::enumerate(normalizedVolumesCache_)) { + if (index >= maxVolumes_) break; + LogInfo("Binding " + volume.first + " at index " + std::to_string(index)); + auto vol = volume.second; + TextureUnit unit; + utilgl::bindTexture(*vol, unit); + shader_.setUniform(StrBuffer{"volume[{}]", index}, unit.getUnitNumber()); + utilgl::setShaderUniforms(shader_, *vol, StrBuffer{"volumeParameters[{}]", index}); + cont.push_back(std::move(unit)); + } + } + + LogInfo("--------------------------------------"); + + shader_.setUniform("dest", 0); + + auto pointTraits = gatherPointTraits(); + auto rangeTraits = gatherRangeTraits(); + auto volumeRanges = gatherVolumeRanges(); + + if (useNormalizedValues_.get()) { + normalizePointTraits(pointTraits); + normalizeRangeTraits(rangeTraits); + normalizeVolumeRanges(volumeRanges); + } + + shader_.setUniform("pointTraits", pointTraits); + shader_.setUniform("rangeTraits", rangeTraits); + shader_.setUniform("volumeRanges", volumeRanges); + + shader_.setUniform("numPointTraits", static_cast(pointTraits.size())); + shader_.setUniform("numRangeTraits", static_cast(rangeTraits.size())); +} + +std::shared_ptr FeatureLevelSetProcessorGL::bindOutputTexture() { + auto inputVolumes = volumes_.getVectorData(); + const size3_t dims = inputVolumes.front()->getDimensions(); + auto referenceBasis = inputVolumes.front()->getBasis(); + auto referenceOffset = inputVolumes.front()->getOffset(); + + for (auto volume : inputVolumes) { + if (glm::any(glm::notEqual(referenceBasis, volume->getBasis()))) { + LogWarn( + "Input volumes do not have the same bases. Setting basis of input volume 1 for the " + "output volume."); + } + if (glm::any(glm::notEqual(referenceOffset, volume->getOffset()))) { + LogWarn( + "Input volumes do not have the same offsets. Setting offset of input volume 1 for " + "the output volume."); + } + } + + auto distanceVolume = std::make_shared(dims, DataFloat32::get()); + distanceVolume->setBasis(inputVolumes.front()->getBasis()); + distanceVolume->setOffset(inputVolumes.front()->getOffset()); + auto distanceVolumeGL = distanceVolume->getEditableRepresentation(); + + glActiveTexture(GL_TEXTURE0); + + auto texHandle = distanceVolumeGL->getTexture()->getID(); + glBindImageTexture(0, texHandle, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); + + distanceVolumeGL->setSwizzleMask(swizzlemasks::rgba); + + distanceVolumeGL->getTexture()->bind(); + + return distanceVolume; +} + +void FeatureLevelSetProcessorGL::gpuDispatch() { + const size3_t dims = volumes_.getData()->getDimensions(); + + const auto x = static_cast(dims.x); + const auto y = static_cast(dims.y); + const auto z = static_cast(dims.z); + + glDispatchCompute(x, y, z); + + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); +} + +void FeatureLevelSetProcessorGL::updateDataRangesCache() { + auto volumes = volumes_.getVectorData(); + + for (auto&& [index, volume] : util::enumerate(volumes)) { + if (auto& range = dataRangesCache_[index]; useVolumesDataMap_.get()) { + range = volume->dataMap_.valueRange; + } else { + auto [mins, maxs] = util::volumeMinMax(volume.get()); + range = dvec2(mins.x, maxs.x); + } + } +} + +void FeatureLevelSetProcessorGL::updateMaximumDistance() { + auto volumes = volumes_.getVectorData(); + + if (useNormalizedValues_.get()) { + maxDist_ = std::sqrt(volumes.size()); + return; + } + + std::vector minVec{}; + std::vector maxVec{}; + + for (auto range : dataRangesCache_) { + minVec.push_back(range.x); + maxVec.push_back(range.y); + } + + maxDist_ = util::euclideanDistance(minVec, maxVec); +} + +void FeatureLevelSetProcessorGL::updateNormalizedVolumesCache() { + normalizedVolumesCache_.clear(); + for (auto& [outport, volume] : volumes_.getSourceVectorData()) { + normalizedVolumesCache_.emplace_back(outport->getProcessor()->getDisplayName(), + normalization_.normalize(*volume)); + } +} + +bool FeatureLevelSetProcessorGL::compareInputsToCache() const { + std::vector names{}; + std::vector intersection{}; + std::vector nameCache(volumeNameCache_); + + for (auto [outport, volume] : volumes_.getSourceVectorData()) { + names.push_back(outport->getProcessor()->getDisplayName()); + } + + std::sort(std::begin(names), std::end(names)); + std::sort(std::begin(nameCache), std::end(nameCache)); + + std::set_intersection(std::begin(names), std::end(names), std::begin(nameCache), + std::end(nameCache), std::back_inserter(intersection)); + + const auto matchesAll = nameCache.size() == intersection.size(); + const auto sameAmount = names.size() == nameCache.size(); + + return matchesAll && sameAmount; +} + +void FeatureLevelSetProcessorGL::updateVolumeNamesCache() { + auto volumes = volumes_.getSourceVectorData(); + + std::vector names{}; + + names.reserve(volumes.size()); + + for (const auto& [outport, volume] : volumes) { + names.push_back(outport->getProcessor()->getDisplayName()); + } + + volumeNameCache_ = names; +} + +void FeatureLevelSetProcessorGL::resizeTraitAllocation() { + auto computeShader = shader_.getShaderObject(ShaderType::Compute); + + computeShader->removeShaderDefine(StrBuffer{"TRAIT_ALLOCATION {}", traitAllocation_}); + + traitAllocation_ += 8; + + computeShader->addShaderDefine("TRAIT_ALLOCATION", StrBuffer{"{}", traitAllocation_}); + + shader_.build(); +} + +void FeatureLevelSetProcessorGL::initializeAllProperties() { + for (auto property : traitPropertiesContainer_.getProperties()) { + auto traitProperty = dynamic_cast(property); + traitProperty->reset(); + for (const auto [outport, volume] : volumes_.getSourceVectorData()) { + traitProperty->addAttribute(outport->getProcessor()->getDisplayName(), volume, + useVolumesDataMap_.get()); + } + } +} + +void FeatureLevelSetProcessorGL::onWillAddProperty(Property* property, size_t) { + if (!volumes_.isConnected()) { + return; + } + + const auto volumes = volumes_.getSourceVectorData(); + + if (auto traitProperty = dynamic_cast(property)) { + + for (const auto& [outport, volume] : volumes) { + traitProperty->addAttribute(outport->getProcessor()->getDisplayName(), volume, + useVolumesDataMap_.get()); + } + + traitProperty->setSerializationMode(PropertySerializationMode::All); + + if (traitPropertiesContainer_.getProperties().size() > traitAllocation_) { + resizeTraitAllocation(); + } + + // traitProperty->onChange([&, this]() { generateTraitMesh(traitProperty->getIdentifier()); + // }); + } +} + +} // namespace inviwo diff --git a/multivis/featurelevelsetsgl/src/properties/implicitfunctiontraitproperty.cpp b/multivis/featurelevelsetsgl/src/properties/implicitfunctiontraitproperty.cpp new file mode 100644 index 00000000..b66c92c7 --- /dev/null +++ b/multivis/featurelevelsetsgl/src/properties/implicitfunctiontraitproperty.cpp @@ -0,0 +1,49 @@ +#include +#include + +namespace inviwo { +const std::string ImplicitFunctionTraitProperty::classIdentifier = + "org.inviwo.ImplicitFunctionTraitProperty"; +std::string ImplicitFunctionTraitProperty::getClassIdentifier() const { return classIdentifier; } + +void ImplicitFunctionTraitProperty::addAttribute(const std::string&, std::shared_ptr, + bool) {} + +void ImplicitFunctionTraitProperty::inject(Shader& shader) { + const auto identifier = getIdentifier(); + const auto itIdentifier = std::find_if(identifier.rbegin(), identifier.rend(), + [](char c) { return !std::isdigit(c); }); + std::string number(itIdentifier.base(), identifier.end()); + if (number.empty()) number = "1"; + + if (originalShader_.empty()) { + originalShader_ = shader.getComputeShaderObject()->getResource()->source(); + } + + auto content = originalShader_; + + replaceInString(content, "// #define ENABLE_IMPLICIT_FUNCTION_" + number, + "#define ENABLE_IMPLICIT_FUNCTION_" + number); + + auto functionCode = shaderInjection_.get(); + replaceInString(functionCode, "\n", "\n "); + + replaceInString(content, "// #IMPLICIT_FUNCTION_" + number, functionCode); + + auto shaderResource = + std::make_shared("featurelevelsets" + number + ".comp", content); + + Shader newShader{{{ + ShaderType::Compute, + shaderResource, + }}, + Shader::Build::No}; + newShader.getComputeShaderObject()->setShaderDefines( + shader.getComputeShaderObject()->getShaderDefines()); + + shader = newShader; + + shader.build(); +} + +} // namespace inviwo diff --git a/multivis/featurelevelsetsgl/src/properties/pointtraitproperty.cpp b/multivis/featurelevelsetsgl/src/properties/pointtraitproperty.cpp new file mode 100644 index 00000000..0f2486e8 --- /dev/null +++ b/multivis/featurelevelsetsgl/src/properties/pointtraitproperty.cpp @@ -0,0 +1,29 @@ +#include +#include + +namespace inviwo { +const std::string PointTraitProperty::classIdentifier = "org.inviwo.PointTraitProperty"; +std::string PointTraitProperty::getClassIdentifier() const { return classIdentifier; } + +vec4 PointTraitProperty::getAsVec4() const { + return vec4(attribute1_.get(), attribute2_.get(), attribute3_.get(), attribute4_.get()); +} + +void PointTraitProperty::addAttribute(const std::string& name, std::shared_ptr volume, + bool useVolumeDataMap) { + const auto minVal = useVolumeDataMap + ? vec2(volume->dataMap_.valueRange).x + : static_cast(util::volumeMinMax(volume.get()).first.x); + const auto maxVal = useVolumeDataMap + ? vec2(volume->dataMap_.valueRange).y + : static_cast(util::volumeMinMax(volume.get()).second.x); + + attributes_[numInitializedAttributes()]->setVisible(true); + attributes_[numInitializedAttributes()]->set(minVal); + attributes_[numInitializedAttributes()]->setMinValue(minVal); + attributes_[numInitializedAttributes()]->setMaxValue(maxVal); + attributes_[numInitializedAttributes()]->setDisplayName(name); + + inc(); +} +} // namespace inviwo diff --git a/multivis/featurelevelsetsgl/src/properties/rangetraitproperty.cpp b/multivis/featurelevelsetsgl/src/properties/rangetraitproperty.cpp new file mode 100644 index 00000000..6aa6825f --- /dev/null +++ b/multivis/featurelevelsetsgl/src/properties/rangetraitproperty.cpp @@ -0,0 +1,43 @@ +#include +#include + +namespace inviwo { +const std::string RangeTraitProperty::classIdentifier = "org.inviwo.RangeTraitProperty"; +std::string RangeTraitProperty::getClassIdentifier() const { return classIdentifier; } + +mat4 RangeTraitProperty::getAsMat4() const { + mat4 minMaxes; + + minMaxes[0][0] = attributes_[0]->get().r; + minMaxes[0][1] = attributes_[0]->get().g; + + minMaxes[1][0] = attributes_[1]->get().r; + minMaxes[1][1] = attributes_[1]->get().g; + + minMaxes[2][0] = attributes_[2]->get().r; + minMaxes[2][1] = attributes_[2]->get().g; + + minMaxes[3][0] = attributes_[3]->get().r; + minMaxes[3][1] = attributes_[3]->get().g; + + return minMaxes; +} + +void RangeTraitProperty::addAttribute(const std::string& name, std::shared_ptr volume, + bool useVolumeDataMap) { + const auto minVal = useVolumeDataMap + ? vec2(volume->dataMap_.valueRange).x + : static_cast(util::volumeMinMax(volume.get()).first.x); + const auto maxVal = useVolumeDataMap + ? vec2(volume->dataMap_.valueRange).y + : static_cast(util::volumeMinMax(volume.get()).second.x); + + attributes_[numInitializedAttributes()]->setVisible(true); + attributes_[numInitializedAttributes()]->setStart(minVal); + attributes_[numInitializedAttributes()]->setEnd(maxVal); + attributes_[numInitializedAttributes()]->setRangeMin(minVal); + attributes_[numInitializedAttributes()]->setRangeMax(maxVal); + attributes_[numInitializedAttributes()]->setDisplayName(name); +} + +} // namespace inviwo diff --git a/multivis/featurelevelsetsgl/src/properties/traitproperty.cpp b/multivis/featurelevelsetsgl/src/properties/traitproperty.cpp new file mode 100644 index 00000000..c5f6b221 --- /dev/null +++ b/multivis/featurelevelsetsgl/src/properties/traitproperty.cpp @@ -0,0 +1,6 @@ +#include + +namespace inviwo { +const std::string TraitProperty::classIdentifier = "org.inviwo.TraitProperty"; +std::string TraitProperty::getClassIdentifier() const { return classIdentifier; } +} // namespace inviwo \ No newline at end of file diff --git a/multivis/featurelevelsetsgl/tests/unittests/featurelevelsetsgl-unittest-main.cpp b/multivis/featurelevelsetsgl/tests/unittests/featurelevelsetsgl-unittest-main.cpp new file mode 100644 index 00000000..98e8f6e8 --- /dev/null +++ b/multivis/featurelevelsetsgl/tests/unittests/featurelevelsetsgl-unittest-main.cpp @@ -0,0 +1,66 @@ +/********************************************************************************* + * + * Inviwo - Interactive Visualization Workshop + * + * Copyright (c) 2021 Inviwo Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *********************************************************************************/ + +#ifdef _MSC_VER +#pragma comment(linker, "/SUBSYSTEM:CONSOLE") +#ifdef IVW_ENABLE_MSVC_MEM_LEAK_TEST +#include +#endif +#endif + +#include +#include +#include + +#include +#include +#include +#include + +int main(int argc, char** argv) { + using namespace inviwo; + LogCentral::init(); + auto logger = std::make_shared(); + LogCentral::getPtr()->setVerbosity(LogVerbosity::Error); + LogCentral::getPtr()->registerLogger(logger); + + int ret = -1; + { +#ifdef IVW_ENABLE_MSVC_MEM_LEAK_TEST + VLDDisable(); + ::testing::InitGoogleTest(&argc, argv); + VLDEnable(); +#else + ::testing::InitGoogleTest(&argc, argv); +#endif + inviwo::ConfigurableGTestEventListener::setup(); + ret = RUN_ALL_TESTS(); + } + return ret; +}