datacontainerinspectorcanvas.cpp 21.9 KB
Newer Older
1 2
// ================================================================================================
// 
schultezub's avatar
schultezub committed
3
// This file is part of the CAMPVis Software Framework.
4
// 
5
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
schultezub's avatar
schultezub committed
6
//      Christian Schulte zu Berge <christian.szb@in.tum.de>
7
//      Chair for Computer Aided Medical Procedures
8 9
//      Technische Universitaet Muenchen
//      Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
10
// 
schultezub's avatar
schultezub committed
11
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
12
// 
13 14 15 16
// 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
17
// 
18 19 20 21
// 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.
22 23 24 25 26
// 
// ================================================================================================

#include "datacontainerinspectorcanvas.h"

27 28 29
#include "cgt/assert.h"
#include "cgt/shadermanager.h"
#include "cgt/textureunit.h"
30

31 32
#include "core/datastructures/datacontainer.h"
#include "core/datastructures/datahandle.h"
33
#include "core/datastructures/renderdata.h"
34
#include "core/datastructures/imagerepresentationgl.h"
35
#include "core/datastructures/imagerepresentationlocal.h"
36
#include "core/datastructures/facegeometry.h"
37
#include "core/datastructures/geometrydatafactory.h"
38 39
#include "core/classification/tfgeometry1d.h"
#include "core/classification/geometry1dtransferfunction.h"
40

41 42
#include "datacontainerinspectorwidget.h"

43
#include "ext/cgt/navigation/trackball.h"
44

45

schultezub's avatar
schultezub committed
46
namespace campvis {
47 48

    DataContainerInspectorCanvas::DataContainerInspectorCanvas(QWidget* parent /*= 0*/) 
49
        : cgt::QtThreadedCanvas("DataContainer Inspector", cgt::ivec2(640, 480), cgt::GLCanvas::RGBA_BUFFER, parent, true)
50
        , p_currentSlice("CurrentSlice", "Slice", -1, -1, -1)
51
        , p_transferFunction("TransferFunction", "Transfer Function", new Geometry1DTransferFunction(256, cgt::vec2(0.f, 1.f)))
52 53 54 55
        , p_renderRChannel("RenderRChannel", "Render Red Channel", true)
        , p_renderGChannel("RenderGChannel", "Render Green Channel", true)
        , p_renderBChannel("RenderBChannel", "Render Blue Channel", true)
        , p_renderAChannel("RenderAChannel", "Render Alpha Channel", true)
56 57 58 59 60
        , p_geometryRendererProperties("GeometryRendererProperties", "GeometryRenderer Properties")
        , _texturesDirty(true)
        , _dataContainer(nullptr)
        , _paintShader(nullptr)
        , _quad(nullptr)
61 62
        , _numTiles(0, 0)
        , _quadSize(0, 0)
63
        , _localDataContainer("Local DataContainer for DataContainerInspectorCanvas")
64
        , p_viewportSize("ViewportSize", "Viewport Size", cgt::ivec2(200), cgt::ivec2(0, 0), cgt::ivec2(10000))
65
        , _tcp(&p_viewportSize)
66
        , _geometryRenderer(&p_viewportSize)
67
    {
68
        static_cast<Geometry1DTransferFunction*>(p_transferFunction.getTF())->addGeometry(TFGeometry1D::createQuad(cgt::vec2(0.f, 1.f), cgt::col4(0, 0, 0, 255), cgt::col4(255, 255, 255, 255)));
69

70 71
        GLCtxtMgr.registerContextAndInitGlew(this, "DataContainerInspector");
        GLCtxtMgr.releaseContext(this, false);
72
        
73 74 75 76 77 78
        addProperty(p_currentSlice);
        addProperty(p_transferFunction);
        addProperty(p_renderRChannel);
        addProperty(p_renderGChannel);
        addProperty(p_renderBChannel);
        addProperty(p_renderAChannel);
79 80 81 82 83 84

        p_geometryRendererProperties.addPropertyCollection(_geometryRenderer);
        _geometryRenderer.p_geometryID.setVisible(false);
        _geometryRenderer.p_textureID.setVisible(false);
        _geometryRenderer.p_renderTargetID.setVisible(false);
        _geometryRenderer.p_lightId.setVisible(false);
85
        _geometryRenderer.p_camera.setVisible(false);
86 87 88 89 90 91 92 93 94 95
        _geometryRenderer.p_coloringMode.setVisible(false);
        _geometryRenderer.p_pointSize.setVisible(false);
        _geometryRenderer.p_lineWidth.setVisible(false);
        _geometryRenderer.p_showWireframe.setVisible(false);
        _geometryRenderer.p_wireframeColor.setVisible(false);
        _geometryRenderer.p_renderMode.selectByOption(GL_POLYGON);
        _geometryRenderer.p_enableShading.s_changed.connect(this, &DataContainerInspectorCanvas::onGeometryRendererPropertyChanged);
        _geometryRenderer.p_renderMode.s_changed.connect(this, &DataContainerInspectorCanvas::onGeometryRendererPropertyChanged);
        _geometryRenderer.p_solidColor.s_changed.connect(this, &DataContainerInspectorCanvas::onGeometryRendererPropertyChanged);
        addProperty(p_geometryRendererProperties);
96 97 98 99 100 101 102 103

        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);
104 105 106 107 108 109
    }

    DataContainerInspectorCanvas::~DataContainerInspectorCanvas() {

    }

110
    void DataContainerInspectorCanvas::init() {
schultezub's avatar
schultezub committed
111 112
        initAllProperties();

113
        _paintShader = ShdrMgr.load("core/glsl/passthrough.vert", "application/glsl/datacontainerinspector.frag", "");
114 115
        createQuad();

116
        // set this as painter to get notified when window size changes
117
        setPainter(this, false);
118
        getEventHandler()->addEventListenerToFront(this);
119

120
        _geometryRenderer.init();
121 122 123
    }

    void DataContainerInspectorCanvas::deinit() {
schultezub's avatar
schultezub committed
124 125
        deinitAllProperties();

126 127 128 129
        if (_dataContainer != 0) {
            _dataContainer->s_dataAdded.disconnect(this);
        }

130
        _geometryRenderer.deinit();
mostajab's avatar
mostajab committed
131

132 133
        _handles.clear();
        _localDataContainer.clear();
mostajab's avatar
mostajab committed
134
        _textures.clear();
135
        ShdrMgr.dispose(_paintShader);
136
        _quad = nullptr;
137 138

        GLCtxtMgr.removeContext(this);
139 140 141 142 143 144 145 146
    }

    QSize DataContainerInspectorCanvas::sizeHint() const {
        return QSize(640, 480);
    }

    void DataContainerInspectorCanvas::paint() {
        tbb::mutex::scoped_lock lock(_localMutex);
147
        if (_texturesDirty) {
148
            updateTextures();
149 150 151 152 153 154 155 156
        }
        if (_geometriesDirty) {
            // update geometry renderings if necessary
            for (auto it = _geometryNames.begin(); it != _geometryNames.end(); ++it) {
                renderGeometryIntoTexture(it->first, it->second);
            }
            _geometriesDirty = false;
        }
157

158 159 160 161
        if (_textures.empty()) {
            return;
        }

162
        glPushAttrib(GL_ALL_ATTRIB_BITS);
163
        glViewport(0, 0, size_.x, size_.y);
164 165 166 167 168
        glClearColor(0.7f, 0.7f, 0.7f, 1.f);
        glClear(GL_COLOR_BUFFER_BIT);
        LGL_ERROR;

        // update layout dimensions
169 170
        _numTiles.x = ceil(sqrt(static_cast<float>(_textures.size())));
        _numTiles.y = ceil(static_cast<float>(_textures.size()) / _numTiles.x);
171
        _quadSize = size_ / _numTiles;
172 173 174

        _paintShader->activate();

175
        cgt::mat4 projection = cgt::mat4::createOrtho(0, size_.x, 0, size_.y, -1, 1);
176 177
        _paintShader->setUniform("_projectionMatrix", projection);

178
        cgt::TextureUnit tfUnit, unit2d, unit3d;
179
        p_transferFunction.getTF()->bind(_paintShader, tfUnit);
180 181
        _paintShader->setUniform("_texture2d", unit2d.getUnitNumber());
        _paintShader->setUniform("_texture3d", unit3d.getUnitNumber());
182

183 184 185 186 187
        _paintShader->setUniform("_renderRChannel", p_renderRChannel.getValue());
        _paintShader->setUniform("_renderGChannel", p_renderGChannel.getValue());
        _paintShader->setUniform("_renderBChannel", p_renderBChannel.getValue());
        _paintShader->setUniform("_renderAChannel", p_renderAChannel.getValue());

188 189 190 191 192 193
        for (int y = 0; y < _numTiles.y; ++y) {
            for (int x = 0; x < _numTiles.x; ++x) {
                int index = (_numTiles.x * y) + x;
                if (index >= static_cast<int>(_textures.size()))
                    break;

194
                // gather image
195
                cgtAssert(dynamic_cast<const ImageData*>(_textures[index].getData()), "Found sth. else than ImageData in render texture vector. This should not happen!");
196 197 198 199
                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);
200 201
                float sliceRatio = (static_cast<float>(id->getSize().x) * std::abs(id->getMappingInformation().getVoxelSize().x))
                                 / (static_cast<float>(id->getSize().y) * std::abs(id->getMappingInformation().getVoxelSize().y));
202
                float ratioRatio = sliceRatio / renderTargetRatio;
203
                cgt::mat4 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));
204

205 206
                cgt::mat4 scaleMatrix = cgt::mat4::createScale(cgt::vec3(_quadSize, 1.f));
                cgt::mat4 translation = cgt::mat4::createTranslation(cgt::vec3(_quadSize.x * x, _quadSize.y * y, 0.f));
207

208 209 210
                _paintShader->setUniform("_modelMatrix", translation * scaleMatrix * viewMatrix);

                // render texture
211
                paintTexture(id->getRepresentation<ImageRepresentationGL>()->getTexture(), unit2d, unit3d);
212 213 214 215 216 217 218 219
            }
        }

        _paintShader->deactivate();
        LGL_ERROR;
        glPopAttrib();
    }

220 221
    void DataContainerInspectorCanvas::paintTexture(const cgt::Texture* texture, const cgt::TextureUnit& unit2d, const cgt::TextureUnit& unit3d) {
        cgtAssert(texture != nullptr, "Texture to paint is 0. This should not happen!");
222 223
        if (texture == nullptr)
            return;
224 225

        _paintShader->setIgnoreUniformLocationError(true);
226
        if (texture->getType() == GL_TEXTURE_2D) {
227 228 229
            unit2d.activate();
            texture->bind();
            _paintShader->setUniform("_is3d", false);
230
            _paintShader->setUniform("_isDepthTexture", texture->isDepthTexture());
231 232
            _paintShader->setUniform("_2dTextureParams._size", cgt::vec2(texture->getDimensions().xy()));
            _paintShader->setUniform("_2dTextureParams._sizeRCP", cgt::vec2(1.f) / cgt::vec2(texture->getDimensions().xy()));
233
            _paintShader->setUniform("_2dTextureParams._numChannels", static_cast<int>(texture->getNumChannels()));
234
        }
235
        else if (texture->getType() == GL_TEXTURE_3D) {
236 237 238
            unit3d.activate();
            texture->bind();
            _paintShader->setUniform("_is3d", true);
239
            _paintShader->setUniform("_sliceNumber", p_currentSlice.getValue());
240 241
            _paintShader->setUniform("_3dTextureParams._size", cgt::vec3(texture->getDimensions()));
            _paintShader->setUniform("_3dTextureParams._sizeRCP", cgt::vec3(1.f) / cgt::vec3(texture->getDimensions()));
242
            _paintShader->setUniform("_3dTextureParams._numChannels", static_cast<int>(texture->getNumChannels()));
243
        }
244 245
        _paintShader->setIgnoreUniformLocationError(false);

246
        _quad->render(GL_TRIANGLE_FAN);
247 248 249
        LGL_ERROR;
    }

250
    void DataContainerInspectorCanvas::invalidate() {
Christian Schulte zu Berge's avatar
Christian Schulte zu Berge committed
251
        // only if inited
252
        if (_quad != 0 && _paintShader != 0) {
253 254 255 256 257 258
            // avoid recursive paints.
            if (! cgt::GlContextManager::getRef().checkWhetherThisThreadHasAcquiredOpenGlContext()) {
                // TODO: check, whether this should be done in an extra thread
                cgt::GLContextScopedLock lock(this);
                paint();
            }
259
        }
260 261
    }

262
    void DataContainerInspectorCanvas::createQuad() {
263
        _quad = GeometryDataFactory::createQuad(cgt::vec3(0.f), cgt::vec3(1.f), cgt::vec3(0.f, 1.f, 0.f), cgt::vec3(1.f, 0.f, 0.f));
264 265
    }

266 267 268 269
    void DataContainerInspectorCanvas::repaint() {
        invalidate();
    }

270
    void DataContainerInspectorCanvas::sizeChanged(const cgt::ivec2& size) {
271 272 273
        invalidate();
    }

274
    void DataContainerInspectorCanvas::mouseMoveEvent(cgt::MouseEvent* e)
275
    {
276
        if (e->modifiers() & cgt::Event::CTRL) {
277
            int texIndx = (e->y() / _quadSize.y) * _numTiles.x + (e->x() / _quadSize.x);
278
            if (texIndx < 0 || texIndx >= static_cast<int>(_textures.size()))
279 280
                return;

281
            const ImageData* id = static_cast<const ImageData*>(_textures[texIndx].getData());
282
            const cgt::Texture* tex = id->getRepresentation<ImageRepresentationGL>()->getTexture();
283
            const ImageRepresentationLocal* localRep = id->getRepresentation<ImageRepresentationLocal>();
284
            cgt::svec2 imageSize = id->getSize().xy();
285

286
            cgt::vec2 lookupTexelFloat = cgt::vec2((e->coord() % _quadSize) * cgt::ivec2(imageSize)) / cgt::vec2(_quadSize);
287 288 289

            // compute transformation matrices
            float renderTargetRatio = static_cast<float>(_quadSize.x) / static_cast<float>(_quadSize.y);
290 291
            float sliceRatio = (static_cast<float>(id->getSize().x) * std::abs(id->getMappingInformation().getVoxelSize().x))
                / (static_cast<float>(id->getSize().y) * std::abs(id->getMappingInformation().getVoxelSize().y));
292 293
            float ratioRatio = sliceRatio / renderTargetRatio;

294
            lookupTexelFloat /= (ratioRatio > 1) ? cgt::vec2(1.f, 1.f / ratioRatio) : cgt::vec2(ratioRatio, 1.f);
295
            
296
            cgt::svec3 lookupTexel(lookupTexelFloat.x, imageSize.y - lookupTexelFloat.y, 0);
297
            if (lookupTexel.x >= 0 && lookupTexel.y >= 0 && lookupTexel.x < imageSize.x && lookupTexel.y < imageSize.y) {
298 299
                if (tex->isDepthTexture()) {
                    emit s_depthChanged(localRep->getElementNormalized(lookupTexel, 0));
300
                }
301 302 303
                else {
                    if (tex->getDimensions().z != 1) {
                        if (p_currentSlice.getValue() >= 0 && p_currentSlice.getValue() < tex->getDimensions().z) {
304 305 306 307 308 309
                            lookupTexel.z = static_cast<size_t>(p_currentSlice.getValue());
                            cgt::vec4 texel(0.f);
                            for (size_t i = 0; i < id->getNumChannels(); ++i) {
                                texel[i] = localRep->getElementNormalized(lookupTexel, i);
                            }
                            emit s_colorChanged(texel);
310 311 312
                        }
                    }
                    else if (tex->getDimensions().y != 1) {
313 314 315 316 317
                        cgt::vec4 texel(0.f);
                        for (size_t i = 0; i < id->getNumChannels(); ++i) {
                            texel[i] = localRep->getElementNormalized(lookupTexel, i);
                        }
                        emit s_colorChanged(texel);
318
                    }
319
                }
320 321 322 323
            }
        }
        else {
            e->ignore();
324
        }
325 326
    }

327 328
    void DataContainerInspectorCanvas::onEvent(cgt::Event* e) {
        cgt::EventListener::onEvent(e);
329
        
330 331 332
        if (!e->isAccepted()) {
            _tcp.onEvent(e);
            _tcp.process(_localDataContainer);
333 334
            e->accept();
            _geometriesDirty = true;
335 336 337 338
            invalidate();
        }
    }

339 340 341 342 343 344 345 346 347 348 349 350 351
    void DataContainerInspectorCanvas::onDataContainerChanged(const QString& key, QtDataHandle dh) {
        {
            tbb::mutex::scoped_lock lock(_localMutex);

            // check whether DataHandle is already existing
            std::map<QString, QtDataHandle>::iterator lb = _handles.lower_bound(key);
            if (lb == _handles.end() || lb->first != key) {
                // not existant -> do nothing
            }
            else {
                // existant -> replace
                lb->second = QtDataHandle(dh);
                // update _textures array
352
                _texturesDirty = true;
353 354
            }
        }
355 356 357

        if (_texturesDirty)
            invalidate();
358 359 360 361 362 363 364 365 366
    }

    void DataContainerInspectorCanvas::setDataHandles(const std::vector< std::pair<QString, QtDataHandle> >& handles) {
        {
            tbb::mutex::scoped_lock lock(_localMutex);
            _handles.clear();
            for (std::vector< std::pair<QString, QtDataHandle> >::const_iterator it = handles.begin(); it != handles.end(); ++it)
                _handles.insert(*it);

367 368 369 370 371 372 373 374 375 376 377 378 379 380
            _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();

381
            _texturesDirty = true;
382 383 384 385 386 387
        }

        invalidate();
    }

    void DataContainerInspectorCanvas::updateTextures() {
388
        /// Calculate the maximum slices of the textures and fill the textures array
389
        int maxSlices = 1;
390
        _textures.clear();
391
        p_viewportSize.setValue(cgt::ivec2(width(), height()));
392

393 394
        for (std::map<QString, QtDataHandle>::iterator it = _handles.begin(); it != _handles.end(); ++it) {
            if (const ImageData* img = dynamic_cast<const ImageData*>(it->second.getData())) {
395
                if (const ImageRepresentationGL* imgGL = img->getRepresentation<ImageRepresentationGL>()) {
396
                    _textures.push_back(it->second);
397
                    maxSlices = std::max(maxSlices, imgGL->getTexture()->getDimensions().z);
398 399
                }
            }
400 401 402
            else if (const RenderData* rd = dynamic_cast<const RenderData*>(it->second.getData())) {
                for (size_t i = 0; i < rd->getNumColorTextures(); ++i) {
                    const ImageRepresentationGL* imgGL = rd->getColorTexture(i)->getRepresentation<ImageRepresentationGL>();
403
                    if (imgGL) {
404
                        _textures.push_back(rd->getColorDataHandle(i));
405
                    }
406 407 408
                }
                if (rd->hasDepthTexture()) {
                    const ImageRepresentationGL* imgGL = rd->getDepthTexture()->getRepresentation<ImageRepresentationGL>();
409
                    if (imgGL) {
410
                        _textures.push_back(rd->getDepthDataHandle());
411
                    }
412 413 414

                }
            }
415 416
            else if (const GeometryData* gd = dynamic_cast<const GeometryData*>(it->second.getData())) {
                std::string name = it->first.toStdString();
417 418

                // copy geometry over to local 
419
                _localDataContainer.addDataHandle(name + ".geometry", it->second);
420 421 422 423 424 425

                // render
                renderGeometryIntoTexture(name);

                // store name
                _geometryNames.push_back(std::make_pair(name, static_cast<int>(_textures.size()) - 1));
426
            }
427
        }
428 429

        if (maxSlices == 1)
Declara Denis's avatar
Declara Denis committed
430
            maxSlices = 0;
431
        p_currentSlice.setMaxValue(maxSlices - 1);
432
        _texturesDirty = false;
433
        _geometriesDirty = false;
434 435
    }

436

437
    void DataContainerInspectorCanvas::onPropertyChanged(const AbstractProperty* prop) {
438
        // ignore properties of the geometry renderer
439 440
        if (prop != &p_geometryRendererProperties)
            invalidate();
441 442
    }

443
    void DataContainerInspectorCanvas::onGeometryRendererPropertyChanged(const AbstractProperty* prop) {
444
        _geometriesDirty = true;
445
        invalidate();
446
    }
447

448 449
    void DataContainerInspectorCanvas::renderGeometryIntoTexture(const std::string& name, int textureIndex) {
        // setup GeometryRenderer
450 451 452
        _geometryRenderer.p_geometryID.setValue(name + ".geometry");
        _geometryRenderer.p_renderTargetID.setValue(name + ".rendered");
        _geometryRenderer.validate(AbstractProcessor::INVALID_PROPERTIES);
453
        _geometryRenderer.invalidate(AbstractProcessor::INVALID_RESULT);
454
        _geometryRenderer.process(_localDataContainer);
455 456 457 458 459 460 461 462 463 464 465 466 467 468

        // 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) {
                if (textureIndex < 0 || textureIndex >= static_cast<int>(_textures.size())) {
                    _textures.push_back(rd->getColorDataHandle(0));
                }
                else {
                    _textures[textureIndex] = rd->getColorDataHandle(0);
                }
            }
            else {
469
                cgtAssert(false, "The rendered geometry does not have an OpenGL representation. Something went terribly wrong.");
470 471 472
            }
        }
        else {
473
            cgtAssert(false, "The rendered geometry does exist. Something went wrong.");
474 475
        }
    }
476
    
477 478
    void DataContainerInspectorCanvas::resetTrackball() {
        // check whether we have to render geometries
479
        cgt::Bounds unionBounds;
480 481 482 483 484 485 486 487
        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()) {
488
            _tcp.reinitializeCamera(unionBounds);
489
        }
490 491 492

        _tcp.invalidate(AbstractProcessor::INVALID_RESULT);
        _tcp.process(_localDataContainer);
493
    }
494

Sebastian Pölsterl's avatar
Sebastian Pölsterl committed
495
}