mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:24:00 +02:00
Merge pull request #3290 from ey6es/metavoxels
Big change in the way shared borders are handled in order to fix seams between blocks. Fixes for height/ray queries. Minimal rectangles for deltas, switch between PNG and deflated JPG encodings.
This commit is contained in:
commit
28abc6ab2a
12 changed files with 1046 additions and 435 deletions
|
@ -17,6 +17,9 @@ uniform sampler2D heightMap;
|
|||
// the distance between height points in texture space
|
||||
uniform float heightScale;
|
||||
|
||||
// the scale between height and color textures
|
||||
uniform float colorScale;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 normal;
|
||||
|
||||
|
@ -29,8 +32,8 @@ void main(void) {
|
|||
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;
|
||||
// pass along the scaled/offset texture coordinates
|
||||
gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * colorScale;
|
||||
|
||||
// add the height to the position
|
||||
float height = texture2D(heightMap, heightCoord).r;
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
// the height texture
|
||||
uniform sampler2D heightMap;
|
||||
|
||||
// the distance between height points in texture space
|
||||
uniform float heightScale;
|
||||
|
||||
void main(void) {
|
||||
// compute the view space coordinates
|
||||
float height = texture2D(heightMap, gl_MultiTexCoord0.st).r;
|
||||
|
|
|
@ -37,6 +37,9 @@ void MetavoxelSystem::init() {
|
|||
_pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new BufferDataAttribute("pointBuffer"));
|
||||
_heightfieldBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(
|
||||
new BufferDataAttribute("heightfieldBuffer"));
|
||||
|
||||
_heightfieldBufferAttribute->setLODThresholdMultiplier(
|
||||
AttributeRegistry::getInstance()->getHeightfieldAttribute()->getLODThresholdMultiplier());
|
||||
}
|
||||
|
||||
MetavoxelLOD MetavoxelSystem::getLOD() {
|
||||
|
@ -118,10 +121,271 @@ void MetavoxelSystem::render() {
|
|||
guideToAugmented(renderVisitor);
|
||||
}
|
||||
|
||||
class RayHeightfieldIntersectionVisitor : public RayIntersectionVisitor {
|
||||
public:
|
||||
|
||||
float intersectionDistance;
|
||||
|
||||
RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info, float distance);
|
||||
};
|
||||
|
||||
RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm::vec3& origin,
|
||||
const glm::vec3& direction, const MetavoxelLOD& lod) :
|
||||
RayIntersectionVisitor(origin, direction, QVector<AttributePointer>() <<
|
||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>(), lod),
|
||||
intersectionDistance(FLT_MAX) {
|
||||
}
|
||||
|
||||
static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / 255.0f;
|
||||
|
||||
int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance) {
|
||||
if (!info.isLeaf) {
|
||||
return _order;
|
||||
}
|
||||
const HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(
|
||||
info.inputValues.at(0).getInlineValue<BufferDataPointer>().data());
|
||||
if (!buffer) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
const QByteArray& contents = buffer->getHeight();
|
||||
const uchar* src = (const uchar*)contents.constData();
|
||||
int size = glm::sqrt((float)contents.size());
|
||||
int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION;
|
||||
int highest = HeightfieldBuffer::HEIGHT_BORDER + unextendedSize;
|
||||
float heightScale = unextendedSize * EIGHT_BIT_MAXIMUM_RECIPROCAL;
|
||||
|
||||
// find the initial location in heightfield coordinates
|
||||
glm::vec3 entry = (_origin + distance * _direction - info.minimum) * (float)unextendedSize / info.size;
|
||||
entry.x += HeightfieldBuffer::HEIGHT_BORDER;
|
||||
entry.z += HeightfieldBuffer::HEIGHT_BORDER;
|
||||
glm::vec3 floors = glm::floor(entry);
|
||||
glm::vec3 ceils = glm::ceil(entry);
|
||||
if (floors.x == ceils.x) {
|
||||
if (_direction.x > 0.0f) {
|
||||
ceils.x += 1.0f;
|
||||
} else {
|
||||
floors.x -= 1.0f;
|
||||
}
|
||||
}
|
||||
if (floors.z == ceils.z) {
|
||||
if (_direction.z > 0.0f) {
|
||||
ceils.z += 1.0f;
|
||||
} else {
|
||||
floors.z -= 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool withinBounds = true;
|
||||
float accumulatedDistance = 0.0f;
|
||||
while (withinBounds) {
|
||||
// find the heights at the corners of the current cell
|
||||
int floorX = qMin(qMax((int)floors.x, HeightfieldBuffer::HEIGHT_BORDER), highest);
|
||||
int floorZ = qMin(qMax((int)floors.z, HeightfieldBuffer::HEIGHT_BORDER), highest);
|
||||
int ceilX = qMin(qMax((int)ceils.x, HeightfieldBuffer::HEIGHT_BORDER), highest);
|
||||
int ceilZ = qMin(qMax((int)ceils.z, HeightfieldBuffer::HEIGHT_BORDER), highest);
|
||||
float upperLeft = src[floorZ * size + floorX] * heightScale;
|
||||
float upperRight = src[floorZ * size + ceilX] * heightScale;
|
||||
float lowerLeft = src[ceilZ * size + floorX] * heightScale;
|
||||
float lowerRight = src[ceilZ * size + ceilX] * heightScale;
|
||||
|
||||
// find the distance to the next x coordinate
|
||||
float xDistance = FLT_MAX;
|
||||
if (_direction.x > 0.0f) {
|
||||
xDistance = (ceils.x - entry.x) / _direction.x;
|
||||
} else if (_direction.x < 0.0f) {
|
||||
xDistance = (floors.x - entry.x) / _direction.x;
|
||||
}
|
||||
|
||||
// and the distance to the next z coordinate
|
||||
float zDistance = FLT_MAX;
|
||||
if (_direction.z > 0.0f) {
|
||||
zDistance = (ceils.z - entry.z) / _direction.z;
|
||||
} else if (_direction.z < 0.0f) {
|
||||
zDistance = (floors.z - entry.z) / _direction.z;
|
||||
}
|
||||
|
||||
// the exit distance is the lower of those two
|
||||
float exitDistance = qMin(xDistance, zDistance);
|
||||
glm::vec3 exit, nextFloors = floors, nextCeils = ceils;
|
||||
if (exitDistance == FLT_MAX) {
|
||||
if (_direction.y > 0.0f) {
|
||||
return SHORT_CIRCUIT; // line points upwards; no collisions possible
|
||||
}
|
||||
withinBounds = false; // line points downwards; check this cell only
|
||||
|
||||
} else {
|
||||
// find the exit point and the next cell, and determine whether it's still within the bounds
|
||||
exit = entry + exitDistance * _direction;
|
||||
withinBounds = (exit.y >= HeightfieldBuffer::HEIGHT_BORDER && exit.y <= highest);
|
||||
if (exitDistance == xDistance) {
|
||||
if (_direction.x > 0.0f) {
|
||||
nextFloors.x += 1.0f;
|
||||
withinBounds &= (nextCeils.x += 1.0f) <= highest;
|
||||
} else {
|
||||
withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldBuffer::HEIGHT_BORDER;
|
||||
nextCeils.x -= 1.0f;
|
||||
}
|
||||
}
|
||||
if (exitDistance == zDistance) {
|
||||
if (_direction.z > 0.0f) {
|
||||
nextFloors.z += 1.0f;
|
||||
withinBounds &= (nextCeils.z += 1.0f) <= highest;
|
||||
} else {
|
||||
withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldBuffer::HEIGHT_BORDER;
|
||||
nextCeils.z -= 1.0f;
|
||||
}
|
||||
}
|
||||
// check the vertical range of the ray against the ranges of the cell heights
|
||||
if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) ||
|
||||
qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) {
|
||||
entry = exit;
|
||||
floors = nextFloors;
|
||||
ceils = nextCeils;
|
||||
accumulatedDistance += exitDistance;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// having passed the bounds check, we must check against the planes
|
||||
glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z);
|
||||
|
||||
// first check the triangle including the Z+ segment
|
||||
glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft);
|
||||
float lowerProduct = glm::dot(lowerNormal, _direction);
|
||||
if (lowerProduct < 0.0f) {
|
||||
float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct;
|
||||
glm::vec3 intersection = relativeEntry + planeDistance * _direction;
|
||||
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
|
||||
intersection.z >= intersection.x) {
|
||||
intersectionDistance = qMin(intersectionDistance, distance +
|
||||
(accumulatedDistance + planeDistance) * (info.size / unextendedSize));
|
||||
return SHORT_CIRCUIT;
|
||||
}
|
||||
}
|
||||
|
||||
// then the one with the X+ segment
|
||||
glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight);
|
||||
float upperProduct = glm::dot(upperNormal, _direction);
|
||||
if (upperProduct < 0.0f) {
|
||||
float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct;
|
||||
glm::vec3 intersection = relativeEntry + planeDistance * _direction;
|
||||
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
|
||||
intersection.x >= intersection.z) {
|
||||
intersectionDistance = qMin(intersectionDistance, distance +
|
||||
(accumulatedDistance + planeDistance) * (info.size / unextendedSize));
|
||||
return SHORT_CIRCUIT;
|
||||
}
|
||||
}
|
||||
|
||||
// no joy; continue on our way
|
||||
entry = exit;
|
||||
floors = nextFloors;
|
||||
ceils = nextCeils;
|
||||
accumulatedDistance += exitDistance;
|
||||
}
|
||||
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
bool MetavoxelSystem::findFirstRayHeightfieldIntersection(const glm::vec3& origin,
|
||||
const glm::vec3& direction, float& distance) {
|
||||
RayHeightfieldIntersectionVisitor visitor(origin, direction, getLOD());
|
||||
guideToAugmented(visitor);
|
||||
if (visitor.intersectionDistance == FLT_MAX) {
|
||||
return false;
|
||||
}
|
||||
distance = visitor.intersectionDistance;
|
||||
return true;
|
||||
}
|
||||
|
||||
class HeightfieldHeightVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
float height;
|
||||
|
||||
HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
glm::vec3 _location;
|
||||
};
|
||||
|
||||
HeightfieldHeightVisitor::HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() <<
|
||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>(), lod),
|
||||
height(-FLT_MAX),
|
||||
_location(location) {
|
||||
}
|
||||
|
||||
static const int REVERSE_ORDER = MetavoxelVisitor::encodeOrder(7, 6, 5, 4, 3, 2, 1, 0);
|
||||
|
||||
int HeightfieldHeightVisitor::visit(MetavoxelInfo& info) {
|
||||
glm::vec3 relative = _location - info.minimum;
|
||||
if (relative.x < 0.0f || relative.z < 0.0f || relative.x > info.size || relative.z > info.size ||
|
||||
height >= info.minimum.y + info.size) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
if (!info.isLeaf) {
|
||||
return REVERSE_ORDER;
|
||||
}
|
||||
const HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(
|
||||
info.inputValues.at(0).getInlineValue<BufferDataPointer>().data());
|
||||
if (!buffer) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
const QByteArray& contents = buffer->getHeight();
|
||||
const uchar* src = (const uchar*)contents.constData();
|
||||
int size = glm::sqrt((float)contents.size());
|
||||
int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION;
|
||||
int highest = HeightfieldBuffer::HEIGHT_BORDER + unextendedSize;
|
||||
relative *= unextendedSize / info.size;
|
||||
relative.x += HeightfieldBuffer::HEIGHT_BORDER;
|
||||
relative.z += HeightfieldBuffer::HEIGHT_BORDER;
|
||||
|
||||
// find the bounds of the cell containing the point and the shared vertex heights
|
||||
glm::vec3 floors = glm::floor(relative);
|
||||
glm::vec3 ceils = glm::ceil(relative);
|
||||
glm::vec3 fracts = glm::fract(relative);
|
||||
int floorX = qMin(qMax((int)floors.x, HeightfieldBuffer::HEIGHT_BORDER), highest);
|
||||
int floorZ = qMin(qMax((int)floors.z, HeightfieldBuffer::HEIGHT_BORDER), highest);
|
||||
int ceilX = qMin(qMax((int)ceils.x, HeightfieldBuffer::HEIGHT_BORDER), highest);
|
||||
int ceilZ = qMin(qMax((int)ceils.z, HeightfieldBuffer::HEIGHT_BORDER), highest);
|
||||
float upperLeft = src[floorZ * size + floorX];
|
||||
float lowerRight = src[ceilZ * size + ceilX];
|
||||
float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z);
|
||||
|
||||
// the final vertex (and thus which triangle we check) depends on which half we're on
|
||||
if (fracts.x >= fracts.z) {
|
||||
float upperRight = src[floorZ * size + ceilX];
|
||||
interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z),
|
||||
(fracts.x - fracts.z) / (1.0f - fracts.z));
|
||||
|
||||
} else {
|
||||
float lowerLeft = src[ceilZ * size + floorX];
|
||||
interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z);
|
||||
}
|
||||
if (interpolatedHeight == 0.0f) {
|
||||
return STOP_RECURSION; // ignore zero values
|
||||
}
|
||||
|
||||
// convert the interpolated height into world space
|
||||
height = qMax(height, info.minimum.y + interpolatedHeight * info.size * EIGHT_BIT_MAXIMUM_RECIPROCAL);
|
||||
return SHORT_CIRCUIT;
|
||||
}
|
||||
|
||||
float MetavoxelSystem::getHeightfieldHeight(const glm::vec3& location) {
|
||||
HeightfieldHeightVisitor visitor(getLOD(), location);
|
||||
guideToAugmented(visitor);
|
||||
return visitor.height;
|
||||
}
|
||||
|
||||
class HeightfieldCursorRenderVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
HeightfieldCursorRenderVisitor(const MetavoxelLOD& lod, const Box& bounds);
|
||||
HeightfieldCursorRenderVisitor(const Box& bounds);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
|
@ -130,9 +394,9 @@ private:
|
|||
Box _bounds;
|
||||
};
|
||||
|
||||
HeightfieldCursorRenderVisitor::HeightfieldCursorRenderVisitor(const MetavoxelLOD& lod, const Box& bounds) :
|
||||
HeightfieldCursorRenderVisitor::HeightfieldCursorRenderVisitor(const Box& bounds) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() <<
|
||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>(), lod),
|
||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()),
|
||||
_bounds(bounds) {
|
||||
}
|
||||
|
||||
|
@ -172,7 +436,7 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r
|
|||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glm::vec3 extents(radius, radius, radius);
|
||||
HeightfieldCursorRenderVisitor visitor(getLOD(), Box(position - extents, position + extents));
|
||||
HeightfieldCursorRenderVisitor visitor(Box(position - extents, position + extents));
|
||||
guideToAugmented(visitor);
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release();
|
||||
|
@ -332,16 +596,32 @@ void PointBuffer::render(bool cursor) {
|
|||
_buffer.release();
|
||||
}
|
||||
|
||||
const int HeightfieldBuffer::HEIGHT_BORDER = 1;
|
||||
const int HeightfieldBuffer::SHARED_EDGE = 1;
|
||||
const int HeightfieldBuffer::HEIGHT_EXTENSION = 2 * HeightfieldBuffer::HEIGHT_BORDER + HeightfieldBuffer::SHARED_EDGE;
|
||||
|
||||
HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale,
|
||||
const QByteArray& height, const QByteArray& color, bool clearAfterLoading) :
|
||||
const QByteArray& height, const QByteArray& color) :
|
||||
_translation(translation),
|
||||
_scale(scale),
|
||||
_heightBounds(translation, translation + glm::vec3(scale, scale, scale)),
|
||||
_colorBounds(_heightBounds),
|
||||
_height(height),
|
||||
_color(color),
|
||||
_clearAfterLoading(clearAfterLoading),
|
||||
_heightTextureID(0),
|
||||
_colorTextureID(0),
|
||||
_heightSize(glm::sqrt(height.size())) {
|
||||
_heightSize(glm::sqrt(height.size())),
|
||||
_heightIncrement(scale / (_heightSize - HEIGHT_EXTENSION)),
|
||||
_colorSize(glm::sqrt(color.size() / HeightfieldData::COLOR_BYTES)),
|
||||
_colorIncrement(scale / (_colorSize - SHARED_EDGE)) {
|
||||
|
||||
_heightBounds.minimum.x -= _heightIncrement * HEIGHT_BORDER;
|
||||
_heightBounds.minimum.z -= _heightIncrement * HEIGHT_BORDER;
|
||||
_heightBounds.maximum.x += _heightIncrement * (SHARED_EDGE + HEIGHT_BORDER);
|
||||
_heightBounds.maximum.z += _heightIncrement * (SHARED_EDGE + HEIGHT_BORDER);
|
||||
|
||||
_colorBounds.maximum.x += _colorIncrement * SHARED_EDGE;
|
||||
_colorBounds.maximum.z += _colorIncrement * SHARED_EDGE;
|
||||
}
|
||||
|
||||
HeightfieldBuffer::~HeightfieldBuffer() {
|
||||
|
@ -355,6 +635,32 @@ HeightfieldBuffer::~HeightfieldBuffer() {
|
|||
}
|
||||
}
|
||||
|
||||
QByteArray HeightfieldBuffer::getUnextendedHeight() const {
|
||||
int srcSize = glm::sqrt(_height.size());
|
||||
int destSize = srcSize - 3;
|
||||
QByteArray unextended(destSize * destSize, 0);
|
||||
const char* src = _height.constData() + srcSize + 1;
|
||||
char* dest = unextended.data();
|
||||
for (int z = 0; z < destSize; z++, src += srcSize, dest += destSize) {
|
||||
memcpy(dest, src, destSize);
|
||||
}
|
||||
return unextended;
|
||||
}
|
||||
|
||||
QByteArray HeightfieldBuffer::getUnextendedColor() const {
|
||||
int srcSize = glm::sqrt(_color.size() / HeightfieldData::COLOR_BYTES);
|
||||
int destSize = srcSize - 1;
|
||||
QByteArray unextended(destSize * destSize * HeightfieldData::COLOR_BYTES, 0);
|
||||
const char* src = _color.constData();
|
||||
int srcStride = srcSize * HeightfieldData::COLOR_BYTES;
|
||||
char* dest = unextended.data();
|
||||
int destStride = destSize * HeightfieldData::COLOR_BYTES;
|
||||
for (int z = 0; z < destSize; z++, src += srcStride, dest += destStride) {
|
||||
memcpy(dest, src, destStride);
|
||||
}
|
||||
return unextended;
|
||||
}
|
||||
|
||||
class HeightfieldPoint {
|
||||
public:
|
||||
glm::vec2 textureCoord;
|
||||
|
@ -366,14 +672,12 @@ void HeightfieldBuffer::render(bool cursor) {
|
|||
if (_heightTextureID == 0) {
|
||||
glGenTextures(1, &_heightTextureID);
|
||||
glBindTexture(GL_TEXTURE_2D, _heightTextureID);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, _heightSize, _heightSize, 0,
|
||||
GL_LUMINANCE, GL_UNSIGNED_BYTE, _height.constData());
|
||||
if (_clearAfterLoading) {
|
||||
_height.clear();
|
||||
}
|
||||
|
||||
glGenTextures(1, &_colorTextureID);
|
||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||
|
@ -385,32 +689,32 @@ void HeightfieldBuffer::render(bool cursor) {
|
|||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, WHITE_COLOR);
|
||||
|
||||
} else {
|
||||
int colorSize = glm::sqrt(_color.size() / 3);
|
||||
int colorSize = glm::sqrt(_color.size() / HeightfieldData::COLOR_BYTES);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colorSize, colorSize, 0, GL_RGB, GL_UNSIGNED_BYTE, _color.constData());
|
||||
if (_clearAfterLoading) {
|
||||
_color.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
// create the buffer objects lazily
|
||||
int sizeWithSkirt = _heightSize + 2;
|
||||
int vertexCount = sizeWithSkirt * sizeWithSkirt;
|
||||
int rows = sizeWithSkirt - 1;
|
||||
int indexCount = rows * rows * 4;
|
||||
int innerSize = _heightSize - 2 * HeightfieldBuffer::HEIGHT_BORDER;
|
||||
int vertexCount = _heightSize * _heightSize;
|
||||
int rows = _heightSize - 1;
|
||||
int indexCount = rows * rows * 3 * 2;
|
||||
BufferPair& bufferPair = _bufferPairs[_heightSize];
|
||||
if (!bufferPair.first.isCreated()) {
|
||||
QVector<HeightfieldPoint> vertices(vertexCount);
|
||||
HeightfieldPoint* point = vertices.data();
|
||||
|
||||
float step = 1.0f / (_heightSize - 1);
|
||||
float z = -step;
|
||||
for (int i = 0; i < sizeWithSkirt; i++, z += step) {
|
||||
float x = -step;
|
||||
float vertexStep = 1.0f / (innerSize - 1);
|
||||
float z = -vertexStep;
|
||||
float textureStep = 1.0f / _heightSize;
|
||||
float t = textureStep / 2.0f;
|
||||
for (int i = 0; i < _heightSize; i++, z += vertexStep, t += textureStep) {
|
||||
float x = -vertexStep;
|
||||
float s = textureStep / 2.0f;
|
||||
const float SKIRT_LENGTH = 0.25f;
|
||||
float baseY = (i == 0 || i == sizeWithSkirt - 1) ? -SKIRT_LENGTH : 0.0f;
|
||||
for (int j = 0; j < sizeWithSkirt; j++, point++, x += step) {
|
||||
point->vertex = glm::vec3(x, (j == 0 || j == sizeWithSkirt - 1) ? -SKIRT_LENGTH : baseY, z);
|
||||
point->textureCoord = glm::vec2(x, z);
|
||||
float baseY = (i == 0 || i == _heightSize - 1) ? -SKIRT_LENGTH : 0.0f;
|
||||
for (int j = 0; j < _heightSize; j++, point++, x += vertexStep, s += textureStep) {
|
||||
point->vertex = glm::vec3(x, (j == 0 || j == _heightSize - 1) ? -SKIRT_LENGTH : baseY, z);
|
||||
point->textureCoord = glm::vec2(s, t);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -422,13 +726,16 @@ void HeightfieldBuffer::render(bool cursor) {
|
|||
QVector<int> indices(indexCount);
|
||||
int* index = indices.data();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
int lineIndex = i * sizeWithSkirt;
|
||||
int nextLineIndex = (i + 1) * sizeWithSkirt;
|
||||
int lineIndex = i * _heightSize;
|
||||
int nextLineIndex = (i + 1) * _heightSize;
|
||||
for (int j = 0; j < rows; j++) {
|
||||
*index++ = lineIndex + j;
|
||||
*index++ = nextLineIndex + j;
|
||||
*index++ = nextLineIndex + j + 1;
|
||||
|
||||
*index++ = nextLineIndex + j + 1;
|
||||
*index++ = lineIndex + j + 1;
|
||||
*index++ = lineIndex + j;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,18 +759,16 @@ void HeightfieldBuffer::render(bool cursor) {
|
|||
|
||||
glBindTexture(GL_TEXTURE_2D, _heightTextureID);
|
||||
|
||||
int heightScaleLocation;
|
||||
if (cursor) {
|
||||
heightScaleLocation = DefaultMetavoxelRendererImplementation::getCursorHeightScaleLocation();
|
||||
} else {
|
||||
heightScaleLocation = DefaultMetavoxelRendererImplementation::getHeightScaleLocation();
|
||||
if (!cursor) {
|
||||
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().setUniformValue(
|
||||
DefaultMetavoxelRendererImplementation::getHeightScaleLocation(), 1.0f / _heightSize);
|
||||
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().setUniformValue(
|
||||
DefaultMetavoxelRendererImplementation::getColorScaleLocation(), (float)_heightSize / innerSize);
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||
}
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().setUniformValue(heightScaleLocation, 1.0f / _heightSize);
|
||||
|
||||
glDrawRangeElements(GL_QUADS, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
||||
glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
||||
|
||||
if (!cursor) {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
@ -518,17 +823,19 @@ BufferDataAttribute::BufferDataAttribute(const QString& name) :
|
|||
}
|
||||
|
||||
bool BufferDataAttribute::merge(void*& parent, void* children[], bool postRead) const {
|
||||
BufferDataPointer firstChild = decodeInline<BufferDataPointer>(children[0]);
|
||||
for (int i = 1; i < MERGE_COUNT; i++) {
|
||||
if (firstChild != decodeInline<BufferDataPointer>(children[i])) {
|
||||
*(BufferDataPointer*)&parent = _defaultValue;
|
||||
*(BufferDataPointer*)&parent = _defaultValue;
|
||||
for (int i = 0; i < MERGE_COUNT; i++) {
|
||||
if (decodeInline<BufferDataPointer>(children[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*(BufferDataPointer*)&parent = firstChild;
|
||||
return true;
|
||||
}
|
||||
|
||||
AttributeValue BufferDataAttribute::inherit(const AttributeValue& parentValue) const {
|
||||
return AttributeValue(parentValue.getAttribute());
|
||||
}
|
||||
|
||||
void DefaultMetavoxelRendererImplementation::init() {
|
||||
if (!_pointProgram.isLinked()) {
|
||||
_pointProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert");
|
||||
|
@ -548,6 +855,7 @@ void DefaultMetavoxelRendererImplementation::init() {
|
|||
_heightfieldProgram.setUniformValue("heightMap", 0);
|
||||
_heightfieldProgram.setUniformValue("diffuseMap", 1);
|
||||
_heightScaleLocation = _heightfieldProgram.uniformLocation("heightScale");
|
||||
_colorScaleLocation = _heightfieldProgram.uniformLocation("colorScale");
|
||||
_heightfieldProgram.release();
|
||||
|
||||
_heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
|
@ -558,7 +866,6 @@ void DefaultMetavoxelRendererImplementation::init() {
|
|||
|
||||
_heightfieldCursorProgram.bind();
|
||||
_heightfieldCursorProgram.setUniformValue("heightMap", 0);
|
||||
_cursorHeightScaleLocation = _heightfieldCursorProgram.uniformLocation("heightScale");
|
||||
_heightfieldCursorProgram.release();
|
||||
}
|
||||
}
|
||||
|
@ -634,33 +941,279 @@ bool PointAugmentVisitor::postVisit(MetavoxelInfo& info) {
|
|||
return true;
|
||||
}
|
||||
|
||||
class HeightfieldAugmentVisitor : public MetavoxelVisitor {
|
||||
class HeightfieldFetchVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
HeightfieldAugmentVisitor(const MetavoxelLOD& lod);
|
||||
|
||||
HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector<Box>& intersections);
|
||||
|
||||
void init(HeightfieldBuffer* buffer) { _buffer = buffer; }
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
const QVector<Box>& _intersections;
|
||||
HeightfieldBuffer* _buffer;
|
||||
};
|
||||
|
||||
HeightfieldAugmentVisitor::HeightfieldAugmentVisitor(const MetavoxelLOD& lod) :
|
||||
HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector<Box>& intersections) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector<AttributePointer>() <<
|
||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod) {
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector<AttributePointer>(), lod),
|
||||
_intersections(intersections) {
|
||||
}
|
||||
|
||||
int HeightfieldAugmentVisitor::visit(MetavoxelInfo& info) {
|
||||
if (info.isLeaf) {
|
||||
HeightfieldBuffer* buffer = NULL;
|
||||
HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
|
||||
if (height) {
|
||||
HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldDataPointer>();
|
||||
buffer = new HeightfieldBuffer(info.minimum, info.size, height->getContents(),
|
||||
color ? color->getContents() : QByteArray());
|
||||
}
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer)));
|
||||
int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) {
|
||||
Box bounds = info.getBounds();
|
||||
const Box& heightBounds = _buffer->getHeightBounds();
|
||||
if (!bounds.intersects(heightBounds)) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
return DEFAULT_ORDER;
|
||||
if (!info.isLeaf && info.size > _buffer->getScale()) {
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
|
||||
if (!height) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
foreach (const Box& intersection, _intersections) {
|
||||
Box overlap = intersection.getIntersection(bounds);
|
||||
if (overlap.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
float heightIncrement = _buffer->getHeightIncrement();
|
||||
int destX = (overlap.minimum.x - heightBounds.minimum.x) / heightIncrement;
|
||||
int destY = (overlap.minimum.z - heightBounds.minimum.z) / heightIncrement;
|
||||
int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / heightIncrement);
|
||||
int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / heightIncrement);
|
||||
int heightSize = _buffer->getHeightSize();
|
||||
char* dest = _buffer->getHeight().data() + destY * heightSize + destX;
|
||||
|
||||
const QByteArray& srcHeight = height->getContents();
|
||||
int srcSize = glm::sqrt(srcHeight.size());
|
||||
float srcIncrement = info.size / srcSize;
|
||||
|
||||
if (info.size == _buffer->getScale() && srcSize == (heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)) {
|
||||
// easy case: same resolution
|
||||
int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
|
||||
const char* src = srcHeight.constData() + srcY * srcSize + srcX;
|
||||
for (int y = 0; y < destHeight; y++, src += srcSize, dest += heightSize) {
|
||||
memcpy(dest, src, destWidth);
|
||||
}
|
||||
} else {
|
||||
// more difficult: different resolutions
|
||||
float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
float srcAdvance = heightIncrement / srcIncrement;
|
||||
int shift = 0;
|
||||
float size = _buffer->getScale();
|
||||
while (size < info.size) {
|
||||
shift++;
|
||||
size *= 2.0f;
|
||||
}
|
||||
const int EIGHT_BIT_MAXIMUM = 255;
|
||||
int subtract = (_buffer->getTranslation().y - info.minimum.y) * EIGHT_BIT_MAXIMUM / _buffer->getScale();
|
||||
for (int y = 0; y < destHeight; y++, dest += heightSize, srcY += srcAdvance) {
|
||||
const uchar* src = (const uchar*)srcHeight.constData() + (int)srcY * srcSize;
|
||||
float lineSrcX = srcX;
|
||||
for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) {
|
||||
*lineDest = qMin(qMax(0, (src[(int)lineSrcX] << shift) - subtract), EIGHT_BIT_MAXIMUM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int colorSize = _buffer->getColorSize();
|
||||
if (colorSize == 0) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldDataPointer>();
|
||||
if (!color) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
const Box& colorBounds = _buffer->getColorBounds();
|
||||
overlap = colorBounds.getIntersection(overlap);
|
||||
float colorIncrement = _buffer->getColorIncrement();
|
||||
destX = (overlap.minimum.x - colorBounds.minimum.x) / colorIncrement;
|
||||
destY = (overlap.minimum.z - colorBounds.minimum.z) / colorIncrement;
|
||||
destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / colorIncrement);
|
||||
destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / colorIncrement);
|
||||
dest = _buffer->getColor().data() + (destY * colorSize + destX) * HeightfieldData::COLOR_BYTES;
|
||||
int destStride = colorSize * HeightfieldData::COLOR_BYTES;
|
||||
int destBytes = destWidth * HeightfieldData::COLOR_BYTES;
|
||||
|
||||
const QByteArray& srcColor = color->getContents();
|
||||
srcSize = glm::sqrt(srcColor.size() / HeightfieldData::COLOR_BYTES);
|
||||
int srcStride = srcSize * HeightfieldData::COLOR_BYTES;
|
||||
srcIncrement = info.size / srcSize;
|
||||
|
||||
if (srcIncrement == colorIncrement) {
|
||||
// easy case: same resolution
|
||||
int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
|
||||
const char* src = srcColor.constData() + (srcY * srcSize + srcX) * HeightfieldData::COLOR_BYTES;
|
||||
for (int y = 0; y < destHeight; y++, src += srcStride, dest += destStride) {
|
||||
memcpy(dest, src, destBytes);
|
||||
}
|
||||
} else {
|
||||
// more difficult: different resolutions
|
||||
float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
float srcAdvance = colorIncrement / srcIncrement;
|
||||
for (int y = 0; y < destHeight; y++, dest += destStride, srcY += srcAdvance) {
|
||||
const char* src = srcColor.constData() + (int)srcY * srcStride;
|
||||
float lineSrcX = srcX;
|
||||
for (char* lineDest = dest, *end = dest + destBytes; lineDest != end; lineDest += HeightfieldData::COLOR_BYTES,
|
||||
lineSrcX += srcAdvance) {
|
||||
const char* lineSrc = src + (int)lineSrcX * HeightfieldData::COLOR_BYTES;
|
||||
lineDest[0] = lineSrc[0];
|
||||
lineDest[1] = lineSrc[1];
|
||||
lineDest[2] = lineSrc[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
class HeightfieldRegionVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
QVector<Box> regions;
|
||||
Box regionBounds;
|
||||
|
||||
HeightfieldRegionVisitor(const MetavoxelLOD& lod);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
void addRegion(const Box& unextended, const Box& extended);
|
||||
|
||||
QVector<Box> _intersections;
|
||||
HeightfieldFetchVisitor _fetchVisitor;
|
||||
};
|
||||
|
||||
HeightfieldRegionVisitor::HeightfieldRegionVisitor(const MetavoxelLOD& lod) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
|
||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>() <<
|
||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod),
|
||||
regionBounds(glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX), glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX)),
|
||||
_fetchVisitor(lod, _intersections) {
|
||||
}
|
||||
|
||||
int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) {
|
||||
if (!info.isLeaf) {
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
HeightfieldBuffer* buffer = NULL;
|
||||
HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
|
||||
if (height) {
|
||||
const QByteArray& heightContents = height->getContents();
|
||||
int size = glm::sqrt(heightContents.size());
|
||||
int extendedSize = size + HeightfieldBuffer::HEIGHT_EXTENSION;
|
||||
int heightContentsSize = extendedSize * extendedSize;
|
||||
|
||||
HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldDataPointer>();
|
||||
int colorContentsSize = 0;
|
||||
if (color) {
|
||||
const QByteArray& colorContents = color->getContents();
|
||||
int colorSize = glm::sqrt(colorContents.size() / HeightfieldData::COLOR_BYTES);
|
||||
int extendedColorSize = colorSize + HeightfieldBuffer::SHARED_EDGE;
|
||||
colorContentsSize = extendedColorSize * extendedColorSize * HeightfieldData::COLOR_BYTES;
|
||||
}
|
||||
|
||||
const HeightfieldBuffer* existingBuffer = static_cast<const HeightfieldBuffer*>(
|
||||
info.inputValues.at(2).getInlineValue<BufferDataPointer>().data());
|
||||
Box bounds = info.getBounds();
|
||||
if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize &&
|
||||
existingBuffer->getColor().size() == colorContentsSize) {
|
||||
// we already have a buffer of the correct resolution
|
||||
addRegion(bounds, existingBuffer->getHeightBounds());
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
// we must create a new buffer and update its borders
|
||||
buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0),
|
||||
QByteArray(colorContentsSize, 0));
|
||||
const Box& heightBounds = buffer->getHeightBounds();
|
||||
addRegion(bounds, heightBounds);
|
||||
|
||||
_intersections.clear();
|
||||
_intersections.append(Box(heightBounds.minimum,
|
||||
glm::vec3(bounds.maximum.x, heightBounds.maximum.y, bounds.minimum.z)));
|
||||
_intersections.append(Box(glm::vec3(bounds.maximum.x, heightBounds.minimum.y, heightBounds.minimum.z),
|
||||
glm::vec3(heightBounds.maximum.x, heightBounds.maximum.y, bounds.maximum.z)));
|
||||
_intersections.append(Box(glm::vec3(bounds.minimum.x, heightBounds.minimum.y, bounds.maximum.z),
|
||||
heightBounds.maximum));
|
||||
_intersections.append(Box(glm::vec3(heightBounds.minimum.x, heightBounds.minimum.y, bounds.minimum.z),
|
||||
glm::vec3(bounds.minimum.x, heightBounds.maximum.y, heightBounds.maximum.z)));
|
||||
|
||||
_fetchVisitor.init(buffer);
|
||||
_data->guide(_fetchVisitor);
|
||||
}
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer)));
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
void HeightfieldRegionVisitor::addRegion(const Box& unextended, const Box& extended) {
|
||||
regions.append(unextended);
|
||||
regionBounds.add(extended);
|
||||
}
|
||||
|
||||
class HeightfieldUpdateVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
HeightfieldUpdateVisitor(const MetavoxelLOD& lod, const QVector<Box>& regions, const Box& regionBounds);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
const QVector<Box>& _regions;
|
||||
const Box& _regionBounds;
|
||||
QVector<Box> _intersections;
|
||||
HeightfieldFetchVisitor _fetchVisitor;
|
||||
};
|
||||
|
||||
HeightfieldUpdateVisitor::HeightfieldUpdateVisitor(const MetavoxelLOD& lod, const QVector<Box>& regions,
|
||||
const Box& regionBounds) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() <<
|
||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>() <<
|
||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod),
|
||||
_regions(regions),
|
||||
_regionBounds(regionBounds),
|
||||
_fetchVisitor(lod, _intersections) {
|
||||
}
|
||||
|
||||
int HeightfieldUpdateVisitor::visit(MetavoxelInfo& info) {
|
||||
if (!info.getBounds().intersects(_regionBounds)) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
if (!info.isLeaf) {
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
const HeightfieldBuffer* buffer = static_cast<const HeightfieldBuffer*>(
|
||||
info.inputValues.at(0).getInlineValue<BufferDataPointer>().data());
|
||||
if (!buffer) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
_intersections.clear();
|
||||
foreach (const Box& region, _regions) {
|
||||
if (region.intersects(buffer->getHeightBounds())) {
|
||||
_intersections.append(region.getIntersection(buffer->getHeightBounds()));
|
||||
}
|
||||
}
|
||||
if (_intersections.isEmpty()) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
HeightfieldBuffer* newBuffer = new HeightfieldBuffer(info.minimum, info.size,
|
||||
buffer->getHeight(), buffer->getColor());
|
||||
_fetchVisitor.init(newBuffer);
|
||||
_data->guide(_fetchVisitor);
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(newBuffer)));
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous,
|
||||
|
@ -687,8 +1240,12 @@ void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const
|
|||
PointAugmentVisitor pointAugmentVisitor(lod);
|
||||
data.guideToDifferent(expandedPrevious, pointAugmentVisitor);
|
||||
|
||||
HeightfieldAugmentVisitor heightfieldAugmentVisitor(lod);
|
||||
data.guideToDifferent(expandedPrevious, heightfieldAugmentVisitor);
|
||||
HeightfieldRegionVisitor heightfieldRegionVisitor(lod);
|
||||
data.guideToDifferent(expandedPrevious, heightfieldRegionVisitor);
|
||||
|
||||
HeightfieldUpdateVisitor heightfieldUpdateVisitor(lod, heightfieldRegionVisitor.regions,
|
||||
heightfieldRegionVisitor.regionBounds);
|
||||
data.guide(heightfieldUpdateVisitor);
|
||||
}
|
||||
|
||||
class SpannerSimulateVisitor : public SpannerVisitor {
|
||||
|
@ -760,7 +1317,7 @@ bool SpannerRenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum,
|
|||
class BufferRenderVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
BufferRenderVisitor(const AttributePointer& attribute, const MetavoxelLOD& lod);
|
||||
BufferRenderVisitor(const AttributePointer& attribute);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
|
@ -770,8 +1327,8 @@ private:
|
|||
int _containmentDepth;
|
||||
};
|
||||
|
||||
BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute, const MetavoxelLOD& lod) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << attribute, QVector<AttributePointer>(), lod),
|
||||
BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << attribute),
|
||||
_order(encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())),
|
||||
_containmentDepth(INT_MAX) {
|
||||
}
|
||||
|
@ -785,11 +1342,14 @@ int BufferRenderVisitor::visit(MetavoxelInfo& info) {
|
|||
}
|
||||
_containmentDepth = (intersection == Frustum::CONTAINS_INTERSECTION) ? _depth : INT_MAX;
|
||||
}
|
||||
if (!info.isLeaf) {
|
||||
return _order;
|
||||
}
|
||||
BufferDataPointer buffer = info.inputValues.at(0).getInlineValue<BufferDataPointer>();
|
||||
if (buffer) {
|
||||
buffer->render();
|
||||
}
|
||||
return info.isLeaf ? STOP_RECURSION : _order;
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) {
|
||||
|
@ -818,7 +1378,7 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox
|
|||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute(), lod);
|
||||
BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute());
|
||||
data.guide(pointRenderVisitor);
|
||||
|
||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
|
||||
|
@ -838,8 +1398,7 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox
|
|||
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(),
|
||||
lod);
|
||||
BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute());
|
||||
data.guide(heightfieldRenderVisitor);
|
||||
|
||||
_heightfieldProgram.release();
|
||||
|
@ -856,8 +1415,8 @@ ProgramObject DefaultMetavoxelRendererImplementation::_pointProgram;
|
|||
int DefaultMetavoxelRendererImplementation::_pointScaleLocation;
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldProgram;
|
||||
int DefaultMetavoxelRendererImplementation::_heightScaleLocation;
|
||||
int DefaultMetavoxelRendererImplementation::_colorScaleLocation;
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldCursorProgram;
|
||||
int DefaultMetavoxelRendererImplementation::_cursorHeightScaleLocation;
|
||||
|
||||
static void enableClipPlane(GLenum plane, float x, float y, float z, float w) {
|
||||
GLdouble coefficients[] = { x, y, z, w };
|
||||
|
|
|
@ -45,6 +45,10 @@ public:
|
|||
|
||||
void renderHeightfieldCursor(const glm::vec3& position, float radius);
|
||||
|
||||
bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance);
|
||||
|
||||
Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location);
|
||||
|
||||
Q_INVOKABLE void deleteTextures(int heightID, int colorID);
|
||||
|
||||
protected:
|
||||
|
@ -131,29 +135,50 @@ private:
|
|||
class HeightfieldBuffer : public BufferData {
|
||||
public:
|
||||
|
||||
/// Creates a new heightfield buffer.
|
||||
/// \param clearAfterLoading if true, clear the data arrays after we load them into textures in order to reclaim the space
|
||||
HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color,
|
||||
bool clearAfterLoading = true);
|
||||
static const int HEIGHT_BORDER;
|
||||
static const int SHARED_EDGE;
|
||||
static const int HEIGHT_EXTENSION;
|
||||
|
||||
HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color);
|
||||
~HeightfieldBuffer();
|
||||
|
||||
const glm::vec3& getTranslation() const { return _translation; }
|
||||
float getScale() const { return _scale; }
|
||||
|
||||
const Box& getHeightBounds() const { return _heightBounds; }
|
||||
const Box& getColorBounds() const { return _colorBounds; }
|
||||
|
||||
QByteArray& getHeight() { return _height; }
|
||||
const QByteArray& getHeight() const { return _height; }
|
||||
|
||||
QByteArray& getColor() { return _color; }
|
||||
const QByteArray& getColor() const { return _color; }
|
||||
|
||||
QByteArray getUnextendedHeight() const;
|
||||
QByteArray getUnextendedColor() const;
|
||||
|
||||
int getHeightSize() const { return _heightSize; }
|
||||
float getHeightIncrement() const { return _heightIncrement; }
|
||||
|
||||
int getColorSize() const { return _colorSize; }
|
||||
float getColorIncrement() const { return _colorIncrement; }
|
||||
|
||||
virtual void render(bool cursor = false);
|
||||
|
||||
private:
|
||||
|
||||
glm::vec3 _translation;
|
||||
float _scale;
|
||||
Box _heightBounds;
|
||||
Box _colorBounds;
|
||||
QByteArray _height;
|
||||
QByteArray _color;
|
||||
bool _clearAfterLoading;
|
||||
GLuint _heightTextureID;
|
||||
GLuint _colorTextureID;
|
||||
int _heightSize;
|
||||
float _heightIncrement;
|
||||
int _colorSize;
|
||||
float _colorIncrement;
|
||||
|
||||
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
|
||||
static QHash<int, BufferPair> _bufferPairs;
|
||||
|
@ -182,6 +207,8 @@ public:
|
|||
Q_INVOKABLE BufferDataAttribute(const QString& name = QString());
|
||||
|
||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||
|
||||
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
||||
};
|
||||
|
||||
/// Renders metavoxels as points.
|
||||
|
@ -194,9 +221,9 @@ public:
|
|||
|
||||
static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; }
|
||||
static int getHeightScaleLocation() { return _heightScaleLocation; }
|
||||
|
||||
static int getColorScaleLocation() { return _colorScaleLocation; }
|
||||
|
||||
static ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; }
|
||||
static int getCursorHeightScaleLocation() { return _cursorHeightScaleLocation; }
|
||||
|
||||
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
|
||||
|
||||
|
@ -211,9 +238,9 @@ private:
|
|||
|
||||
static ProgramObject _heightfieldProgram;
|
||||
static int _heightScaleLocation;
|
||||
static int _colorScaleLocation;
|
||||
|
||||
static ProgramObject _heightfieldCursorProgram;
|
||||
static int _cursorHeightScaleLocation;
|
||||
};
|
||||
|
||||
/// Base class for spanner renderers; provides clipping.
|
||||
|
|
|
@ -956,11 +956,11 @@ void ImportHeightfieldTool::apply() {
|
|||
HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(bufferData.data());
|
||||
MetavoxelData data;
|
||||
data.setSize(scale);
|
||||
HeightfieldDataPointer heightPointer(new HeightfieldData(buffer->getHeight()));
|
||||
HeightfieldDataPointer heightPointer(new HeightfieldData(buffer->getUnextendedHeight()));
|
||||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue(
|
||||
AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer))));
|
||||
if (!buffer->getColor().isEmpty()) {
|
||||
HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getColor()));
|
||||
HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getUnextendedColor()));
|
||||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue(
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer))));
|
||||
}
|
||||
|
@ -1003,34 +1003,38 @@ void ImportHeightfieldTool::updatePreview() {
|
|||
if (_heightImage.width() > 0 && _heightImage.height() > 0) {
|
||||
float z = 0.0f;
|
||||
int blockSize = pow(2.0, _blockSize->value());
|
||||
int blockAdvancement = blockSize - 1;
|
||||
for (int i = 0; i < _heightImage.height(); i += blockAdvancement, z++) {
|
||||
int heightSize = blockSize + HeightfieldBuffer::HEIGHT_EXTENSION;
|
||||
int colorSize = blockSize + HeightfieldBuffer::SHARED_EDGE;
|
||||
for (int i = 0; i < _heightImage.height(); i += blockSize, z++) {
|
||||
float x = 0.0f;
|
||||
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 j = 0; j < _heightImage.width(); j += blockSize, x++) {
|
||||
QByteArray height(heightSize * heightSize, 0);
|
||||
int extendedI = qMax(i - HeightfieldBuffer::HEIGHT_BORDER, 0);
|
||||
int extendedJ = qMax(j - HeightfieldBuffer::HEIGHT_BORDER, 0);
|
||||
int offsetY = extendedI - i + HeightfieldBuffer::HEIGHT_BORDER;
|
||||
int offsetX = extendedJ - j + HeightfieldBuffer::HEIGHT_BORDER;
|
||||
int rows = qMin(heightSize - offsetY, _heightImage.height() - extendedI);
|
||||
int columns = qMin(heightSize - offsetX, _heightImage.width() - extendedJ);
|
||||
for (int y = 0; y < rows; y++) {
|
||||
uchar* src = _heightImage.scanLine(i + y) + j * BYTES_PER_COLOR;
|
||||
char* dest = height.data() + y * blockSize;
|
||||
uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * HeightfieldData::COLOR_BYTES;
|
||||
char* dest = height.data() + (y + offsetY) * heightSize + offsetX;
|
||||
for (int x = 0; x < columns; x++) {
|
||||
*dest++ = *src;
|
||||
src += BYTES_PER_COLOR;
|
||||
src += HeightfieldData::COLOR_BYTES;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray color;
|
||||
if (!_colorImage.isNull()) {
|
||||
color = QByteArray(blockSize * blockSize * BYTES_PER_COLOR, 0);
|
||||
rows = qMax(0, qMin(blockSize, _colorImage.height() - i));
|
||||
columns = qMax(0, qMin(blockSize, _colorImage.width() - j));
|
||||
color = QByteArray(colorSize * colorSize * HeightfieldData::COLOR_BYTES, 0);
|
||||
rows = qMax(0, qMin(colorSize, _colorImage.height() - i));
|
||||
columns = qMax(0, qMin(colorSize, _colorImage.width() - j));
|
||||
for (int y = 0; y < rows; y++) {
|
||||
memcpy(color.data() + y * blockSize * BYTES_PER_COLOR,
|
||||
_colorImage.scanLine(i + y) + j * BYTES_PER_COLOR, columns * BYTES_PER_COLOR);
|
||||
memcpy(color.data() + y * colorSize * HeightfieldData::COLOR_BYTES,
|
||||
_colorImage.scanLine(i + y) + j * HeightfieldData::COLOR_BYTES,
|
||||
columns * HeightfieldData::COLOR_BYTES);
|
||||
}
|
||||
}
|
||||
buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color, false)));
|
||||
buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -489,22 +489,92 @@ HeightfieldData::HeightfieldData(const QByteArray& contents) :
|
|||
_contents(contents) {
|
||||
}
|
||||
|
||||
const int BYTES_PER_PIXEL = 3;
|
||||
HeightfieldData::HeightfieldData(Bitstream& in, int bytes, bool color) {
|
||||
read(in, bytes, color);
|
||||
}
|
||||
|
||||
HeightfieldData::HeightfieldData(Bitstream& in, int bytes, bool color) :
|
||||
_encoded(in.readAligned(bytes)) {
|
||||
|
||||
QImage image = QImage::fromData(_encoded).convertToFormat(QImage::Format_RGB888);
|
||||
if (color) {
|
||||
_contents.resize(image.width() * image.height() * BYTES_PER_PIXEL);
|
||||
memcpy(_contents.data(), image.constBits(), _contents.size());
|
||||
enum HeightfieldImage { NULL_HEIGHTFIELD_IMAGE, NORMAL_HEIGHTFIELD_IMAGE, DEFLATED_HEIGHTFIELD_IMAGE };
|
||||
|
||||
static QByteArray encodeHeightfieldImage(const QImage& image) {
|
||||
if (image.isNull()) {
|
||||
return QByteArray(1, NULL_HEIGHTFIELD_IMAGE);
|
||||
}
|
||||
QBuffer buffer;
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
const int JPEG_ENCODE_THRESHOLD = 16;
|
||||
if (image.width() >= JPEG_ENCODE_THRESHOLD && image.height() >= JPEG_ENCODE_THRESHOLD) {
|
||||
qint32 offsetX = image.offset().x(), offsetY = image.offset().y();
|
||||
buffer.write((char*)&offsetX, sizeof(qint32));
|
||||
buffer.write((char*)&offsetY, sizeof(qint32));
|
||||
image.save(&buffer, "JPG");
|
||||
return QByteArray(1, DEFLATED_HEIGHTFIELD_IMAGE) + qCompress(buffer.data());
|
||||
|
||||
} else {
|
||||
_contents.resize(image.width() * image.height());
|
||||
char* dest = _contents.data();
|
||||
for (const uchar* src = image.constBits(), *end = src + _contents.size() * BYTES_PER_PIXEL;
|
||||
src != end; src += BYTES_PER_PIXEL) {
|
||||
*dest++ = *src;
|
||||
buffer.putChar(NORMAL_HEIGHTFIELD_IMAGE);
|
||||
image.save(&buffer, "PNG");
|
||||
return buffer.data();
|
||||
}
|
||||
}
|
||||
|
||||
const QImage decodeHeightfieldImage(const QByteArray& data) {
|
||||
switch (data.at(0)) {
|
||||
case NULL_HEIGHTFIELD_IMAGE:
|
||||
default:
|
||||
return QImage();
|
||||
|
||||
case NORMAL_HEIGHTFIELD_IMAGE:
|
||||
return QImage::fromData(QByteArray::fromRawData(data.constData() + 1, data.size() - 1));
|
||||
|
||||
case DEFLATED_HEIGHTFIELD_IMAGE: {
|
||||
QByteArray inflated = qUncompress((const uchar*)data.constData() + 1, data.size() - 1);
|
||||
const int OFFSET_SIZE = sizeof(qint32) * 2;
|
||||
QImage image = QImage::fromData((const uchar*)inflated.constData() + OFFSET_SIZE, inflated.size() - OFFSET_SIZE);
|
||||
const qint32* offsets = (const qint32*)inflated.constData();
|
||||
image.setOffset(QPoint(offsets[0], offsets[1]));
|
||||
return image;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HeightfieldData::HeightfieldData(Bitstream& in, int bytes, const HeightfieldDataPointer& reference, bool color) {
|
||||
if (!reference) {
|
||||
read(in, bytes, color);
|
||||
return;
|
||||
}
|
||||
QMutexLocker locker(&reference->_encodedDeltaMutex);
|
||||
reference->_encodedDelta = in.readAligned(bytes);
|
||||
reference->_deltaData = this;
|
||||
_contents = reference->_contents;
|
||||
QImage image = decodeHeightfieldImage(reference->_encodedDelta);
|
||||
if (image.isNull()) {
|
||||
return;
|
||||
}
|
||||
QPoint offset = image.offset();
|
||||
image = image.convertToFormat(QImage::Format_RGB888);
|
||||
if (offset.x() == 0) {
|
||||
set(image, color);
|
||||
return;
|
||||
}
|
||||
int minX = offset.x() - 1;
|
||||
int minY = offset.y() - 1;
|
||||
if (color) {
|
||||
int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES);
|
||||
char* dest = _contents.data() + (minY * size + minX) * COLOR_BYTES;
|
||||
int destStride = size * COLOR_BYTES;
|
||||
int srcStride = image.width() * COLOR_BYTES;
|
||||
for (int y = 0; y < image.height(); y++) {
|
||||
memcpy(dest, image.constScanLine(y), srcStride);
|
||||
dest += destStride;
|
||||
}
|
||||
} else {
|
||||
int size = glm::sqrt((float)_contents.size());
|
||||
char* lineDest = _contents.data() + minY * size + minX;
|
||||
for (int y = 0; y < image.height(); y++) {
|
||||
const uchar* src = image.constScanLine(y);
|
||||
for (char* dest = lineDest, *end = dest + image.width(); dest != end; dest++, src += COLOR_BYTES) {
|
||||
*dest = *src;
|
||||
}
|
||||
lineDest += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -514,7 +584,7 @@ void HeightfieldData::write(Bitstream& out, bool color) {
|
|||
if (_encoded.isEmpty()) {
|
||||
QImage image;
|
||||
if (color) {
|
||||
int size = glm::sqrt(_contents.size() / (float)BYTES_PER_PIXEL);
|
||||
int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES);
|
||||
image = QImage((uchar*)_contents.data(), size, size, QImage::Format_RGB888);
|
||||
} else {
|
||||
int size = glm::sqrt((float)_contents.size());
|
||||
|
@ -526,38 +596,170 @@ void HeightfieldData::write(Bitstream& out, bool color) {
|
|||
*dest++ = *src;
|
||||
}
|
||||
}
|
||||
QBuffer buffer(&_encoded);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
image.save(&buffer, "JPG");
|
||||
_encoded = encodeHeightfieldImage(image);
|
||||
}
|
||||
out << _encoded.size();
|
||||
out.writeAligned(_encoded);
|
||||
}
|
||||
|
||||
void HeightfieldData::writeDelta(Bitstream& out, const HeightfieldDataPointer& reference, bool color) {
|
||||
if (!reference || reference->getContents().size() != _contents.size()) {
|
||||
write(out, color);
|
||||
return;
|
||||
}
|
||||
QMutexLocker locker(&reference->_encodedDeltaMutex);
|
||||
if (reference->_encodedDelta.isEmpty() || reference->_deltaData != this) {
|
||||
QImage image;
|
||||
int minX, minY;
|
||||
if (color) {
|
||||
int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES);
|
||||
minX = size;
|
||||
minY = size;
|
||||
int maxX = -1, maxY = -1;
|
||||
const char* src = _contents.constData();
|
||||
const char* ref = reference->_contents.constData();
|
||||
for (int y = 0; y < size; y++) {
|
||||
bool difference = false;
|
||||
for (int x = 0; x < size; x++, src += COLOR_BYTES, ref += COLOR_BYTES) {
|
||||
if (src[0] != ref[0] || src[1] != ref[1] || src[2] != ref[2]) {
|
||||
minX = qMin(minX, x);
|
||||
maxX = qMax(maxX, x);
|
||||
difference = true;
|
||||
}
|
||||
}
|
||||
if (difference) {
|
||||
minY = qMin(minY, y);
|
||||
maxY = qMax(maxY, y);
|
||||
}
|
||||
}
|
||||
if (maxX >= minX) {
|
||||
int width = maxX - minX + 1;
|
||||
int height = maxY - minY + 1;
|
||||
image = QImage(width, height, QImage::Format_RGB888);
|
||||
src = _contents.constData() + (minY * size + minX) * COLOR_BYTES;
|
||||
int srcStride = size * COLOR_BYTES;
|
||||
int destStride = width * COLOR_BYTES;
|
||||
for (int y = 0; y < height; y++) {
|
||||
memcpy(image.scanLine(y), src, destStride);
|
||||
src += srcStride;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int size = glm::sqrt((float)_contents.size());
|
||||
minX = size;
|
||||
minY = size;
|
||||
int maxX = -1, maxY = -1;
|
||||
const char* src = _contents.constData();
|
||||
const char* ref = reference->_contents.constData();
|
||||
for (int y = 0; y < size; y++) {
|
||||
bool difference = false;
|
||||
for (int x = 0; x < size; x++) {
|
||||
if (*src++ != *ref++) {
|
||||
minX = qMin(minX, x);
|
||||
maxX = qMax(maxX, x);
|
||||
difference = true;
|
||||
}
|
||||
}
|
||||
if (difference) {
|
||||
minY = qMin(minY, y);
|
||||
maxY = qMax(maxY, y);
|
||||
}
|
||||
}
|
||||
if (maxX >= minX) {
|
||||
int width = qMax(maxX - minX + 1, 0);
|
||||
int height = qMax(maxY - minY + 1, 0);
|
||||
image = QImage(width, height, QImage::Format_RGB888);
|
||||
const uchar* lineSrc = (const uchar*)_contents.constData() + minY * size + minX;
|
||||
for (int y = 0; y < height; y++) {
|
||||
uchar* dest = image.scanLine(y);
|
||||
for (const uchar* src = lineSrc, *end = src + width; src != end; src++) {
|
||||
*dest++ = *src;
|
||||
*dest++ = *src;
|
||||
*dest++ = *src;
|
||||
}
|
||||
lineSrc += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
image.setOffset(QPoint(minX + 1, minY + 1));
|
||||
reference->_encodedDelta = encodeHeightfieldImage(image);
|
||||
reference->_deltaData = this;
|
||||
}
|
||||
out << reference->_encodedDelta.size();
|
||||
out.writeAligned(reference->_encodedDelta);
|
||||
}
|
||||
|
||||
void HeightfieldData::read(Bitstream& in, int bytes, bool color) {
|
||||
set(decodeHeightfieldImage(_encoded = in.readAligned(bytes)).convertToFormat(QImage::Format_RGB888), color);
|
||||
}
|
||||
|
||||
void HeightfieldData::set(const QImage& image, bool color) {
|
||||
if (color) {
|
||||
_contents.resize(image.width() * image.height() * COLOR_BYTES);
|
||||
memcpy(_contents.data(), image.constBits(), _contents.size());
|
||||
|
||||
} else {
|
||||
_contents.resize(image.width() * image.height());
|
||||
char* dest = _contents.data();
|
||||
for (const uchar* src = image.constBits(), *end = src + _contents.size() * COLOR_BYTES;
|
||||
src != end; src += COLOR_BYTES) {
|
||||
*dest++ = *src;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HeightfieldAttribute::HeightfieldAttribute(const QString& name) :
|
||||
InlineAttribute<HeightfieldDataPointer>(name) {
|
||||
}
|
||||
|
||||
void HeightfieldAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
|
||||
if (isLeaf) {
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
|
||||
} else {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, false));
|
||||
}
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
|
||||
} else {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, false));
|
||||
}
|
||||
}
|
||||
|
||||
void HeightfieldAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
|
||||
if (isLeaf) {
|
||||
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
|
||||
if (data) {
|
||||
data->write(out, false);
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
|
||||
if (data) {
|
||||
data->write(out, false);
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
}
|
||||
|
||||
void HeightfieldAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
|
||||
} else {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(
|
||||
in, size, decodeInline<HeightfieldDataPointer>(reference), false));
|
||||
}
|
||||
}
|
||||
|
||||
void HeightfieldAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
|
||||
if (data) {
|
||||
data->writeDelta(out, decodeInline<HeightfieldDataPointer>(reference), false);
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -635,25 +837,53 @@ HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) :
|
|||
}
|
||||
|
||||
void HeightfieldColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
|
||||
if (isLeaf) {
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
|
||||
} else {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, true));
|
||||
}
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
|
||||
} else {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, true));
|
||||
}
|
||||
}
|
||||
|
||||
void HeightfieldColorAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
|
||||
if (isLeaf) {
|
||||
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
|
||||
if (data) {
|
||||
data->write(out, true);
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
|
||||
if (data) {
|
||||
data->write(out, true);
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
}
|
||||
|
||||
void HeightfieldColorAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
|
||||
} else {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(
|
||||
in, size, decodeInline<HeightfieldDataPointer>(reference), true));
|
||||
}
|
||||
}
|
||||
|
||||
void HeightfieldColorAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
|
||||
if (data) {
|
||||
data->writeDelta(out, decodeInline<HeightfieldDataPointer>(reference), true);
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -669,8 +899,8 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post
|
|||
*(HeightfieldDataPointer*)&parent = HeightfieldDataPointer();
|
||||
return true;
|
||||
}
|
||||
int size = glm::sqrt(maxSize / (float)BYTES_PER_PIXEL);
|
||||
QByteArray contents(size * size * BYTES_PER_PIXEL, 0);
|
||||
int size = glm::sqrt(maxSize / (float)HeightfieldData::COLOR_BYTES);
|
||||
QByteArray contents(size * size * HeightfieldData::COLOR_BYTES, 0);
|
||||
int halfSize = size / 2;
|
||||
for (int i = 0; i < MERGE_COUNT; i++) {
|
||||
HeightfieldDataPointer child = decodeInline<HeightfieldDataPointer>(children[i]);
|
||||
|
@ -678,7 +908,7 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post
|
|||
continue;
|
||||
}
|
||||
const QByteArray& childContents = child->getContents();
|
||||
int childSize = glm::sqrt(childContents.size() / (float)BYTES_PER_PIXEL);
|
||||
int childSize = glm::sqrt(childContents.size() / (float)HeightfieldData::COLOR_BYTES);
|
||||
const int INDEX_MASK = 1;
|
||||
int xIndex = i & INDEX_MASK;
|
||||
const int Y_SHIFT = 1;
|
||||
|
@ -688,24 +918,25 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post
|
|||
}
|
||||
int Z_SHIFT = 2;
|
||||
int zIndex = (i >> Z_SHIFT) & INDEX_MASK;
|
||||
char* dest = contents.data() + ((zIndex * halfSize * size) + (xIndex * halfSize)) * BYTES_PER_PIXEL;
|
||||
char* dest = contents.data() + ((zIndex * halfSize * size) + (xIndex * halfSize)) * HeightfieldData::COLOR_BYTES;
|
||||
uchar* src = (uchar*)childContents.data();
|
||||
int childStride = childSize * BYTES_PER_PIXEL;
|
||||
int stride = size * BYTES_PER_PIXEL;
|
||||
int childStride = childSize * HeightfieldData::COLOR_BYTES;
|
||||
int stride = size * HeightfieldData::COLOR_BYTES;
|
||||
int halfStride = stride / 2;
|
||||
int childStep = 2 * BYTES_PER_PIXEL;
|
||||
int redOffset3 = childStride + BYTES_PER_PIXEL;
|
||||
int greenOffset1 = BYTES_PER_PIXEL + 1;
|
||||
int childStep = 2 * HeightfieldData::COLOR_BYTES;
|
||||
int redOffset3 = childStride + HeightfieldData::COLOR_BYTES;
|
||||
int greenOffset1 = HeightfieldData::COLOR_BYTES + 1;
|
||||
int greenOffset2 = childStride + 1;
|
||||
int greenOffset3 = childStride + BYTES_PER_PIXEL + 1;
|
||||
int blueOffset1 = BYTES_PER_PIXEL + 2;
|
||||
int greenOffset3 = childStride + HeightfieldData::COLOR_BYTES + 1;
|
||||
int blueOffset1 = HeightfieldData::COLOR_BYTES + 2;
|
||||
int blueOffset2 = childStride + 2;
|
||||
int blueOffset3 = childStride + BYTES_PER_PIXEL + 2;
|
||||
int blueOffset3 = childStride + HeightfieldData::COLOR_BYTES + 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;
|
||||
for (char* end = dest + halfSize * HeightfieldData::COLOR_BYTES; dest != end; src += childStep) {
|
||||
*dest++ = ((int)src[0] + (int)src[HeightfieldData::COLOR_BYTES] +
|
||||
(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;
|
||||
}
|
||||
|
@ -717,13 +948,14 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post
|
|||
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;
|
||||
for (uchar* end = src + childSize * HeightfieldData::COLOR_BYTES; src != end; src += childStep) {
|
||||
*dest++ = ((int)src[0] + (int)src[HeightfieldData::COLOR_BYTES] +
|
||||
(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;
|
||||
memcpy(dest, dest - HeightfieldData::COLOR_BYTES, HeightfieldData::COLOR_BYTES);
|
||||
dest += HeightfieldData::COLOR_BYTES;
|
||||
}
|
||||
}
|
||||
dest += halfStride;
|
||||
|
|
|
@ -28,6 +28,7 @@ class QScriptEngine;
|
|||
class QScriptValue;
|
||||
|
||||
class Attribute;
|
||||
class HeightfieldData;
|
||||
class MetavoxelData;
|
||||
class MetavoxelLOD;
|
||||
class MetavoxelNode;
|
||||
|
@ -421,26 +422,37 @@ public:
|
|||
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
||||
};
|
||||
|
||||
typedef QExplicitlySharedDataPointer<HeightfieldData> HeightfieldDataPointer;
|
||||
|
||||
/// Contains a block of heightfield data.
|
||||
class HeightfieldData : public QSharedData {
|
||||
public:
|
||||
|
||||
static const int COLOR_BYTES = 3;
|
||||
|
||||
HeightfieldData(const QByteArray& contents);
|
||||
HeightfieldData(Bitstream& in, int bytes, bool color);
|
||||
|
||||
HeightfieldData(Bitstream& in, int bytes, const HeightfieldDataPointer& reference, bool color);
|
||||
|
||||
const QByteArray& getContents() const { return _contents; }
|
||||
|
||||
void write(Bitstream& out, bool color);
|
||||
void writeDelta(Bitstream& out, const HeightfieldDataPointer& reference, bool color);
|
||||
|
||||
private:
|
||||
|
||||
void read(Bitstream& in, int bytes, bool color);
|
||||
void set(const QImage& image, bool color);
|
||||
|
||||
QByteArray _contents;
|
||||
QByteArray _encoded;
|
||||
QMutex _encodedMutex;
|
||||
|
||||
HeightfieldDataPointer _deltaData;
|
||||
QByteArray _encodedDelta;
|
||||
QMutex _encodedDeltaMutex;
|
||||
};
|
||||
|
||||
typedef QExplicitlySharedDataPointer<HeightfieldData> HeightfieldDataPointer;
|
||||
|
||||
/// An attribute that stores heightfield data.
|
||||
class HeightfieldAttribute : public InlineAttribute<HeightfieldDataPointer> {
|
||||
Q_OBJECT
|
||||
|
@ -451,7 +463,10 @@ public:
|
|||
|
||||
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
|
||||
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
|
||||
|
||||
|
||||
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
|
||||
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
|
||||
|
||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||
};
|
||||
|
||||
|
@ -466,6 +481,9 @@ public:
|
|||
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
|
||||
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
|
||||
|
||||
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
|
||||
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
|
||||
|
||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||
};
|
||||
|
||||
|
|
|
@ -61,180 +61,6 @@ SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(cons
|
|||
return closestSpanner;
|
||||
}
|
||||
|
||||
class RayHeightfieldIntersectionVisitor : public RayIntersectionVisitor {
|
||||
public:
|
||||
|
||||
float intersectionDistance;
|
||||
|
||||
RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info, float distance);
|
||||
};
|
||||
|
||||
RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm::vec3& origin,
|
||||
const glm::vec3& direction, const MetavoxelLOD& lod) :
|
||||
RayIntersectionVisitor(origin, direction, QVector<AttributePointer>() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldAttribute(), QVector<AttributePointer>(), lod),
|
||||
intersectionDistance(FLT_MAX) {
|
||||
}
|
||||
|
||||
static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / 255.0f;
|
||||
|
||||
int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance) {
|
||||
if (!info.isLeaf) {
|
||||
return _order;
|
||||
}
|
||||
HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
|
||||
if (!pointer) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
const QByteArray& contents = pointer->getContents();
|
||||
const uchar* src = (const uchar*)contents.constData();
|
||||
int size = glm::sqrt((float)contents.size());
|
||||
int highest = size - 1;
|
||||
float heightScale = highest * EIGHT_BIT_MAXIMUM_RECIPROCAL;
|
||||
|
||||
// find the initial location in heightfield coordinates
|
||||
glm::vec3 entry = (_origin + distance * _direction - info.minimum) * (float)highest / info.size;
|
||||
glm::vec3 floors = glm::floor(entry);
|
||||
glm::vec3 ceils = glm::ceil(entry);
|
||||
if (floors.x == ceils.x) {
|
||||
if (_direction.x > 0.0f) {
|
||||
ceils.x += 1.0f;
|
||||
} else {
|
||||
floors.x -= 1.0f;
|
||||
}
|
||||
}
|
||||
if (floors.z == ceils.z) {
|
||||
if (_direction.z > 0.0f) {
|
||||
ceils.z += 1.0f;
|
||||
} else {
|
||||
floors.z -= 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool withinBounds = true;
|
||||
float accumulatedDistance = 0.0f;
|
||||
while (withinBounds) {
|
||||
// find the heights at the corners of the current cell
|
||||
int floorX = qMin(qMax((int)floors.x, 0), highest);
|
||||
int floorZ = qMin(qMax((int)floors.z, 0), highest);
|
||||
int ceilX = qMin(qMax((int)ceils.x, 0), highest);
|
||||
int ceilZ = qMin(qMax((int)ceils.z, 0), highest);
|
||||
float upperLeft = src[floorZ * size + floorX] * heightScale;
|
||||
float upperRight = src[floorZ * size + ceilX] * heightScale;
|
||||
float lowerLeft = src[ceilZ * size + floorX] * heightScale;
|
||||
float lowerRight = src[ceilZ * size + ceilX] * heightScale;
|
||||
|
||||
// find the distance to the next x coordinate
|
||||
float xDistance = FLT_MAX;
|
||||
if (_direction.x > 0.0f) {
|
||||
xDistance = (ceils.x - entry.x) / _direction.x;
|
||||
} else if (_direction.x < 0.0f) {
|
||||
xDistance = (floors.x - entry.x) / _direction.x;
|
||||
}
|
||||
|
||||
// and the distance to the next z coordinate
|
||||
float zDistance = FLT_MAX;
|
||||
if (_direction.z > 0.0f) {
|
||||
zDistance = (ceils.z - entry.z) / _direction.z;
|
||||
} else if (_direction.z < 0.0f) {
|
||||
zDistance = (floors.z - entry.z) / _direction.z;
|
||||
}
|
||||
|
||||
// the exit distance is the lower of those two
|
||||
float exitDistance = qMin(xDistance, zDistance);
|
||||
glm::vec3 exit, nextFloors = floors, nextCeils = ceils;
|
||||
if (exitDistance == FLT_MAX) {
|
||||
if (_direction.y > 0.0f) {
|
||||
return SHORT_CIRCUIT; // line points upwards; no collisions possible
|
||||
}
|
||||
withinBounds = false; // line points downwards; check this cell only
|
||||
|
||||
} else {
|
||||
// find the exit point and the next cell, and determine whether it's still within the bounds
|
||||
exit = entry + exitDistance * _direction;
|
||||
withinBounds = (exit.y >= 0.0f && exit.y <= highest);
|
||||
if (exitDistance == xDistance) {
|
||||
if (_direction.x > 0.0f) {
|
||||
nextFloors.x += 1.0f;
|
||||
withinBounds &= (nextCeils.x += 1.0f) <= highest;
|
||||
} else {
|
||||
withinBounds &= (nextFloors.x -= 1.0f) >= 0.0f;
|
||||
nextCeils.x -= 1.0f;
|
||||
}
|
||||
}
|
||||
if (exitDistance == zDistance) {
|
||||
if (_direction.z > 0.0f) {
|
||||
nextFloors.z += 1.0f;
|
||||
withinBounds &= (nextCeils.z += 1.0f) <= highest;
|
||||
} else {
|
||||
withinBounds &= (nextFloors.z -= 1.0f) >= 0.0f;
|
||||
nextCeils.z -= 1.0f;
|
||||
}
|
||||
}
|
||||
// check the vertical range of the ray against the ranges of the cell heights
|
||||
if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) ||
|
||||
qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) {
|
||||
entry = exit;
|
||||
floors = nextFloors;
|
||||
ceils = nextCeils;
|
||||
accumulatedDistance += exitDistance;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// having passed the bounds check, we must check against the planes
|
||||
glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z);
|
||||
|
||||
// first check the triangle including the Z+ segment
|
||||
glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft);
|
||||
float lowerProduct = glm::dot(lowerNormal, _direction);
|
||||
if (lowerProduct < 0.0f) {
|
||||
float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct;
|
||||
glm::vec3 intersection = relativeEntry + planeDistance * _direction;
|
||||
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
|
||||
intersection.z >= intersection.x) {
|
||||
intersectionDistance = qMin(intersectionDistance, distance +
|
||||
(accumulatedDistance + planeDistance) * (info.size / highest));
|
||||
return SHORT_CIRCUIT;
|
||||
}
|
||||
}
|
||||
|
||||
// then the one with the X+ segment
|
||||
glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight);
|
||||
float upperProduct = glm::dot(upperNormal, _direction);
|
||||
if (upperProduct < 0.0f) {
|
||||
float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct;
|
||||
glm::vec3 intersection = relativeEntry + planeDistance * _direction;
|
||||
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
|
||||
intersection.x >= intersection.z) {
|
||||
intersectionDistance = qMin(intersectionDistance, distance +
|
||||
(accumulatedDistance + planeDistance) * (info.size / highest));
|
||||
return SHORT_CIRCUIT;
|
||||
}
|
||||
}
|
||||
|
||||
// no joy; continue on our way
|
||||
entry = exit;
|
||||
floors = nextFloors;
|
||||
ceils = nextCeils;
|
||||
accumulatedDistance += exitDistance;
|
||||
}
|
||||
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
bool MetavoxelClientManager::findFirstRayHeightfieldIntersection(const glm::vec3& origin,
|
||||
const glm::vec3& direction, float& distance) {
|
||||
RayHeightfieldIntersectionVisitor visitor(origin, direction, getLOD());
|
||||
guide(visitor);
|
||||
if (visitor.intersectionDistance == FLT_MAX) {
|
||||
return false;
|
||||
}
|
||||
distance = visitor.intersectionDistance;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MetavoxelClientManager::setSphere(const glm::vec3& center, float radius, const QColor& color) {
|
||||
Sphere* sphere = new Sphere();
|
||||
sphere->setTranslation(center);
|
||||
|
@ -252,84 +78,6 @@ void MetavoxelClientManager::applyEdit(const MetavoxelEditMessage& edit, bool re
|
|||
QMetaObject::invokeMethod(_updater, "applyEdit", Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, reliable));
|
||||
}
|
||||
|
||||
class HeightfieldHeightVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
float height;
|
||||
|
||||
HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
glm::vec3 _location;
|
||||
};
|
||||
|
||||
HeightfieldHeightVisitor::HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute(),
|
||||
QVector<AttributePointer>(), lod),
|
||||
height(-FLT_MAX),
|
||||
_location(location) {
|
||||
}
|
||||
|
||||
static const int REVERSE_ORDER = MetavoxelVisitor::encodeOrder(7, 6, 5, 4, 3, 2, 1, 0);
|
||||
|
||||
int HeightfieldHeightVisitor::visit(MetavoxelInfo& info) {
|
||||
glm::vec3 relative = _location - info.minimum;
|
||||
if (relative.x < 0.0f || relative.z < 0.0f || relative.x > info.size || relative.z > info.size ||
|
||||
height >= info.minimum.y + info.size) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
if (!info.isLeaf) {
|
||||
return REVERSE_ORDER;
|
||||
}
|
||||
HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
|
||||
if (!pointer) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
const QByteArray& contents = pointer->getContents();
|
||||
const uchar* src = (const uchar*)contents.constData();
|
||||
int size = glm::sqrt((float)contents.size());
|
||||
int highest = size - 1;
|
||||
relative *= highest / info.size;
|
||||
|
||||
// find the bounds of the cell containing the point and the shared vertex heights
|
||||
glm::vec3 floors = glm::floor(relative);
|
||||
glm::vec3 ceils = glm::ceil(relative);
|
||||
glm::vec3 fracts = glm::fract(relative);
|
||||
int floorX = qMin(qMax((int)floors.x, 0), highest);
|
||||
int floorZ = qMin(qMax((int)floors.z, 0), highest);
|
||||
int ceilX = qMin(qMax((int)ceils.x, 0), highest);
|
||||
int ceilZ = qMin(qMax((int)ceils.z, 0), highest);
|
||||
float upperLeft = src[floorZ * size + floorX];
|
||||
float lowerRight = src[ceilZ * size + ceilX];
|
||||
float interpolatedHeight;
|
||||
|
||||
// the final vertex (and thus which triangle we check) depends on which half we're on
|
||||
if (fracts.x > fracts.z) {
|
||||
float upperRight = src[floorZ * size + ceilX];
|
||||
interpolatedHeight = glm::mix(glm::mix(upperLeft, upperRight, fracts.x), lowerRight, fracts.z);
|
||||
|
||||
} else {
|
||||
float lowerLeft = src[ceilZ * size + floorX];
|
||||
interpolatedHeight = glm::mix(upperLeft, glm::mix(lowerLeft, lowerRight, fracts.x), fracts.z);
|
||||
}
|
||||
if (interpolatedHeight == 0.0f) {
|
||||
return STOP_RECURSION; // ignore zero values
|
||||
}
|
||||
|
||||
// convert the interpolated height into world space
|
||||
height = qMax(height, info.minimum.y + interpolatedHeight * info.size * EIGHT_BIT_MAXIMUM_RECIPROCAL);
|
||||
return SHORT_CIRCUIT;
|
||||
}
|
||||
|
||||
float MetavoxelClientManager::getHeightfieldHeight(const glm::vec3& location) {
|
||||
HeightfieldHeightVisitor visitor(getLOD(), location);
|
||||
guide(visitor);
|
||||
return visitor.height;
|
||||
}
|
||||
|
||||
MetavoxelLOD MetavoxelClientManager::getLOD() {
|
||||
return MetavoxelLOD();
|
||||
}
|
||||
|
|
|
@ -37,16 +37,12 @@ public:
|
|||
SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
const AttributePointer& attribute, float& distance);
|
||||
|
||||
bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance);
|
||||
|
||||
Q_INVOKABLE void setSphere(const glm::vec3& center, float radius, const QColor& color = QColor(Qt::gray));
|
||||
|
||||
Q_INVOKABLE void setSpanner(const SharedObjectPointer& object, bool reliable = false);
|
||||
|
||||
Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false);
|
||||
|
||||
Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location);
|
||||
|
||||
/// Returns the current LOD. This must be thread-safe, as it will be called from the updater thread.
|
||||
virtual MetavoxelLOD getLOD();
|
||||
|
||||
|
|
|
@ -361,7 +361,7 @@ int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) {
|
|||
QByteArray contents(pointer->getContents());
|
||||
int size = glm::sqrt((float)contents.size());
|
||||
int highest = size - 1;
|
||||
float heightScale = highest / info.size;
|
||||
float heightScale = size / info.size;
|
||||
|
||||
glm::vec3 center = (_edit.position - info.minimum) * heightScale;
|
||||
float scaledRadius = _edit.radius * heightScale;
|
||||
|
@ -378,6 +378,7 @@ int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) {
|
|||
float squaredRadiusReciprocal = 1.0f / squaredRadius;
|
||||
const int EIGHT_BIT_MAXIMUM = 255;
|
||||
float scaledHeight = _edit.height * EIGHT_BIT_MAXIMUM / info.size;
|
||||
bool changed = false;
|
||||
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
|
||||
uchar* dest = lineDest;
|
||||
for (float x = startX; x <= endX; x += 1.0f, dest++) {
|
||||
|
@ -386,14 +387,18 @@ int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) {
|
|||
if (distanceSquared <= squaredRadius) {
|
||||
// height falls off towards edges
|
||||
int value = *dest + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal;
|
||||
*dest = qMin(qMax(value, 0), EIGHT_BIT_MAXIMUM);
|
||||
if (value != *dest) {
|
||||
*dest = qMin(qMax(value, 0), EIGHT_BIT_MAXIMUM);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
lineDest += size;
|
||||
}
|
||||
|
||||
HeightfieldDataPointer newPointer(new HeightfieldData(contents));
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer));
|
||||
if (changed) {
|
||||
HeightfieldDataPointer newPointer(new HeightfieldData(contents));
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer));
|
||||
}
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
|
@ -445,7 +450,7 @@ int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) {
|
|||
const int BYTES_PER_PIXEL = 3;
|
||||
int size = glm::sqrt((float)contents.size() / BYTES_PER_PIXEL);
|
||||
int highest = size - 1;
|
||||
float heightScale = highest / info.size;
|
||||
float heightScale = size / info.size;
|
||||
|
||||
glm::vec3 center = (_edit.position - info.minimum) * heightScale;
|
||||
float scaledRadius = _edit.radius * heightScale;
|
||||
|
@ -461,6 +466,7 @@ int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) {
|
|||
char* lineDest = contents.data() + (int)z * stride + (int)startX * BYTES_PER_PIXEL;
|
||||
float squaredRadius = scaledRadius * scaledRadius;
|
||||
char red = _edit.color.red(), green = _edit.color.green(), blue = _edit.color.blue();
|
||||
bool changed = false;
|
||||
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
|
||||
char* dest = lineDest;
|
||||
for (float x = startX; x <= endX; x += 1.0f, dest += BYTES_PER_PIXEL) {
|
||||
|
@ -469,13 +475,15 @@ int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) {
|
|||
dest[0] = red;
|
||||
dest[1] = green;
|
||||
dest[2] = blue;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
lineDest += stride;
|
||||
}
|
||||
|
||||
HeightfieldDataPointer newPointer(new HeightfieldData(contents));
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer));
|
||||
if (changed) {
|
||||
HeightfieldDataPointer newPointer(new HeightfieldData(contents));
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer));
|
||||
}
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
|
|
|
@ -164,6 +164,11 @@ Box::Box(const glm::vec3& minimum, const glm::vec3& maximum) :
|
|||
minimum(minimum), maximum(maximum) {
|
||||
}
|
||||
|
||||
void Box::add(const Box& other) {
|
||||
minimum = glm::min(minimum, other.minimum);
|
||||
maximum = glm::max(maximum, other.maximum);
|
||||
}
|
||||
|
||||
bool Box::contains(const glm::vec3& point) const {
|
||||
return point.x >= minimum.x && point.x <= maximum.x &&
|
||||
point.y >= minimum.y && point.y <= maximum.y &&
|
||||
|
@ -182,6 +187,14 @@ bool Box::intersects(const Box& other) const {
|
|||
other.maximum.z >= minimum.z && other.minimum.z <= maximum.z;
|
||||
}
|
||||
|
||||
Box Box::getIntersection(const Box& other) const {
|
||||
return Box(glm::max(minimum, other.minimum), glm::min(maximum, other.maximum));
|
||||
}
|
||||
|
||||
bool Box::isEmpty() const {
|
||||
return minimum.x >= maximum.x || minimum.y >= maximum.y || minimum.z >= maximum.z;
|
||||
}
|
||||
|
||||
const int X_MAXIMUM_FLAG = 1;
|
||||
const int Y_MAXIMUM_FLAG = 2;
|
||||
const int Z_MAXIMUM_FLAG = 4;
|
||||
|
|
|
@ -44,12 +44,18 @@ public:
|
|||
|
||||
explicit Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3());
|
||||
|
||||
void add(const Box& other);
|
||||
|
||||
bool contains(const glm::vec3& point) const;
|
||||
|
||||
bool contains(const Box& other) const;
|
||||
|
||||
bool intersects(const Box& other) const;
|
||||
|
||||
Box getIntersection(const Box& other) const;
|
||||
|
||||
bool isEmpty() const;
|
||||
|
||||
float getLongestSide() const { return qMax(qMax(maximum.x - minimum.x, maximum.y - minimum.y), maximum.z - minimum.z); }
|
||||
|
||||
glm::vec3 getVertex(int index) const;
|
||||
|
|
Loading…
Reference in a new issue