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

Commit aae87572 authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Merge branch 'neuro-project-cleaned' into 'development'

Neuro project cleaned

See merge request !109
parents 45a42e7b dbe26e96
......@@ -199,8 +199,8 @@ namespace campvis {
// 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 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));
float ratioRatio = sliceRatio / renderTargetRatio;
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));
......@@ -291,8 +291,8 @@ namespace campvis {
// 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 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));
float ratioRatio = sliceRatio / renderTargetRatio;
lookupTexelFloat /= (ratioRatio > 1) ? cgt::vec2(1.f, 1.f / ratioRatio) : cgt::vec2(ratioRatio, 1.f);
......
......@@ -322,7 +322,7 @@ namespace campvis {
std::string pipeScript = "pipeline = pipelines[\"" + _selectedPipeline->getName()+"\"]\n\n";
for (size_t i = 0; i < _selectedPipeline->getProcessors().size(); i++) {
pipeScript += "proc = pipeline:getProcessor(" + StringUtils::toString(i) + ")\n";
AbstractProcessor* proc = _selectedPipeline->getProcessor(i);
AbstractProcessor* proc = _selectedPipeline->getProcessor(int(i));
_pcLua->updatePropCollection(proc, &_selectedPipeline->getDataContainer());
std::string res = _pcLua->getLuaScript(std::string(""), std::string("proc:"));
......
......@@ -28,6 +28,7 @@
#include <QComboBox>
#include <QLineEdit>
#include <QSortFilterProxyModel>
namespace campvis {
DataNamePropertyWidget::DataNamePropertyWidget(DataNameProperty* property, DataContainer* dc, QWidget* parent /*= 0*/)
......@@ -39,6 +40,11 @@ namespace campvis {
_combobox = new QComboBox(this);
_combobox->setEditable(true);
QSortFilterProxyModel* proxy = new QSortFilterProxyModel(_combobox);
proxy->setSourceModel(_combobox->model());
_combobox->model()->setParent(proxy); // combo's current model must be reparented, otherwise QComboBox::setModel() will delete it
_combobox->setModel(proxy);
if (dc != 0) {
std::vector< std::pair<std::string, DataHandle> > tmp = dc->getDataHandlesCopy();
QStringList sl;
......@@ -48,6 +54,7 @@ namespace campvis {
dc->s_dataAdded.connect(this, &DataNamePropertyWidget::onDataAdded);
connect(this, SIGNAL(s_dataAddedQt(const QString&, QtDataHandle)), this, SLOT(onDataAddedQt(const QString&, QtDataHandle)));
_combobox->model()->sort(Qt::AscendingOrder);
setCurrentComboBoxText(QString::fromStdString(property->getValue()));
}
......@@ -106,6 +113,7 @@ namespace campvis {
void DataNamePropertyWidget::onDataAddedQt(const QString& key, QtDataHandle dh) {
if (_combobox->findText(key) == -1) {
_combobox->addItem(key);
_combobox->model()->sort(Qt::AscendingOrder);
}
}
......
......@@ -71,7 +71,7 @@ namespace campvis {
_layout->setSpacing(8);
_layout->setMargin(0);
setLayout(_layout);
connect(this, SIGNAL(s_widgetVisibilityChanged(QWidget*, bool)), this, SLOT(onWidgetVisibilityChanged(QWidget*, bool)));
connect(this, SIGNAL(s_propertyVisibilityChanged(const AbstractProperty*)), this, SLOT(onWidgetVisibilityChanged(const AbstractProperty*)), Qt::QueuedConnection);
connect(this, SIGNAL(propertyAdded(AbstractProperty*)), this, SLOT(addProperty(AbstractProperty*)));
connect(this, SIGNAL(propertyRemoved(AbstractProperty*, QWidget*)), this, SLOT(removeProperty(AbstractProperty*, QWidget*)));
}
......@@ -91,14 +91,18 @@ namespace campvis {
}
void PropertyCollectionWidget::onPropertyVisibilityChanged(const AbstractProperty* prop) {
// This method is not always called on the main thread, so it is not safe to check anything here.
// The event is instead forwarded using the QT sigslot mechanism through a queued connection to the
// main thread.
emit s_propertyVisibilityChanged(prop);
}
void PropertyCollectionWidget::onWidgetVisibilityChanged(const AbstractProperty* prop) {
// const_cast does not harm anything.
std::map<AbstractProperty*, QWidget*>::iterator item = _widgetMap.find(const_cast<AbstractProperty*>(prop));
if (item != _widgetMap.end())
emit s_widgetVisibilityChanged(item->second, item->first->isVisible());
}
void PropertyCollectionWidget::onWidgetVisibilityChanged(QWidget* widget, bool visibility) {
widget->setVisible(visibility);
item->second->setVisible(item->first->isVisible());
}
void PropertyCollectionWidget::onPropCollectionPropAdded(AbstractProperty* prop) {
......
......@@ -72,7 +72,7 @@ namespace campvis {
/**
* Gets called when the property has changed, so that widget can update its state.
*/
virtual void onWidgetVisibilityChanged(QWidget* widget, bool visibility);
virtual void onWidgetVisibilityChanged(const AbstractProperty* prop);
/**
* Creates the property widget for \a prop, connects all neccesary signals, etc.
......@@ -87,7 +87,7 @@ namespace campvis {
void removeProperty(AbstractProperty* prop, QWidget* widget);
signals:
void s_widgetVisibilityChanged(QWidget* widget, bool visibility);
void s_propertyVisibilityChanged(const AbstractProperty* prop);
void propertyAdded(AbstractProperty* prop);
void propertyRemoved(AbstractProperty* prop, QWidget* widget);
......
......@@ -160,7 +160,7 @@ namespace campvis {
return toReturn;
}
MultiIndexedGeometry* GeometryDataFactory::createSphere(uint16_t numStacks /*= 6*/, uint16_t numSlices /*= 12*/) {
MultiIndexedGeometry* GeometryDataFactory::createSphere(uint16_t numStacks /*= 6*/, uint16_t numSlices /*= 12*/, const cgt::vec3& exponents /*= cgt::vec3(1.f)*/) {
cgtAssert(numStacks > 1 && numSlices > 2, "Sphere must have minimum 2 stacks and 3 slices!");
std::vector<cgt::vec3> vertices;
std::vector<cgt::vec3> textureCoordinates;
......@@ -175,7 +175,17 @@ namespace campvis {
for (int j = 0; j < numSlices; ++j) {
float theta = static_cast<float>(j) * 2.f*cgt::PIf / static_cast<float>(numSlices);
vertices.push_back(cgt::vec3(cos(theta) * sin(phi), sin(theta) * sin(phi), cos(phi)));
// apply exponents for supersphere
cgt::vec3 theVertex(cos(theta) * sin(phi), sin(theta) * sin(phi), cos(phi));
for (size_t e = 0; e < 3; ++e) {
if (theVertex[e] < 0)
theVertex[e] = -pow(-theVertex[e], exponents[e]);
else
theVertex[e] = pow(theVertex[e], exponents[e]);
}
vertices.push_back(theVertex);
textureCoordinates.push_back(cgt::vec3(theta / (2.f * cgt::PIf), phi / cgt::PIf, 0.f));
}
}
......
......@@ -70,9 +70,10 @@ namespace campvis {
* Creates an MultiIndexedGeometry storing a unit sphere around the origin.
* \param numStacks Number of stacks in the sphere
* \param numSlices Number of slices in the sphere
* \param exponents Exponent for each dimension to define a supersphere (defines the roundness)
* \return MultiIndexedGeometry storing a unit sphere around the origin.
*/
static MultiIndexedGeometry* createSphere(uint16_t numStacks = 6, uint16_t numSlices = 12);
static MultiIndexedGeometry* createSphere(uint16_t numStacks = 6, uint16_t numSlices = 12, const cgt::vec3& exponents = cgt::vec3(1.f));
/**
......
......@@ -88,13 +88,11 @@ namespace campvis {
}
cgt::Bounds ImageData::getWorldBounds() const {
return cgt::Bounds(_mappingInformation.getOffset(), _mappingInformation.getOffset() + (cgt::vec3(_size) * _mappingInformation.getVoxelSize()));
return getWorldBounds(cgt::svec3(0, 0, 0), cgt::svec3(_size));
}
cgt::Bounds ImageData::getWorldBounds(const cgt::svec3& llf, const cgt::svec3& urb) const {
return cgt::Bounds(
_mappingInformation.getOffset() + (cgt::vec3(llf) * _mappingInformation.getVoxelSize()),
_mappingInformation.getOffset() + (cgt::vec3(urb) * _mappingInformation.getVoxelSize()));
return cgt::Bounds(_mappingInformation.getVoxelToWorldMatrix() * cgt::vec3(llf), _mappingInformation.getVoxelToWorldMatrix() * cgt::vec3(urb));
}
size_t ImageData::getNumElements() const {
......
......@@ -215,7 +215,7 @@ namespace campvis {
template<typename T>
const T* campvis::ImageData::getRepresentation(bool performConversion) const {
// look, whether we already have a suitable representation
for (tbb::concurrent_vector<const AbstractImageRepresentation*>::const_iterator it = _representations.begin(); it != _representations.end(); ++it) {
for (auto it = _representations.begin(); it != _representations.end(); ++it) {
if (const T* tester = dynamic_cast<const T*>(*it)) {
return tester;
}
......@@ -226,7 +226,7 @@ namespace campvis {
tbb::spin_mutex::scoped_lock lock(_conversionMutex);
// in the meantime, there something might have changed, so check again whether there is a new rep.
for (tbb::concurrent_vector<const AbstractImageRepresentation*>::const_iterator it = _representations.begin(); it != _representations.end(); ++it) {
for (auto it = _representations.begin(); it != _representations.end(); ++it) {
if (const T* tester = dynamic_cast<const T*>(*it)) {
return tester;
}
......@@ -240,7 +240,7 @@ namespace campvis {
template<typename T>
const T* campvis::ImageData::tryPerformConversion() const {
for (tbb::concurrent_vector<const AbstractImageRepresentation*>::const_iterator it = _representations.begin(); it != _representations.end(); ++it) {
for (auto it = _representations.begin(); it != _representations.end(); ++it) {
const T* tester = ImageRepresentationConverter::getRef().tryConvertFrom<T>(*it);
if (tester != 0) {
//LDEBUG("Performed a image representation conversion from " + std::string(typeid(**it).name()) + " to " + std::string(typeid(T).name()) + ".");
......
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
......@@ -53,11 +54,11 @@ namespace campvis {
}
void ImageMappingInformation::updateMatrices() {
_textureToWorldTransformation = cgt::mat4::createTranslation(_offset) * _customTransformation * cgt::mat4::createScale(_voxelSize * _size);
_textureToWorldTransformation = _customTransformation * cgt::mat4::createTranslation(_offset) * cgt::mat4::createScale(_voxelSize * _size);
if (! _textureToWorldTransformation.invert(_worldToTextureTransformation))
cgtAssert(false, "Could not invert texture-to-world matrix. That should not happen!");
_voxelToWorldTransformation = cgt::mat4::createTranslation(_offset) * _customTransformation * cgt::mat4::createScale(_voxelSize);
_voxelToWorldTransformation = _customTransformation * cgt::mat4::createTranslation(_offset) * cgt::mat4::createScale(_voxelSize);
if (! _voxelToWorldTransformation.invert(_worldToVoxelTransformation))
cgtAssert(false, "Could not invert voxel-to-world matrix. That should not happen!");
}
......
......@@ -60,8 +60,8 @@ namespace campvis {
std::ifstream file(_url.c_str(), std::ios::in | std::ios::binary | std::ios::ate);
if (file.is_open()) {
size_t fileSize = static_cast<size_t>(file.tellg());
if (fileSize < numBytes) {
LERROR("File is smaller than expected.");
if (fileSize < numBytes + _offset) {
LERROR("File is smaller than expected, " << (numBytes + _offset) - fileSize << " Bytes missing.");
return WeaklyTypedPointer(_type, _parent->getNumChannels(), 0);
}
......
......@@ -82,5 +82,13 @@ namespace campvis {
return "Image Series";
}
cgt::Bounds ImageSeries::getWorldBounds() const {
cgt::Bounds b;
for (size_t i = 0; i < _images.size(); ++i) {
b.addVolume(static_cast<const ImageData*>(_images[i].getData())->getWorldBounds());
}
return b;
}
}
\ No newline at end of file
......@@ -35,7 +35,7 @@ namespace campvis {
/**
* Class encapsulating a series of images.
*/
class CAMPVIS_CORE_API ImageSeries : public AbstractData {
class CAMPVIS_CORE_API ImageSeries : public AbstractData, public IHasWorldBounds {
public:
/**
* Constructor
......@@ -69,6 +69,12 @@ namespace campvis {
/// \see AbstractData::getTypeAsString()
virtual std::string getTypeAsString() const;
/**
* Returns the data extent in world coordinates.
* \return The data extent in world coordinates.
*/
virtual cgt::Bounds getWorldBounds() const;
/**
* Appends the image \a image to the series.
* \param image Image to be added.
......
......@@ -59,8 +59,6 @@ namespace campvis {
MultiIndexedGeometry::MultiIndexedGeometry(const MultiIndexedGeometry& rhs)
: GeometryData(rhs)
, _indices(rhs._indices)
, _offsets(rhs._offsets)
, _counts(rhs._counts)
, _vertices(rhs._vertices)
, _textureCoordinates(rhs._textureCoordinates)
, _colors(rhs._colors)
......@@ -80,8 +78,6 @@ namespace campvis {
GeometryData::operator=(rhs);
_indices = rhs._indices;
_offsets = rhs._offsets;
_counts = rhs._counts;
_vertices = rhs._vertices;
_textureCoordinates = rhs._textureCoordinates;
......@@ -97,8 +93,6 @@ namespace campvis {
MultiIndexedGeometry* MultiIndexedGeometry::clone() const {
MultiIndexedGeometry* toReturn = new MultiIndexedGeometry(_vertices, _textureCoordinates, _colors, _normals);
toReturn->_indices = _indices;
toReturn->_offsets = _offsets;
toReturn->_counts = _counts;
return toReturn;
}
......@@ -121,12 +115,11 @@ namespace campvis {
}
void MultiIndexedGeometry::addPrimitive(const std::vector<uint16_t>& indices) {
_offsets.push_back(reinterpret_cast<void*>(_indices.size() * 2));
_counts.push_back(static_cast<GLsizei>(indices.size()));
_indices.insert(_indices.end(), indices.begin(), indices.end());
if (! _indices.empty())
_indices.push_back(65535);
_indices.insert(_indices.end(), indices.begin(), indices.end());
_buffersDirty = true;
}
const std::vector<cgt::col4>& MultiIndexedGeometry::getPickingInformation() const {
......@@ -140,7 +133,38 @@ namespace campvis {
}
void MultiIndexedGeometry::render(GLenum mode) const {
if (_counts.empty())
if (_indices.empty())
return;
createGLBuffers();
if (_buffersDirty) {
LERROR("Cannot render without initialized OpenGL buffers.");
return;
}
cgt::VertexArrayObject vao;
if (_verticesBuffer)
vao.setVertexAttributePointer(0, _verticesBuffer);
if (_texCoordsBuffer)
vao.setVertexAttributePointer(1, _texCoordsBuffer);
if (_colorsBuffer)
vao.setVertexAttributePointer(2, _colorsBuffer);
if (_normalsBuffer)
vao.setVertexAttributePointer(3, _normalsBuffer);
if (_pickingBuffer)
vao.setVertexAttributePointer(4, _pickingBuffer);
vao.bindIndexBuffer(_indicesBuffer);
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(65535);
glDrawElements(mode, static_cast<GLsizei>(_indices.size()), GL_UNSIGNED_SHORT, 0);
glDisable(GL_PRIMITIVE_RESTART);
LGL_ERROR;
}
void MultiIndexedGeometry::renderInstanced(GLsizei count, GLenum mode /*= GL_TRIANGLE_FAN*/) const {
if (_indices.empty())
return;
createGLBuffers();
......@@ -162,8 +186,10 @@ namespace campvis {
vao.setVertexAttributePointer(4, _pickingBuffer);
vao.bindIndexBuffer(_indicesBuffer);
const GLvoid** ptr = (const GLvoid**)(&_offsets.front()); // <- hidden reinterpret_cast<const GLvoid**> here, ugly OpenGL...
glMultiDrawElements(mode, &_counts.front(), GL_UNSIGNED_SHORT, ptr, static_cast<GLsizei>(_offsets.size()));
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(65535);
glDrawElementsInstanced(mode, static_cast<GLsizei>(_indices.size()), GL_UNSIGNED_SHORT, 0, count);
glDisable(GL_PRIMITIVE_RESTART);
LGL_ERROR;
}
......
......@@ -110,6 +110,14 @@ namespace campvis {
*/
virtual void render(GLenum mode) const;
/**
* Renders multiple instances of this GeometryData.
* Must be called from a valid OpenGL context.
* \param count Number of instances
* \param mode OpenGL rendering mode
*/
virtual void renderInstanced(GLsizei count, GLenum mode = GL_TRIANGLE_FAN) const;
/// \see GeometryData::getWorldBounds
virtual cgt::Bounds getWorldBounds() const;
/// \see GeometryData::hasTextureCoordinates
......@@ -130,8 +138,6 @@ namespace campvis {
void deleteIndicesBuffer() const;
std::vector<uint16_t> _indices; ///< Index list defining the faces
std::vector<void*> _offsets; ///< Byte offsets for each primitive to render
std::vector<GLsizei> _counts; ///< Numer of vertices for each primitive to render
std::vector<cgt::vec3> _vertices; ///< The list of the vertex positions of the face.
std::vector<cgt::vec3> _textureCoordinates; ///< The list of vertex texture coordinates, may be empty.
......
......@@ -32,6 +32,11 @@ void jitterEntryPoint(inout vec3 position, in vec3 direction, in float stepSize)
position = position + direction * (stepSize * random);
}
void jitterFloat(inout float t, in float stepSize) {
float random = fract(sin(gl_FragCoord.x * 12.9898 + gl_FragCoord.y * 78.233) * 43758.5453);
t += (stepSize * random);
}
/**
* Computes the intersection of the given ray with the given axis-aligned box.
* \param rayOrigin Origin of ray
......@@ -60,6 +65,18 @@ float rayBoxIntersection(in vec3 rayOrigin, in vec3 rayDirection, in vec3 boxLlf
return min(min(tMin.x, min(tMin.y, tMin.z)) , min(tMax.x, min(tMax.y, tMax.z)));
}
// compute the near and far intersections of the cube (stored in the x and y components) using the slab method
// no intersection means vec.x > vec.y (really tNear > tFar)
vec2 intersectAABB(vec3 rayOrigin, vec3 rayDirection, in vec3 boxLlf, in vec3 boxUrb) {
vec3 tMin = (boxLlf - rayOrigin) / rayDirection;
vec3 tMax = (boxUrb - rayOrigin) / rayDirection;
vec3 t1 = min(tMin, tMax);
vec3 t2 = max(tMin, tMax);
float tNear = max(max(t1.x, t1.y), t1.z);
float tFar = min(min(t2.x, t2.y), t2.z);
return vec2(tNear, tFar);
};
/**
* Converts a depth value in eye space to the corresponding depth value in viewport space.
* \param depth Depth value in eye space.
......
// ================================================================================================
//
// 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 "viewportsplitter.h"
#include "cgt/framebufferobject.h"
#include "cgt/textureunit.h"
#include "cgt/event/mouseevent.h"
#include "cgt/event/keyevent.h"
#include "core/datastructures/geometrydatafactory.h"
#include "core/datastructures/renderdata.h"
namespace campvis {
const std::string ViewportSplitter::loggerCat_ = "CAMPVis.core.datastructures.ViewportSplitter";
// ================================================================================================
ViewportSplitter::ViewportSplitter(size_t numSubViews, SplitMode splitMode, IVec2Property* viewportSizeProp)
: p_outputImageId("OutputImageId", "Output Image ID", "", DataNameProperty::WRITE)
, p_subViewViewportSize("ElementViewportSize", "Element Viewport Size", cgt::ivec2(128), cgt::ivec2(1), cgt::ivec2(2048))
, _numSubViews(numSubViews)
, _splitMode(splitMode)
, _mousePressed(false)
, _viewIndexOfEvent(0)
, _fbo(nullptr)
, p_viewportSizeProperty(viewportSizeProp)
, _copyShader(nullptr)
{
p_viewportSizeProperty->s_changed.connect(this, &ViewportSplitter::onViewportSizePropertyChanged);
p_inputImageIds.resize(_numSubViews, nullptr);
}
ViewportSplitter::~ViewportSplitter() {
}
void ViewportSplitter::init() {
_quad = GeometryDataFactory::createQuad(cgt::vec3(0.f), cgt::vec3(1.f), cgt::vec3(0.f), cgt::vec3(1.f));
_fbo = new cgt::FramebufferObject();
_copyShader = ShdrMgr.load("core/glsl/passthrough.vert", "core/glsl/copyimage.frag", "");
_copyShader->setAttributeLocation(0, "in_Position");
_copyShader->setAttributeLocation(1, "in_TexCoord");
}
void ViewportSplitter::deinit() {
delete _fbo;
delete _quad;
ShdrMgr.dispose(_copyShader);
}
void ViewportSplitter::setInputImageIdProperty(size_t index, DataNameProperty* prop) {
cgtAssert(index < _numSubViews, "Index out of bounds");
p_inputImageIds[index] = prop;
}
void ViewportSplitter::onEvent(cgt::Event* e) {
if (typeid(*e) == typeid(cgt::MouseEvent)) {
cgt::MouseEvent* me = static_cast<cgt::MouseEvent*>(e);
cgt::ivec2 position(me->x(), me->y());
if (! _mousePressed) {
if (_splitMode == HORIZONTAL)
_viewIndexOfEvent = std::min(size_t(position.x / p_subViewViewportSize.getValue().x), _numSubViews);
else if (_splitMode == VERTICAL)
_viewIndexOfEvent = std::min(size_t(position.y / p_subViewViewportSize.getValue().y), _numSubViews);
}
if (me->action() == cgt::MouseEvent::PRESSED)
_mousePressed = true;
else if (me->action() == cgt::MouseEvent::RELEASED)
_mousePressed = false;
// compute adjusted mouse event
if (_splitMode == HORIZONTAL)
position.x -= int(_viewIndexOfEvent) * p_subViewViewportSize.getValue().x;
else if (_splitMode == VERTICAL)
position.y -= int(_viewIndexOfEvent) * p_subViewViewportSize.getValue().y;
cgt::MouseEvent adjustedMe(position.x, position.y, me->action(), me->modifiers(), me->button(), p_subViewViewportSize.getValue());
// trigger signal, this HAS to be done synchroneously
s_onEvent.triggerSignal(_viewIndexOfEvent, &adjustedMe);
}
else {
// trigger signal, this HAS to be done synchroneously
s_onEvent.triggerSignal(_viewIndexOfEvent, e);
}
}
void ViewportSplitter::render(DataContainer& dataContainer) {
cgt::vec2 vps(p_viewportSizeProperty->getValue());
cgt::vec2 evps(p_subViewViewportSize.getValue());
cgt::TextureUnit rtUnit, colorUnit, depthUnit;
rtUnit.activate();
cgt::Texture* tex = new cgt::Texture(GL_TEXTURE_2D, cgt::ivec3(p_viewportSizeProperty->getValue(), 1), GL_RGBA8, cgt::Texture::LINEAR);
tex->setWrapping(cgt::Texture::CLAMP_TO_EDGE);
_fbo->activate();
_fbo->attachTexture(tex, GL_COLOR_ATTACHMENT0);
glViewport(0, 0, static_cast<GLsizei>(vps.x), static_cast<GLsizei>(vps.y));
_copyShader->activate();
_copyShader->setUniform("_projectionMatrix", cgt::mat4::createOrtho(0, vps.x, vps.y, 0, -1, 1));
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for (size_t i = 0; i < _numSubViews; ++i) {
if (p_inputImageIds[i] != nullptr) {
ScopedTypedData<RenderData> rd(dataContainer, p_inputImageIds[i]->getValue());
if (rd != nullptr) {
rd->bind(_copyShader, colorUnit, depthUnit);
_copyShader->setUniform("_modelMatrix", cgt::mat4::createScale(cgt::vec3(evps.x, evps.y, .5f)));
if (_splitMode == HORIZONTAL)
_copyShader->setUniform("_viewMatrix", cgt::mat4::createTranslation(cgt::vec3(float(i) * evps.x, 0.f, 0.f)));
else if (_splitMode == VERTICAL)
_copyShader->setUniform("_viewMatrix", cgt::mat4::createTranslation(cgt::vec3(0.f, float(_numSubViews - i - 1) * evps.y, 0.f)));
_quad->render(GL_TRIANGLE_FAN);
}
}
}
_copyShader->deactivate();
dataContainer.addData(p_outputImageId.getValue(), new RenderData(_fbo));
_fbo->detachAll();
_fbo->deactivate();
}
void ViewportSplitter::onViewportSizePropertyChanged(const AbstractProperty* prop) {