Merge pull request #3256 from ey6es/metavoxels

Allow configurable block sizes, use back face culling for heightfields, custom expansion for spanner attributes, compute normals in vertex shader.
This commit is contained in:
Philip Rosedale 2014-08-05 18:32:05 -07:00
commit 6976d0f51e
9 changed files with 165 additions and 63 deletions

View file

@ -14,18 +14,26 @@
// the height texture
uniform sampler2D heightMap;
// the distance between height points in texture space
uniform float heightScale;
// the interpolated normal
varying vec4 normal;
void main(void) {
// transform and store the normal for interpolation
normal = normalize(gl_ModelViewMatrix * vec4(0.0, 1.0, 0.0, 0.0));
vec2 heightCoord = gl_MultiTexCoord0.st;
float deltaX = texture2D(heightMap, heightCoord - vec2(heightScale, 0.0)).r -
texture2D(heightMap, heightCoord + vec2(heightScale, 0.0)).r;
float deltaZ = texture2D(heightMap, heightCoord - vec2(0.0, heightScale)).r -
texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r;
normal = normalize(gl_ModelViewMatrix * vec4(deltaX, heightScale, deltaZ, 0.0));
// pass along the texture coordinates
gl_TexCoord[0] = gl_MultiTexCoord0;
// add the height to the position
float height = texture2D(heightMap, gl_MultiTexCoord0.st).r;
float height = texture2D(heightMap, heightCoord).r;
gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
// the zero height should be invisible

View file

@ -359,9 +359,9 @@ void HeightfieldBuffer::render() {
int nextLineIndex = (i + 1) * sizeWithSkirt;
for (int j = 0; j < rows; j++) {
*index++ = lineIndex + j;
*index++ = lineIndex + j + 1;
*index++ = nextLineIndex + j + 1;
*index++ = nextLineIndex + j;
*index++ = nextLineIndex + j + 1;
*index++ = lineIndex + j + 1;
}
}
@ -388,6 +388,9 @@ void HeightfieldBuffer::render() {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().setUniformValue(
DefaultMetavoxelRendererImplementation::getHeightScaleLocation(), 1.0f / _heightSize);
glDrawRangeElements(GL_QUADS, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
@ -405,6 +408,7 @@ QHash<int, HeightfieldBuffer::BufferPair> HeightfieldBuffer::_bufferPairs;
void HeightfieldPreview::render(const glm::vec3& translation, float scale) const {
glDisable(GL_BLEND);
glEnable(GL_CULL_FACE);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_EQUAL, 0.0f);
@ -431,6 +435,7 @@ void HeightfieldPreview::render(const glm::vec3& translation, float scale) const
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_ALPHA_TEST);
glDisable(GL_CULL_FACE);
glEnable(GL_BLEND);
}
@ -468,6 +473,7 @@ void DefaultMetavoxelRendererImplementation::init() {
_heightfieldProgram.bind();
_heightfieldProgram.setUniformValue("heightMap", 0);
_heightfieldProgram.setUniformValue("diffuseMap", 1);
_heightScaleLocation = _heightfieldProgram.uniformLocation("heightScale");
_heightfieldProgram.release();
}
}
@ -737,6 +743,7 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox
_pointProgram.release();
glEnable(GL_CULL_FACE);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_EQUAL, 0.0f);
@ -756,12 +763,14 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_ALPHA_TEST);
glDisable(GL_CULL_FACE);
glEnable(GL_BLEND);
}
ProgramObject DefaultMetavoxelRendererImplementation::_pointProgram;
int DefaultMetavoxelRendererImplementation::_pointScaleLocation;
ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldProgram;
int DefaultMetavoxelRendererImplementation::_heightScaleLocation;
static void enableClipPlane(GLenum plane, float x, float y, float z, float w) {
GLdouble coefficients[] = { x, y, z, w };

View file

@ -191,6 +191,7 @@ public:
static void init();
static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; }
static int getHeightScaleLocation() { return _heightScaleLocation; }
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
@ -204,6 +205,7 @@ private:
static int _pointScaleLocation;
static ProgramObject _heightfieldProgram;
static int _heightScaleLocation;
};
/// Base class for spanner renderers; provides clipping.

View file

@ -930,6 +930,13 @@ void HeightfieldTool::render() {
ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) :
HeightfieldTool(editor, "Import Heightfield") {
_form->addRow("Block Size:", _blockSize = new QSpinBox());
_blockSize->setPrefix("2^");
_blockSize->setMinimum(1);
_blockSize->setValue(5);
connect(_blockSize, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
&ImportHeightfieldTool::updatePreview);
_form->addRow("Height:", _height = new QPushButton());
connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile);
_form->addRow("Color:", _color = new QPushButton());
@ -989,23 +996,22 @@ void ImportHeightfieldTool::selectColorFile() {
updatePreview();
}
const int BLOCK_SIZE = 32;
const int BLOCK_ADVANCEMENT = BLOCK_SIZE - 1;
void ImportHeightfieldTool::updatePreview() {
QVector<BufferDataPointer> buffers;
if (_heightImage.width() > 0 && _heightImage.height() > 0) {
float z = 0.0f;
for (int i = 0; i < _heightImage.height(); i += BLOCK_ADVANCEMENT, z++) {
int blockSize = pow(2.0, _blockSize->value());
int blockAdvancement = blockSize - 1;
for (int i = 0; i < _heightImage.height(); i += blockAdvancement, z++) {
float x = 0.0f;
for (int j = 0; j < _heightImage.width(); j += BLOCK_ADVANCEMENT, x++) {
QByteArray height(BLOCK_SIZE * BLOCK_SIZE, 0);
int rows = qMin(BLOCK_SIZE, _heightImage.height() - i);
int columns = qMin(BLOCK_SIZE, _heightImage.width() - j);
for (int j = 0; j < _heightImage.width(); j += blockAdvancement, x++) {
QByteArray height(blockSize * blockSize, 0);
int rows = qMin(blockSize, _heightImage.height() - i);
int columns = qMin(blockSize, _heightImage.width() - j);
const int BYTES_PER_COLOR = 3;
for (int y = 0; y < rows; y++) {
uchar* src = _heightImage.scanLine(i + y) + j * BYTES_PER_COLOR;
char* dest = height.data() + y * BLOCK_SIZE;
char* dest = height.data() + y * blockSize;
for (int x = 0; x < columns; x++) {
*dest++ = *src;
src += BYTES_PER_COLOR;
@ -1014,11 +1020,11 @@ void ImportHeightfieldTool::updatePreview() {
QByteArray color;
if (!_colorImage.isNull()) {
color = QByteArray(BLOCK_SIZE * BLOCK_SIZE * BYTES_PER_COLOR, 0);
rows = qMax(0, qMin(BLOCK_SIZE, _colorImage.height() - i));
columns = qMax(0, qMin(BLOCK_SIZE, _colorImage.width() - j));
color = QByteArray(blockSize * blockSize * BYTES_PER_COLOR, 0);
rows = qMax(0, qMin(blockSize, _colorImage.height() - i));
columns = qMax(0, qMin(blockSize, _colorImage.width() - j));
for (int y = 0; y < rows; y++) {
memcpy(color.data() + y * BLOCK_SIZE * BYTES_PER_COLOR,
memcpy(color.data() + y * blockSize * BYTES_PER_COLOR,
_colorImage.scanLine(i + y) + j * BYTES_PER_COLOR, columns * BYTES_PER_COLOR);
}
}

View file

@ -267,10 +267,11 @@ private slots:
void selectHeightFile();
void selectColorFile();
void updatePreview();
private:
void updatePreview();
QSpinBox* _blockSize;
QPushButton* _height;
QPushButton* _color;

View file

@ -242,6 +242,30 @@ bool Attribute::metavoxelRootsEqual(const MetavoxelNode& firstRoot, const Metavo
return firstRoot.deepEquals(this, secondRoot, minimum, size, lod);
}
MetavoxelNode* Attribute::expandMetavoxelRoot(const MetavoxelNode& root) {
AttributePointer attribute(this);
MetavoxelNode* newParent = new MetavoxelNode(attribute);
for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) {
MetavoxelNode* newChild = new MetavoxelNode(attribute);
newParent->setChild(i, newChild);
int index = MetavoxelNode::getOppositeChildIndex(i);
if (root.isLeaf()) {
newChild->setChild(index, new MetavoxelNode(root.getAttributeValue(attribute)));
} else {
MetavoxelNode* grandchild = root.getChild(i);
grandchild->incrementReferenceCount();
newChild->setChild(index, grandchild);
}
for (int j = 1; j < MetavoxelNode::CHILD_COUNT; j++) {
MetavoxelNode* newGrandchild = new MetavoxelNode(attribute);
newChild->setChild((index + j) % MetavoxelNode::CHILD_COUNT, newGrandchild);
}
newChild->mergeChildren(attribute);
}
newParent->mergeChildren(attribute);
return newParent;
}
FloatAttribute::FloatAttribute(const QString& name, float defaultValue) :
SimpleInlineAttribute<float>(name, defaultValue) {
}
@ -559,9 +583,6 @@ bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead)
}
const QByteArray& childContents = child->getContents();
int childSize = glm::sqrt((float)childContents.size());
if (childSize != size) {
continue; // TODO: handle differently-sized children
}
const int INDEX_MASK = 1;
int xIndex = i & INDEX_MASK;
const int Y_SHIFT = 1;
@ -576,13 +597,33 @@ bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead)
char* dest = contents.data() + (zIndex * halfSize * size) + (xIndex * halfSize);
uchar* src = (uchar*)childContents.data();
int childSizePlusOne = childSize + 1;
for (int z = 0; z < halfSize; z++) {
for (char* end = dest + halfSize; dest != end; src += 2) {
int max = qMax(qMax(src[0], src[1]), qMax(src[childSize], src[childSizePlusOne]));
*dest++ = (max == 0) ? 0 : (yOffset + (max >> 1));
if (childSize == size) {
// simple case: one destination value for four child values
for (int z = 0; z < halfSize; z++) {
for (char* end = dest + halfSize; dest != end; src += 2) {
int max = qMax(qMax(src[0], src[1]), qMax(src[childSize], src[childSizePlusOne]));
*dest++ = (max == 0) ? 0 : (yOffset + (max >> 1));
}
dest += halfSize;
src += childSize;
}
} else {
// more complex: N destination values for four child values
int halfChildSize = childSize / 2;
int destPerSrc = size / childSize;
for (int z = 0; z < halfChildSize; z++) {
for (uchar* end = src + childSize; src != end; src += 2) {
int max = qMax(qMax(src[0], src[1]), qMax(src[childSize], src[childSizePlusOne]));
memset(dest, (max == 0) ? 0 : (yOffset + (max >> 1)), destPerSrc);
dest += destPerSrc;
}
dest += halfSize;
for (int j = 1; j < destPerSrc; j++) {
memcpy(dest, dest - size, halfSize);
dest += size;
}
src += childSize;
}
dest += halfSize;
src += childSize;
}
}
*(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(new HeightfieldData(contents));
@ -638,9 +679,6 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post
}
const QByteArray& childContents = child->getContents();
int childSize = glm::sqrt(childContents.size() / (float)BYTES_PER_PIXEL);
if (childSize != size) {
continue; // TODO: handle differently-sized children
}
const int INDEX_MASK = 1;
int xIndex = i & INDEX_MASK;
const int Y_SHIFT = 1;
@ -653,7 +691,8 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post
char* dest = contents.data() + ((zIndex * halfSize * size) + (xIndex * halfSize)) * BYTES_PER_PIXEL;
uchar* src = (uchar*)childContents.data();
int childStride = childSize * BYTES_PER_PIXEL;
int halfStride = halfSize * BYTES_PER_PIXEL;
int stride = size * BYTES_PER_PIXEL;
int halfStride = stride / 2;
int childStep = 2 * BYTES_PER_PIXEL;
int redOffset3 = childStride + BYTES_PER_PIXEL;
int greenOffset1 = BYTES_PER_PIXEL + 1;
@ -662,14 +701,38 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post
int blueOffset1 = BYTES_PER_PIXEL + 2;
int blueOffset2 = childStride + 2;
int blueOffset3 = childStride + BYTES_PER_PIXEL + 2;
for (int z = 0; z < halfSize; z++) {
for (char* end = dest + halfSize * BYTES_PER_PIXEL; dest != end; src += childStep) {
*dest++ = ((int)src[0] + (int)src[BYTES_PER_PIXEL] + (int)src[childStride] + (int)src[redOffset3]) >> 2;
*dest++ = ((int)src[1] + (int)src[greenOffset1] + (int)src[greenOffset2] + (int)src[greenOffset3]) >> 2;
*dest++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2;
if (childSize == size) {
// simple case: one destination value for four child values
for (int z = 0; z < halfSize; z++) {
for (char* end = dest + halfSize * BYTES_PER_PIXEL; dest != end; src += childStep) {
*dest++ = ((int)src[0] + (int)src[BYTES_PER_PIXEL] + (int)src[childStride] + (int)src[redOffset3]) >> 2;
*dest++ = ((int)src[1] + (int)src[greenOffset1] + (int)src[greenOffset2] + (int)src[greenOffset3]) >> 2;
*dest++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2;
}
dest += halfStride;
src += childStride;
}
} else {
// more complex: N destination values for four child values
int halfChildSize = childSize / 2;
int destPerSrc = size / childSize;
for (int z = 0; z < halfChildSize; z++) {
for (uchar* end = src + childSize * BYTES_PER_PIXEL; src != end; src += childStep) {
*dest++ = ((int)src[0] + (int)src[BYTES_PER_PIXEL] + (int)src[childStride] + (int)src[redOffset3]) >> 2;
*dest++ = ((int)src[1] + (int)src[greenOffset1] + (int)src[greenOffset2] + (int)src[greenOffset3]) >> 2;
*dest++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2;
for (int j = 1; j < destPerSrc; j++) {
memcpy(dest, dest - BYTES_PER_PIXEL, BYTES_PER_PIXEL);
dest += BYTES_PER_PIXEL;
}
}
dest += halfStride;
for (int j = 1; j < destPerSrc; j++) {
memcpy(dest, dest - stride, halfStride);
dest += stride;
}
src += childStride;
}
dest += halfStride;
src += childStride;
}
}
*(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(new HeightfieldData(contents));
@ -770,6 +833,29 @@ bool SharedObjectSetAttribute::deepEqual(void* first, void* second) const {
return setsEqual(decodeInline<SharedObjectSet>(first), decodeInline<SharedObjectSet>(second));
}
MetavoxelNode* SharedObjectSetAttribute::expandMetavoxelRoot(const MetavoxelNode& root) {
AttributePointer attribute(this);
MetavoxelNode* newParent = new MetavoxelNode(attribute);
for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) {
MetavoxelNode* newChild = new MetavoxelNode(root.getAttributeValue(attribute));
newParent->setChild(i, newChild);
if (root.isLeaf()) {
continue;
}
MetavoxelNode* grandchild = root.getChild(i);
grandchild->incrementReferenceCount();
int index = MetavoxelNode::getOppositeChildIndex(i);
newChild->setChild(index, grandchild);
for (int j = 1; j < MetavoxelNode::CHILD_COUNT; j++) {
MetavoxelNode* newGrandchild = new MetavoxelNode(attribute);
newChild->setChild((index + j) % MetavoxelNode::CHILD_COUNT, newGrandchild);
}
newChild->mergeChildren(attribute);
}
newParent->mergeChildren(attribute);
return newParent;
}
bool SharedObjectSetAttribute::merge(void*& parent, void* children[], bool postRead) const {
for (int i = 0; i < MERGE_COUNT; i++) {
if (!decodeInline<SharedObjectSet>(children[i]).isEmpty()) {

View file

@ -238,6 +238,10 @@ public:
virtual bool metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot,
const glm::vec3& minimum, float size, const MetavoxelLOD& lod);
/// Expands the specified root, doubling its size in each dimension.
/// \return a new node representing the result
virtual MetavoxelNode* expandMetavoxelRoot(const MetavoxelNode& root);
/// Merges the value of a parent and its children.
/// \param postRead whether or not the merge is happening after a read
/// \return whether or not the children and parent values are all equal
@ -511,6 +515,8 @@ public:
virtual bool deepEqual(void* first, void* second) const;
virtual MetavoxelNode* expandMetavoxelRoot(const MetavoxelNode& root);
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual AttributeValue inherit(const AttributeValue& parentValue) const;

View file

@ -512,33 +512,11 @@ void MetavoxelData::set(const glm::vec3& minimum, const MetavoxelData& data, boo
}
}
static int getOppositeIndex(int index) {
return index ^ MAXIMUM_FLAG_MASK;
}
void MetavoxelData::expand() {
for (QHash<AttributePointer, MetavoxelNode*>::iterator it = _roots.begin(); it != _roots.end(); it++) {
MetavoxelNode* newParent = new MetavoxelNode(it.key());
for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) {
MetavoxelNode* newChild = new MetavoxelNode(it.key());
newParent->setChild(i, newChild);
int index = getOppositeIndex(i);
if (it.value()->isLeaf()) {
newChild->setChild(index, new MetavoxelNode(it.value()->getAttributeValue(it.key())));
} else {
MetavoxelNode* grandchild = it.value()->getChild(i);
grandchild->incrementReferenceCount();
newChild->setChild(index, grandchild);
}
for (int j = 1; j < MetavoxelNode::CHILD_COUNT; j++) {
MetavoxelNode* newGrandchild = new MetavoxelNode(it.key());
newChild->setChild((index + j) % MetavoxelNode::CHILD_COUNT, newGrandchild);
}
newChild->mergeChildren(it.key());
}
newParent->mergeChildren(it.key());
MetavoxelNode* newNode = it.key()->expandMetavoxelRoot(*it.value());
it.value()->decrementReferenceCount(it.key());
it.value() = newParent;
it.value() = newNode;
}
_size *= 2.0f;
}
@ -823,6 +801,10 @@ void MetavoxelStreamState::setMinimum(const glm::vec3& lastMinimum, int index) {
minimum = getNextMinimum(lastMinimum, size, index);
}
int MetavoxelNode::getOppositeChildIndex(int index) {
return index ^ MAXIMUM_FLAG_MASK;
}
MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue, const MetavoxelNode* copyChildren) :
_referenceCount(1) {

View file

@ -197,6 +197,8 @@ public:
static const int CHILD_COUNT = 8;
static int getOppositeChildIndex(int index);
MetavoxelNode(const AttributeValue& attributeValue, const MetavoxelNode* copyChildren = NULL);
MetavoxelNode(const AttributePointer& attribute, const MetavoxelNode* copy);