mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-14 15:31:24 +02:00
Migrate getHeightfieldHeight to spanner heightfields.
This commit is contained in:
parent
dd87a45211
commit
75e1cf8a7b
6 changed files with 137 additions and 88 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue