11.3.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

slicerenderprocessor.cpp 21.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// ================================================================================================
// 
// This file is part of the CAMPVis Software Framework.
// 
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
//      Christian Schulte zu Berge <christian.szb@in.tum.de>
//      Chair for Computer Aided Medical Procedures
//      Technische Universitaet Muenchen
//      Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
// 
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
// 
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 
// except in compliance with the License. You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software distributed under the 
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
// either express or implied. See the License for the specific language governing permissions 
// and limitations under the License.
// 
// ================================================================================================

#include "slicerenderprocessor.h"
26 27 28 29
#include "cgt/logmanager.h"
#include "cgt/shadermanager.h"
#include "cgt/textureunit.h"
#include "cgt/event/mouseevent.h"
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60

#include "core/datastructures/geometrydata.h"
#include "core/datastructures/imagedata.h"
#include "core/datastructures/imagerepresentationgl.h"
#include "core/pipeline/processordecoratorbackground.h"

#include "core/tools/quadrenderer.h"

namespace campvis {
    static const GenericOption<SliceRenderProcessor::SliceOrientation> sliceOrientationOptions[3] = {
        GenericOption<SliceRenderProcessor::SliceOrientation>("z", "XY Plane", SliceRenderProcessor::XY_PLANE),
        GenericOption<SliceRenderProcessor::SliceOrientation>("y", "XZ Plane", SliceRenderProcessor::XZ_PLANE),
        GenericOption<SliceRenderProcessor::SliceOrientation>("x", "YZ Plane", SliceRenderProcessor::YZ_PLANE)
    };

    static const GenericOption<GLenum> renderOptions[4] = {
        GenericOption<GLenum>("points", "GL_POINTS", GL_POINTS),
        GenericOption<GLenum>("lines", "GL_LINES", GL_LINES),
        GenericOption<GLenum>("linestrip", "GL_LINE_STRIP", GL_LINE_STRIP),
        GenericOption<GLenum>("polygon", "GL_POLYGON", GL_POLYGON)
    };

    const std::string SliceRenderProcessor::loggerCat_ = "CAMPVis.modules.vis.SliceRenderProcessor";

    SliceRenderProcessor::SliceRenderProcessor(IVec2Property* viewportSizeProp, const std::string& fragmentShaderFileName, const std::string& customGlslVersion /*= ""*/)
        : VisualizationProcessor(viewportSizeProp)
        , p_sourceImageID("sourceImageID", "Input Image", "", DataNameProperty::READ)
        , p_geometryID("GeometryId", "Optional Input Geometry", "", DataNameProperty::READ)
        , p_targetImageID("targetImageID", "Output Image", "", DataNameProperty::WRITE)
        , p_sliceOrientation("SliceOrientation", "Slice Orientation", sliceOrientationOptions, 3)
        , p_xSliceNumber("XSliceNumber", "X Slice Number", 0, 0, 0)
61
        , p_xSliceColor("XSliceColor", "X Slice Color", cgt::vec4(1.f, 0.f, 0.f, 1.f), cgt::vec4(0.f), cgt::vec4(1.f))
62
        , p_ySliceNumber("YSliceNumber", "Y Slice Number", 0, 0, 0)
63
        , p_ySliceColor("YSliceColor", "Y Slice Color", cgt::vec4(0.f, 1.f, 0.f, 1.f), cgt::vec4(0.f), cgt::vec4(1.f))
64
        , p_zSliceNumber("ZSliceNumber", "Z Slice Number", 0, 0, 0)
65
        , p_zSliceColor("ZSliceColor", "Z Slice Color", cgt::vec4(0.f, 0.f, 1.f, 1.f), cgt::vec4(0.f), cgt::vec4(1.f))
66 67 68
        , p_renderCrosshair("RenderCrosshair", "Render Crosshair", true)
        , p_fitToWindow("FitToWindow", "Fit to Window", true)
        , p_scalingFactor("ScalingFactor", "Scaling Factor", 1.f, 0.f, 10.f, .1f, 2)
69
        , p_offset("Offset", "Offset", cgt::ivec2(0), cgt::ivec2(0), cgt::ivec2(100))
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
        , p_geometryRenderMode("GeometryRenderMode", "Geometry Render Mode", renderOptions, 4)
        , p_geometryRenderSize("GeometryRenderSize", "Geometry Render Size", 4.f, 1.f, 10.f, 1.f, 1)
        , _shader(nullptr)
        , _currentImage(nullptr)
        , _inScribbleMode(false)
        , _fragmentShaderFilename(fragmentShaderFileName)
        , _customGlslVersion(customGlslVersion)
    {
        addProperty(p_sourceImageID, INVALID_RESULT | INVALID_PROPERTIES);
        addProperty(p_geometryID);
        addProperty(p_targetImageID);
        addProperty(p_sliceOrientation);
        addProperty(p_xSliceNumber);
        addProperty(p_xSliceColor);
        addProperty(p_ySliceNumber);
        addProperty(p_ySliceColor);
        addProperty(p_zSliceNumber);
        addProperty(p_zSliceColor);
        addProperty(p_renderCrosshair);
        addProperty(p_fitToWindow, INVALID_RESULT | INVALID_PROPERTIES);
        addProperty(p_scalingFactor);
        addProperty(p_offset);
        addProperty(p_geometryRenderMode);
        addProperty(p_geometryRenderSize);
    }

    SliceRenderProcessor::~SliceRenderProcessor() {

    }

    void SliceRenderProcessor::init() {
        VisualizationProcessor::init();
        _shader = ShdrMgr.loadWithCustomGlslVersion("core/glsl/passthrough.vert", "", _fragmentShaderFilename, getGlslHeader(), _customGlslVersion);
    }

    void SliceRenderProcessor::deinit() {
        VisualizationProcessor::deinit();
        ShdrMgr.dispose(_shader);
        _currentImage = DataHandle(nullptr);
    }

    void SliceRenderProcessor::updateResult(DataContainer& data) {
        ImageRepresentationGL::ScopedRepresentation img(data, p_sourceImageID.getValue());

        if (img != 0) {
            if (img->getDimensionality() == 3) {
                setupMatrices(img);
                renderImageImpl(data, img);
            }
            else {
                LERROR("Input image must have dimensionality of 3.");
            }
        }
        else {
124
            LDEBUG("No suitable input image found.");
125 126 127 128 129 130 131 132
        }
    }

    void SliceRenderProcessor::updateProperties(DataContainer& dc) {
        ScopedTypedData<ImageData> img(dc, p_sourceImageID.getValue());
        _currentImage = img.getDataHandle();

        if (img != 0) {
133
            cgt::ivec3 imgSize = img->getSize();
134 135 136 137 138 139 140 141 142 143
            if (p_xSliceNumber.getMaxValue() != imgSize.x - 1){
                p_xSliceNumber.setMaxValue(imgSize.x - 1);
            }
            if (p_ySliceNumber.getMaxValue() != imgSize.y - 1){
                p_ySliceNumber.setMaxValue(imgSize.y - 1);
            }
            if (p_zSliceNumber.getMaxValue() != imgSize.z - 1){
                p_zSliceNumber.setMaxValue(imgSize.z - 1);
            }

144 145
            p_offset.setMinValue(cgt::ivec2(-cgt::max(imgSize)));
            p_offset.setMaxValue(cgt::ivec2( cgt::max(imgSize)));
146 147 148 149 150 151 152 153 154 155 156 157
        }

        p_scalingFactor.setVisible(! p_fitToWindow.getValue());
        p_offset.setVisible(! p_fitToWindow.getValue());
    }

    void SliceRenderProcessor::updateShader() {
        std::string header = getGlslHeader();
        _shader->setHeaders(header);
        _shader->rebuild();
    }

158
    void SliceRenderProcessor::onEvent(cgt::Event* e) {
159 160 161 162 163 164 165 166 167
        // if there is nobody listening to the scribble signal, we can save the expensive computations...
        if (! s_scribblePainted.has_connections())
            return;

        // we need an image as reference
        if (_currentImage.getData() != nullptr) {
            if (const ImageData* id = static_cast<const ImageData*>(_currentImage.getData())) {

                // we only handle mouse events
168
                if (cgt::MouseEvent* me = dynamic_cast<cgt::MouseEvent*>(e)) {
169 170
                    // transform viewport coordinates to voxel coordinates
                    // this is the inverse computation performed by the shader during rendering
171
                    cgt::vec2 viewportSize = getEffectiveViewportSize();
172
                    float renderTargetRatio = viewportSize.x / viewportSize.y;
173 174
                    cgt::vec2 posNormalized = cgt::vec2(static_cast<float>(me->x()), static_cast<float>(me->y())) / cgt::vec2(_viewportSizeProperty->getValue());
                    cgt::vec3 imgSize(id->getSize());
175

176
                    cgt::vec2 imageSize(0.f);
177 178
                    switch (p_sliceOrientation.getOptionValue()) {
                        case XY_PLANE:
179
                            imageSize = cgt::vec2((static_cast<float>(imgSize.x) * id->getMappingInformation().getVoxelSize().x),
180 181 182
                                                  (static_cast<float>(imgSize.y) * id->getMappingInformation().getVoxelSize().y));
                            break;
                        case XZ_PLANE:
183
                            imageSize = cgt::vec2((static_cast<float>(imgSize.x) * id->getMappingInformation().getVoxelSize().x),
184 185 186
                                                  (static_cast<float>(imgSize.z) * id->getMappingInformation().getVoxelSize().z));
                            break;
                        case YZ_PLANE:
187
                            imageSize = cgt::vec2((static_cast<float>(imgSize.y) * id->getMappingInformation().getVoxelSize().y),
188 189 190 191 192 193 194
                                                  (static_cast<float>(imgSize.z) * id->getMappingInformation().getVoxelSize().z));
                            break;
                    }

                    if (p_fitToWindow.getValue()) {
                        float sliceRatio = imageSize.x / imageSize.y;
                        float ratioRatio = sliceRatio / renderTargetRatio;
195 196
                        posNormalized -= (ratioRatio > 1) ? cgt::vec2(0.f, (1.f - (1.f / ratioRatio)) / 2.f) : cgt::vec2((1.f - ratioRatio) / 2.f, 0.f);
                        posNormalized *= (ratioRatio > 1) ? cgt::vec2(1.f, ratioRatio) : cgt::vec2(1.f / ratioRatio, 1.f);
197 198 199 200
                    }
                    else {
                        posNormalized -= .5f;
                        posNormalized *= viewportSize / (imageSize * p_scalingFactor.getValue());
201
                        posNormalized -= cgt::vec2(p_offset.getValue()) / imageSize;
202 203 204
                        posNormalized += .5f;
                    }

205
                    cgt::vec3 voxel;
206 207
                    switch (p_sliceOrientation.getOptionValue()) {
                        case XY_PLANE:
208
                            voxel = cgt::vec3(posNormalized.x * imgSize.x, posNormalized.y * imgSize.y, static_cast<float>(p_zSliceNumber.getValue()));
209 210
                            break;
                        case XZ_PLANE:
211
                            voxel = cgt::vec3(posNormalized.x * imgSize.x, static_cast<float>(p_ySliceNumber.getValue()), posNormalized.y * imgSize.z);
212 213
                            break;
                        case YZ_PLANE:
214
                            voxel = cgt::vec3(static_cast<float>(p_xSliceNumber.getValue()), posNormalized.x * imgSize.y, posNormalized.y * imgSize.z);
215 216 217 218
                            break;
                    }

                    // okay, we computed the voxel under the mouse arrow, now we need to tell the outer world
219
                    if ((me->action() == cgt::MouseEvent::PRESSED) && (me->button() == cgt::MouseEvent::MOUSE_BUTTON_LEFT)) {
220
                        _inScribbleMode = true;
221
                        if (cgt::hand(cgt::greaterThanEqual(voxel, cgt::vec3(0.f))) && cgt::hand(cgt::lessThan(voxel, imgSize))) {
222 223 224
                            s_scribblePainted(voxel);
                        }
                    }
225 226
                    else if (_inScribbleMode && me->action() == cgt::MouseEvent::MOTION) {
                        if (cgt::hand(cgt::greaterThanEqual(voxel, cgt::vec3(0.f))) && cgt::hand(cgt::lessThan(voxel, imgSize))) {
227 228 229
                            s_scribblePainted(voxel);
                        }
                    }
230
                    else if (_inScribbleMode && me->action() == cgt::MouseEvent::RELEASED) {
231 232 233 234 235 236 237 238 239 240
                        _inScribbleMode = false;
                        return;
                    }
                }
            }
        }

    }

    void SliceRenderProcessor::setupMatrices(const ImageRepresentationGL::ScopedRepresentation& img) {
241
        cgt::vec3 imgSize(img->getSize());
242 243

        // current slices in texture coordinates
244
        cgt::vec3 sliceTexCoord = cgt::vec3(.5f + p_xSliceNumber.getValue(), .5f + p_ySliceNumber.getValue(), .5f + p_zSliceNumber.getValue()) / imgSize;
245 246
        float clip = 0.f;

247 248
        cgt::ivec2 viewportSize = getEffectiveViewportSize();
        cgt::vec2 imageSize(0.f);
249 250
        float renderTargetRatio = static_cast<float>(viewportSize.x) / static_cast<float>(viewportSize.y);

251 252
        _texCoordMatrix = cgt::mat4::zero;
        _geometryModelMatrix = cgt::mat4::zero;
253 254 255 256 257 258 259 260 261
        switch (p_sliceOrientation.getValue()) {
            case XY_PLANE:
                // keep texture coordinates for x,y, shift z coordinates to slice value
                _texCoordMatrix.t00 = 1.f;
                _texCoordMatrix.t11 = 1.f;
                _texCoordMatrix.t22 = 1.f;
                _texCoordMatrix.t33 = 1.f;
                _texCoordMatrix.t23 = sliceTexCoord.z;

262
                _geometryModelMatrix = cgt::mat4::identity;
263

264 265
                // compute clip volume so that we only show the geometry at the current slice.
                clip = (-2.f * static_cast<float>(p_zSliceNumber.getValue()) / imgSize.z) + 1.f;
266
                _geometryProjectionMatrix = cgt::mat4::createOrtho(-1.f, 1.f, 1.f, -1.f, clip - (.5f / imgSize.z), clip + (.5f / imgSize.z));
267

268
                imageSize = cgt::vec2((static_cast<float>(imgSize.x) * img.getImageData()->getMappingInformation().getVoxelSize().x),
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
                    (static_cast<float>(imgSize.y) * img.getImageData()->getMappingInformation().getVoxelSize().y));
                break;

            case XZ_PLANE:
                // permute y and z coordinates, shift y to slice 
                _texCoordMatrix.t00 = 1.f;
                _texCoordMatrix.t12 = 1.f;
                _texCoordMatrix.t21 = 1.f;
                _texCoordMatrix.t33 = 1.f;
                _texCoordMatrix.t13 = sliceTexCoord.y;

                _geometryModelMatrix.t00 = 1.f;
                _geometryModelMatrix.t12 = 1.f;
                _geometryModelMatrix.t21 = 1.f;
                _geometryModelMatrix.t33 = 1.f;

                // compute clip volume so that we only show the geometry at the current slice.
                clip = (-2.f * static_cast<float>(p_ySliceNumber.getValue()) / imgSize.y) + 1.f;
287
                _geometryProjectionMatrix = cgt::mat4::createOrtho(-1.f, 1.f, 1.f, -1.f, clip - (.5f / imgSize.y), clip + (.5f / imgSize.y));
288

289
                imageSize = cgt::vec2((static_cast<float>(imgSize.x) * img.getImageData()->getMappingInformation().getVoxelSize().x), 
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
                    (static_cast<float>(imgSize.z) * img.getImageData()->getMappingInformation().getVoxelSize().z));
                break;

            case YZ_PLANE:
                // permute x,y and z coordinates, shift x to slice
                _texCoordMatrix.t02 = 1.f; 
                _texCoordMatrix.t10 = 1.f;
                _texCoordMatrix.t21 = 1.f;
                _texCoordMatrix.t33 = 1.f;
                _texCoordMatrix.t03 = sliceTexCoord.x;

                _geometryModelMatrix.t01 = 1.f; 
                _geometryModelMatrix.t12 = 1.f;
                _geometryModelMatrix.t20 = 1.f;
                _geometryModelMatrix.t33 = 1.f;

                // compute clip volume so that we only show the geometry at the current slice.
                clip = (-2.f * static_cast<float>(p_xSliceNumber.getValue()) / imgSize.x) + 1.f;
308
                _geometryProjectionMatrix = cgt::mat4::createOrtho(-1.f, 1.f, 1.f, -1.f, clip - (.5f / imgSize.x), clip + (.5f / imgSize.x));
309

310
                imageSize = cgt::vec2((static_cast<float>(imgSize.y) * img.getImageData()->getMappingInformation().getVoxelSize().y),
311 312 313 314 315 316 317 318 319
                    (static_cast<float>(imgSize.z) * img.getImageData()->getMappingInformation().getVoxelSize().z));
                break;
        }

        // configure model matrix so that slices are rendered with correct aspect posNormalized
        float sliceRatio = imageSize.x / imageSize.y;
        float ratioRatio = sliceRatio / renderTargetRatio;

        if (p_fitToWindow.getValue()) {
320
            _viewMatrix = (ratioRatio > 1) ? cgt::mat4::createScale(cgt::vec3(1.f, 1.f / ratioRatio, 1.f)) : cgt::mat4::createScale(cgt::vec3(ratioRatio, 1.f, 1.f));
321 322
        }
        else {
323 324
            _viewMatrix = cgt::mat4::createTranslation(cgt::vec3(2.f * p_offset.getValue().x * p_scalingFactor.getValue() / viewportSize.x, -2.f * p_offset.getValue().y * p_scalingFactor.getValue() / viewportSize.y, 0.f));
            _viewMatrix *= cgt::mat4::createScale(cgt::vec3(p_scalingFactor.getValue() * imageSize.x / viewportSize.x, p_scalingFactor.getValue() * imageSize.y / viewportSize.y, 1.f));
325 326 327 328 329 330 331 332 333 334 335 336
        }
        _viewMatrix.t11 *= -1;
    }

    void SliceRenderProcessor::renderCrosshair(const ImageRepresentationGL::ScopedRepresentation& img) {

        // render slice markers
        // for each slice render a bounding box (GL_LINE_LOOP) in slice color and horizontal/vertical
        // lines (GL_LINE_STRIP) as reference for the other axis-aligned slices
        glLineWidth(2.f);
        _shader->setUniform("_useTexturing", false);

337 338
        cgt::mat4 modelMatrix = cgt::mat4::identity;
        cgt::vec3 sliceTexCoord = cgt::vec3(.5f + p_xSliceNumber.getValue(), .5f + p_ySliceNumber.getValue(), .5f + p_zSliceNumber.getValue()) / cgt::vec3(img->getSize());
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401

        switch (p_sliceOrientation.getValue()) {
            case XY_PLANE:
                _shader->setUniform("_color", p_zSliceColor.getValue());
                QuadRdr.renderQuad(GL_LINE_LOOP);

                modelMatrix.t00 = 0.f;
                modelMatrix.t03 = 2.f * sliceTexCoord.x - 1.f;
                _shader->setUniform("_modelMatrix", modelMatrix);
                _shader->setUniform("_color", p_xSliceColor.getValue());
                QuadRdr.renderQuad(GL_LINE_STRIP);

                modelMatrix.t00 = 1.f;
                modelMatrix.t11 = 0.f;
                modelMatrix.t03 = 0.f;
                modelMatrix.t13 = 2.f * sliceTexCoord.y - 1.f;
                _shader->setUniform("_modelMatrix", modelMatrix);
                _shader->setUniform("_color", p_ySliceColor.getValue());
                QuadRdr.renderQuad(GL_LINE_STRIP);
                break;

            case XZ_PLANE:
                _shader->setUniform("_color", p_ySliceColor.getValue());
                QuadRdr.renderQuad(GL_LINE_LOOP);

                modelMatrix.t00 = 0.f;
                modelMatrix.t03 = 2.f * sliceTexCoord.x - 1.f;
                _shader->setUniform("_modelMatrix", modelMatrix);
                _shader->setUniform("_color", p_xSliceColor.getValue());
                QuadRdr.renderQuad(GL_LINE_STRIP);

                modelMatrix.t00 = 1.f;
                modelMatrix.t11 = 0.f;
                modelMatrix.t03 = 0.f;
                modelMatrix.t13 = 2.f * sliceTexCoord.z - 1.f;
                _shader->setUniform("_modelMatrix", modelMatrix);
                _shader->setUniform("_color", p_zSliceColor.getValue());
                QuadRdr.renderQuad(GL_LINE_STRIP);
                break;

            case YZ_PLANE:
                _shader->setUniform("_color", p_xSliceColor.getValue());
                QuadRdr.renderQuad(GL_LINE_LOOP);

                modelMatrix.t00 = 0.f;
                modelMatrix.t03 = 2.f * sliceTexCoord.y - 1.f;
                _shader->setUniform("_modelMatrix", modelMatrix);
                _shader->setUniform("_color", p_ySliceColor.getValue());
                QuadRdr.renderQuad(GL_LINE_STRIP);

                modelMatrix.t00 = 1.f; //(ratioRatio > 1) ? 1.f : ratioRatio;
                modelMatrix.t11 = 0.f;
                modelMatrix.t03 = 0.f;
                modelMatrix.t13 = 2.f * sliceTexCoord.z - 1.f;
                _shader->setUniform("_modelMatrix", modelMatrix);
                _shader->setUniform("_color", p_zSliceColor.getValue());
                QuadRdr.renderQuad(GL_LINE_STRIP);
                break;
        }

    }

    void SliceRenderProcessor::renderGeometry(DataContainer& dataContainer, const ImageRepresentationGL::ScopedRepresentation& img) {
402
        ScopedTypedData<GeometryData> geometry(dataContainer, p_geometryID.getValue(), true);
403 404 405 406 407 408

        // render optional geometry
        if (geometry) {
            // setup for geometry rendering
            _shader->setUniform("_projectionMatrix", _geometryProjectionMatrix);
            _shader->setUniform("_viewMatrix", _viewMatrix);
409
            _shader->setUniform("_modelMatrix", _geometryModelMatrix * cgt::mat4::createTranslation(cgt::vec3(-1.f, -1.f, -1.f)) * cgt::mat4::createScale(2.f / cgt::vec3(img->getSize())));
410 411 412 413 414 415 416 417 418 419
            _shader->setUniform("_useTexturing", false);
            _shader->setUniform("_useSolidColor", false);

            glPointSize(p_geometryRenderSize.getValue());
            glLineWidth(p_geometryRenderSize.getValue());

            // render
            geometry->render(p_geometryRenderMode.getValue());

            // recover
420 421
            _shader->setUniform("_projectionMatrix", cgt::mat4::identity);
            _shader->setUniform("_modelMatrix", cgt::mat4::identity);
422 423 424 425 426 427 428 429 430 431 432
            _shader->setUniform("_useSolidColor", true);
            glPointSize(1.f);
            glLineWidth(1.f);
        }
    }

    std::string SliceRenderProcessor::getGlslHeader() {
        return "";
    }

}