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 7be16186 authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Introducing scribble technology to SliceExtractor and VolumeExplorer:

* Introducing SliceExtractor::onEvent() to handle mouse events and generate point scribbles from it, which are then published using the s_scribblePainted signal
* Implemented SliceExtractor being able to render geometry in the slice space (with correct clipping)
* Implemented creating and handling of scribbles in VolumeExplorer in cooperation with the SliceExtractor. Therefore, mouse events are passed to the SliceExtractor, the VolumeExplorer takes care of assembling the scribbles into proper geometry, which is finally rendered by the SliceExtractor. The scribbling can be optionally turned on using the VolumeExplorer::p_enableSclibbling property.
parent 3fbe6879
......@@ -23,6 +23,7 @@
// ================================================================================================
in vec3 ex_TexCoord;
in vec4 ex_Color;
out vec4 out_Color;
//#include "tools/background.frag"
......@@ -37,8 +38,9 @@ uniform TFParameters1D _transferFunctionParams;
uniform mat4 _texCoordsMatrix;
uniform bool _useTexturing;
uniform vec4 _color;
uniform bool _useTexturing = true;
uniform bool _useSolidColor = true;
uniform vec4 _color = vec4(1.0, 1.0, 1.0, 1.0);
void main() {
if (_useTexturing) {
......@@ -54,7 +56,10 @@ void main() {
out_Color = (abs(texel) - vec4(_transferFunctionParams._intensityDomain.x)) / (_transferFunctionParams._intensityDomain.y - _transferFunctionParams._intensityDomain.x);
}
}
else {
else if (_useSolidColor) {
out_Color = _color;
}
}
else {
out_Color = ex_Color;
}
}
......@@ -54,8 +54,6 @@ namespace campvis {
void VolumeExplorerDemo::init() {
AutoEvaluationPipeline::init();
_imageReader.s_validated.connect(this, &VolumeExplorerDemo::onProcessorValidated);
_ve.p_outputImage.setValue("combine");
_renderTargetID.setValue("combine");
......@@ -70,20 +68,11 @@ namespace campvis {
dvrTF->addGeometry(TFGeometry1D::createQuad(tgt::vec2(.41f, .51f), tgt::col4(170, 170, 128, 64), tgt::col4(192, 192, 128, 64)));
static_cast<TransferFunctionProperty*>(_ve.getNestedProperty("VolumeRendererProperties::RaycasterProps::TransferFunction"))->replaceTF(dvrTF);
static_cast<FloatProperty*>(_ve.getNestedProperty("VolumeRendererProperties::RaycasterProps::SamplingRate"))->setValue(4.f);
_canvasSize.s_changed.connect<VolumeExplorerDemo>(this, &VolumeExplorerDemo::onRenderTargetSizeChanged);
}
void VolumeExplorerDemo::deinit() {
_canvasSize.s_changed.disconnect(this);
AutoEvaluationPipeline::deinit();
}
void VolumeExplorerDemo::onRenderTargetSizeChanged(const AbstractProperty* prop) {
}
void VolumeExplorerDemo::onProcessorValidated(AbstractProcessor* processor) {
}
}
\ No newline at end of file
......@@ -52,20 +52,12 @@ namespace campvis {
/// \see AbstractPipeline::getName()
virtual const std::string getName() const { return getId(); };
/// \see AbstractPipeline::getId()
static const std::string getId() { return "VolumeExplorerDemo"; };
void onRenderTargetSizeChanged(const AbstractProperty* prop);
protected:
/**
* Slot getting called when one of the observed processors got validated.
* Updates the camera properties, when the input image has changed.
* \param processor The processor that emitted the signal
*/
virtual void onProcessorValidated(AbstractProcessor* processor);
//MhdImageReader _imageReader;
GenericImageReader _imageReader;
GenericImageReader _imageReader;
VolumeExplorer _ve;
};
......
This diff is collapsed.
......@@ -29,6 +29,7 @@
#include "tgt/buffer.h"
#include "tgt/vertexarrayobject.h"
#include "tgt/event/eventlistener.h"
#include "core/pipeline/abstractprocessordecorator.h"
#include "core/pipeline/visualizationprocessor.h"
......@@ -50,8 +51,9 @@ namespace campvis {
/**
* Extracts a slice from a 3D image and renders it into a rendertarget.
*/
class SliceExtractor : public VisualizationProcessor, public HasProcessorDecorators {
class SliceExtractor : public VisualizationProcessor, public tgt::EventListener {
public:
/// Slice Orientation to render
enum SliceOrientation {
XY_PLANE = 0,
XZ_PLANE = 1,
......@@ -81,9 +83,16 @@ namespace campvis {
/// \see AbstractProcessor::getAuthor()
virtual const std::string getAuthor() const { return "Christian Schulte zu Berge <christian.szb@in.tum.de>"; };
/// \see AbstractProcessor::getProcessorState()
virtual ProcessorState getProcessorState() const { return AbstractProcessor::EXPERIMENTAL; };
virtual ProcessorState getProcessorState() const { return AbstractProcessor::TESTING; };
/// \see tgt::EventListener::onEvent()
virtual void onEvent(tgt::Event* e);
/// Signal emitted when a scribble was painted, parameter gives the position in image coordinates.
sigslot::signal1<tgt::vec3> s_scribblePainted;
DataNameProperty p_sourceImageID; ///< image ID for input image
DataNameProperty p_geometryID; ///< ID for input geometry
DataNameProperty p_targetImageID; ///< image ID for output image
GenericOptionProperty<SliceOrientation> p_sliceOrientation; ///< orientation of the slice to extract
......@@ -101,10 +110,11 @@ namespace campvis {
/// \see AbstractProcessor::updateProperties
virtual void updateProperties(DataContainer& dataContainer);
void updateBorderGeometry();
tgt::Shader* _shader; ///< Shader for slice rendering
DataHandle _currentImage; ///< cached DataHandle to shown image (needed for scribbles)
bool _inScribbleMode; ///< Flag whether processor is in scribble mode (i.e. mouse is pressed)
static const std::string loggerCat_;
private:
......
......@@ -42,6 +42,7 @@ namespace campvis {
: VisualizationProcessor(viewportSizeProp)
, p_inputVolume("InputVolume", "Input Volume", "", DataNameProperty::READ, AbstractProcessor::INVALID_PROPERTIES)
, p_outputImage("OutputImage", "Output Image", "ve.output", DataNameProperty::WRITE)
, p_enableScribbling("EnableScribbling", "Enable Scribbling in Slice Views", false)
, p_seProperties("SliceExtractorProperties", "Slice Extractor Properties", AbstractProcessor::VALID)
, p_vrProperties("VolumeRendererProperties", "Volume Renderer Properties", AbstractProcessor::VALID)
, _raycaster(viewportSizeProp, raycaster)
......@@ -53,10 +54,12 @@ namespace campvis {
, _zSliceHandler(&_sliceExtractor.p_zSliceNumber)
, _windowingHandler(&_sliceExtractor.p_transferFunction)
, _trackballEH(0)
, _mousePressed(false)
, _mousePressedInRaycaster(false)
, _scribblePointer(nullptr)
{
addProperty(&p_inputVolume);
addProperty(&p_outputImage);
addProperty(&p_enableScribbling);
addDecorator(new ProcessorDecoratorBackground());
decoratePropertyCollection(this);
......@@ -124,6 +127,27 @@ namespace campvis {
void VolumeExplorer::updateResult(DataContainer& data) {
// launch sub-renderers if necessary
if (getInvalidationLevel() & SCRIBBLE_INVALID) {
std::vector<tgt::vec3> vertices;
std::vector<tgt::vec4> colors;
for (size_t i = 0; i < _yesScribbles.size(); ++i) {
vertices.push_back(tgt::vec3(_yesScribbles[i]));
colors.push_back(tgt::vec4(.2f, .8f, 0.f, 1.f));
}
for (size_t i = 0; i < _noScribbles.size(); ++i) {
vertices.push_back(tgt::vec3(_noScribbles[i]));
colors.push_back(tgt::vec4(.85f, .2f, 0.f, 1.f));
}
FaceGeometry* g = new FaceGeometry(vertices, std::vector<tgt::vec3>(), colors);
data.addData(p_outputImage.getValue() + ".scribbles", g);
validate(SCRIBBLE_INVALID);
// force update of slice renderer if necessary
if (! (getInvalidationLevel() & VR_INVALID))
invalidate(SLICES_INVALID);
}
if (getInvalidationLevel() & VR_INVALID) {
_raycaster.process(data);
}
......@@ -158,6 +182,17 @@ namespace campvis {
if (prop == &p_inputVolume) {
invalidate(VR_INVALID | SLICES_INVALID);
}
if (prop == &p_enableScribbling) {
if (p_enableScribbling.getValue() == true) {
_sliceExtractor.s_scribblePainted.connect(this, &VolumeExplorer::onSliceExtractorScribblePainted);
_sliceExtractor.p_geometryID.setValue(p_outputImage.getValue() + ".scribbles");
}
else {
_sliceExtractor.s_scribblePainted.disconnect(this);
_sliceExtractor.p_geometryID.setValue("");
}
}
VisualizationProcessor::onPropertyChanged(prop);
}
......@@ -268,8 +303,9 @@ namespace campvis {
if (typeid(*e) == typeid(tgt::MouseEvent)) {
tgt::MouseEvent* me = static_cast<tgt::MouseEvent*>(e);
if (!_mousePressed && me->x() <= p_sliceRenderSize.getValue().x) {
// cycle slices
// we're currently on the slice view (left-hand) side and not in the process of changing the camera trackball
if (!_mousePressedInRaycaster && me->x() <= p_sliceRenderSize.getValue().x) {
// Mouse wheel has changed -> cycle slices
if (me->action() == tgt::MouseEvent::WHEEL) {
if (me->y() <= p_sliceRenderSize.getValue().y)
_zSliceHandler.onEvent(e);
......@@ -279,6 +315,50 @@ namespace campvis {
_xSliceHandler.onEvent(e);
}
// CTRL pressed -> forward to SliceExtractor's scribbling
else if (p_enableScribbling.getValue() && (me->modifiers() & tgt::Event::CTRL || me->modifiers() & tgt::Event::ALT)) {
if (me->action() == tgt::MouseEvent::PRESSED) {
_scribblePointer = (me->modifiers() & tgt::Event::CTRL) ? &_yesScribbles : &_noScribbles;
if (! (me->modifiers() & tgt::Event::SHIFT))
_scribblePointer->clear();
}
else if (_scribblePointer != nullptr && me->action() == tgt::MouseEvent::RELEASED) {
_scribblePointer = nullptr;
}
// lock this processor, so that the slice orientation's setting does not change
AbstractProcessor::ScopedLock lock(this, false);
if (me->y() <= p_sliceRenderSize.getValue().y) {
_sliceExtractor.p_sliceOrientation.selectByOption(SliceExtractor::XY_PLANE);
tgt::MouseEvent adjustedMe(
me->x(), me->y(),
me->action(), me->modifiers(), me->button(),
p_sliceRenderSize.getValue()
);
_sliceExtractor.onEvent(&adjustedMe);
}
else if (me->y() <= 2*p_sliceRenderSize.getValue().y) {
_sliceExtractor.p_sliceOrientation.selectByOption(SliceExtractor::XZ_PLANE);
tgt::MouseEvent adjustedMe(
me->x(), me->y() - p_sliceRenderSize.getValue().x,
me->action(), me->modifiers(), me->button(),
p_sliceRenderSize.getValue()
);
_sliceExtractor.onEvent(&adjustedMe);
}
else {
_sliceExtractor.p_sliceOrientation.selectByOption(SliceExtractor::YZ_PLANE);
tgt::MouseEvent adjustedMe(
me->x(), me->y() - (2 * p_sliceRenderSize.getValue().x),
me->action(), me->modifiers(), me->button(),
p_sliceRenderSize.getValue()
);
_sliceExtractor.onEvent(&adjustedMe);
}
}
// adjust slice TF windowing
else {
_windowingHandler.onEvent(e);
......@@ -287,9 +367,9 @@ namespace campvis {
else {
// raycasting trackball navigation
if (me->action() == tgt::MouseEvent::PRESSED)
_mousePressed = true;
_mousePressedInRaycaster = true;
else if (me->action() == tgt::MouseEvent::RELEASED)
_mousePressed = false;
_mousePressedInRaycaster = false;
tgt::MouseEvent adjustedMe(
me->x() - p_sliceRenderSize.getValue().x,
......@@ -304,5 +384,11 @@ namespace campvis {
}
}
}
void VolumeExplorer::onSliceExtractorScribblePainted(tgt::vec3 voxel) {
if (_scribblePointer != nullptr) {
_scribblePointer->push_back(voxel);
invalidate(INVALID_RESULT | SCRIBBLE_INVALID);
}
}
}
......@@ -83,8 +83,10 @@ namespace campvis {
/// \see tgt::EventListener::onEvent()
virtual void onEvent(tgt::Event* e);
DataNameProperty p_inputVolume; ///< image ID for first input image
DataNameProperty p_outputImage; ///< image ID for output image
DataNameProperty p_inputVolume; ///< image ID for first input image
DataNameProperty p_outputImage; ///< image ID for output image
BoolProperty p_enableScribbling; ///< Enable Scribbling in Slice Views
MetaProperty p_seProperties; ///< MetaProperty for SliceExtractor properties
MetaProperty p_vrProperties; ///< MetaProperty for Raycaster properties
......@@ -94,8 +96,9 @@ namespace campvis {
/// Additional invalidation levels for this processor.
/// Not the most beautiful design though.
enum ProcessorInvalidationLevel {
VR_INVALID = 1 << 4,
SLICES_INVALID = 1 << 5,
VR_INVALID = FIRST_FREE_TO_USE_INVALIDATION_LEVEL,
SLICES_INVALID = FIRST_FREE_TO_USE_INVALIDATION_LEVEL << 1,
SCRIBBLE_INVALID = FIRST_FREE_TO_USE_INVALIDATION_LEVEL << 2,
};
/// \see AbstractProcessor::updateResult
......@@ -113,6 +116,12 @@ namespace campvis {
*/
virtual void onPropertyChanged(const AbstractProperty* prop);
/**
* Callback called from SliceExtractor when a scribble has been painted.
* \param voxel Voxel position of scribble
*/
void onSliceExtractorScribblePainted(tgt::vec3 voxel);
void composeFinalRendering(DataContainer& data);
/// \see AbstractProcessor::updateProperties
......@@ -133,7 +142,11 @@ namespace campvis {
MWheelToNumericPropertyEventListener _zSliceHandler;
TransFuncWindowingEventListener _windowingHandler;
TrackballNavigationEventListener* _trackballEH;
bool _mousePressed;
bool _mousePressedInRaycaster; ///< Flag whether mouse was pressed in raycaster
std::vector<tgt::vec3>* _scribblePointer; ///< Pointer encoding whether the mouse was pressed (!= nullptr) and whether we have yes-scribbles or no-scribbles.
std::vector<tgt::vec3> _yesScribbles; ///< All voxels of the current yes-scribbles
std::vector<tgt::vec3> _noScribbles; ///< All voxels of the current no-scribbles
static const std::string loggerCat_;
};
......
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