diff --git a/data/tasks/generate_volume.task b/data/tasks/generate_volume.task index c5451b2c1a..1dd93aac66 100644 --- a/data/tasks/generate_volume.task +++ b/data/tasks/generate_volume.task @@ -1,10 +1,18 @@ +local fn = + "return function (x, y, z) " .. + " if math.sqrt(x^2 + y^2 + z^2) < 0.4 then " .. + " return 0.8 " .. + " end " .. + " return 0.0 " .. + "end" + return {{ Type = "GenerateRawVolumeTask", Dimensions = {32, 32, 32}, LowerDomainBound = {-0.5, -0.5, -0.5}, UpperDomainBound = {0.5, 0.5, 0.5}, - ValueFunction = "return function (x, y, z) return math.sqrt((x + 0.5)^2 + (y + 0.5)^2 + (z + 0.5)^2) end", + ValueFunction = fn, Time = "2018-05-04T00:00:00", - RawVolumeOutput = "${DATA}/assets/examples/generatedvolume.raw", - DictionaryOutput = "${DATA}/assets/examples/generatedvolume.dictionary" + RawVolumeOutput = "${DATA}/assets/examples/generatedvolume/generatedvolume.rawvolume", + DictionaryOutput = "${DATA}/assets/examples/generatedvolume/generatedvolume.dictionary" }} \ No newline at end of file diff --git a/modules/toyvolume/rendering/toyvolumeraycaster.cpp b/modules/toyvolume/rendering/toyvolumeraycaster.cpp index 691b295ab4..f3c6182d88 100644 --- a/modules/toyvolume/rendering/toyvolumeraycaster.cpp +++ b/modules/toyvolume/rendering/toyvolumeraycaster.cpp @@ -24,18 +24,21 @@ #include -#include -#include -#include -#include #include #include #include +#include +#include +#include +#include + +#include + namespace { - const char* GlslRaycastPath = "${MODULES}/toyvolume/shaders/raycast.glsl"; - const char* GlslBoundsVsPath = "${MODULES}/toyvolume/shaders/boundsvs.glsl"; - const char* GlslBoundsFsPath = "${MODULES}/toyvolume/shaders/boundsfs.glsl"; + const char* GlslRaycastPath = "${MODULE_TOYVOLUME}/shaders/raycast.glsl"; + const char* GlslBoundsVsPath = "${MODULE_TOYVOLUME}/shaders/boundsvs.glsl"; + const char* GlslBoundsFsPath = "${MODULE_TOYVOLUME}/shaders/boundsfs.glsl"; } // namespace namespace openspace { @@ -125,15 +128,15 @@ bool ToyVolumeRaycaster::cameraIsInside(const RenderData& data, } std::string ToyVolumeRaycaster::getBoundsVsPath() const { - return GlslBoundsVsPath; + return absPath(GlslBoundsVsPath); } std::string ToyVolumeRaycaster::getBoundsFsPath() const { - return GlslBoundsFsPath; + return absPath(GlslBoundsFsPath); } std::string ToyVolumeRaycaster::getRaycastPath() const { - return GlslRaycastPath; + return absPath(GlslRaycastPath); } std::string ToyVolumeRaycaster::getHelperPath() const { diff --git a/modules/volume/CMakeLists.txt b/modules/volume/CMakeLists.txt index 0899b91e15..d036f0af87 100644 --- a/modules/volume/CMakeLists.txt +++ b/modules/volume/CMakeLists.txt @@ -27,11 +27,9 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/envelope.h ${CMAKE_CURRENT_SOURCE_DIR}/rawvolume.h - ${CMAKE_CURRENT_SOURCE_DIR}/rawvolume.inl + ${CMAKE_CURRENT_SOURCE_DIR}/rawvolumemetadata.h ${CMAKE_CURRENT_SOURCE_DIR}/rawvolumereader.h - ${CMAKE_CURRENT_SOURCE_DIR}/rawvolumereader.inl ${CMAKE_CURRENT_SOURCE_DIR}/rawvolumewriter.h - ${CMAKE_CURRENT_SOURCE_DIR}/rawvolumewriter.inl ${CMAKE_CURRENT_SOURCE_DIR}/textureslicevolumereader.h ${CMAKE_CURRENT_SOURCE_DIR}/textureslicevolumereader.inl ${CMAKE_CURRENT_SOURCE_DIR}/transferfunction.h @@ -54,6 +52,7 @@ source_group("Header Files" FILES ${HEADER_FILES}) set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/envelope.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rawvolume.inl + ${CMAKE_CURRENT_SOURCE_DIR}/rawvolumemetadata.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rawvolumereader.inl ${CMAKE_CURRENT_SOURCE_DIR}/rawvolumewriter.inl ${CMAKE_CURRENT_SOURCE_DIR}/textureslicevolumereader.inl diff --git a/modules/volume/rawvolumemetadata.cpp b/modules/volume/rawvolumemetadata.cpp new file mode 100644 index 0000000000..b77e36cbe1 --- /dev/null +++ b/modules/volume/rawvolumemetadata.cpp @@ -0,0 +1,182 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include + +#include + +namespace { + const constexpr char* KeyDimensions = "Dimensions"; + const constexpr char* KeyLowerDomainBound = "LowerDomainBound"; + const constexpr char* KeyUpperDomainBound = "UpperDomainBound"; + + const constexpr char* KeyMinValue = "MinValue"; + const constexpr char* KeyMaxValue = "MaxValue"; + + const constexpr char* KeyTime = "Time"; + const constexpr char* KeyDomainUnit = "DomainUnit"; + const constexpr char* KeyValueUnit = "ValueUnit"; + + const constexpr char* KeyGridType = "GridType"; +} + +namespace openspace::volume { + +RawVolumeMetadata RawVolumeMetadata::CreateFromDictionary(const ghoul::Dictionary& dictionary) { + documentation::testSpecificationAndThrow( + Documentation(), + dictionary, + "RawVolumeMetadata" + ); + + RawVolumeMetadata metadata; + metadata.dimensions = dictionary.value(KeyDimensions); + + metadata.hasDomainBounds = dictionary.hasValue(KeyLowerDomainBound) && + dictionary.hasValue(KeyUpperDomainBound); + + if (metadata.hasDomainBounds) { + metadata.lowerDomainBound = dictionary.value(KeyLowerDomainBound); + metadata.upperDomainBound = dictionary.value(KeyUpperDomainBound); + } + metadata.hasDomainUnit = dictionary.hasValue(KeyDomainUnit); + if (metadata.hasDomainUnit) { + metadata.domainUnit = dictionary.value(KeyDomainUnit); + } + + metadata.hasValueRange = dictionary.hasValue(KeyMinValue) && + dictionary.hasValue(KeyMaxValue); + + if (metadata.hasValueRange) { + metadata.minValue = dictionary.value(KeyMinValue); + metadata.maxValue = dictionary.value(KeyMaxValue); + } + metadata.hasValueUnit = dictionary.hasValue(KeyValueUnit); + if (metadata.hasValueUnit) { + metadata.valueUnit = dictionary.value(KeyValueUnit); + } + + metadata.hasTime = dictionary.hasValue(KeyTime); + if (metadata.hasTime) { + std::string timeString = dictionary.value(KeyTime); + metadata.time = Time::convertTime(timeString); + } + + return metadata; +} + +ghoul::Dictionary RawVolumeMetadata::dictionary() { + ghoul::Dictionary dict; + dict.setValue(KeyDimensions, dimensions); + dict.setValue(KeyGridType, gridTypeToString(gridType)); + + if (hasDomainUnit) { + dict.setValue(KeyDomainUnit, domainUnit); + } + if (hasDomainBounds) { + dict.setValue(KeyLowerDomainBound, lowerDomainBound); + dict.setValue(KeyUpperDomainBound, upperDomainBound); + } + + if (hasValueRange) { + dict.setValue(KeyMinValue, static_cast(minValue)); + dict.setValue(KeyMaxValue, static_cast(maxValue)); + } + if (hasDomainUnit) { + dict.setValue(KeyValueUnit, valueUnit); + } + + if (hasTime) { + std::string timeString = Time(time).ISO8601(); + // Do not include time offset in time string + if (timeString.back() == 'Z') { + timeString.pop_back(); + } + dict.setValue(KeyTime, timeString); + } + return dict; +} + +documentation::Documentation RawVolumeMetadata::Documentation() { + using namespace documentation; + return { + "RawVolumeMetadata", + "volume_rawvolumemetadata", + { + { + KeyDimensions, + new Vector3Verifier, + Optional::No, + "Specifies the number of grid cells in each dimension", + }, + { + KeyDomainUnit, + new StringVerifier, + Optional::Yes, + "Specifies the unit used to specity the domain", + }, + { + KeyLowerDomainBound, + new Vector3Verifier, + Optional::Yes, + "Specifies the lower domain bounds in the model coordinate system", + }, + { + KeyUpperDomainBound, + new Vector3Verifier, + Optional::Yes, + "Specifies the upper domain bounds in the model coordinate system", + }, + { + KeyTime, + new StringVerifier, + Optional::Yes, + "Specifies the time on the format YYYY-MM-DDTHH:MM:SS.000Z", + }, + { + KeyValueUnit, + new StringVerifier, + Optional::Yes, + "Specifies the unit used to specity the value", + }, + { + KeyMinValue, + new DoubleVerifier, + Optional::Yes, + "Specifies the minimum value stored in the volume" + }, + { + KeyMaxValue, + new DoubleVerifier, + Optional::Yes, + "Specifies the maximum value stored in the volume" + } + } + }; +} + +} // namespace openspace::volume diff --git a/modules/volume/rawvolumemetadata.h b/modules/volume/rawvolumemetadata.h new file mode 100644 index 0000000000..25ccc3d9d9 --- /dev/null +++ b/modules/volume/rawvolumemetadata.h @@ -0,0 +1,62 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_VOLUME___RAWVOLUMEMETADATA___H__ +#define __OPENSPACE_MODULE_VOLUME___RAWVOLUMEMETADATA___H__ + +#include +#include + +#include + +namespace openspace::volume { + +struct RawVolumeMetadata { + static RawVolumeMetadata CreateFromDictionary(const ghoul::Dictionary& dictionary); + static documentation::Documentation Documentation(); + + ghoul::Dictionary dictionary(); + + glm::uvec3 dimensions; + VolumeGridType gridType; + + bool hasTime; + double time; + + bool hasValueRange; + float minValue; + float maxValue; + bool hasValueUnit; + std::string valueUnit; + + bool hasDomainBounds; + glm::vec3 lowerDomainBound; + glm::vec3 upperDomainBound; + bool hasDomainUnit; + std::string domainUnit; +}; + +} // namespace openspace::volume + +#endif // __OPENSPACE_MODULE_VOLUME___RAWVOLUMEMETADATA___H__ diff --git a/modules/volume/rawvolumereader.inl b/modules/volume/rawvolumereader.inl index e11d44818b..48623ab1ae 100644 --- a/modules/volume/rawvolumereader.inl +++ b/modules/volume/rawvolumereader.inl @@ -86,6 +86,11 @@ std::unique_ptr> RawVolumeReader::read() { std::make_unique>(dims); std::ifstream file(_path, std::ios::binary); + + if (file.fail()) { + throw ghoul::FileNotFoundError("Volume file not found"); + } + char *buffer = reinterpret_cast(volume->data()); size_t length = static_cast(dims.x) * static_cast(dims.y) * @@ -93,6 +98,11 @@ std::unique_ptr> RawVolumeReader::read() { sizeof(VoxelType); file.read(buffer, length); + + if (file.fail()) { + throw ghoul::RuntimeError("Error reading volume file"); + } + return volume; } diff --git a/modules/volume/rawvolumewriter.inl b/modules/volume/rawvolumewriter.inl index 4080d9ba8b..a1b6f78b63 100644 --- a/modules/volume/rawvolumewriter.inl +++ b/modules/volume/rawvolumewriter.inl @@ -97,6 +97,11 @@ void RawVolumeWriter::write(const RawVolume& volume) { size_t length = volume.nCells() * sizeof(VoxelType); std::ofstream file(_path, std::ios::binary); + + if (!file.good()) { + throw ghoul::RuntimeError("Could not create file '" + _path + "'"); + } + file.write(buffer, length); file.close(); } diff --git a/modules/volume/rendering/basicvolumeraycaster.cpp b/modules/volume/rendering/basicvolumeraycaster.cpp index fa1598dd5c..82a5e0f29a 100644 --- a/modules/volume/rendering/basicvolumeraycaster.cpp +++ b/modules/volume/rendering/basicvolumeraycaster.cpp @@ -45,10 +45,10 @@ namespace openspace::volume { BasicVolumeRaycaster::BasicVolumeRaycaster( std::shared_ptr volumeTexture, - std::shared_ptr transferFunctionHandler, + std::shared_ptr transferFunction, std::shared_ptr clipPlanes) : _volumeTexture(volumeTexture) - , _transferFunctionHandler(transferFunctionHandler) + , _transferFunction(transferFunction) , _clipPlanes(clipPlanes) , _boundingBox(glm::vec3(1.0)) , _opacity(20.0) @@ -112,7 +112,7 @@ void BasicVolumeRaycaster::preRaycast( const RaycastData& data, ghoul::opengl::ProgramObject& program) { - if (!_volumeTexture || !_transferFunctionHandler) { + if (!_volumeTexture || !_transferFunction) { return; } @@ -121,9 +121,10 @@ void BasicVolumeRaycaster::preRaycast( std::string id = std::to_string(data.id); + _transferFunction->update(); _tfUnit = std::make_unique(); _tfUnit->activate(); - _transferFunctionHandler->getTexture().bind(); + _transferFunction->getTexture().bind(); program.setUniform("transferFunction_" + id, _tfUnit->unitNumber()); _textureUnit = std::make_unique(); @@ -182,10 +183,10 @@ std::string BasicVolumeRaycaster::getHelperPath() const { } -void BasicVolumeRaycaster::setTransferFunctionHandler( - std::shared_ptr transferFunctionHandler) +void BasicVolumeRaycaster::setTransferFunction( + std::shared_ptr transferFunction) { - _transferFunctionHandler = transferFunctionHandler; + _transferFunction = transferFunction; } void BasicVolumeRaycaster::setVolumeTexture( diff --git a/modules/volume/rendering/basicvolumeraycaster.h b/modules/volume/rendering/basicvolumeraycaster.h index 9c691ed68d..e2f71178d0 100644 --- a/modules/volume/rendering/basicvolumeraycaster.h +++ b/modules/volume/rendering/basicvolumeraycaster.h @@ -33,8 +33,8 @@ #include #include +#include #include -#include #include #include @@ -56,7 +56,7 @@ class BasicVolumeRaycaster : public VolumeRaycaster { public: BasicVolumeRaycaster( std::shared_ptr texture, - std::shared_ptr transferFunctionHandler, + std::shared_ptr transferFunction, std::shared_ptr clipPlanes); virtual ~BasicVolumeRaycaster(); void initialize(); @@ -80,8 +80,8 @@ public: void setVolumeTexture(std::shared_ptr texture); std::shared_ptr volumeTexture() const; - void setTransferFunctionHandler( - std::shared_ptr transferFunctionHandler); + void setTransferFunction( + std::shared_ptr transferFunction); void setStepSize(float stepSize); float opacity() const; @@ -99,7 +99,7 @@ private: std::shared_ptr _clipPlanes; std::shared_ptr _volumeTexture; - std::shared_ptr _transferFunctionHandler; + std::shared_ptr _transferFunction; BoxGeometry _boundingBox; VolumeGridType _gridType; glm::mat4 _modelTransform; diff --git a/modules/volume/rendering/renderabletimevaryingvolume.cpp b/modules/volume/rendering/renderabletimevaryingvolume.cpp index 09f331c3d3..afec76e872 100644 --- a/modules/volume/rendering/renderabletimevaryingvolume.cpp +++ b/modules/volume/rendering/renderabletimevaryingvolume.cpp @@ -47,20 +47,14 @@ namespace { } // namespace namespace { - const char* KeyDimensions = "Dimensions"; const char* KeyStepSize = "StepSize"; const char* KeyTransferFunction = "TransferFunction"; const char* KeySourceDirectory = "SourceDirectory"; - const char* KeyLowerDomainBound = "LowerDomainBound"; - const char* KeyUpperDomainBound = "UpperDomainBound"; + const char* KeyClipPlanes = "ClipPlanes"; const char* KeySecondsBefore = "SecondsBefore"; const char* KeySecondsAfter = "SecondsAfter"; - const char* KeyGridType = "GridType"; - const char* KeyMinValue = "MinValue"; - const char* KeyMaxValue = "MaxValue"; - const char* KeyTime = "Time"; - const char* KeyUnit = "VisUnit"; + const float SecondsInOneDay = 60 * 60 * 24; static const openspace::properties::Property::PropertyInfo StepSizeInfo = { @@ -168,8 +162,6 @@ RenderableTimeVaryingVolume::RenderableTimeVaryingVolume( , _jumpToTimestep(JumpToTimestepInfo, 0, 0, 256) , _currentTimestep(CurrentTimeStepInfo, 0, 0, 256) , _raycaster(nullptr) - , _transferFunctionHandler(nullptr) - { documentation::testSpecificationAndThrow( Documentation(), @@ -179,8 +171,10 @@ RenderableTimeVaryingVolume::RenderableTimeVaryingVolume( _sourceDirectory = absPath(dictionary.value(KeySourceDirectory)); _transferFunctionPath = absPath(dictionary.value(KeyTransferFunction)); - _transferFunctionHandler = std::make_shared(_transferFunctionPath); - + _transferFunction = std::make_shared( + _transferFunctionPath, + [](const openspace::TransferFunction&) {} + ); _gridType.addOptions({ { static_cast(volume::VolumeGridType::Cartesian), "Cartesian grid" }, @@ -203,12 +197,12 @@ RenderableTimeVaryingVolume::RenderableTimeVaryingVolume( _clipPlanes->setIdentifier("clipPlanes"); _clipPlanes->setGuiName("Clip Planes"); - if (dictionary.hasValue(KeyGridType)) { + /*if (dictionary.hasValue(KeyGridType)) { VolumeGridType gridType = volume::parseGridType( dictionary.value(KeyGridType) ); _gridType = (gridType == VolumeGridType::Spherical) ? 1 : 0; - } + }*/ } RenderableTimeVaryingVolume::~RenderableTimeVaryingVolume() {} @@ -233,9 +227,6 @@ void RenderableTimeVaryingVolume::initializeGL() { if (extension == "dictionary") { loadTimestepMetadata(path); } - if (extension == "tf") { - _transferFunctionHandler->setFilepath(path); - } } @@ -245,11 +236,11 @@ void RenderableTimeVaryingVolume::initializeGL() { std::string path = FileSys.pathByAppendingComponent( _sourceDirectory, t.baseName ) + ".rawvolume"; - RawVolumeReader reader(path, t.dimensions); + RawVolumeReader reader(path, t.metadata.dimensions); t.rawVolume = reader.read(); - float min = t.minValue; - float diff = t.maxValue - t.minValue; + float min = t.metadata.minValue; + float diff = t.metadata.maxValue - t.metadata.minValue; float *data = t.rawVolume->data(); for (size_t i = 0; i < t.rawVolume->nCells(); ++i) { data[i] = glm::clamp((data[i] - min) / diff, 0.0f, 1.0f); @@ -263,7 +254,7 @@ void RenderableTimeVaryingVolume::initializeGL() { // TODO: handle normalization properly for different timesteps + transfer function t.texture = std::make_shared( - t.dimensions, + t.metadata.dimensions, ghoul::opengl::Texture::Format::Red, GL_RED, GL_FLOAT, @@ -278,10 +269,10 @@ void RenderableTimeVaryingVolume::initializeGL() { t.texture->uploadTexture(); } - _transferFunctionHandler->initialize(); + //_transferFunction->initialize(); _clipPlanes->initialize(); - _raycaster = std::make_unique(nullptr, _transferFunctionHandler, _clipPlanes); + _raycaster = std::make_unique(nullptr, _transferFunction, _clipPlanes); _raycaster->initialize(); OsEng.renderEngine().raycasterManager().attachRaycaster(*_raycaster.get()); @@ -312,7 +303,6 @@ void RenderableTimeVaryingVolume::initializeGL() { addProperty(_transferFunctionPath); addProperty(_sourceDirectory); addPropertySubOwner(_clipPlanes.get()); - addPropertySubOwner(_transferFunctionHandler.get()); addProperty(_triggerTimeJump); addProperty(_jumpToTimestep); @@ -335,35 +325,30 @@ void RenderableTimeVaryingVolume::initializeGL() { }); _transferFunctionPath.onChange([this] { - _transferFunctionHandler = - std::make_shared(_transferFunctionPath); - _raycaster->setTransferFunctionHandler(_transferFunctionHandler); + _transferFunction = + std::make_shared(_transferFunctionPath); + _raycaster->setTransferFunction(_transferFunction); }); } void RenderableTimeVaryingVolume::loadTimestepMetadata(const std::string& path) { - ghoul::Dictionary dictionary = ghoul::lua::loadDictionaryFromFile(path); - documentation::testSpecificationAndThrow( - TimestepDocumentation(), - dictionary, - "TimeVaryingVolumeTimestep" - ); + RawVolumeMetadata metadata; + + try { + ghoul::Dictionary dictionary = ghoul::lua::loadDictionaryFromFile(path); + metadata = RawVolumeMetadata::CreateFromDictionary(dictionary); + } catch (...) { + return; + } Timestep t; + t.metadata = metadata; t.baseName = ghoul::filesystem::File(path).baseName(); - t.dimensions = dictionary.value(KeyDimensions); - t.lowerDomainBound = dictionary.value(KeyLowerDomainBound); - t.upperDomainBound = dictionary.value(KeyUpperDomainBound); - t.minValue = dictionary.value(KeyMinValue); - t.maxValue = dictionary.value(KeyMaxValue); - t.unit = dictionary.value(KeyUnit); - std::string timeString = dictionary.value(KeyTime); - t.time = Time::convertTime(timeString); t.inRam = false; t.onGpu = false; - _volumeTimesteps[t.time] = std::move(t); + _volumeTimesteps[t.metadata.time] = std::move(t); } RenderableTimeVaryingVolume::Timestep* RenderableTimeVaryingVolume::currentTimestep() { @@ -377,14 +362,16 @@ RenderableTimeVaryingVolume::Timestep* RenderableTimeVaryingVolume::currentTimes if (currentTimestepIt == _volumeTimesteps.end()) { // No such timestep was found: show last timestep if it is within the time margin. Timestep* lastTimestep = &(_volumeTimesteps.rbegin()->second); - double threshold = lastTimestep->time + static_cast(_secondsAfter); + double threshold = lastTimestep->metadata.time + + static_cast(_secondsAfter); return currentTime < threshold ? lastTimestep : nullptr; } if (currentTimestepIt == _volumeTimesteps.begin()) { // No such timestep was found: show first timestep if it is within the time margin Timestep* firstTimestep = &(_volumeTimesteps.begin()->second); - double threshold = firstTimestep->time - static_cast(_secondsBefore); + double threshold = firstTimestep->metadata.time - + static_cast(_secondsBefore); return currentTime >= threshold ? firstTimestep : nullptr; } @@ -430,7 +417,7 @@ void RenderableTimeVaryingVolume::jumpToTimestep(int target) { if (!t) { return; } - OsEng.timeManager().setTimeNextFrame(t->time); + OsEng.timeManager().setTimeNextFrame(t->metadata.time); } void RenderableTimeVaryingVolume::update(const UpdateData&) { @@ -439,9 +426,10 @@ void RenderableTimeVaryingVolume::update(const UpdateData&) { _currentTimestep = timestepIndex(t); if (t && t->texture) { if (_raycaster->gridType() == volume::VolumeGridType::Cartesian) { - glm::dvec3 scale = t->upperDomainBound - t->lowerDomainBound; + glm::dvec3 scale = t->metadata.upperDomainBound - + t->metadata.lowerDomainBound; glm::dvec3 translation = - (t->lowerDomainBound + t->upperDomainBound) * 0.5f; + (t->metadata.lowerDomainBound + t->metadata.upperDomainBound) * 0.5f; glm::dmat4 modelTransform = glm::translate(glm::dmat4(1.0), translation); glm::dmat4 scaleMatrix = glm::scale(glm::dmat4(1.0), scale); @@ -451,14 +439,16 @@ void RenderableTimeVaryingVolume::update(const UpdateData&) { _raycaster->setModelTransform( glm::scale( glm::dmat4(1.0), - glm::dvec3(t->upperDomainBound[0]) + glm::dvec3(t->metadata.upperDomainBound[0]) ) ); } _raycaster->setVolumeTexture(t->texture); - _transferFunctionHandler->setUnit(t->unit); - _transferFunctionHandler->setMinAndMaxValue(t->minValue, t->maxValue); - _transferFunctionHandler->setHistogramProperty(t->histogram); + //_transferFunctionHandler->setUnit(t->metadata.valueUnit); + //_transferFunctionHandler->setMinAndMaxValue( + // t->metadata.minValue, t->metadata.maxValue); + + //_transferFunctionHandler->setHistogramProperty(t->histogram); } else { _raycaster->setVolumeTexture(nullptr); } @@ -504,12 +494,6 @@ documentation::Documentation RenderableTimeVaryingVolume::Documentation() { Optional::No, "Specifies the transfer function file path" }, - { - KeyGridType, - new StringInListVerifier({"Cartesian", "Spherical"}), - Optional::Yes, - "Specifies the grid type" - }, { KeySecondsBefore, new DoubleVerifier, @@ -529,51 +513,5 @@ documentation::Documentation RenderableTimeVaryingVolume::Documentation() { } -documentation::Documentation RenderableTimeVaryingVolume::TimestepDocumentation() { - using namespace documentation; - return { - "TimevaryingVolumeTimestep", - "volume_timevaryingvolumetimestep", - { - { - KeyLowerDomainBound, - new Vector3Verifier, - Optional::No, - "Specifies the lower domain bounds in the model coordinate system", - }, - { - KeyUpperDomainBound, - new Vector3Verifier, - Optional::No, - "Specifies the upper domain bounds in the model coordinate system", - }, - { - KeyDimensions, - new Vector3Verifier, - Optional::No, - "Specifies the number of grid cells in each dimension", - }, - { - KeyTime, - new StringVerifier, - Optional::No, - "Specifies the time on the format YYYY-MM-DDTHH:MM:SS.000Z", - }, - { - KeyMinValue, - new DoubleVerifier, - Optional::No, - "Specifies the minimum value stored in the volume" - }, - { - KeyMaxValue, - new DoubleVerifier, - Optional::No, - "Specifies the maximum value stored in the volume" - } - } - }; -} - } // namespace volume } // namespace openspace diff --git a/modules/volume/rendering/renderabletimevaryingvolume.h b/modules/volume/rendering/renderabletimevaryingvolume.h index e3169645d2..b195676c97 100644 --- a/modules/volume/rendering/renderabletimevaryingvolume.h +++ b/modules/volume/rendering/renderabletimevaryingvolume.h @@ -28,16 +28,16 @@ #include #include +#include #include #include -#include - #include #include #include #include #include +#include namespace openspace { @@ -57,21 +57,14 @@ public: void update(const UpdateData& data) override; static documentation::Documentation Documentation(); - static documentation::Documentation TimestepDocumentation(); private: struct Timestep { std::string baseName; - double time; - float minValue; - float maxValue; - glm::uvec3 dimensions; - glm::vec3 lowerDomainBound; - glm::vec3 upperDomainBound; - std::string unit; bool inRam; bool onGpu; - std::unique_ptr> rawVolume; + RawVolumeMetadata metadata; + std::shared_ptr> rawVolume; std::shared_ptr texture; std::shared_ptr histogram; }; @@ -102,7 +95,7 @@ private: std::map _volumeTimesteps; std::unique_ptr _raycaster; - std::shared_ptr _transferFunctionHandler; + std::shared_ptr _transferFunction; }; diff --git a/modules/volume/shaders/raycast.glsl b/modules/volume/shaders/raycast.glsl index 49be9581ec..67888194be 100644 --- a/modules/volume/shaders/raycast.glsl +++ b/modules/volume/shaders/raycast.glsl @@ -72,6 +72,7 @@ void sample#{id}(vec3 samplePos, vec3 dir, inout vec3 accumulatedColor, } vec4 color = texture(transferFunction_#{id}, val); + vec3 backColor = color.rgb; vec3 backAlpha = color.aaa; diff --git a/modules/volume/tasks/generaterawvolumetask.cpp b/modules/volume/tasks/generaterawvolumetask.cpp index ef4f721ad0..a857f47d7f 100644 --- a/modules/volume/tasks/generaterawvolumetask.cpp +++ b/modules/volume/tasks/generaterawvolumetask.cpp @@ -25,17 +25,19 @@ #include #include +#include #include #include +#include +#include -#include #include - #include -#include #include #include +#include +#include #include @@ -48,8 +50,8 @@ namespace { constexpr const char* KeyLowerDomainBound = "LowerDomainBound"; constexpr const char* KeyUpperDomainBound = "UpperDomainBound"; - // constexpr const char* KeyMinValue = "MinValue"; - // constexpr const char* KeyMaxValue = "MaxValue"; + constexpr const char* KeyMinValue = "MinValue"; + constexpr const char* KeyMaxValue = "MaxValue"; // constexpr const char* KeyVisUnit = "VisUnit"; } // namespace @@ -91,6 +93,15 @@ std::string GenerateRawVolumeTask::description() { } void GenerateRawVolumeTask::perform(const Task::ProgressCallback& progressCallback) { + // Spice kernel is required for time conversions. + // Todo: Make this dependency less hard coded. + SpiceManager::KernelHandle kernel = + SpiceManager::ref().loadKernel(absPath("${DATA}/assets/spice/naif0012.tls")); + + defer { + SpiceManager::ref().unloadKernel(kernel); + }; + volume::RawVolume rawVolume(_dimensions); progressCallback(0.1f); @@ -105,9 +116,13 @@ void GenerateRawVolumeTask::perform(const Task::ProgressCallback& progressCallba glm::vec3 domainSize = _upperDomainBound - _lowerDomainBound; + + float minVal = std::numeric_limits::max(); + float maxVal = std::numeric_limits::min(); + rawVolume.forEachVoxel([&](glm::uvec3 cell, float) { glm::vec3 coord = _lowerDomainBound + - glm::vec3(cell) * glm::vec3(_dimensions) * domainSize; + glm::vec3(cell) / glm::vec3(_dimensions) * domainSize; ghoul::lua::verifyStackSize(state, 0); lua_rawgeti(state, LUA_REGISTRYINDEX, functionReference); @@ -125,6 +140,9 @@ void GenerateRawVolumeTask::perform(const Task::ProgressCallback& progressCallba float value = luaL_checknumber(state, 1); lua_pop(state, 1); rawVolume.set(cell, value); + + minVal = std::min(minVal, value); + maxVal = std::max(maxVal, value); }); luaL_unref(state, LUA_REGISTRYINDEX, functionReference); @@ -134,35 +152,24 @@ void GenerateRawVolumeTask::perform(const Task::ProgressCallback& progressCallba progressCallback(0.9f); - ghoul::Dictionary outputMetadata; - - std::string time = _time; - - // Do not include time offset in time string - if (time.back() == 'Z') { - time.pop_back(); - } - - outputMetadata.setValue(KeyTime, time); - outputMetadata.setValue(KeyDimensions, _dimensions); - outputMetadata.setValue(KeyLowerDomainBound, _lowerDomainBound); - outputMetadata.setValue(KeyUpperDomainBound, _upperDomainBound); - - /*outputMetadata.setValue( - KeyMinValue, - static_cast(reader.minValue(_variable)) - ); - outputMetadata.setValue( - KeyMaxValue, - static_cast(reader.maxValue(_variable)) - ); - outputMetadata.setValue( - KeyVisUnit, - static_cast(reader.getVisUnit(_variable)) - );*/ + RawVolumeMetadata metadata; + metadata.time = Time::convertTime(_time); + metadata.dimensions = _dimensions; + metadata.hasDomainUnit = true; + metadata.domainUnit = "m"; + metadata.hasValueUnit = true; + metadata.valueUnit = "kg/m^3"; + metadata.gridType = VolumeGridType::Cartesian; + metadata.hasDomainBounds = true; + metadata.lowerDomainBound = _lowerDomainBound; + metadata.upperDomainBound = _upperDomainBound; + metadata.hasValueRange = true; + metadata.minValue = minVal; + metadata.maxValue = maxVal; + ghoul::Dictionary outputDictionary = metadata.dictionary(); ghoul::DictionaryLuaFormatter formatter; - std::string metadataString = formatter.format(outputMetadata); + std::string metadataString = formatter.format(outputDictionary); std::fstream f(_dictionaryOutputPath, std::ios::out); f << "return " << metadataString; diff --git a/modules/volume/volumegridtype.cpp b/modules/volume/volumegridtype.cpp index 87fe622a12..b540dcb9c6 100644 --- a/modules/volume/volumegridtype.cpp +++ b/modules/volume/volumegridtype.cpp @@ -38,6 +38,14 @@ VolumeGridType parseGridType(const std::string& gridType) { throw InvalidGridTypeError(gridType); } +std::string gridTypeToString(VolumeGridType gridType) { + switch (gridType) { + case VolumeGridType::Cartesian: return "Cartesian"; + case VolumeGridType::Spherical: return "Spherical"; + } + return "Unknown"; +} + InvalidGridTypeError::InvalidGridTypeError(std::string gt) : RuntimeError("Invalid grid type: '" + gt + "'") , gridType(std::move(gt)) diff --git a/modules/volume/volumegridtype.h b/modules/volume/volumegridtype.h index 8997d699db..66698c7abf 100644 --- a/modules/volume/volumegridtype.h +++ b/modules/volume/volumegridtype.h @@ -40,6 +40,7 @@ struct InvalidGridTypeError : public ghoul::RuntimeError { }; VolumeGridType parseGridType(const std::string& gridType); +std::string gridTypeToString(VolumeGridType); } // namespace openspace::volume diff --git a/src/rendering/transferfunction.cpp b/src/rendering/transferfunction.cpp index 4d3800be54..cd124f14f5 100644 --- a/src/rendering/transferfunction.cpp +++ b/src/rendering/transferfunction.cpp @@ -92,7 +92,7 @@ ghoul::opengl::Texture& TransferFunction::getTexture() { } void TransferFunction::update() { - /* if (_needsUpdate) { + if (_needsUpdate) { if (hasExtension(_filepath, "txt")) { setTextureFromTxt(); } else { @@ -103,7 +103,7 @@ void TransferFunction::update() { if (_tfChangedCallback) { _tfChangedCallback(*this); } - }*/ + } } void TransferFunction::setCallback(TfChangedCallback callback) {