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

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

Introducing ViewportSplitter class to campvis-core, a nice proxy class to...

Introducing ViewportSplitter class to campvis-core, a nice proxy class to support splitting the viewport into multiple views.
parent 1335ed20
// ================================================================================================
//
// 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(i) * 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) {
cgtAssert(prop == p_viewportSizeProperty, "Wrong property in signal, this should not happen!");
const cgt::ivec2& vps = p_viewportSizeProperty->getValue();
if (_splitMode == HORIZONTAL) {
p_subViewViewportSize.setValue(cgt::ivec2(vps.x / int(_numSubViews), vps.y));
}
else if (_splitMode == VERTICAL) {
p_subViewViewportSize.setValue(cgt::ivec2(vps.x, vps.y / int(_numSubViews)));
}
}
}
// ================================================================================================
//
// 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.
//
// ================================================================================================
#ifndef VIEWPORTSPLITTER_H__
#define VIEWPORTSPLITTER_H__
#include "cgt/vector.h"
#include "cgt/event/eventlistener.h"
#include "core/properties/datanameproperty.h"
#include "core/properties/numericproperty.h"
namespace cgt {
class FramebufferObject;
class Shader;
}
namespace campvis {
class FaceGeometry;
class ImageData;
/**
* Helper class to facilitate splitting the viewport into multiple views.
*
* Splitting can be done either vertically or horizontally. ViewportSplitter takes care of
* computing the viewport sizes of the sub views (you can access them via properties for easy
* forwarding to processors), rendering each sub view, as well as handling events, computing
* the corresponding view and forwarding them to potential listeners.
*/
class CAMPVIS_CORE_API ViewportSplitter : public cgt::EventListener, public sigslot::has_slots {
public:
/// Enumeration on how to split the view
enum SplitMode {
HORIZONTAL, ///< Split the view horizontally
VERTICAL ///< Split the view vertically
};
/**
* Creates a ViewportSplitter.
*
* \note This processor will keep and access \a viewportSizeProp, so make sure the referenced
* property exists at least as long as this processor or you set it to a different
* property before using setViewportSizeProperty().
*
* \param numSubViews Number of sub views to create
* \param splitMode Mode how to split the view
* \param viewportSizeProp Pointer to the property defining the viewport size, must not be 0.
*/
explicit ViewportSplitter(size_t numSubViews, SplitMode splitMode, IVec2Property* viewportSizeProp);
/**
* Virtual Destructor
**/
virtual ~ViewportSplitter();
/**
* Initialize OpenGL-related stuff.
*/
virtual void init();
/**
* Deinitialize OpenGL-related stuff.
*/
virtual void deinit();
/**
* Sets the property to lookup the input image ID for the given view index.
* \param index Index of the subview for which to define the property.
* \param prop DataNameProperty in which ViewportSplitter can lookup the image ID.
*/
void setInputImageIdProperty(size_t index, DataNameProperty* prop);
/**
* Computes the view under the mouse position and forwards them to potential listeners using
* ths s_onEvent signal.
* Overloads cgt::onEvent()
* \param e The event.
*/
virtual void onEvent(cgt::Event* e);
/**
* Renders the each sub view into the final view and stores the result in the DataContainer.
* \param dataContainer DataContainer to work on.
*/
void render(DataContainer& dataContainer);
/// Event emitted from ViewportSplitter::onEvent, but with adjusted viewport parameters.
/// The first signal parameter gives the index of the view of the event.
sigslot::signal2<size_t, cgt::Event*> s_onEvent;
DataNameProperty p_outputImageId; ///< Image ID for rendered image.
IVec2Property p_subViewViewportSize; ///< Viewport size of each sub view.
protected:
/// Callback for s_changed signal of p_viewportSizeProperty
void onViewportSizePropertyChanged(const AbstractProperty* prop);
std::vector<DataNameProperty*> p_inputImageIds; ///< Vector of the properties to lookup input image IDs
size_t _numSubViews; ///< Number of sub views to create
SplitMode _splitMode; ///< Mode how to split the viewport into sub views
bool _mousePressed; ///< Flag whether the mous was pressed in this view (to correctly compute the view of the event)
size_t _viewIndexOfEvent; ///< Index of the sub view for the forwarded event.
FaceGeometry* _quad; ///< Geometry used for rendering
cgt::FramebufferObject* _fbo; ///< The FBO used for rendering
IVec2Property* p_viewportSizeProperty; ///< Pointer to the property defining the viewport (canvas) size.
cgt::Shader* _copyShader; ///< Shader used for rendering
static const std::string loggerCat_;
};
}
#endif // VIEWPORTSPLITTER_H__
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment