mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 19:59:28 +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
|
// the distance between height points in texture space
|
||||||
uniform float heightScale;
|
uniform float heightScale;
|
||||||
|
|
||||||
|
// the scale between height and color textures
|
||||||
|
uniform float colorScale;
|
||||||
|
|
||||||
// the interpolated normal
|
// the interpolated normal
|
||||||
varying vec4 normal;
|
varying vec4 normal;
|
||||||
|
|
||||||
|
@ -29,8 +32,8 @@ void main(void) {
|
||||||
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));
|
normal = normalize(gl_ModelViewMatrix * vec4(deltaX, heightScale, deltaZ, 0.0));
|
||||||
|
|
||||||
// pass along the texture coordinates
|
// pass along the scaled/offset texture coordinates
|
||||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * colorScale;
|
||||||
|
|
||||||
// add the height to the position
|
// add the height to the position
|
||||||
float height = texture2D(heightMap, heightCoord).r;
|
float height = texture2D(heightMap, heightCoord).r;
|
||||||
|
|
|
@ -14,9 +14,6 @@
|
||||||
// the height texture
|
// the height texture
|
||||||
uniform sampler2D heightMap;
|
uniform sampler2D heightMap;
|
||||||
|
|
||||||
// the distance between height points in texture space
|
|
||||||
uniform float heightScale;
|
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
// compute the view space coordinates
|
// compute the view space coordinates
|
||||||
float height = texture2D(heightMap, gl_MultiTexCoord0.st).r;
|
float height = texture2D(heightMap, gl_MultiTexCoord0.st).r;
|
||||||
|
|
|
@ -37,6 +37,9 @@ void MetavoxelSystem::init() {
|
||||||
_pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new BufferDataAttribute("pointBuffer"));
|
_pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new BufferDataAttribute("pointBuffer"));
|
||||||
_heightfieldBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(
|
_heightfieldBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(
|
||||||
new BufferDataAttribute("heightfieldBuffer"));
|
new BufferDataAttribute("heightfieldBuffer"));
|
||||||
|
|
||||||
|
_heightfieldBufferAttribute->setLODThresholdMultiplier(
|
||||||
|
AttributeRegistry::getInstance()->getHeightfieldAttribute()->getLODThresholdMultiplier());
|
||||||
}
|
}
|
||||||
|
|
||||||
MetavoxelLOD MetavoxelSystem::getLOD() {
|
MetavoxelLOD MetavoxelSystem::getLOD() {
|
||||||
|
@ -118,10 +121,271 @@ void MetavoxelSystem::render() {
|
||||||
guideToAugmented(renderVisitor);
|
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 {
|
class HeightfieldCursorRenderVisitor : public MetavoxelVisitor {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
HeightfieldCursorRenderVisitor(const MetavoxelLOD& lod, const Box& bounds);
|
HeightfieldCursorRenderVisitor(const Box& bounds);
|
||||||
|
|
||||||
virtual int visit(MetavoxelInfo& info);
|
virtual int visit(MetavoxelInfo& info);
|
||||||
|
|
||||||
|
@ -130,9 +394,9 @@ private:
|
||||||
Box _bounds;
|
Box _bounds;
|
||||||
};
|
};
|
||||||
|
|
||||||
HeightfieldCursorRenderVisitor::HeightfieldCursorRenderVisitor(const MetavoxelLOD& lod, const Box& bounds) :
|
HeightfieldCursorRenderVisitor::HeightfieldCursorRenderVisitor(const Box& bounds) :
|
||||||
MetavoxelVisitor(QVector<AttributePointer>() <<
|
MetavoxelVisitor(QVector<AttributePointer>() <<
|
||||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>(), lod),
|
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()),
|
||||||
_bounds(bounds) {
|
_bounds(bounds) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +436,7 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
glm::vec3 extents(radius, radius, radius);
|
glm::vec3 extents(radius, radius, radius);
|
||||||
HeightfieldCursorRenderVisitor visitor(getLOD(), Box(position - extents, position + extents));
|
HeightfieldCursorRenderVisitor visitor(Box(position - extents, position + extents));
|
||||||
guideToAugmented(visitor);
|
guideToAugmented(visitor);
|
||||||
|
|
||||||
DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release();
|
DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release();
|
||||||
|
@ -332,16 +596,32 @@ void PointBuffer::render(bool cursor) {
|
||||||
_buffer.release();
|
_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,
|
HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale,
|
||||||
const QByteArray& height, const QByteArray& color, bool clearAfterLoading) :
|
const QByteArray& height, const QByteArray& color) :
|
||||||
_translation(translation),
|
_translation(translation),
|
||||||
_scale(scale),
|
_scale(scale),
|
||||||
|
_heightBounds(translation, translation + glm::vec3(scale, scale, scale)),
|
||||||
|
_colorBounds(_heightBounds),
|
||||||
_height(height),
|
_height(height),
|
||||||
_color(color),
|
_color(color),
|
||||||
_clearAfterLoading(clearAfterLoading),
|
|
||||||
_heightTextureID(0),
|
_heightTextureID(0),
|
||||||
_colorTextureID(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() {
|
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 {
|
class HeightfieldPoint {
|
||||||
public:
|
public:
|
||||||
glm::vec2 textureCoord;
|
glm::vec2 textureCoord;
|
||||||
|
@ -366,14 +672,12 @@ void HeightfieldBuffer::render(bool cursor) {
|
||||||
if (_heightTextureID == 0) {
|
if (_heightTextureID == 0) {
|
||||||
glGenTextures(1, &_heightTextureID);
|
glGenTextures(1, &_heightTextureID);
|
||||||
glBindTexture(GL_TEXTURE_2D, _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_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 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,
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, _heightSize, _heightSize, 0,
|
||||||
GL_LUMINANCE, GL_UNSIGNED_BYTE, _height.constData());
|
GL_LUMINANCE, GL_UNSIGNED_BYTE, _height.constData());
|
||||||
if (_clearAfterLoading) {
|
|
||||||
_height.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
glGenTextures(1, &_colorTextureID);
|
glGenTextures(1, &_colorTextureID);
|
||||||
glBindTexture(GL_TEXTURE_2D, _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);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, WHITE_COLOR);
|
||||||
|
|
||||||
} else {
|
} 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());
|
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
|
// create the buffer objects lazily
|
||||||
int sizeWithSkirt = _heightSize + 2;
|
int innerSize = _heightSize - 2 * HeightfieldBuffer::HEIGHT_BORDER;
|
||||||
int vertexCount = sizeWithSkirt * sizeWithSkirt;
|
int vertexCount = _heightSize * _heightSize;
|
||||||
int rows = sizeWithSkirt - 1;
|
int rows = _heightSize - 1;
|
||||||
int indexCount = rows * rows * 4;
|
int indexCount = rows * rows * 3 * 2;
|
||||||
BufferPair& bufferPair = _bufferPairs[_heightSize];
|
BufferPair& bufferPair = _bufferPairs[_heightSize];
|
||||||
if (!bufferPair.first.isCreated()) {
|
if (!bufferPair.first.isCreated()) {
|
||||||
QVector<HeightfieldPoint> vertices(vertexCount);
|
QVector<HeightfieldPoint> vertices(vertexCount);
|
||||||
HeightfieldPoint* point = vertices.data();
|
HeightfieldPoint* point = vertices.data();
|
||||||
|
|
||||||
float step = 1.0f / (_heightSize - 1);
|
float vertexStep = 1.0f / (innerSize - 1);
|
||||||
float z = -step;
|
float z = -vertexStep;
|
||||||
for (int i = 0; i < sizeWithSkirt; i++, z += step) {
|
float textureStep = 1.0f / _heightSize;
|
||||||
float x = -step;
|
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;
|
const float SKIRT_LENGTH = 0.25f;
|
||||||
float baseY = (i == 0 || i == sizeWithSkirt - 1) ? -SKIRT_LENGTH : 0.0f;
|
float baseY = (i == 0 || i == _heightSize - 1) ? -SKIRT_LENGTH : 0.0f;
|
||||||
for (int j = 0; j < sizeWithSkirt; j++, point++, x += step) {
|
for (int j = 0; j < _heightSize; j++, point++, x += vertexStep, s += textureStep) {
|
||||||
point->vertex = glm::vec3(x, (j == 0 || j == sizeWithSkirt - 1) ? -SKIRT_LENGTH : baseY, z);
|
point->vertex = glm::vec3(x, (j == 0 || j == _heightSize - 1) ? -SKIRT_LENGTH : baseY, z);
|
||||||
point->textureCoord = glm::vec2(x, z);
|
point->textureCoord = glm::vec2(s, t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,13 +726,16 @@ void HeightfieldBuffer::render(bool cursor) {
|
||||||
QVector<int> indices(indexCount);
|
QVector<int> indices(indexCount);
|
||||||
int* index = indices.data();
|
int* index = indices.data();
|
||||||
for (int i = 0; i < rows; i++) {
|
for (int i = 0; i < rows; i++) {
|
||||||
int lineIndex = i * sizeWithSkirt;
|
int lineIndex = i * _heightSize;
|
||||||
int nextLineIndex = (i + 1) * sizeWithSkirt;
|
int nextLineIndex = (i + 1) * _heightSize;
|
||||||
for (int j = 0; j < rows; j++) {
|
for (int j = 0; j < rows; j++) {
|
||||||
*index++ = lineIndex + j;
|
*index++ = lineIndex + j;
|
||||||
*index++ = nextLineIndex + j;
|
*index++ = nextLineIndex + j;
|
||||||
|
*index++ = nextLineIndex + j + 1;
|
||||||
|
|
||||||
*index++ = nextLineIndex + j + 1;
|
*index++ = nextLineIndex + j + 1;
|
||||||
*index++ = lineIndex + j + 1;
|
*index++ = lineIndex + j + 1;
|
||||||
|
*index++ = lineIndex + j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,18 +759,16 @@ void HeightfieldBuffer::render(bool cursor) {
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, _heightTextureID);
|
glBindTexture(GL_TEXTURE_2D, _heightTextureID);
|
||||||
|
|
||||||
int heightScaleLocation;
|
if (!cursor) {
|
||||||
if (cursor) {
|
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().setUniformValue(
|
||||||
heightScaleLocation = DefaultMetavoxelRendererImplementation::getCursorHeightScaleLocation();
|
DefaultMetavoxelRendererImplementation::getHeightScaleLocation(), 1.0f / _heightSize);
|
||||||
} else {
|
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().setUniformValue(
|
||||||
heightScaleLocation = DefaultMetavoxelRendererImplementation::getHeightScaleLocation();
|
DefaultMetavoxelRendererImplementation::getColorScaleLocation(), (float)_heightSize / innerSize);
|
||||||
glActiveTexture(GL_TEXTURE1);
|
glActiveTexture(GL_TEXTURE1);
|
||||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().setUniformValue(heightScaleLocation, 1.0f / _heightSize);
|
glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
||||||
|
|
||||||
glDrawRangeElements(GL_QUADS, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
|
||||||
|
|
||||||
if (!cursor) {
|
if (!cursor) {
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
@ -518,17 +823,19 @@ BufferDataAttribute::BufferDataAttribute(const QString& name) :
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BufferDataAttribute::merge(void*& parent, void* children[], bool postRead) const {
|
bool BufferDataAttribute::merge(void*& parent, void* children[], bool postRead) const {
|
||||||
BufferDataPointer firstChild = decodeInline<BufferDataPointer>(children[0]);
|
*(BufferDataPointer*)&parent = _defaultValue;
|
||||||
for (int i = 1; i < MERGE_COUNT; i++) {
|
for (int i = 0; i < MERGE_COUNT; i++) {
|
||||||
if (firstChild != decodeInline<BufferDataPointer>(children[i])) {
|
if (decodeInline<BufferDataPointer>(children[i])) {
|
||||||
*(BufferDataPointer*)&parent = _defaultValue;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*(BufferDataPointer*)&parent = firstChild;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AttributeValue BufferDataAttribute::inherit(const AttributeValue& parentValue) const {
|
||||||
|
return AttributeValue(parentValue.getAttribute());
|
||||||
|
}
|
||||||
|
|
||||||
void DefaultMetavoxelRendererImplementation::init() {
|
void DefaultMetavoxelRendererImplementation::init() {
|
||||||
if (!_pointProgram.isLinked()) {
|
if (!_pointProgram.isLinked()) {
|
||||||
_pointProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert");
|
_pointProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert");
|
||||||
|
@ -548,6 +855,7 @@ void DefaultMetavoxelRendererImplementation::init() {
|
||||||
_heightfieldProgram.setUniformValue("heightMap", 0);
|
_heightfieldProgram.setUniformValue("heightMap", 0);
|
||||||
_heightfieldProgram.setUniformValue("diffuseMap", 1);
|
_heightfieldProgram.setUniformValue("diffuseMap", 1);
|
||||||
_heightScaleLocation = _heightfieldProgram.uniformLocation("heightScale");
|
_heightScaleLocation = _heightfieldProgram.uniformLocation("heightScale");
|
||||||
|
_colorScaleLocation = _heightfieldProgram.uniformLocation("colorScale");
|
||||||
_heightfieldProgram.release();
|
_heightfieldProgram.release();
|
||||||
|
|
||||||
_heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
_heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||||
|
@ -558,7 +866,6 @@ void DefaultMetavoxelRendererImplementation::init() {
|
||||||
|
|
||||||
_heightfieldCursorProgram.bind();
|
_heightfieldCursorProgram.bind();
|
||||||
_heightfieldCursorProgram.setUniformValue("heightMap", 0);
|
_heightfieldCursorProgram.setUniformValue("heightMap", 0);
|
||||||
_cursorHeightScaleLocation = _heightfieldCursorProgram.uniformLocation("heightScale");
|
|
||||||
_heightfieldCursorProgram.release();
|
_heightfieldCursorProgram.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -634,33 +941,279 @@ bool PointAugmentVisitor::postVisit(MetavoxelInfo& info) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
class HeightfieldAugmentVisitor : public MetavoxelVisitor {
|
class HeightfieldFetchVisitor : public MetavoxelVisitor {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
HeightfieldAugmentVisitor(const MetavoxelLOD& lod);
|
HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector<Box>& intersections);
|
||||||
|
|
||||||
|
void init(HeightfieldBuffer* buffer) { _buffer = buffer; }
|
||||||
|
|
||||||
virtual int visit(MetavoxelInfo& info);
|
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() <<
|
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
|
||||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector<AttributePointer>() <<
|
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector<AttributePointer>(), lod),
|
||||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod) {
|
_intersections(intersections) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int HeightfieldAugmentVisitor::visit(MetavoxelInfo& info) {
|
int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) {
|
||||||
if (info.isLeaf) {
|
Box bounds = info.getBounds();
|
||||||
HeightfieldBuffer* buffer = NULL;
|
const Box& heightBounds = _buffer->getHeightBounds();
|
||||||
HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
|
if (!bounds.intersects(heightBounds)) {
|
||||||
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)));
|
|
||||||
return STOP_RECURSION;
|
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,
|
void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous,
|
||||||
|
@ -687,8 +1240,12 @@ void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const
|
||||||
PointAugmentVisitor pointAugmentVisitor(lod);
|
PointAugmentVisitor pointAugmentVisitor(lod);
|
||||||
data.guideToDifferent(expandedPrevious, pointAugmentVisitor);
|
data.guideToDifferent(expandedPrevious, pointAugmentVisitor);
|
||||||
|
|
||||||
HeightfieldAugmentVisitor heightfieldAugmentVisitor(lod);
|
HeightfieldRegionVisitor heightfieldRegionVisitor(lod);
|
||||||
data.guideToDifferent(expandedPrevious, heightfieldAugmentVisitor);
|
data.guideToDifferent(expandedPrevious, heightfieldRegionVisitor);
|
||||||
|
|
||||||
|
HeightfieldUpdateVisitor heightfieldUpdateVisitor(lod, heightfieldRegionVisitor.regions,
|
||||||
|
heightfieldRegionVisitor.regionBounds);
|
||||||
|
data.guide(heightfieldUpdateVisitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpannerSimulateVisitor : public SpannerVisitor {
|
class SpannerSimulateVisitor : public SpannerVisitor {
|
||||||
|
@ -760,7 +1317,7 @@ bool SpannerRenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum,
|
||||||
class BufferRenderVisitor : public MetavoxelVisitor {
|
class BufferRenderVisitor : public MetavoxelVisitor {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
BufferRenderVisitor(const AttributePointer& attribute, const MetavoxelLOD& lod);
|
BufferRenderVisitor(const AttributePointer& attribute);
|
||||||
|
|
||||||
virtual int visit(MetavoxelInfo& info);
|
virtual int visit(MetavoxelInfo& info);
|
||||||
|
|
||||||
|
@ -770,8 +1327,8 @@ private:
|
||||||
int _containmentDepth;
|
int _containmentDepth;
|
||||||
};
|
};
|
||||||
|
|
||||||
BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute, const MetavoxelLOD& lod) :
|
BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute) :
|
||||||
MetavoxelVisitor(QVector<AttributePointer>() << attribute, QVector<AttributePointer>(), lod),
|
MetavoxelVisitor(QVector<AttributePointer>() << attribute),
|
||||||
_order(encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())),
|
_order(encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())),
|
||||||
_containmentDepth(INT_MAX) {
|
_containmentDepth(INT_MAX) {
|
||||||
}
|
}
|
||||||
|
@ -785,11 +1342,14 @@ int BufferRenderVisitor::visit(MetavoxelInfo& info) {
|
||||||
}
|
}
|
||||||
_containmentDepth = (intersection == Frustum::CONTAINS_INTERSECTION) ? _depth : INT_MAX;
|
_containmentDepth = (intersection == Frustum::CONTAINS_INTERSECTION) ? _depth : INT_MAX;
|
||||||
}
|
}
|
||||||
|
if (!info.isLeaf) {
|
||||||
|
return _order;
|
||||||
|
}
|
||||||
BufferDataPointer buffer = info.inputValues.at(0).getInlineValue<BufferDataPointer>();
|
BufferDataPointer buffer = info.inputValues.at(0).getInlineValue<BufferDataPointer>();
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
buffer->render();
|
buffer->render();
|
||||||
}
|
}
|
||||||
return info.isLeaf ? STOP_RECURSION : _order;
|
return STOP_RECURSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) {
|
void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) {
|
||||||
|
@ -818,7 +1378,7 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox
|
||||||
|
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
|
|
||||||
BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute(), lod);
|
BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute());
|
||||||
data.guide(pointRenderVisitor);
|
data.guide(pointRenderVisitor);
|
||||||
|
|
||||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
|
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
|
||||||
|
@ -838,8 +1398,7 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox
|
||||||
|
|
||||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
|
||||||
BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(),
|
BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute());
|
||||||
lod);
|
|
||||||
data.guide(heightfieldRenderVisitor);
|
data.guide(heightfieldRenderVisitor);
|
||||||
|
|
||||||
_heightfieldProgram.release();
|
_heightfieldProgram.release();
|
||||||
|
@ -856,8 +1415,8 @@ ProgramObject DefaultMetavoxelRendererImplementation::_pointProgram;
|
||||||
int DefaultMetavoxelRendererImplementation::_pointScaleLocation;
|
int DefaultMetavoxelRendererImplementation::_pointScaleLocation;
|
||||||
ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldProgram;
|
ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldProgram;
|
||||||
int DefaultMetavoxelRendererImplementation::_heightScaleLocation;
|
int DefaultMetavoxelRendererImplementation::_heightScaleLocation;
|
||||||
|
int DefaultMetavoxelRendererImplementation::_colorScaleLocation;
|
||||||
ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldCursorProgram;
|
ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldCursorProgram;
|
||||||
int DefaultMetavoxelRendererImplementation::_cursorHeightScaleLocation;
|
|
||||||
|
|
||||||
static void enableClipPlane(GLenum plane, float x, float y, float z, float w) {
|
static void enableClipPlane(GLenum plane, float x, float y, float z, float w) {
|
||||||
GLdouble coefficients[] = { x, y, z, w };
|
GLdouble coefficients[] = { x, y, z, w };
|
||||||
|
|
|
@ -45,6 +45,10 @@ public:
|
||||||
|
|
||||||
void renderHeightfieldCursor(const glm::vec3& position, float radius);
|
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);
|
Q_INVOKABLE void deleteTextures(int heightID, int colorID);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -131,29 +135,50 @@ private:
|
||||||
class HeightfieldBuffer : public BufferData {
|
class HeightfieldBuffer : public BufferData {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// Creates a new heightfield buffer.
|
static const int HEIGHT_BORDER;
|
||||||
/// \param clearAfterLoading if true, clear the data arrays after we load them into textures in order to reclaim the space
|
static const int SHARED_EDGE;
|
||||||
HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color,
|
static const int HEIGHT_EXTENSION;
|
||||||
bool clearAfterLoading = true);
|
|
||||||
|
HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color);
|
||||||
~HeightfieldBuffer();
|
~HeightfieldBuffer();
|
||||||
|
|
||||||
const glm::vec3& getTranslation() const { return _translation; }
|
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; }
|
const QByteArray& getHeight() const { return _height; }
|
||||||
|
|
||||||
|
QByteArray& getColor() { return _color; }
|
||||||
const QByteArray& getColor() const { 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);
|
virtual void render(bool cursor = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
glm::vec3 _translation;
|
glm::vec3 _translation;
|
||||||
float _scale;
|
float _scale;
|
||||||
|
Box _heightBounds;
|
||||||
|
Box _colorBounds;
|
||||||
QByteArray _height;
|
QByteArray _height;
|
||||||
QByteArray _color;
|
QByteArray _color;
|
||||||
bool _clearAfterLoading;
|
|
||||||
GLuint _heightTextureID;
|
GLuint _heightTextureID;
|
||||||
GLuint _colorTextureID;
|
GLuint _colorTextureID;
|
||||||
int _heightSize;
|
int _heightSize;
|
||||||
|
float _heightIncrement;
|
||||||
|
int _colorSize;
|
||||||
|
float _colorIncrement;
|
||||||
|
|
||||||
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
|
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
|
||||||
static QHash<int, BufferPair> _bufferPairs;
|
static QHash<int, BufferPair> _bufferPairs;
|
||||||
|
@ -182,6 +207,8 @@ public:
|
||||||
Q_INVOKABLE BufferDataAttribute(const QString& name = QString());
|
Q_INVOKABLE BufferDataAttribute(const QString& name = QString());
|
||||||
|
|
||||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||||
|
|
||||||
|
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Renders metavoxels as points.
|
/// Renders metavoxels as points.
|
||||||
|
@ -194,9 +221,9 @@ public:
|
||||||
|
|
||||||
static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; }
|
static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; }
|
||||||
static int getHeightScaleLocation() { return _heightScaleLocation; }
|
static int getHeightScaleLocation() { return _heightScaleLocation; }
|
||||||
|
static int getColorScaleLocation() { return _colorScaleLocation; }
|
||||||
|
|
||||||
static ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; }
|
static ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; }
|
||||||
static int getCursorHeightScaleLocation() { return _cursorHeightScaleLocation; }
|
|
||||||
|
|
||||||
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
|
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
|
||||||
|
|
||||||
|
@ -211,9 +238,9 @@ private:
|
||||||
|
|
||||||
static ProgramObject _heightfieldProgram;
|
static ProgramObject _heightfieldProgram;
|
||||||
static int _heightScaleLocation;
|
static int _heightScaleLocation;
|
||||||
|
static int _colorScaleLocation;
|
||||||
|
|
||||||
static ProgramObject _heightfieldCursorProgram;
|
static ProgramObject _heightfieldCursorProgram;
|
||||||
static int _cursorHeightScaleLocation;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Base class for spanner renderers; provides clipping.
|
/// Base class for spanner renderers; provides clipping.
|
||||||
|
|
|
@ -956,11 +956,11 @@ void ImportHeightfieldTool::apply() {
|
||||||
HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(bufferData.data());
|
HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(bufferData.data());
|
||||||
MetavoxelData data;
|
MetavoxelData data;
|
||||||
data.setSize(scale);
|
data.setSize(scale);
|
||||||
HeightfieldDataPointer heightPointer(new HeightfieldData(buffer->getHeight()));
|
HeightfieldDataPointer heightPointer(new HeightfieldData(buffer->getUnextendedHeight()));
|
||||||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue(
|
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue(
|
||||||
AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer))));
|
AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer))));
|
||||||
if (!buffer->getColor().isEmpty()) {
|
if (!buffer->getColor().isEmpty()) {
|
||||||
HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getColor()));
|
HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getUnextendedColor()));
|
||||||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue(
|
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue(
|
||||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer))));
|
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer))));
|
||||||
}
|
}
|
||||||
|
@ -1003,34 +1003,38 @@ void ImportHeightfieldTool::updatePreview() {
|
||||||
if (_heightImage.width() > 0 && _heightImage.height() > 0) {
|
if (_heightImage.width() > 0 && _heightImage.height() > 0) {
|
||||||
float z = 0.0f;
|
float z = 0.0f;
|
||||||
int blockSize = pow(2.0, _blockSize->value());
|
int blockSize = pow(2.0, _blockSize->value());
|
||||||
int blockAdvancement = blockSize - 1;
|
int heightSize = blockSize + HeightfieldBuffer::HEIGHT_EXTENSION;
|
||||||
for (int i = 0; i < _heightImage.height(); i += blockAdvancement, z++) {
|
int colorSize = blockSize + HeightfieldBuffer::SHARED_EDGE;
|
||||||
|
for (int i = 0; i < _heightImage.height(); i += blockSize, z++) {
|
||||||
float x = 0.0f;
|
float x = 0.0f;
|
||||||
for (int j = 0; j < _heightImage.width(); j += blockAdvancement, x++) {
|
for (int j = 0; j < _heightImage.width(); j += blockSize, x++) {
|
||||||
QByteArray height(blockSize * blockSize, 0);
|
QByteArray height(heightSize * heightSize, 0);
|
||||||
int rows = qMin(blockSize, _heightImage.height() - i);
|
int extendedI = qMax(i - HeightfieldBuffer::HEIGHT_BORDER, 0);
|
||||||
int columns = qMin(blockSize, _heightImage.width() - j);
|
int extendedJ = qMax(j - HeightfieldBuffer::HEIGHT_BORDER, 0);
|
||||||
const int BYTES_PER_COLOR = 3;
|
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++) {
|
for (int y = 0; y < rows; y++) {
|
||||||
uchar* src = _heightImage.scanLine(i + y) + j * BYTES_PER_COLOR;
|
uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * HeightfieldData::COLOR_BYTES;
|
||||||
char* dest = height.data() + y * blockSize;
|
char* dest = height.data() + (y + offsetY) * heightSize + offsetX;
|
||||||
for (int x = 0; x < columns; x++) {
|
for (int x = 0; x < columns; x++) {
|
||||||
*dest++ = *src;
|
*dest++ = *src;
|
||||||
src += BYTES_PER_COLOR;
|
src += HeightfieldData::COLOR_BYTES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray color;
|
QByteArray color;
|
||||||
if (!_colorImage.isNull()) {
|
if (!_colorImage.isNull()) {
|
||||||
color = QByteArray(blockSize * blockSize * BYTES_PER_COLOR, 0);
|
color = QByteArray(colorSize * colorSize * HeightfieldData::COLOR_BYTES, 0);
|
||||||
rows = qMax(0, qMin(blockSize, _colorImage.height() - i));
|
rows = qMax(0, qMin(colorSize, _colorImage.height() - i));
|
||||||
columns = qMax(0, qMin(blockSize, _colorImage.width() - j));
|
columns = qMax(0, qMin(colorSize, _colorImage.width() - j));
|
||||||
for (int y = 0; y < rows; y++) {
|
for (int y = 0; y < rows; y++) {
|
||||||
memcpy(color.data() + y * blockSize * BYTES_PER_COLOR,
|
memcpy(color.data() + y * colorSize * HeightfieldData::COLOR_BYTES,
|
||||||
_colorImage.scanLine(i + y) + j * BYTES_PER_COLOR, columns * BYTES_PER_COLOR);
|
_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) {
|
_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) :
|
enum HeightfieldImage { NULL_HEIGHTFIELD_IMAGE, NORMAL_HEIGHTFIELD_IMAGE, DEFLATED_HEIGHTFIELD_IMAGE };
|
||||||
_encoded(in.readAligned(bytes)) {
|
|
||||||
|
static QByteArray encodeHeightfieldImage(const QImage& image) {
|
||||||
QImage image = QImage::fromData(_encoded).convertToFormat(QImage::Format_RGB888);
|
if (image.isNull()) {
|
||||||
if (color) {
|
return QByteArray(1, NULL_HEIGHTFIELD_IMAGE);
|
||||||
_contents.resize(image.width() * image.height() * BYTES_PER_PIXEL);
|
}
|
||||||
memcpy(_contents.data(), image.constBits(), _contents.size());
|
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 {
|
} else {
|
||||||
_contents.resize(image.width() * image.height());
|
buffer.putChar(NORMAL_HEIGHTFIELD_IMAGE);
|
||||||
char* dest = _contents.data();
|
image.save(&buffer, "PNG");
|
||||||
for (const uchar* src = image.constBits(), *end = src + _contents.size() * BYTES_PER_PIXEL;
|
return buffer.data();
|
||||||
src != end; src += BYTES_PER_PIXEL) {
|
}
|
||||||
*dest++ = *src;
|
}
|
||||||
|
|
||||||
|
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()) {
|
if (_encoded.isEmpty()) {
|
||||||
QImage image;
|
QImage image;
|
||||||
if (color) {
|
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);
|
image = QImage((uchar*)_contents.data(), size, size, QImage::Format_RGB888);
|
||||||
} else {
|
} else {
|
||||||
int size = glm::sqrt((float)_contents.size());
|
int size = glm::sqrt((float)_contents.size());
|
||||||
|
@ -526,38 +596,170 @@ void HeightfieldData::write(Bitstream& out, bool color) {
|
||||||
*dest++ = *src;
|
*dest++ = *src;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QBuffer buffer(&_encoded);
|
_encoded = encodeHeightfieldImage(image);
|
||||||
buffer.open(QIODevice::WriteOnly);
|
|
||||||
image.save(&buffer, "JPG");
|
|
||||||
}
|
}
|
||||||
out << _encoded.size();
|
out << _encoded.size();
|
||||||
out.writeAligned(_encoded);
|
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) :
|
HeightfieldAttribute::HeightfieldAttribute(const QString& name) :
|
||||||
InlineAttribute<HeightfieldDataPointer>(name) {
|
InlineAttribute<HeightfieldDataPointer>(name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeightfieldAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
|
void HeightfieldAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
|
||||||
if (isLeaf) {
|
if (!isLeaf) {
|
||||||
int size;
|
return;
|
||||||
in >> size;
|
}
|
||||||
if (size == 0) {
|
int size;
|
||||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
|
in >> size;
|
||||||
} else {
|
if (size == 0) {
|
||||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, false));
|
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
|
||||||
}
|
} else {
|
||||||
|
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeightfieldAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
|
void HeightfieldAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
|
||||||
if (isLeaf) {
|
if (!isLeaf) {
|
||||||
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
|
return;
|
||||||
if (data) {
|
}
|
||||||
data->write(out, false);
|
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
|
||||||
} else {
|
if (data) {
|
||||||
out << 0;
|
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 {
|
void HeightfieldColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
|
||||||
if (isLeaf) {
|
if (!isLeaf) {
|
||||||
int size;
|
return;
|
||||||
in >> size;
|
}
|
||||||
if (size == 0) {
|
int size;
|
||||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
|
in >> size;
|
||||||
} else {
|
if (size == 0) {
|
||||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, true));
|
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
|
||||||
}
|
} else {
|
||||||
|
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeightfieldColorAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
|
void HeightfieldColorAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
|
||||||
if (isLeaf) {
|
if (!isLeaf) {
|
||||||
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
|
return;
|
||||||
if (data) {
|
}
|
||||||
data->write(out, true);
|
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
|
||||||
} else {
|
if (data) {
|
||||||
out << 0;
|
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();
|
*(HeightfieldDataPointer*)&parent = HeightfieldDataPointer();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int size = glm::sqrt(maxSize / (float)BYTES_PER_PIXEL);
|
int size = glm::sqrt(maxSize / (float)HeightfieldData::COLOR_BYTES);
|
||||||
QByteArray contents(size * size * BYTES_PER_PIXEL, 0);
|
QByteArray contents(size * size * HeightfieldData::COLOR_BYTES, 0);
|
||||||
int halfSize = size / 2;
|
int halfSize = size / 2;
|
||||||
for (int i = 0; i < MERGE_COUNT; i++) {
|
for (int i = 0; i < MERGE_COUNT; i++) {
|
||||||
HeightfieldDataPointer child = decodeInline<HeightfieldDataPointer>(children[i]);
|
HeightfieldDataPointer child = decodeInline<HeightfieldDataPointer>(children[i]);
|
||||||
|
@ -678,7 +908,7 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const QByteArray& childContents = child->getContents();
|
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;
|
const int INDEX_MASK = 1;
|
||||||
int xIndex = i & INDEX_MASK;
|
int xIndex = i & INDEX_MASK;
|
||||||
const int Y_SHIFT = 1;
|
const int Y_SHIFT = 1;
|
||||||
|
@ -688,24 +918,25 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post
|
||||||
}
|
}
|
||||||
int Z_SHIFT = 2;
|
int Z_SHIFT = 2;
|
||||||
int zIndex = (i >> Z_SHIFT) & INDEX_MASK;
|
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();
|
uchar* src = (uchar*)childContents.data();
|
||||||
int childStride = childSize * BYTES_PER_PIXEL;
|
int childStride = childSize * HeightfieldData::COLOR_BYTES;
|
||||||
int stride = size * BYTES_PER_PIXEL;
|
int stride = size * HeightfieldData::COLOR_BYTES;
|
||||||
int halfStride = stride / 2;
|
int halfStride = stride / 2;
|
||||||
int childStep = 2 * BYTES_PER_PIXEL;
|
int childStep = 2 * HeightfieldData::COLOR_BYTES;
|
||||||
int redOffset3 = childStride + BYTES_PER_PIXEL;
|
int redOffset3 = childStride + HeightfieldData::COLOR_BYTES;
|
||||||
int greenOffset1 = BYTES_PER_PIXEL + 1;
|
int greenOffset1 = HeightfieldData::COLOR_BYTES + 1;
|
||||||
int greenOffset2 = childStride + 1;
|
int greenOffset2 = childStride + 1;
|
||||||
int greenOffset3 = childStride + BYTES_PER_PIXEL + 1;
|
int greenOffset3 = childStride + HeightfieldData::COLOR_BYTES + 1;
|
||||||
int blueOffset1 = BYTES_PER_PIXEL + 2;
|
int blueOffset1 = HeightfieldData::COLOR_BYTES + 2;
|
||||||
int blueOffset2 = childStride + 2;
|
int blueOffset2 = childStride + 2;
|
||||||
int blueOffset3 = childStride + BYTES_PER_PIXEL + 2;
|
int blueOffset3 = childStride + HeightfieldData::COLOR_BYTES + 2;
|
||||||
if (childSize == size) {
|
if (childSize == size) {
|
||||||
// simple case: one destination value for four child values
|
// simple case: one destination value for four child values
|
||||||
for (int z = 0; z < halfSize; z++) {
|
for (int z = 0; z < halfSize; z++) {
|
||||||
for (char* end = dest + halfSize * BYTES_PER_PIXEL; dest != end; src += childStep) {
|
for (char* end = dest + halfSize * HeightfieldData::COLOR_BYTES; dest != end; src += childStep) {
|
||||||
*dest++ = ((int)src[0] + (int)src[BYTES_PER_PIXEL] + (int)src[childStride] + (int)src[redOffset3]) >> 2;
|
*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[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++ = ((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 halfChildSize = childSize / 2;
|
||||||
int destPerSrc = size / childSize;
|
int destPerSrc = size / childSize;
|
||||||
for (int z = 0; z < halfChildSize; z++) {
|
for (int z = 0; z < halfChildSize; z++) {
|
||||||
for (uchar* end = src + childSize * BYTES_PER_PIXEL; src != end; src += childStep) {
|
for (uchar* end = src + childSize * HeightfieldData::COLOR_BYTES; src != end; src += childStep) {
|
||||||
*dest++ = ((int)src[0] + (int)src[BYTES_PER_PIXEL] + (int)src[childStride] + (int)src[redOffset3]) >> 2;
|
*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[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++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2;
|
||||||
for (int j = 1; j < destPerSrc; j++) {
|
for (int j = 1; j < destPerSrc; j++) {
|
||||||
memcpy(dest, dest - BYTES_PER_PIXEL, BYTES_PER_PIXEL);
|
memcpy(dest, dest - HeightfieldData::COLOR_BYTES, HeightfieldData::COLOR_BYTES);
|
||||||
dest += BYTES_PER_PIXEL;
|
dest += HeightfieldData::COLOR_BYTES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dest += halfStride;
|
dest += halfStride;
|
||||||
|
|
|
@ -28,6 +28,7 @@ class QScriptEngine;
|
||||||
class QScriptValue;
|
class QScriptValue;
|
||||||
|
|
||||||
class Attribute;
|
class Attribute;
|
||||||
|
class HeightfieldData;
|
||||||
class MetavoxelData;
|
class MetavoxelData;
|
||||||
class MetavoxelLOD;
|
class MetavoxelLOD;
|
||||||
class MetavoxelNode;
|
class MetavoxelNode;
|
||||||
|
@ -421,26 +422,37 @@ public:
|
||||||
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef QExplicitlySharedDataPointer<HeightfieldData> HeightfieldDataPointer;
|
||||||
|
|
||||||
/// Contains a block of heightfield data.
|
/// Contains a block of heightfield data.
|
||||||
class HeightfieldData : public QSharedData {
|
class HeightfieldData : public QSharedData {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
static const int COLOR_BYTES = 3;
|
||||||
|
|
||||||
HeightfieldData(const QByteArray& contents);
|
HeightfieldData(const QByteArray& contents);
|
||||||
HeightfieldData(Bitstream& in, int bytes, bool color);
|
HeightfieldData(Bitstream& in, int bytes, bool color);
|
||||||
|
HeightfieldData(Bitstream& in, int bytes, const HeightfieldDataPointer& reference, bool color);
|
||||||
|
|
||||||
const QByteArray& getContents() const { return _contents; }
|
const QByteArray& getContents() const { return _contents; }
|
||||||
|
|
||||||
void write(Bitstream& out, bool color);
|
void write(Bitstream& out, bool color);
|
||||||
|
void writeDelta(Bitstream& out, const HeightfieldDataPointer& reference, bool color);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void read(Bitstream& in, int bytes, bool color);
|
||||||
|
void set(const QImage& image, bool color);
|
||||||
|
|
||||||
QByteArray _contents;
|
QByteArray _contents;
|
||||||
QByteArray _encoded;
|
QByteArray _encoded;
|
||||||
QMutex _encodedMutex;
|
QMutex _encodedMutex;
|
||||||
|
|
||||||
|
HeightfieldDataPointer _deltaData;
|
||||||
|
QByteArray _encodedDelta;
|
||||||
|
QMutex _encodedDeltaMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef QExplicitlySharedDataPointer<HeightfieldData> HeightfieldDataPointer;
|
|
||||||
|
|
||||||
/// An attribute that stores heightfield data.
|
/// An attribute that stores heightfield data.
|
||||||
class HeightfieldAttribute : public InlineAttribute<HeightfieldDataPointer> {
|
class HeightfieldAttribute : public InlineAttribute<HeightfieldDataPointer> {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -451,7 +463,10 @@ public:
|
||||||
|
|
||||||
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
|
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
|
||||||
virtual void write(Bitstream& out, 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;
|
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 read(Bitstream& in, void*& value, bool isLeaf) const;
|
||||||
virtual void write(Bitstream& out, 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;
|
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -61,180 +61,6 @@ SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(cons
|
||||||
return closestSpanner;
|
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) {
|
void MetavoxelClientManager::setSphere(const glm::vec3& center, float radius, const QColor& color) {
|
||||||
Sphere* sphere = new Sphere();
|
Sphere* sphere = new Sphere();
|
||||||
sphere->setTranslation(center);
|
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));
|
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() {
|
MetavoxelLOD MetavoxelClientManager::getLOD() {
|
||||||
return MetavoxelLOD();
|
return MetavoxelLOD();
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,16 +37,12 @@ public:
|
||||||
SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
const AttributePointer& attribute, float& distance);
|
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 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 setSpanner(const SharedObjectPointer& object, bool reliable = false);
|
||||||
|
|
||||||
Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, 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.
|
/// Returns the current LOD. This must be thread-safe, as it will be called from the updater thread.
|
||||||
virtual MetavoxelLOD getLOD();
|
virtual MetavoxelLOD getLOD();
|
||||||
|
|
||||||
|
|
|
@ -361,7 +361,7 @@ int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) {
|
||||||
QByteArray contents(pointer->getContents());
|
QByteArray contents(pointer->getContents());
|
||||||
int size = glm::sqrt((float)contents.size());
|
int size = glm::sqrt((float)contents.size());
|
||||||
int highest = size - 1;
|
int highest = size - 1;
|
||||||
float heightScale = highest / info.size;
|
float heightScale = size / info.size;
|
||||||
|
|
||||||
glm::vec3 center = (_edit.position - info.minimum) * heightScale;
|
glm::vec3 center = (_edit.position - info.minimum) * heightScale;
|
||||||
float scaledRadius = _edit.radius * heightScale;
|
float scaledRadius = _edit.radius * heightScale;
|
||||||
|
@ -378,6 +378,7 @@ int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) {
|
||||||
float squaredRadiusReciprocal = 1.0f / squaredRadius;
|
float squaredRadiusReciprocal = 1.0f / squaredRadius;
|
||||||
const int EIGHT_BIT_MAXIMUM = 255;
|
const int EIGHT_BIT_MAXIMUM = 255;
|
||||||
float scaledHeight = _edit.height * EIGHT_BIT_MAXIMUM / info.size;
|
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) {
|
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
|
||||||
uchar* dest = lineDest;
|
uchar* dest = lineDest;
|
||||||
for (float x = startX; x <= endX; x += 1.0f, dest++) {
|
for (float x = startX; x <= endX; x += 1.0f, dest++) {
|
||||||
|
@ -386,14 +387,18 @@ int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) {
|
||||||
if (distanceSquared <= squaredRadius) {
|
if (distanceSquared <= squaredRadius) {
|
||||||
// height falls off towards edges
|
// height falls off towards edges
|
||||||
int value = *dest + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal;
|
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;
|
lineDest += size;
|
||||||
}
|
}
|
||||||
|
if (changed) {
|
||||||
HeightfieldDataPointer newPointer(new HeightfieldData(contents));
|
HeightfieldDataPointer newPointer(new HeightfieldData(contents));
|
||||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer));
|
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer));
|
||||||
|
}
|
||||||
return STOP_RECURSION;
|
return STOP_RECURSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,7 +450,7 @@ int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) {
|
||||||
const int BYTES_PER_PIXEL = 3;
|
const int BYTES_PER_PIXEL = 3;
|
||||||
int size = glm::sqrt((float)contents.size() / BYTES_PER_PIXEL);
|
int size = glm::sqrt((float)contents.size() / BYTES_PER_PIXEL);
|
||||||
int highest = size - 1;
|
int highest = size - 1;
|
||||||
float heightScale = highest / info.size;
|
float heightScale = size / info.size;
|
||||||
|
|
||||||
glm::vec3 center = (_edit.position - info.minimum) * heightScale;
|
glm::vec3 center = (_edit.position - info.minimum) * heightScale;
|
||||||
float scaledRadius = _edit.radius * 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;
|
char* lineDest = contents.data() + (int)z * stride + (int)startX * BYTES_PER_PIXEL;
|
||||||
float squaredRadius = scaledRadius * scaledRadius;
|
float squaredRadius = scaledRadius * scaledRadius;
|
||||||
char red = _edit.color.red(), green = _edit.color.green(), blue = _edit.color.blue();
|
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) {
|
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
|
||||||
char* dest = lineDest;
|
char* dest = lineDest;
|
||||||
for (float x = startX; x <= endX; x += 1.0f, dest += BYTES_PER_PIXEL) {
|
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[0] = red;
|
||||||
dest[1] = green;
|
dest[1] = green;
|
||||||
dest[2] = blue;
|
dest[2] = blue;
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lineDest += stride;
|
lineDest += stride;
|
||||||
}
|
}
|
||||||
|
if (changed) {
|
||||||
HeightfieldDataPointer newPointer(new HeightfieldData(contents));
|
HeightfieldDataPointer newPointer(new HeightfieldData(contents));
|
||||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer));
|
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer));
|
||||||
|
}
|
||||||
return STOP_RECURSION;
|
return STOP_RECURSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -164,6 +164,11 @@ Box::Box(const glm::vec3& minimum, const glm::vec3& maximum) :
|
||||||
minimum(minimum), maximum(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 {
|
bool Box::contains(const glm::vec3& point) const {
|
||||||
return point.x >= minimum.x && point.x <= maximum.x &&
|
return point.x >= minimum.x && point.x <= maximum.x &&
|
||||||
point.y >= minimum.y && point.y <= maximum.y &&
|
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;
|
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 X_MAXIMUM_FLAG = 1;
|
||||||
const int Y_MAXIMUM_FLAG = 2;
|
const int Y_MAXIMUM_FLAG = 2;
|
||||||
const int Z_MAXIMUM_FLAG = 4;
|
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());
|
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 glm::vec3& point) const;
|
||||||
|
|
||||||
bool contains(const Box& other) const;
|
bool contains(const Box& other) const;
|
||||||
|
|
||||||
bool intersects(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); }
|
float getLongestSide() const { return qMax(qMax(maximum.x - minimum.x, maximum.y - minimum.y), maximum.z - minimum.z); }
|
||||||
|
|
||||||
glm::vec3 getVertex(int index) const;
|
glm::vec3 getVertex(int index) const;
|
||||||
|
|
Loading…
Reference in a new issue