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

Started on implementing simple empty-space skipping for SimpleRaycaster:

 * Introducing BinaryBrickingVolume
 * Prototype implementation of computing the BBV lookup volume
parent 7c9beba2
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universität München
// Boltzmannstr. 3, 85748 Garching b. München, Germany
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// The licensing of this softare is not yet resolved. Until then, redistribution in source or
// binary forms outside the CAMP chair is not permitted, unless explicitly stated in legal form.
// However, the names of the original authors and the above copyright notice must retain in its
// original state in any case.
//
// Legal disclaimer provided by the BSD license:
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// ================================================================================================
#include "volumebricking.h"
#include "tgt/assert.h"
#include "tgt/glmath.h"
#include "core/datastructures/genericimagerepresentationlocal.h"
#define DIV_CEIL(x,y) ((x) > 0) ? (1 + ((x) - 1)/(y)) : ((x) / (y))
namespace campvis {
BinaryBrickedVolume::BinaryBrickedVolume(const ImageData* referenceImage, size_t brickSize)
: _referenceImage(referenceImage)
, _brickSize(brickSize)
, _bricks(0)
{
tgtAssert(_referenceImage != 0, "Reference Image must not be 0!");
// perform ceiling integer division:
_numBricks = referenceImage->getSize();
for (int i = 0; i < 3; ++i)
_numBricks.elem[i] = DIV_CEIL(_numBricks.elem[i], _brickSize);
_numBrickIndices = tgt::hmul(_numBricks);
_numElementsInBricksArray = DIV_CEIL(_numBrickIndices, 8);
_bricks = new uint8_t[_numElementsInBricksArray];
memset(_bricks, 0, _numElementsInBricksArray);
}
BinaryBrickedVolume::~BinaryBrickedVolume() {
delete _bricks;
}
std::vector<tgt::svec3> BinaryBrickedVolume::getAllVoxelsForBrick(size_t brickIndex) const {
const tgt::svec3& refImageSize = _referenceImage->getSize();
std::vector<tgt::svec3> toReturn;
toReturn.reserve(_brickSize * _brickSize * _brickSize);
// traverse each dimension, check that voxel is within reference image size
tgt::svec3 startVoxel = indexToBrick(brickIndex) * _brickSize;
for (size_t x = 0; x < _brickSize && startVoxel.x + x < refImageSize.x; ++x) {
for (size_t y = 0; y < _brickSize && startVoxel.y + y < refImageSize.y; ++y) {
for (size_t z = 0; z < _brickSize && startVoxel.z + z < refImageSize.z; ++z) {
toReturn.push_back(tgt::svec3(startVoxel.x + x, startVoxel.y + y, startVoxel.z + z));
}
}
}
return toReturn;
}
tgt::svec3 BinaryBrickedVolume::indexToBrick(size_t brickIndex) const {
size_t z = brickIndex / (_numBricks.x * _numBricks.y);
size_t y = (brickIndex % (_numBricks.x * _numBricks.y)) / _numBricks.x;
size_t x = brickIndex % _numBricks.x;
return tgt::svec3(x, y, z);
}
size_t BinaryBrickedVolume::brickToIndex(const tgt::svec3& brick) const {
return brick.x + (_numBricks.x * brick.y) + (_numBricks.x * _numBricks.y * brick.z);
}
bool BinaryBrickedVolume::getValueForIndex(size_t brickIndex) const {
size_t byte = brickIndex / 8;
size_t bit = brickIndex % 8;
tgtAssert(brickIndex < _numElementsInBricksArray, "Brick brickIndex out of bounds!");
return (_bricks[byte] & (1 << bit)) != 0;
}
void BinaryBrickedVolume::setValueForIndex(size_t brickIndex, bool value) {
size_t byte = brickIndex / 8;
size_t bit = brickIndex % 8;
tgtAssert(byte < _numElementsInBricksArray, "Brick brickIndex out of bounds!");
if (value)
_bricks[byte] |= (1 << bit);
else
_bricks[byte] &= ~(1 << bit);
}
ImageData* BinaryBrickedVolume::exportToImageData() const {
tgt::svec3 size = _numBricks;
size.z = DIV_CEIL(size.z, 8);
size_t numElementsInCopy = tgt::hmul(size);
uint8_t* copy = new uint8_t[numElementsInCopy];
memset(copy, 0, numElementsInCopy);
memcpy(copy, _bricks, _numElementsInBricksArray);
ImageData* toReturn = new ImageData(_referenceImage->getDimensionality(), size, 1);
GenericImageRepresentationLocal<uint8_t, 1>::create(toReturn, copy);
return toReturn;
}
const tgt::svec3& BinaryBrickedVolume::getNumBricks() const {
return _numBricks;
}
size_t BinaryBrickedVolume::getNumBrickIndices() const {
return _numBrickIndices;
}
}
\ No newline at end of file
#ifndef VOLUMEBRICKING_H__
#define VOLUMEBRICKING_H__
#include "tgt/vector.h"
#include "core/datastructures/imagedata.h"
#include <vector>
namespace campvis {
class BinaryBrickedVolume {
public:
BinaryBrickedVolume(const ImageData* referenceImage, size_t brickSize);
~BinaryBrickedVolume();
/**
* Gets the number of bricks in each dimension.
* \return _numBricks
*/
const tgt::svec3& getNumBricks() const;
/**
* Gets the number of brick indices (= hmul(_numBricks)).
* \return _numBrickIndices
*/
size_t getNumBrickIndices() const;
/**
* Returns the boolean value for the brick with index \a brickIndex.
* \param brickIndex Lookup brick index
* \return The boolean value for the brick with index \a brickIndex.
*/
bool getValueForIndex(size_t brickIndex) const;
/**
* Sets the boolean value for the brick with index \a brickIndex to \a value.
* \param brickIndex Lookup brick index
* \param value The new boolean value for this brick.
*/
void setValueForIndex(size_t brickIndex, bool value);
/**
* Return a vector of all voxels positions in the reference image that are in the brick with the index \a brickIndex.
* \param brickIndex Lookup brick index.
* \return A vector of all voxels positions in the reference image that are in the brick with the index \a brickIndex.
*/
std::vector<tgt::svec3> getAllVoxelsForBrick(size_t brickIndex) const;
ImageData* exportToImageData() const;
private:
/**
* Returns the brick coordinates for the brick with index \a brickIndex.
* \param brickIndex The Brick index to look up
* \return The corresponding 3D brick coordinates.
*/
tgt::svec3 indexToBrick(size_t brickIndex) const;
/**
* Transforms brick coordinates to the corresponding index.
* \param brick Brick coordinates.
* \return The corresponding index in if all bricks are in contiguous storage.
*/
size_t brickToIndex(const tgt::svec3& brick) const;
const ImageData* _referenceImage; ///< the reference image
size_t _brickSize; ///< number of voxels a brick is covering in each dimension
tgt::svec3 _numBricks; ///< number of bricks in each dimension
size_t _numBrickIndices; ///< number of brick indices (= hmul(_numBricks))
uint8_t* _bricks; ///< the densly packed bricks
size_t _numElementsInBricksArray; ///< number of elements in _bricks
};
}
#endif // VOLUMEBRICKING_H__
......@@ -33,6 +33,8 @@
#include "core/datastructures/renderdata.h"
#include "core/pipeline/processordecoratorshading.h"
#include <tbb/tbb.h>
namespace campvis {
const std::string SimpleRaycaster::loggerCat_ = "CAMPVis.modules.vis.SimpleRaycaster";
......@@ -42,6 +44,7 @@ namespace campvis {
, p_enableShadowing("EnableShadowing", "Enable Hard Shadows (Expensive!)", false, AbstractProcessor::INVALID_RESULT | AbstractProcessor::INVALID_SHADER | AbstractProcessor::INVALID_PROPERTIES)
, p_shadowIntensity("ShadowIntensity", "Shadow Intensity", .5f, .0f, 1.f)
, p_enableAdaptiveStepsize("EnableAdaptiveStepSize", "Enable Adaptive Step Size", true, AbstractProcessor::INVALID_RESULT | AbstractProcessor::INVALID_SHADER)
, _bbv(0)
{
addDecorator(new ProcessorDecoratorShading());
......@@ -56,10 +59,24 @@ namespace campvis {
}
SimpleRaycaster::~SimpleRaycaster() {
delete _bbv;
}
void SimpleRaycaster::init() {
RaycastingProcessor::init();
}
void SimpleRaycaster::deinit() {
RaycastingProcessor::deinit();
}
void SimpleRaycaster::processImpl(DataContainer& data, ImageRepresentationGL::ScopedRepresentation& image) {
DataHandle dh = DataHandle(const_cast<ImageData*>(image->getParent())); // HACK HACK HACK
generateBbv(dh);
if (_bbv != 0) {
data.addData("nanananana BATMAN!", _bbv->exportToImageData());
}
FramebufferActivationGuard fag(this);
createAndAttachTexture(GL_RGBA8);
createAndAttachTexture(GL_RGBA32F);
......@@ -95,4 +112,59 @@ namespace campvis {
validate(AbstractProcessor::INVALID_PROPERTIES);
}
void SimpleRaycaster::generateBbv(DataHandle dh) {
delete _bbv;
if (dh.getData() == 0) {
_bbv = 0;
return;
}
else {
if (const ImageData* id = dynamic_cast<const ImageData*>(dh.getData())) {
if (const ImageRepresentationLocal* rep = id->getRepresentation<ImageRepresentationLocal>(true)) {
_bbv = new BinaryBrickedVolume(rep->getParent(), 2);
GLubyte* tfBuffer = p_transferFunction.getTF()->getTexture()->downloadTextureToBuffer(GL_RGBA, GL_UNSIGNED_BYTE);
size_t tfNumElements = p_transferFunction.getTF()->getTexture()->getDimensions().x;
// parallelly traverse the bricks
tbb::parallel_for(tbb::blocked_range<size_t>(0, _bbv->getNumBrickIndices()), [&] (const tbb::blocked_range<size_t>& range) {
const tgt::vec2& tfIntensityDomain = p_transferFunction.getTF()->getIntensityDomain();
for (size_t i = range.begin(); i != range.end(); ++i) {
// for each brick, get all corresponding voxels in the reference volume
std::vector<tgt::svec3> voxels = _bbv->getAllVoxelsForBrick(i);
// traverse the voxels to check whether their intensities are mapped to some opacity
for (size_t v = 0; v < voxels.size(); ++v) {
// apply same TF lookup as in shader...
float intensity = rep->getElementNormalized(voxels[v], 0);
if (intensity >= tfIntensityDomain.x || intensity <= tfIntensityDomain.y) {
float mappedIntensity = (intensity - tfIntensityDomain.x) / (tfIntensityDomain.y - tfIntensityDomain.x);
tgtAssert(mappedIntensity >= 0.f && mappedIntensity <= 1.f, "Mapped intensity out of bounds!");
// ...but with nearest neighbour interpolation
size_t scaled = static_cast<size_t>(mappedIntensity * static_cast<float>(tfNumElements - 1));
tgtAssert(scaled < tfNumElements, "Somebody did the math wrong...");
GLubyte opacity = tfBuffer[(4 * (scaled)) + 3];
if (opacity != 0) {
_bbv->setValueForIndex(i, true);
break;
}
}
}
}
});
}
else {
LERROR("Could not convert to a local representation.");
}
}
else {
tgtAssert(false, "The data type in the given DataHandle is WRONG!");
}
}
}
}
......@@ -34,6 +34,7 @@
#include "core/properties/floatingpointproperty.h"
#include "core/properties/genericproperty.h"
#include "core/properties/transferfunctionproperty.h"
#include "core/tools/volumebricking.h"
#include <string>
......@@ -44,8 +45,6 @@ namespace tgt {
namespace campvis {
/**
* Performs a simple volume ray casting.
* \todo OpenGL supports up to 4 bound FBO. We can use them to generate multiple images
* in a single pass, e.g. first hit point, normals, MIP, DVR.
*/
class SimpleRaycaster : public RaycastingProcessor {
public:
......@@ -68,6 +67,11 @@ namespace campvis {
/// \see AbstractProcessor::getProcessorState()
virtual ProcessorState getProcessorState() const { return AbstractProcessor::TESTING; };
/// \see AbstractProcessor::init
virtual void init();
/// \see AbstractProcessor::deinit
virtual void deinit();
DataNameProperty p_targetImageID; ///< image ID for output image
BoolProperty p_enableShadowing;
FloatProperty p_shadowIntensity;
......@@ -84,6 +88,11 @@ namespace campvis {
/// \see RaycastingProcessor::generateHeader()
virtual std::string generateHeader() const;
void generateBbv(DataHandle dh);
BinaryBrickedVolume* _bbv;
static const std::string loggerCat_;
};
......
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