Work on volume generation and rendering

This commit is contained in:
Emil Axelsson
2018-05-09 15:43:32 +02:00
parent 49abff980f
commit ae8b63d437
16 changed files with 396 additions and 178 deletions

View File

@@ -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"
}}

View File

@@ -24,18 +24,21 @@
#include <modules/toyvolume/rendering/toyvolumeraycaster.h>
#include <ghoul/glm.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <sstream>
#include <ghoul/opengl/programobject.h>
#include <openspace/util/powerscaledcoordinate.h>
#include <openspace/util/updatestructures.h>
#include <openspace/rendering/renderable.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/glm.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <ghoul/filesystem/filesystem.h>
#include <sstream>
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 {

View File

@@ -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

View File

@@ -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 <modules/volume/rawvolumemetadata.h>
#include <openspace/properties/vectorproperty.h>
#include <openspace/documentation/verifier.h>
#include <openspace/util/time.h>
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<glm::vec3>(KeyDimensions);
metadata.hasDomainBounds = dictionary.hasValue<glm::vec3>(KeyLowerDomainBound) &&
dictionary.hasValue<glm::vec3>(KeyUpperDomainBound);
if (metadata.hasDomainBounds) {
metadata.lowerDomainBound = dictionary.value<glm::vec3>(KeyLowerDomainBound);
metadata.upperDomainBound = dictionary.value<glm::vec3>(KeyUpperDomainBound);
}
metadata.hasDomainUnit = dictionary.hasValue<float>(KeyDomainUnit);
if (metadata.hasDomainUnit) {
metadata.domainUnit = dictionary.value<std::string>(KeyDomainUnit);
}
metadata.hasValueRange = dictionary.hasValue<float>(KeyMinValue) &&
dictionary.hasValue<float>(KeyMaxValue);
if (metadata.hasValueRange) {
metadata.minValue = dictionary.value<float>(KeyMinValue);
metadata.maxValue = dictionary.value<float>(KeyMaxValue);
}
metadata.hasValueUnit = dictionary.hasValue<float>(KeyValueUnit);
if (metadata.hasValueUnit) {
metadata.valueUnit = dictionary.value<std::string>(KeyValueUnit);
}
metadata.hasTime = dictionary.hasValue<std::string>(KeyTime);
if (metadata.hasTime) {
std::string timeString = dictionary.value<std::string>(KeyTime);
metadata.time = Time::convertTime(timeString);
}
return metadata;
}
ghoul::Dictionary RawVolumeMetadata::dictionary() {
ghoul::Dictionary dict;
dict.setValue<glm::vec3>(KeyDimensions, dimensions);
dict.setValue<std::string>(KeyGridType, gridTypeToString(gridType));
if (hasDomainUnit) {
dict.setValue<std::string>(KeyDomainUnit, domainUnit);
}
if (hasDomainBounds) {
dict.setValue<glm::vec3>(KeyLowerDomainBound, lowerDomainBound);
dict.setValue<glm::vec3>(KeyUpperDomainBound, upperDomainBound);
}
if (hasValueRange) {
dict.setValue<double>(KeyMinValue, static_cast<float>(minValue));
dict.setValue<double>(KeyMaxValue, static_cast<float>(maxValue));
}
if (hasDomainUnit) {
dict.setValue<std::string>(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<std::string>(KeyTime, timeString);
}
return dict;
}
documentation::Documentation RawVolumeMetadata::Documentation() {
using namespace documentation;
return {
"RawVolumeMetadata",
"volume_rawvolumemetadata",
{
{
KeyDimensions,
new Vector3Verifier<float>,
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<float>,
Optional::Yes,
"Specifies the lower domain bounds in the model coordinate system",
},
{
KeyUpperDomainBound,
new Vector3Verifier<float>,
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

View File

@@ -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 <openspace/documentation/documentation.h>
#include <modules/volume/volumegridtype.h>
#include <ghoul/misc/dictionary.h>
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__

View File

@@ -86,6 +86,11 @@ std::unique_ptr<RawVolume<VoxelType>> RawVolumeReader<VoxelType>::read() {
std::make_unique<RawVolume<VoxelType>>(dims);
std::ifstream file(_path, std::ios::binary);
if (file.fail()) {
throw ghoul::FileNotFoundError("Volume file not found");
}
char *buffer = reinterpret_cast<char*>(volume->data());
size_t length = static_cast<size_t>(dims.x) *
static_cast<size_t>(dims.y) *
@@ -93,6 +98,11 @@ std::unique_ptr<RawVolume<VoxelType>> RawVolumeReader<VoxelType>::read() {
sizeof(VoxelType);
file.read(buffer, length);
if (file.fail()) {
throw ghoul::RuntimeError("Error reading volume file");
}
return volume;
}

View File

@@ -97,6 +97,11 @@ void RawVolumeWriter<VoxelType>::write(const RawVolume<VoxelType>& 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();
}

View File

@@ -45,10 +45,10 @@ namespace openspace::volume {
BasicVolumeRaycaster::BasicVolumeRaycaster(
std::shared_ptr<ghoul::opengl::Texture> volumeTexture,
std::shared_ptr<TransferFunctionHandler> transferFunctionHandler,
std::shared_ptr<openspace::TransferFunction> transferFunction,
std::shared_ptr<VolumeClipPlanes> 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<ghoul::opengl::TextureUnit>();
_tfUnit->activate();
_transferFunctionHandler->getTexture().bind();
_transferFunction->getTexture().bind();
program.setUniform("transferFunction_" + id, _tfUnit->unitNumber());
_textureUnit = std::make_unique<ghoul::opengl::TextureUnit>();
@@ -182,10 +183,10 @@ std::string BasicVolumeRaycaster::getHelperPath() const {
}
void BasicVolumeRaycaster::setTransferFunctionHandler(
std::shared_ptr<TransferFunctionHandler> transferFunctionHandler)
void BasicVolumeRaycaster::setTransferFunction(
std::shared_ptr<openspace::TransferFunction> transferFunction)
{
_transferFunctionHandler = transferFunctionHandler;
_transferFunction = transferFunction;
}
void BasicVolumeRaycaster::setVolumeTexture(

View File

@@ -33,8 +33,8 @@
#include <ghoul/opengl/texture.h>
#include <openspace/rendering/volumeraycaster.h>
#include <openspace/rendering/transferfunction.h>
#include <openspace/util/boxgeometry.h>
#include <modules/volume/transferfunctionhandler.h>
#include <modules/volume/rendering/volumeclipplanes.h>
#include <modules/volume/volumegridtype.h>
@@ -56,7 +56,7 @@ class BasicVolumeRaycaster : public VolumeRaycaster {
public:
BasicVolumeRaycaster(
std::shared_ptr<ghoul::opengl::Texture> texture,
std::shared_ptr<TransferFunctionHandler> transferFunctionHandler,
std::shared_ptr<openspace::TransferFunction> transferFunction,
std::shared_ptr<VolumeClipPlanes> clipPlanes);
virtual ~BasicVolumeRaycaster();
void initialize();
@@ -80,8 +80,8 @@ public:
void setVolumeTexture(std::shared_ptr<ghoul::opengl::Texture> texture);
std::shared_ptr<ghoul::opengl::Texture> volumeTexture() const;
void setTransferFunctionHandler(
std::shared_ptr<TransferFunctionHandler> transferFunctionHandler);
void setTransferFunction(
std::shared_ptr<openspace::TransferFunction> transferFunction);
void setStepSize(float stepSize);
float opacity() const;
@@ -99,7 +99,7 @@ private:
std::shared_ptr<VolumeClipPlanes> _clipPlanes;
std::shared_ptr<ghoul::opengl::Texture> _volumeTexture;
std::shared_ptr<TransferFunctionHandler> _transferFunctionHandler;
std::shared_ptr<openspace::TransferFunction> _transferFunction;
BoxGeometry _boundingBox;
VolumeGridType _gridType;
glm::mat4 _modelTransform;

View File

@@ -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<std::string>(KeySourceDirectory));
_transferFunctionPath = absPath(dictionary.value<std::string>(KeyTransferFunction));
_transferFunctionHandler = std::make_shared<TransferFunctionHandler>(_transferFunctionPath);
_transferFunction = std::make_shared<openspace::TransferFunction>(
_transferFunctionPath,
[](const openspace::TransferFunction&) {}
);
_gridType.addOptions({
{ static_cast<int>(volume::VolumeGridType::Cartesian), "Cartesian grid" },
@@ -203,12 +197,12 @@ RenderableTimeVaryingVolume::RenderableTimeVaryingVolume(
_clipPlanes->setIdentifier("clipPlanes");
_clipPlanes->setGuiName("Clip Planes");
if (dictionary.hasValue<std::string>(KeyGridType)) {
/*if (dictionary.hasValue<std::string>(KeyGridType)) {
VolumeGridType gridType = volume::parseGridType(
dictionary.value<std::string>(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<float> reader(path, t.dimensions);
RawVolumeReader<float> 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<ghoul::opengl::Texture>(
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<volume::BasicVolumeRaycaster>(nullptr, _transferFunctionHandler, _clipPlanes);
_raycaster = std::make_unique<volume::BasicVolumeRaycaster>(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<TransferFunctionHandler>(_transferFunctionPath);
_raycaster->setTransferFunctionHandler(_transferFunctionHandler);
_transferFunction =
std::make_shared<TransferFunction>(_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<glm::vec3>(KeyDimensions);
t.lowerDomainBound = dictionary.value<glm::vec3>(KeyLowerDomainBound);
t.upperDomainBound = dictionary.value<glm::vec3>(KeyUpperDomainBound);
t.minValue = dictionary.value<float>(KeyMinValue);
t.maxValue = dictionary.value<float>(KeyMaxValue);
t.unit = dictionary.value<std::string>(KeyUnit);
std::string timeString = dictionary.value<std::string>(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<double>(_secondsAfter);
double threshold = lastTimestep->metadata.time +
static_cast<double>(_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<double>(_secondsBefore);
double threshold = firstTimestep->metadata.time -
static_cast<double>(_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<float>,
Optional::No,
"Specifies the lower domain bounds in the model coordinate system",
},
{
KeyUpperDomainBound,
new Vector3Verifier<float>,
Optional::No,
"Specifies the upper domain bounds in the model coordinate system",
},
{
KeyDimensions,
new Vector3Verifier<float>,
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

View File

@@ -28,16 +28,16 @@
#include <openspace/rendering/renderable.h>
#include <modules/volume/rawvolume.h>
#include <modules/volume/rawvolumemetadata.h>
#include <modules/volume/rendering/basicvolumeraycaster.h>
#include <modules/volume/rendering/volumeclipplanes.h>
#include <modules/volume/transferfunctionhandler.h>
#include <openspace/properties/vectorproperty.h>
#include <openspace/properties/optionproperty.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/util/boxgeometry.h>
#include <openspace/util/histogram.h>
#include <openspace/rendering/transferfunction.h>
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<float>> rawVolume;
RawVolumeMetadata metadata;
std::shared_ptr<RawVolume<float>> rawVolume;
std::shared_ptr<ghoul::opengl::Texture> texture;
std::shared_ptr<openspace::Histogram> histogram;
};
@@ -102,7 +95,7 @@ private:
std::map<double, Timestep> _volumeTimesteps;
std::unique_ptr<BasicVolumeRaycaster> _raycaster;
std::shared_ptr<TransferFunctionHandler> _transferFunctionHandler;
std::shared_ptr<TransferFunction> _transferFunction;
};

View File

@@ -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;

View File

@@ -25,17 +25,19 @@
#include <modules/volume/tasks/generaterawvolumetask.h>
#include <modules/volume/rawvolume.h>
#include <modules/volume/rawvolumemetadata.h>
#include <modules/volume/rawvolumewriter.h>
#include <openspace/documentation/verifier.h>
#include <openspace/util/time.h>
#include <openspace/util/spicemanager.h>
#include <ghoul/misc/dictionaryjsonformatter.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/dictionaryluaformatter.h>
#include <ghoul/lua/luastate.h>
#include <ghoul/lua/lua_helper.h>
#include <ghoul/misc/dictionaryluaformatter.h>
#include <ghoul/misc/defer.h>
#include <fstream>
@@ -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<float> 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<float>::max();
float maxVal = std::numeric_limits<float>::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<std::string>(KeyTime, time);
outputMetadata.setValue<glm::vec3>(KeyDimensions, _dimensions);
outputMetadata.setValue<glm::vec3>(KeyLowerDomainBound, _lowerDomainBound);
outputMetadata.setValue<glm::vec3>(KeyUpperDomainBound, _upperDomainBound);
/*outputMetadata.setValue<float>(
KeyMinValue,
static_cast<float>(reader.minValue(_variable))
);
outputMetadata.setValue<float>(
KeyMaxValue,
static_cast<float>(reader.maxValue(_variable))
);
outputMetadata.setValue<std::string>(
KeyVisUnit,
static_cast<std::string>(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;

View File

@@ -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))

View File

@@ -40,6 +40,7 @@ struct InvalidGridTypeError : public ghoul::RuntimeError {
};
VolumeGridType parseGridType(const std::string& gridType);
std::string gridTypeToString(VolumeGridType);
} // namespace openspace::volume

View File

@@ -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) {