Starting from 2021-07-01, all LRZ GitLab users will be required to explicitly accept the GitLab Terms of Service. Please see the detailed information at https://doku.lrz.de/display/PUBLIC/GitLab and make sure that your projects conform to the requirements.

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