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
        , p_geometryRendererProperties("GeometryRendererProperties", "GeometryRenderer Properties")
        , _texturesDirty(true)
58
        , _geometriesDirty(true)
59
60
61
        , _dataContainer(nullptr)
        , _paintShader(nullptr)
        , _quad(nullptr)
62
63
        , _numTiles(0, 0)
        , _quadSize(0, 0)
64
        , _localDataContainer("Local DataContainer for DataContainerInspectorCanvas")
65
        , p_viewportSize("ViewportSize", "Viewport Size", cgt::ivec2(200), cgt::ivec2(0, 0), cgt::ivec2(10000))
66
        , _tcp(&p_viewportSize)
67
        , _geometryRenderer(&p_viewportSize)
68
    {
69
        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)));
70

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

        p_geometryRendererProperties.addPropertyCollection(_geometryRenderer);
        _geometryRenderer.p_geometryID.setVisible(false);
        _geometryRenderer.p_textureID.setVisible(false);
        _geometryRenderer.p_renderTargetID.setVisible(false);
        _geometryRenderer.p_lightId.setVisible(false);
86
        _geometryRenderer.p_camera.setVisible(false);
87
88
89
90
91
92
93
94
95
96
        _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);
97
98
99
100
101
102
103
104

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

    DataContainerInspectorCanvas::~DataContainerInspectorCanvas() {

    }

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

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

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

121
        _geometryRenderer.init();
122
123
124
    }

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

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

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

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

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

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

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

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

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

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

        _paintShader->activate();

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

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

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

189
190
191
192
193
194
        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;

195
                // gather image
196
                cgtAssert(dynamic_cast<const ImageData*>(_textures[index].getData()), "Found sth. else than ImageData in render texture vector. This should not happen!");
197
198
199
200
                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);
201
202
                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));
203
                float ratioRatio = sliceRatio / renderTargetRatio;
204
                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));
205

206
207
                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));
208

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

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

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

221
222
    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!");
223
224
        if (texture == nullptr)
            return;
225
226

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

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

251
    void DataContainerInspectorCanvas::invalidate() {
Christian Schulte zu Berge's avatar
Christian Schulte zu Berge committed
252
        // only if inited
253
        if (_quad != 0 && _paintShader != 0) {
254
255
            // avoid recursive paints.
            if (! cgt::GlContextManager::getRef().checkWhetherThisThreadHasAcquiredOpenGlContext()) {
256
                std::thread([this] () {
257
258
259
                    cgt::GLContextScopedLock lock(this);
                    paint();
                });
260
            }
261
        }
262
263
    }

264
    void DataContainerInspectorCanvas::createQuad() {
265
        _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));
266
267
    }

268
269
270
271
    void DataContainerInspectorCanvas::repaint() {
        invalidate();
    }

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

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

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

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

            // compute transformation matrices
            float renderTargetRatio = static_cast<float>(_quadSize.x) / static_cast<float>(_quadSize.y);
292
293
            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));
294
295
            float ratioRatio = sliceRatio / renderTargetRatio;

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

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

341
342
343
344
345
346
347
348
349
350
351
352
353
    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
354
                _texturesDirty = true;
355
356
            }
        }
357
358
359

        if (_texturesDirty)
            invalidate();
360
361
362
363
364
365
366
367
368
    }

    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);

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

383
            _texturesDirty = true;
384
385
386
387
388
389
        }

        invalidate();
    }

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

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

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

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

                // render
                renderGeometryIntoTexture(name);

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

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

438

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

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

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

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

        _tcp.invalidate(AbstractProcessor::INVALID_RESULT);
        _tcp.process(_localDataContainer);
495
    }
496

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