Migrate getHeightfieldHeight to spanner heightfields.

This commit is contained in:
Andrzej Kapolka 2014-11-13 18:00:25 -08:00
parent dd87a45211
commit 75e1cf8a7b
6 changed files with 137 additions and 88 deletions

View file

@ -393,89 +393,6 @@ bool MetavoxelSystem::findFirstRayVoxelIntersection(const glm::vec3& origin, con
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;
}
void MetavoxelSystem::paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color) {
MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldMaterialEdit(position, radius, SharedObjectPointer(), color)) };
applyEdit(edit, true);

View file

@ -68,8 +68,6 @@ public:
bool findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance);
Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location);
Q_INVOKABLE void paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color);
Q_INVOKABLE void paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material);

View file

@ -63,6 +63,50 @@ SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(cons
return closestSpanner;
}
class HeightfieldHeightVisitor : public SpannerVisitor {
public:
float height;
HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location);
virtual bool visit(Spanner* spanner);
virtual int visit(MetavoxelInfo& info);
private:
glm::vec3 _location;
};
HeightfieldHeightVisitor::HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location) :
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
QVector<AttributePointer>(), QVector<AttributePointer>(), lod),
height(-FLT_MAX),
_location(location) {
}
bool HeightfieldHeightVisitor::visit(Spanner* spanner) {
height = qMax(height, spanner->getHeight(_location));
return true;
}
static const int REVERSE_ORDER = MetavoxelVisitor::encodeOrder(7, 6, 5, 4, 3, 2, 1, 0);
int HeightfieldHeightVisitor::visit(MetavoxelInfo& info) {
if (_location.x < info.minimum.x || _location.z < info.minimum.z || _location.x > info.minimum.x + info.size ||
_location.z > info.minimum.z + info.size) {
return STOP_RECURSION;
}
SpannerVisitor::visit(info);
return (height == -FLT_MAX) ? (info.isLeaf ? STOP_RECURSION : REVERSE_ORDER) : SHORT_CIRCUIT;
}
float MetavoxelClientManager::getHeightfieldHeight(const glm::vec3& location) {
HeightfieldHeightVisitor visitor(getLOD(), location);
guide(visitor);
return visitor.height;
}
void MetavoxelClientManager::paintHeightfieldHeight(const glm::vec3& position, float radius, float height) {
MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldHeightEdit(position, radius, height)) };
applyEdit(edit, true);

View file

@ -37,6 +37,8 @@ public:
SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction,
const AttributePointer& attribute, float& distance);
Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location);
Q_INVOKABLE void paintHeightfieldHeight(const glm::vec3& position, float radius, float height);
Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false);

View file

@ -96,6 +96,10 @@ SpannerRenderer* Spanner::getRenderer() {
return _renderer;
}
float Spanner::getHeight(const glm::vec3& location) const {
return -FLT_MAX;
}
bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
return _bounds.findRayIntersection(origin, direction, distance);
}
@ -600,6 +604,8 @@ bool TempHeightfield::intersects(const glm::vec3& start, const glm::vec3& end, f
return false;
}
const int HeightfieldData::SHARED_EDGE = 1;
HeightfieldData::HeightfieldData(int width) :
_width(width) {
}
@ -676,6 +682,9 @@ static QVector<quint16> decodeHeightfieldHeight(const QByteArray& encoded, int&
return unfiltered;
}
const int HeightfieldHeight::HEIGHT_BORDER = 1;
const int HeightfieldHeight::HEIGHT_EXTENSION = SHARED_EDGE + 2 * HEIGHT_BORDER;
HeightfieldHeight::HeightfieldHeight(int width, const QVector<quint16>& contents) :
HeightfieldData(width),
_contents(contents) {
@ -1385,6 +1394,78 @@ void Heightfield::setMaterial(const HeightfieldMaterialPointer& material) {
}
}
float Heightfield::getHeight(const glm::vec3& location) const {
if (!_height) {
return -FLT_MAX;
}
int width = _height->getWidth();
const QVector<quint16>& contents = _height->getContents();
const quint16* src = contents.constData();
int height = contents.size() / width;
int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION;
int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION;
int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER;
int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER;
glm::vec3 relative = glm::inverse(getRotation()) * (location - getTranslation()) * glm::vec3(1.0f / getScale(),
1.0f, 1.0f / (getScale() * _aspectZ));
if (relative.x < 0.0f || relative.z < 0.0f || relative.x > 1.0f || relative.z > 1.0f) {
return -FLT_MAX;
}
relative.x = relative.x * innerWidth + HeightfieldHeight::HEIGHT_BORDER;
relative.z = relative.z * innerHeight + HeightfieldHeight::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, HeightfieldHeight::HEIGHT_BORDER), highestX);
int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ);
int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX);
int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ);
float upperLeft = src[floorZ * width + floorX];
float lowerRight = src[ceilZ * width + 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 * width + ceilX];
interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z),
(fracts.x - fracts.z) / (1.0f - fracts.z));
} else {
float lowerLeft = src[ceilZ * width + floorX];
interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z);
}
if (interpolatedHeight == 0.0f) {
return -FLT_MAX; // ignore zero values
}
// convert the interpolated height into world space
return getTranslation().y + interpolatedHeight * getScale() * _aspectY / numeric_limits<quint16>::max();
}
bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
if (!_height) {
return false;
}
float boundsDistance;
if (!getBounds().findRayIntersection(origin, direction, boundsDistance)) {
return false;
}
int width = _height->getWidth();
const QVector<quint16>& contents = _height->getContents();
int height = contents.size() / width;
glm::quat inverseRotation = glm::inverse(getRotation());
glm::vec3 inverseScale(1.0f / getScale(), 1.0f / (getScale() * _aspectY), 1.0f / (getScale() * _aspectZ));
glm::vec3 dir = inverseRotation * direction * inverseScale;
glm::vec3 entry = inverseRotation * (origin + direction * boundsDistance - getTranslation()) * inverseScale;
return false;
}
QByteArray Heightfield::getRendererClassName() const {
return "HeightfieldRenderer";
}

View file

@ -55,6 +55,9 @@ public:
/// Returns a pointer to the renderer, creating it if necessary.
SpannerRenderer* getRenderer();
/// Finds the height at the specified location, or returns -FLT_MAX for none.
virtual float getHeight(const glm::vec3& location) const;
/// Finds the intersection between the described ray and this spanner.
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
@ -303,7 +306,7 @@ private:
class HeightfieldData : public DataBlock {
public:
static const int SHARED_EDGE = 1;
static const int SHARED_EDGE;
HeightfieldData(int width = 0);
@ -320,8 +323,8 @@ typedef QExplicitlySharedDataPointer<HeightfieldHeight> HeightfieldHeightPointer
class HeightfieldHeight : public HeightfieldData {
public:
static const int HEIGHT_BORDER = 1;
static const int HEIGHT_EXTENSION = SHARED_EDGE + 2 * HEIGHT_BORDER;
static const int HEIGHT_BORDER;
static const int HEIGHT_EXTENSION;
HeightfieldHeight(int width, const QVector<quint16>& contents);
HeightfieldHeight(Bitstream& in, int bytes);
@ -501,6 +504,10 @@ public:
void setMaterial(const HeightfieldMaterialPointer& material);
const HeightfieldMaterialPointer& getMaterial() const { return _material; }
virtual float getHeight(const glm::vec3& location) const;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
signals:
void aspectYChanged(float aspectY);