Files
OpenSpace/modules/multiresvolume/rendering/renderablemultiresvolume.cpp
Alexander Bock 9f1c4e847d Feature/globals (#690)
* Move global objects out of OpenSpaceEngine
 * General cleanup of main.cpp
 * Add default_joystick asset to all scenes
 * No longer suppress mouse interaction on slave nodes
 * Window delegate uses function pointers rather than subclassing
 * Fix for false overwriting of ImGUI configuration file
 * Change default color and tilt angle of fisheye rendering
 * Restructured performance manager
 * Simplify screenshot handling
2018-08-30 11:38:47 -04:00

743 lines
28 KiB
C++

/*****************************************************************************************
* *
* 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/multiresvolume/rendering/renderablemultiresvolume.h>
#include <modules/kameleon/include/kameleonwrapper.h>
#include <modules/multiresvolume/rendering/atlasmanager.h>
#include <modules/multiresvolume/rendering/errorhistogrammanager.h>
#include <modules/multiresvolume/rendering/histogrammanager.h>
#include <modules/multiresvolume/rendering/localerrorhistogrammanager.h>
#include <modules/multiresvolume/rendering/localtfbrickselector.h>
#include <modules/multiresvolume/rendering/multiresvolumeraycaster.h>
#include <modules/multiresvolume/rendering/shenbrickselector.h>
#include <modules/multiresvolume/rendering/simpletfbrickselector.h>
#include <modules/multiresvolume/rendering/tfbrickselector.h>
#include <modules/multiresvolume/rendering/tsp.h>
#include <openspace/engine/globals.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/rendering/raycastermanager.h>
#include <openspace/rendering/transferfunction.h>
#include <openspace/util/spicemanager.h>
#include <openspace/util/time.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/fmt.h>
#include <ghoul/glm.h>
#include <ghoul/filesystem/cachemanager.h>
#include <ghoul/filesystem/file.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/io/texture/texturereader.h>
#include <ghoul/misc/dictionary.h>
#include <ghoul/opengl/framebufferobject.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/opengl/texture.h>
#include <algorithm>
#include <chrono>
#include <fstream>
#include <iterator>
namespace {
constexpr const char* _loggerCat = "RenderableMultiresVolume";
constexpr const char* KeyDataSource = "Source";
constexpr const char* KeyErrorHistogramsSource = "ErrorHistogramsSource";
constexpr const char* KeyHints = "Hints";
constexpr const char* KeyTransferFunction = "TransferFunction";
constexpr const char* KeyVolumeName = "VolumeName";
constexpr const char* KeyBrickSelector = "BrickSelector";
constexpr const char* KeyStartTime = "StartTime";
constexpr const char* KeyEndTime = "EndTime";
constexpr const char* GlslHelpersPath =
"${MODULES}/multiresvolume/shaders/helpers_fs.glsl";
constexpr const char* GlslHelperPath =
"${MODULES}/multiresvolume/shaders/helper.glsl";
constexpr const char* GlslHeaderPath =
"${MODULES}/multiresvolume/shaders/header.glsl";
constexpr openspace::properties::Property::PropertyInfo StepSizeCoefficientInfo = {
"StepSizeCoefficient",
"Stepsize Coefficient",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo CurrentTimeInfo = {
"CurrentTime",
"Current Time",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo MemoryBudgetInfo = {
"MemoryBudget",
"Memory Budget",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo StreamingBudgetInfo = {
"StreamingBudget",
"Streaming Budget",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo UseGlobalTimeInfo = {
"UseGlobalTime",
"Global Time",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo LoopInfo = {
"Loop",
"Loop",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo SelectorNameInfo = {
"Selector",
"Brick Selector",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo StatsToFileInfo = {
"PrintStats",
"Print Stats",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo StatsToFileNameInfo = {
"PrintStatsFileName",
"Stats Filename",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo ScalingExponentInfo = {
"ScalingExponent",
"Scaling Exponent",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo ScalingInfo = {
"Scaling",
"Scaling",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo TranslationInfo = {
"Translation",
"Translation",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo RotationInfo = {
"Rotation",
"Euler rotation",
"" // @TODO Missing documentation
};
} // namespace
namespace openspace {
RenderableMultiresVolume::RenderableMultiresVolume(const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
, _useGlobalTime(UseGlobalTimeInfo, false)
, _loop(LoopInfo, false)
, _currentTime(CurrentTimeInfo, 0, 0, 0)
, _memoryBudget(MemoryBudgetInfo, 0, 0, 0)
, _streamingBudget(StreamingBudgetInfo, 0, 0, 0)
, _stepSizeCoefficient(StepSizeCoefficientInfo, 1.f, 0.01f, 10.f)
, _selectorName(SelectorNameInfo, "tf")
, _statsToFile(StatsToFileInfo, false)
, _statsToFileName(StatsToFileNameInfo)
, _scalingExponent(ScalingExponentInfo, 1, -10, 20)
, _translation(TranslationInfo, glm::vec3(0.f), glm::vec3(0.f), glm::vec3(10.f))
, _rotation(RotationInfo, glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f), glm::vec3(6.28f))
, _scaling(ScalingInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(10.f))
{
if (dictionary.hasKeyAndValue<std::string>(KeyDataSource)) {
_filename = absPath(dictionary.value<std::string>(KeyDataSource));
}
else {
LERROR(fmt::format("Node did not contain a valid '{}'", KeyDataSource));
return;
}
if (dictionary.hasKeyAndValue<std::string>(KeyErrorHistogramsSource)) {
_errorHistogramsPath = absPath(
dictionary.value<std::string>(KeyErrorHistogramsSource)
);
}
if (dictionary.hasKeyAndValue<double>("ScalingExponent")) {
_scalingExponent = static_cast<int>(dictionary.value<double>("ScalingExponent"));
}
if (dictionary.hasKeyAndValue<double>("StepSizeCoefficient")) {
_stepSizeCoefficient = static_cast<float>(
dictionary.value<double>("StepSizeCoefficient")
);
}
if (dictionary.hasKeyAndValue<glm::vec3>("Scaling")) {
_scaling = dictionary.value<glm::vec3>("Scaling");
}
if (dictionary.hasKeyAndValue<glm::vec3>("Translation")) {
_translation = dictionary.value<glm::vec3>("Translation");
}
if (dictionary.hasKeyAndValue<glm::vec3>("Rotation")) {
_rotation = dictionary.value<glm::vec3>("Rotation");
}
std::string startTimeString, endTimeString;
bool hasTimeData = dictionary.getValue(KeyStartTime, startTimeString) &&
dictionary.getValue(KeyEndTime, endTimeString);
if (hasTimeData) {
_startTime = SpiceManager::ref().ephemerisTimeFromDate(startTimeString);
_endTime = SpiceManager::ref().ephemerisTimeFromDate(endTimeString);
_loop = false;
} else {
_loop = true;
LWARNING("Node does not provide time information. Viewing one image / frame");
}
if (dictionary.hasKeyAndValue<std::string>(KeyTransferFunction)) {
_transferFunctionPath = absPath(
dictionary.value<std::string>(KeyTransferFunction)
);
_transferFunction = std::make_shared<TransferFunction>(_transferFunctionPath);
}
else {
LERROR(fmt::format("Node did not contain a valid '{}'", KeyTransferFunction));
return;
}
//_pscOffset = psc(glm::vec4(0.0));
//_boxScaling = glm::vec3(1.0);
/*if (dictionary.hasKey(KeyBoxScaling)) {
glm::vec4 scalingVec4(_boxScaling, _w);
success = dictionary.getValue(KeyBoxScaling, scalingVec4);
if (success) {
_boxScaling = scalingVec4.xyz;
_w = scalingVec4.w;
}
else {
success = dictionary.getValue(KeyBoxScaling, _boxScaling);
if (!success) {
LERROR("Node '" << name << "' did not contain a valid '" <<
KeyBoxScaling << "'");
return;
}
}
}*/
_tsp = std::make_shared<TSP>(_filename);
_atlasManager = std::make_shared<AtlasManager>(_tsp.get());
if (dictionary.hasKeyAndValue<std::string>(KeyBrickSelector)) {
_selectorName = dictionary.value<std::string>(KeyBrickSelector);
}
std::string selectorName = _selectorName;
if (selectorName == "simple") {
_selector = Selector::SIMPLE;
} else if (selectorName == "local") {
_selector = Selector::LOCAL;
} else {
_selector = Selector::TF;
}
addProperty(_selectorName);
_selectorName.onChange([&]() {
Selector s;
std::string newSelectorName = _selectorName;
if (newSelectorName == "simple") {
s = Selector::SIMPLE;
} else if (newSelectorName == "local") {
s = Selector::LOCAL;
} else if (newSelectorName == "tf") {
s = Selector::TF;
} else {
return;
}
setSelectorType(s);
});
addProperty(_stepSizeCoefficient);
addProperty(_useGlobalTime);
addProperty(_loop);
addProperty(_statsToFile);
addProperty(_statsToFileName);
addProperty(_scaling);
addProperty(_scalingExponent);
addProperty(_translation);
addProperty(_rotation);
}
RenderableMultiresVolume::~RenderableMultiresVolume() {}
void RenderableMultiresVolume::setSelectorType(Selector selector) {
// @TODO(abock): Can these if statements be simplified by checking if
// selector == _selector before and bailing out early?
_selector = selector;
switch (_selector) {
case Selector::TF:
if (!_tfBrickSelector) {
_errorHistogramManager = std::make_unique<ErrorHistogramManager>(
_tsp.get()
);
_tfBrickSelector = std::make_unique<TfBrickSelector>(
_tsp.get(),
_errorHistogramManager.get(),
_transferFunction.get(),
_memoryBudget,
_streamingBudget
);
_transferFunction->setCallback([this](const TransferFunction&) {
_tfBrickSelector->calculateBrickErrors();
});
if (initializeSelector()) {
_tfBrickSelector->calculateBrickErrors();
return;
}
}
break;
case Selector::SIMPLE:
if (!_simpleTfBrickSelector) {
_histogramManager = std::make_unique<HistogramManager>();
_simpleTfBrickSelector = std::make_unique<SimpleTfBrickSelector>(
_tsp.get(),
_histogramManager.get(),
_transferFunction.get(),
_memoryBudget,
_streamingBudget
);
_transferFunction->setCallback([this](const TransferFunction&) {
_simpleTfBrickSelector->calculateBrickImportances();
});
if (initializeSelector()) {
_simpleTfBrickSelector->calculateBrickImportances();
return;
}
}
break;
case Selector::LOCAL:
if (!_localTfBrickSelector) {
_localErrorHistogramManager =
std::make_unique<LocalErrorHistogramManager>(_tsp.get());
_localTfBrickSelector = std::make_unique<LocalTfBrickSelector>(
_tsp.get(),
_localErrorHistogramManager.get(),
_transferFunction.get(),
_memoryBudget,
_streamingBudget
);
_transferFunction->setCallback([this](const TransferFunction&) {
_localTfBrickSelector->calculateBrickErrors();
});
if (initializeSelector()) {
_localTfBrickSelector->calculateBrickErrors();
return;
}
}
break;
}
}
void RenderableMultiresVolume::initializeGL() {
bool success = _tsp && _tsp->load();
unsigned int maxNumBricks = _tsp->header().xNumBricks * _tsp->header().yNumBricks *
_tsp->header().zNumBricks;
constexpr unsigned int MaxInitialBudget = 2048;
int initialBudget = std::min(MaxInitialBudget, maxNumBricks);
_currentTime = properties::IntProperty(
CurrentTimeInfo,
0,
0,
_tsp->header().numTimesteps - 1
);
_memoryBudget = properties::IntProperty(
MemoryBudgetInfo,
initialBudget,
0,
maxNumBricks
);
_streamingBudget = properties::IntProperty(
StreamingBudgetInfo,
initialBudget,
0,
maxNumBricks
);
addProperty(_currentTime);
addProperty(_memoryBudget);
addProperty(_streamingBudget);
if (success) {
_brickIndices.resize(maxNumBricks, 0);
setSelectorType(_selector);
}
success &= _atlasManager && _atlasManager->initialize();
_transferFunction->update();
success &= isReady();
_raycaster = std::make_unique<MultiresVolumeRaycaster>(
_tsp,
_atlasManager,
_transferFunction
);
_raycaster->initialize();
global::raycasterManager.attachRaycaster(*_raycaster);
auto onChange = [&](bool enabled) {
if (enabled) {
global::raycasterManager.attachRaycaster(*_raycaster);
}
else {
global::raycasterManager.detachRaycaster(*_raycaster);
}
};
onEnabledChange(onChange);
if (!success) {
throw ghoul::RuntimeError("Error during initialization");
}
}
void RenderableMultiresVolume::deinitializeGL() {
_tsp = nullptr;
_transferFunction = nullptr;
}
bool RenderableMultiresVolume::isReady() const {
return true;
}
bool RenderableMultiresVolume::initializeSelector() {
int nHistograms = 50;
bool success = true;
switch (_selector) {
case Selector::TF:
if (_errorHistogramManager) {
std::stringstream cacheName;
ghoul::filesystem::File f = _filename;
cacheName << f.baseName() << "_" << nHistograms << "_errorHistograms";
std::string cacheFilename;
cacheFilename = FileSys.cacheManager()->cachedFilename(
cacheName.str(),
"",
ghoul::filesystem::CacheManager::Persistent::Yes
);
std::ifstream cacheFile(cacheFilename, std::ios::in | std::ios::binary);
std::string errorHistogramsPath = _errorHistogramsPath;
if (cacheFile.is_open()) {
// Read histograms from cache.
cacheFile.close();
LINFO(
fmt::format("Loading histograms from cache: {}", cacheFilename)
);
success &= _errorHistogramManager->loadFromFile(cacheFilename);
} else if (_errorHistogramsPath != "") {
// Read histograms from scene data.
LINFO(fmt::format(
"Loading histograms from scene data: {}", _errorHistogramsPath
));
success &= _errorHistogramManager->loadFromFile(_errorHistogramsPath);
} else {
// Build histograms from tsp file.
LWARNING(fmt::format("Failed to open {}", cacheFilename));
success &= _errorHistogramManager->buildHistograms(nHistograms);
if (success) {
LINFO(fmt::format("Writing cache to {}", cacheFilename));
_errorHistogramManager->saveToFile(cacheFilename);
}
}
success &= _tfBrickSelector && _tfBrickSelector->initialize();
}
break;
case Selector::SIMPLE:
if (_histogramManager) {
std::stringstream cacheName;
ghoul::filesystem::File f = _filename;
cacheName << f.baseName() << "_" << nHistograms << "_histograms";
std::string cacheFilename;
cacheFilename = FileSys.cacheManager()->cachedFilename(
cacheName.str(),
"",
ghoul::filesystem::CacheManager::Persistent::Yes
);
std::ifstream cacheFile(cacheFilename, std::ios::in | std::ios::binary);
if (cacheFile.is_open()) {
// Read histograms from cache.
cacheFile.close();
LINFO(fmt::format("Loading histograms from {}", cacheFilename));
success &= _histogramManager->loadFromFile(cacheFilename);
} else {
// Build histograms from tsp file.
LWARNING(fmt::format("Failed to open '{}'", cacheFilename));
success &= _histogramManager->buildHistograms(
_tsp.get(),
nHistograms
);
if (success) {
LINFO(fmt::format("Writing cache to {}", cacheFilename));
_histogramManager->saveToFile(cacheFilename);
}
}
success &= _simpleTfBrickSelector && _simpleTfBrickSelector->initialize();
}
break;
case Selector::LOCAL:
if (_localErrorHistogramManager) {
ghoul::filesystem::File f = _filename;
std::string cacheFilename;
cacheFilename = FileSys.cacheManager()->cachedFilename(
fmt::format("{}_{}_localErrorHistograms", f.baseName(), nHistograms),
"",
ghoul::filesystem::CacheManager::Persistent::Yes
);
std::ifstream cacheFile(cacheFilename, std::ios::in | std::ios::binary);
if (cacheFile.is_open()) {
// Read histograms from cache.
cacheFile.close();
LINFO(fmt::format("Loading histograms from {}", cacheFilename));
success &= _localErrorHistogramManager->loadFromFile(cacheFilename);
} else {
// Build histograms from tsp file.
LWARNING(fmt::format("Failed to open {}", cacheFilename));
success &= _localErrorHistogramManager->buildHistograms(nHistograms);
if (success) {
LINFO(fmt::format("Writing cache to {}", cacheFilename));
_localErrorHistogramManager->saveToFile(cacheFilename);
}
}
success &= _localTfBrickSelector && _localTfBrickSelector->initialize();
}
break;
}
return success;
}
/*
void RenderableMultiresVolume::preResolve(ghoul::opengl::ProgramObject* program) {
RenderableVolume::preResolve(program);
std::stringstream ss;
ss << "opacity_" << getId();
program->setUniform(ss.str(), visible ? 1.0f : 0.0f);
ss.str(std::string());
ss << "stepSizeCoefficient_" << getId();
program->setUniform(ss.str(), _stepSizeCoefficient);
ss.str(std::string());
ss << "transferFunction_" << getId();
program->setUniform(ss.str(), getTextureUnit(_transferFunction->texture()));
ss.str(std::string());
ss << "textureAtlas_" << getId();
program->setUniform(ss.str(), getTextureUnit(_atlasManager->textureAtlas()));
ss.str(std::string());
ss << "atlasMapBlock_" << getId();
program->setSsboBinding(ss.str(), getSsboBinding(_atlasManager->atlasMapBuffer()));
ss.str(std::string());
ss << "gridType" << getId();
program->setUniform(ss.str(), static_cast<int>(_tsp->header().gridType));
ss.str(std::string());
ss << "maxNumBricksPerAxis_" << getId();
program->setUniform(ss.str(), static_cast<unsigned int>(_tsp->header().xNumBricks));
ss.str(std::string());
ss << "paddedBrickDim_" << getId();
program->setUniform(ss.str(), static_cast<unsigned int>(_tsp->paddedBrickDim()));
ss.str(std::string());
ss << "atlasSize_" << getId();
glm::size3_t size = _atlasManager->textureSize();
glm::ivec3 atlasSize(size.x, size.y, size.z);
program->setUniform(ss.str(), atlasSize);
_timestep++;
}
*/
/*
std::vector<ghoul::opengl::Texture*> RenderableMultiresVolume::getTextures() {
std::vector<ghoul::opengl::Texture*> textures{
_transferFunction->texture(),
_atlasManager->textureAtlas()
};
return textures;
}
std::vector<unsigned int> RenderableMultiresVolume::getBuffers() {
std::vector<unsigned int> buffers{_atlasManager->atlasMapBuffer()};
return buffers;
}*/
void RenderableMultiresVolume::update(const UpdateData& data) {
_timestep++;
_time = data.time.j2000Seconds();
if (_gatheringStats) {
std::chrono::system_clock::time_point frameEnd = std::chrono::system_clock::now();
std::chrono::duration<double> frameDuration = frameEnd - _frameStart;
// Make sure that the directory exists
ghoul::filesystem::File file(_statsFileName);
ghoul::filesystem::Directory directory(file.directoryName());
FileSys.createDirectory(directory, ghoul::filesystem::FileSystem::Recursive::Yes);
std::ofstream ofs(_statsFileName, std::ofstream::out);
ofs << frameDuration.count() << " "
<< _selectionDuration.count() << " "
<< _uploadDuration.count() << " "
<< _nUsedBricks << " "
<< _nStreamedBricks << " "
<< _nDiskReads;
ofs.close();
_gatheringStats = false;
}
if (_statsToFile) {
// Start frame timer
_frameStart = std::chrono::system_clock::now();
_statsFileName = _statsToFileName;
_gatheringStats = true;
_statsToFile = false;
}
int numTimesteps = _tsp->header().numTimesteps;
int currentTimestep;
bool visible = true;
if (_loop) {
currentTimestep = _timestep % numTimesteps;
}
else if (_useGlobalTime) {
double t = (_time - _startTime) / (_endTime - _startTime);
currentTimestep = static_cast<int>(t * numTimesteps);
visible = currentTimestep >= 0 && currentTimestep < numTimesteps;
}
else {
currentTimestep = _currentTime;
}
if (visible) {
std::chrono::system_clock::time_point selectionStart;
if (_gatheringStats) {
selectionStart = std::chrono::system_clock::now();
}
switch (_selector) {
case Selector::TF:
if (_tfBrickSelector) {
_tfBrickSelector->setMemoryBudget(_memoryBudget);
_tfBrickSelector->setStreamingBudget(_streamingBudget);
_tfBrickSelector->selectBricks(currentTimestep, _brickIndices);
}
break;
case Selector::SIMPLE:
if (_simpleTfBrickSelector) {
_simpleTfBrickSelector->setMemoryBudget(_memoryBudget);
_simpleTfBrickSelector->setStreamingBudget(_streamingBudget);
_simpleTfBrickSelector->selectBricks(currentTimestep, _brickIndices);
}
break;
case Selector::LOCAL:
if (_localTfBrickSelector) {
_localTfBrickSelector->setMemoryBudget(_memoryBudget);
_localTfBrickSelector->setStreamingBudget(_streamingBudget);
_localTfBrickSelector->selectBricks(currentTimestep, _brickIndices);
}
break;
}
std::chrono::system_clock::time_point uploadStart;
if (_gatheringStats) {
std::chrono::system_clock::time_point selectionEnd =
std::chrono::system_clock::now();
_selectionDuration = selectionEnd - selectionStart;
uploadStart = selectionEnd;
}
_atlasManager->updateAtlas(AtlasManager::EVEN, _brickIndices);
if (_gatheringStats) {
std::chrono::system_clock::time_point uploadEnd =
std::chrono::system_clock::now();
_uploadDuration = uploadEnd - uploadStart;
_nDiskReads = _atlasManager->numDiskReads();
_nUsedBricks = _atlasManager->numUsedBricks();
_nStreamedBricks = _atlasManager->numStreamedBricks();
}
}
if (_raycaster) {
glm::mat4 transform = glm::translate(
glm::mat4(1.0),
static_cast<glm::vec3>(_translation) *
std::pow(10.f, static_cast<float>(_scalingExponent))
);
glm::vec3 eulerRotation = static_cast<glm::vec3>(_rotation);
transform = glm::rotate(transform, eulerRotation.x, glm::vec3(1.f, 0.f, 0.f));
transform = glm::rotate(transform, eulerRotation.y, glm::vec3(0.f, 1.f, 0.f));
transform = glm::rotate(transform, eulerRotation.z, glm::vec3(0.f, 0.f, 1.f));
transform = glm::scale(
transform,
static_cast<glm::vec3>(_scaling) *
std::pow(10.f, static_cast<float>(_scalingExponent))
);
_raycaster->setStepSizeCoefficient(_stepSizeCoefficient);
_raycaster->setModelTransform(transform);
//_raycaster->setTime(data.time);
}
}
void RenderableMultiresVolume::render(const RenderData& data, RendererTasks& tasks) {
RaycasterTask task { _raycaster.get(), data };
tasks.raycasterTasks.push_back(task);
}
} // namespace openspace