Currently job artifacts in CI/CD pipelines on LRZ GitLab never expire. Starting from Wed 26.1.2022 the default expiration time will be 30 days (GitLab default). Currently existing artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

Commit 68aa2c83 authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Further work on DataContainerInspectorCanvas:

* Implemented trackball navigation interaction for rendered geometries
* Implemented correct rendering of textures with correct aspect ration (no longer weirdly stretched textures)
* removed obsolete (because redundant) renderFullscreen option

refs #546
refs #37
parent 4229330a
......@@ -60,8 +60,6 @@ namespace campvis {
, _quad(nullptr)
, _numTiles(0, 0)
, _quadSize(0, 0)
, _selectedTexture(0)
, _renderFullscreen(false)
, _currentSlice(-1)
, _localDataContainer("Local DataContainer for DataContainerInspectorCanvas")
, p_viewportSize("ViewportSize", "Viewport Size", tgt::ivec2(200), tgt::ivec2(0, 0), tgt::ivec2(10000))
......@@ -93,6 +91,7 @@ namespace campvis {
_geometryRenderer.p_textureID.setVisible(false);
_geometryRenderer.p_renderTargetID.setVisible(false);
_geometryRenderer.p_lightId.setVisible(false);
_geometryRenderer.p_camera.setVisible(false);
_geometryRenderer.p_coloringMode.setVisible(false);
_geometryRenderer.p_pointSize.setVisible(false);
_geometryRenderer.p_lineWidth.setVisible(false);
......@@ -103,6 +102,14 @@ namespace campvis {
_geometryRenderer.p_renderMode.s_changed.connect(this, &DataContainerInspectorCanvas::onGeometryRendererPropertyChanged);
_geometryRenderer.p_solidColor.s_changed.connect(this, &DataContainerInspectorCanvas::onGeometryRendererPropertyChanged);
addProperty(p_geometryRendererProperties);
p_geometryRendererProperties.setVisible(false);
p_currentSlice.setVisible(false);
p_transferFunction.setVisible(false);
p_renderRChannel.setVisible(false);
p_renderGChannel.setVisible(false);
p_renderBChannel.setVisible(false);
p_renderAChannel.setVisible(false);
}
DataContainerInspectorCanvas::~DataContainerInspectorCanvas() {
......@@ -122,16 +129,7 @@ namespace campvis {
setPainter(this, false);
getEventHandler()->addEventListenerToFront(this);
// use LightSourceProvider processor to create lighting information.
// This is needed to be done only once, therfore here in init().
LightSourceProvider lsp;
lsp.init();
lsp.invalidate(AbstractProcessor::INVALID_RESULT);
lsp.process(_localDataContainer);
lsp.deinit();
_geometryRenderer.init();
_trackballEH = new TrackballNavigationEventListener(&_geometryRenderer.p_camera, &p_viewportSize);
}
void DataContainerInspectorCanvas::deinit() {
......@@ -159,11 +157,16 @@ namespace campvis {
void DataContainerInspectorCanvas::paint() {
tbb::mutex::scoped_lock lock(_localMutex);
if (_texturesDirty)
if (_texturesDirty) {
updateTextures();
if (_textures.empty())
return;
}
if (_geometriesDirty) {
// update geometry renderings if necessary
for (auto it = _geometryNames.begin(); it != _geometryNames.end(); ++it) {
renderGeometryIntoTexture(it->first, it->second);
}
_geometriesDirty = false;
}
glPushAttrib(GL_ALL_ATTRIB_BITS);
glViewport(0, 0, size_.x, size_.y);
......@@ -171,6 +174,10 @@ namespace campvis {
glClear(GL_COLOR_BUFFER_BIT);
LGL_ERROR;
if (_textures.empty()) {
return;
}
// update layout dimensions
_numTiles.x = ceil(sqrt(static_cast<float>(_textures.size())));
_numTiles.y = ceil(static_cast<float>(_textures.size()) / _numTiles.x);
......@@ -197,12 +204,23 @@ namespace campvis {
if (index >= static_cast<int>(_textures.size()))
break;
// gather image
tgtAssert(dynamic_cast<const ImageData*>(_textures[index].getData()), "Found sth. else than ImageData in render texture vector. This should not happen!");
const ImageData* id = static_cast<const ImageData*>(_textures[index].getData());
// compute transformation matrices
float renderTargetRatio = static_cast<float>(_quadSize.x) / static_cast<float>(_quadSize.y);
float sliceRatio = (static_cast<float>(id->getSize().x) * id->getMappingInformation().getVoxelSize().x)
/ (static_cast<float>(id->getSize().y) * id->getMappingInformation().getVoxelSize().y);
float ratioRatio = sliceRatio / renderTargetRatio;
tgt::mat4 viewMatrix = (ratioRatio > 1) ? tgt::mat4::createScale(tgt::vec3(1.f, 1.f / ratioRatio, 1.f)) : tgt::mat4::createScale(tgt::vec3(ratioRatio, 1.f, 1.f));
tgt::mat4 scaleMatrix = tgt::mat4::createScale(tgt::vec3(_quadSize, 1.f));
tgt::mat4 translation = tgt::mat4::createTranslation(tgt::vec3(_quadSize.x * x, _quadSize.y * y, 0.f));
_paintShader->setUniform("_modelMatrix", translation * scaleMatrix);
tgtAssert(dynamic_cast<const ImageData*>(_textures[index].getData()), "Found sth. else than ImageData in render texture vector. This should not happen!");
const ImageData* id = static_cast<const ImageData*>(_textures[index].getData());
_paintShader->setUniform("_modelMatrix", translation * scaleMatrix * viewMatrix);
// render texture
paintTexture(id->getRepresentation<ImageRepresentationGL>()->getTexture(), unit2d, unit3d);
}
}
......@@ -265,63 +283,56 @@ namespace campvis {
invalidate();
}
void DataContainerInspectorCanvas::mouseDoubleClickEvent(tgt::MouseEvent* e) {
if (_renderFullscreen) {
_renderFullscreen = false;
}
else {
tgt::ivec2 selectedIndex(e->x() / _quadSize.x, e->y() / _quadSize.y);
_selectedTexture = (selectedIndex.y * _numTiles.x) + selectedIndex.x;
_renderFullscreen = true;
}
e->ignore();
invalidate();
}
void DataContainerInspectorCanvas::mouseMoveEvent(tgt::MouseEvent* e)
{
{
if (e->modifiers() & tgt::Event::CTRL) {
int texIndx = (e->y() / _quadSize.y) * _numTiles.x + (e->x() / _quadSize.x);
if (texIndx < 0 || texIndx >= _textures.size())
return;
const tgt::Texture* tex = static_cast<const ImageData*>(_textures[texIndx].getData())->getRepresentation<ImageRepresentationGL>()->getTexture();
const int texWidth = tex->getWidth();
const int texHeight = tex->getHeight();
int cursorPosX = static_cast<int>(static_cast<float>(e->x() % _quadSize.x) / _quadSize.x * texWidth);
int cursorPosY = static_cast<int>(static_cast<float>(e->y() % _quadSize.y) / _quadSize.y * texHeight);
const ImageData* id = static_cast<const ImageData*>(_textures[texIndx].getData());
const tgt::Texture* tex = id->getRepresentation<ImageRepresentationGL>()->getTexture();
tgt::ivec2 imageSize = id->getSize().xy();
if(tex->isDepthTexture()) {
emit s_depthChanged(tex->depthAsFloat(cursorPosX, texHeight - cursorPosY - 1));
}
else {
if (tex->getDimensions().z != 1) {
if (p_currentSlice.getValue() >= 0 && p_currentSlice.getValue() < tex->getDimensions().z) {
emit s_colorChanged(tex->texelAsFloat(cursorPosX, texHeight - cursorPosY - 1, p_currentSlice.getValue()));
}
tgt::vec2 lookupTexelFloat = tgt::vec2((e->coord() % _quadSize) * imageSize) / tgt::vec2(_quadSize);
// compute transformation matrices
float renderTargetRatio = static_cast<float>(_quadSize.x) / static_cast<float>(_quadSize.y);
float sliceRatio = (static_cast<float>(id->getSize().x) * id->getMappingInformation().getVoxelSize().x)
/ (static_cast<float>(id->getSize().y) * id->getMappingInformation().getVoxelSize().y);
float ratioRatio = sliceRatio / renderTargetRatio;
lookupTexelFloat /= (ratioRatio > 1) ? tgt::vec2(1.f, 1.f / ratioRatio) : tgt::vec2(ratioRatio, 1.f);
tgt::ivec2 lookupTexel(lookupTexelFloat);
if (lookupTexel.x >= 0 && lookupTexel.y >= 0 && lookupTexel.x < imageSize.x && lookupTexel.y < imageSize.y) {
if(tex->isDepthTexture()) {
emit s_depthChanged(tex->depthAsFloat(lookupTexel.x, imageSize.y - lookupTexel.y - 1));
}
else if (tex->getDimensions().y != 1) {
emit s_colorChanged(tex->texelAsFloat(cursorPosX, texHeight - cursorPosY - 1));
else {
if (tex->getDimensions().z != 1) {
if (p_currentSlice.getValue() >= 0 && p_currentSlice.getValue() < tex->getDimensions().z) {
emit s_colorChanged(tex->texelAsFloat(lookupTexel.x, imageSize.y - lookupTexel.y - 1, p_currentSlice.getValue()));
}
}
else if (tex->getDimensions().y != 1) {
emit s_colorChanged(tex->texelAsFloat(lookupTexel.x, imageSize.y - lookupTexel.y - 1));
}
}
}
}
}
else {
e->ignore();
}
}
void DataContainerInspectorCanvas::wheelEvent(tgt::MouseEvent* e) {
if (_renderFullscreen) {
switch (e->button()) {
case tgt::MouseEvent::MOUSE_WHEEL_UP:
++_currentSlice; // we cant clamp the value here to the number of slices - we do this during rendering
e->ignore();
break;
case tgt::MouseEvent::MOUSE_WHEEL_DOWN:
if (_currentSlice >= -1)
--_currentSlice;
e->ignore();
break;
default:
break;
}
void DataContainerInspectorCanvas::onEvent(tgt::Event* e) {
tgt::EventListener::onEvent(e);
if (_trackballEH && !e->isAccepted()) {
_trackballEH->onEvent(e);
e->accept();
_geometriesDirty = true;
invalidate();
}
}
......@@ -354,6 +365,20 @@ namespace campvis {
for (std::vector< std::pair<QString, QtDataHandle> >::const_iterator it = handles.begin(); it != handles.end(); ++it)
_handles.insert(*it);
_localDataContainer.clear();
_geometryNames.clear();
// use LightSourceProvider processor to create lighting information.
// This is needed to be done once after the local DataContainer got cleared.
LightSourceProvider lsp;
lsp.init();
lsp.invalidate(AbstractProcessor::INVALID_RESULT);
lsp.process(_localDataContainer);
lsp.deinit();
// reset trackball
resetTrackball();
_texturesDirty = true;
}
......@@ -392,52 +417,85 @@ namespace campvis {
}
}
else if (const GeometryData* gd = dynamic_cast<const GeometryData*>(it->second.getData())) {
// render geometry into texture
std::string name = it->first.toStdString();
renderGeometryIntoTexture(name, gd);
// grab render result texture from local DataContainer and push into texture vector.
ScopedTypedData<RenderData> rd(_localDataContainer, name + ".rendered");
if (rd != nullptr && rd->getNumColorTextures() > 0) {
auto rep = rd->getColorTexture(0)->getRepresentation<ImageRepresentationGL>();
if (rep != nullptr) {
const_cast<tgt::Texture*>(rep->getTexture())->downloadTexture();
_textures.push_back(rd->getColorDataHandle(0));
}
else {
tgtAssert(false, "The rendered geometry does not have an OpenGL representation. Something went terribly wrong.");
}
}
else {
tgtAssert(false, "The rendered geometry does exist. Something went wrong.");
}
// copy geometry over to local
_localDataContainer.addData(name + ".geometry", const_cast<GeometryData*>(gd));
// render
renderGeometryIntoTexture(name);
// store name
_geometryNames.push_back(std::make_pair(name, static_cast<int>(_textures.size()) - 1));
}
}
if (maxSlices == 1)
maxSlices = -1;
p_currentSlice.setMaxValue(maxSlices);
p_currentSlice.setMaxValue(maxSlices - 1);
_texturesDirty = false;
_geometriesDirty = false;
}
void DataContainerInspectorCanvas::onPropertyChanged(const AbstractProperty* prop) {
invalidate();
if (prop != &p_geometryRendererProperties)
invalidate();
}
void DataContainerInspectorCanvas::onGeometryRendererPropertyChanged(const AbstractProperty* prop) {
_texturesDirty = true;
_geometriesDirty = true;
invalidate();
}
void DataContainerInspectorCanvas::renderGeometryIntoTexture(const std::string& name, const GeometryData* geometry) {
_localDataContainer.addData(name + ".geometry", const_cast<GeometryData*>(geometry));
_trackballEH->reinitializeCamera(geometry);
void DataContainerInspectorCanvas::renderGeometryIntoTexture(const std::string& name, int textureIndex) {
// setup GeometryRenderer
_geometryRenderer.p_geometryID.setValue(name + ".geometry");
_geometryRenderer.p_renderTargetID.setValue(name + ".rendered");
_geometryRenderer.validate(AbstractProcessor::INVALID_PROPERTIES);
_geometryRenderer.process(_localDataContainer, false);
// grab render result texture from local DataContainer and push into texture vector.
ScopedTypedData<RenderData> rd(_localDataContainer, name + ".rendered");
if (rd != nullptr && rd->getNumColorTextures() > 0) {
auto rep = rd->getColorTexture(0)->getRepresentation<ImageRepresentationGL>();
if (rep != nullptr) {
const_cast<tgt::Texture*>(rep->getTexture())->downloadTexture();
if (textureIndex < 0 || textureIndex >= static_cast<int>(_textures.size())) {
_textures.push_back(rd->getColorDataHandle(0));
}
else {
_textures[textureIndex] = rd->getColorDataHandle(0);
}
}
else {
tgtAssert(false, "The rendered geometry does not have an OpenGL representation. Something went terribly wrong.");
}
}
else {
tgtAssert(false, "The rendered geometry does exist. Something went wrong.");
}
}
void DataContainerInspectorCanvas::resetTrackball() {
// delete old trackball
delete _trackballEH;
_trackballEH = nullptr;
// check whether we have to render geometries
tgt::Bounds unionBounds;
for (std::map<QString, QtDataHandle>::iterator it = _handles.begin(); it != _handles.end(); ++it) {
if (const GeometryData* gd = dynamic_cast<const GeometryData*>(it->second.getData())) {
unionBounds.addVolume(gd->getWorldBounds());
}
}
// if so, create a new trackball
if (unionBounds.isDefined()) {
_trackballEH = new TrackballNavigationEventListener(&_geometryRenderer.p_camera, &p_viewportSize);
_trackballEH->reinitializeCamera(unionBounds);
}
}
}
......@@ -96,8 +96,6 @@ namespace campvis {
*/
QSize sizeHint() const;
QSize minimumSizeHint() const { return QSize(320, 320); }
/**
* Schedule a repaint job for the inspector's render target
*/
......@@ -106,23 +104,13 @@ namespace campvis {
/// This is meant be overridden to adjust camera settings to new canvas dimensions
virtual void sizeChanged(const tgt::ivec2&);
/**
* Called on double click event on this canvas
* \param e Mouse event arguments
*/
virtual void mouseDoubleClickEvent(tgt::MouseEvent* e);
/**
* Called on mouse move event on this canvas
* \param e Mouse event arguments
*/
virtual void mouseMoveEvent(tgt::MouseEvent* e);
/**
* Called on mouse wheel event on this canvas.
* \param e Mouse event arguments
*/
virtual void wheelEvent(tgt::MouseEvent* e);
virtual void onEvent(tgt::Event* e);
IntProperty p_currentSlice;
TransferFunctionProperty p_transferFunction; ///< Transfer function
......@@ -172,6 +160,8 @@ namespace campvis {
*/
void updateTextures();
void resetTrackball();
/**
* To be called when the canvas is invalidated, issues new paint job.
*/
......@@ -192,7 +182,7 @@ namespace campvis {
* \param name
* \param geometry
*/
void renderGeometryIntoTexture(const std::string& name, const GeometryData* geometry);
void renderGeometryIntoTexture(const std::string& name, int textureIndex = -1);
/**
* Creates the quad used for rendering the textures.
......@@ -203,9 +193,13 @@ namespace campvis {
/// Vector of textures to render. Each DataHandle contains an ImageData that has an OpenGL representation.
/// This ensures thread safety.
std::vector<QtDataHandle> _textures;
std::vector<QtDataHandle> _textures;
/// List of the names of all rendered geometries. This simplifies to update their rendering.
std::vector< std::pair<std::string, int> > _geometryNames;
bool _texturesDirty; ///< Flag that shows that the textures need update or not.
bool _texturesDirty; ///< Flag that shows that the textures need update or not.
bool _geometriesDirty; ///< Flag that shows that the rendered geometries need update or not.
DataContainer* _dataContainer; ///< The DataContainer this widget is inspecting
tbb::mutex _localMutex; ///< Mutex protecting the local members
......@@ -215,8 +209,6 @@ namespace campvis {
tgt::ivec2 _numTiles; ///< number of tiles on texture overview
tgt::ivec2 _quadSize; ///< size in pixels for each tile in overview
size_t _selectedTexture; ///< index of selected texture by mouse
bool _renderFullscreen; ///< flag whether to render in full screen
int _currentSlice; ///< current slice if rendering a 3D image fullscreen, render MIP if negative
......
......@@ -200,11 +200,13 @@ namespace campvis {
_canvas = new DataContainerInspectorCanvas(_infoWidget);
_canvas->setMinimumSize(QSize(100, 100));
_infoWidgetLayout->addWidget(_canvas, 5, 0, 1, 2);
_infoWidgetLayout->setRowStretch(5, 2);
QScrollArea* _pipelinePropertiesScrollArea = new QScrollArea(_infoWidget);
_pipelinePropertiesScrollArea->setWidgetResizable(true);
_pipelinePropertiesScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
_pipelinePropertiesScrollArea->setFrameStyle(QScrollArea::NoFrame);
_pipelinePropertiesScrollArea->setMinimumHeight(224);
_pcWidget = new PropertyCollectionWidget(_pipelinePropertiesScrollArea);
_pcWidget->updatePropCollection(_canvas, _dataContainer);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment