Merge pull request #3269 from ey6es/metavoxels

Script access for heightfield heights, ray intersection against heightfields, basic color and height brush tools.
This commit is contained in:
Philip Rosedale 2014-08-12 00:40:14 -07:00
commit 0e72c6a0b6
12 changed files with 758 additions and 12 deletions

View file

@ -0,0 +1,32 @@
#version 120
//
// metavoxel_heightfield_cursor.frag
// fragment shader
//
// Created by Andrzej Kapolka on 8/7/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the inner radius of the outline, squared
const float SQUARED_OUTLINE_INNER_RADIUS = 0.81;
// the outer radius of the outline, squared
const float SQUARED_OUTLINE_OUTER_RADIUS = 1.0;
// the inner radius of the inset, squared
const float SQUARED_INSET_INNER_RADIUS = 0.855625;
// the outer radius of the inset, squared
const float SQUARED_INSET_OUTER_RADIUS = 0.950625;
void main(void) {
// use the distance to compute the ring color, then multiply it by the varying color
float squaredDistance = dot(gl_TexCoord[0].st, gl_TexCoord[0].st);
float alpha = step(SQUARED_OUTLINE_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_OUTLINE_OUTER_RADIUS);
float white = step(SQUARED_INSET_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_INSET_OUTER_RADIUS);
gl_FragColor = gl_Color * vec4(white, white, white, alpha);
}

View file

@ -0,0 +1,31 @@
#version 120
//
// metavoxel_heighfield_cursor.vert
// vertex shader
//
// Created by Andrzej Kapolka on 8/7/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the height texture
uniform sampler2D heightMap;
// the distance between height points in texture space
uniform float heightScale;
void main(void) {
// compute the view space coordinates
float height = texture2D(heightMap, gl_MultiTexCoord0.st).r;
vec4 viewPosition = gl_ModelViewMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
gl_Position = gl_ProjectionMatrix * viewPosition;
// generate the texture coordinates from the view position
gl_TexCoord[0] = vec4(dot(viewPosition, gl_EyePlaneS[4]), dot(viewPosition, gl_EyePlaneT[4]), 0.0, 1.0);
// the zero height should be invisible
gl_FrontColor = vec4(1.0, 1.0, 1.0, 1.0 - step(height, 0.0));
}

View file

@ -118,6 +118,73 @@ void MetavoxelSystem::render() {
guideToAugmented(renderVisitor);
}
class HeightfieldCursorRenderVisitor : public MetavoxelVisitor {
public:
HeightfieldCursorRenderVisitor(const MetavoxelLOD& lod, const Box& bounds);
virtual int visit(MetavoxelInfo& info);
private:
Box _bounds;
};
HeightfieldCursorRenderVisitor::HeightfieldCursorRenderVisitor(const MetavoxelLOD& lod, const Box& bounds) :
MetavoxelVisitor(QVector<AttributePointer>() <<
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>(), lod),
_bounds(bounds) {
}
int HeightfieldCursorRenderVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
BufferDataPointer buffer = info.inputValues.at(0).getInlineValue<BufferDataPointer>();
if (buffer) {
buffer->render(true);
}
return STOP_RECURSION;
}
void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float radius) {
glDepthFunc(GL_LEQUAL);
glEnable(GL_CULL_FACE);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1.0f, -1.0f);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().bind();
glActiveTexture(GL_TEXTURE4);
float scale = 1.0f / radius;
glm::vec4 sCoefficients(scale, 0.0f, 0.0f, -scale * position.x);
glm::vec4 tCoefficients(0.0f, 0.0f, scale, -scale * position.z);
glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&sCoefficients);
glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&tCoefficients);
glActiveTexture(GL_TEXTURE0);
glm::vec3 extents(radius, radius, radius);
HeightfieldCursorRenderVisitor visitor(getLOD(), Box(position - extents, position + extents));
guideToAugmented(visitor);
DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release();
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_POLYGON_OFFSET_FILL);
glDisable(GL_CULL_FACE);
glDepthFunc(GL_LESS);
}
void MetavoxelSystem::deleteTextures(int heightID, int colorID) {
glDeleteTextures(1, (GLuint*)&heightID);
glDeleteTextures(1, (GLuint*)&colorID);
@ -239,7 +306,7 @@ PointBuffer::PointBuffer(const BufferPointVector& points) :
_points(points) {
}
void PointBuffer::render() {
void PointBuffer::render(bool cursor) {
// initialize buffer, etc. on first render
if (!_buffer.isCreated()) {
_buffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
@ -294,7 +361,7 @@ public:
glm::vec3 vertex;
};
void HeightfieldBuffer::render() {
void HeightfieldBuffer::render(bool cursor) {
// initialize textures, etc. on first render
if (_heightTextureID == 0) {
glGenTextures(1, &_heightTextureID);
@ -385,17 +452,24 @@ void HeightfieldBuffer::render() {
glBindTexture(GL_TEXTURE_2D, _heightTextureID);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
int heightScaleLocation;
if (cursor) {
heightScaleLocation = DefaultMetavoxelRendererImplementation::getCursorHeightScaleLocation();
} else {
heightScaleLocation = DefaultMetavoxelRendererImplementation::getHeightScaleLocation();
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
}
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().setUniformValue(
DefaultMetavoxelRendererImplementation::getHeightScaleLocation(), 1.0f / _heightSize);
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().setUniformValue(heightScaleLocation, 1.0f / _heightSize);
glDrawRangeElements(GL_QUADS, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
if (!cursor) {
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glPopMatrix();
@ -475,6 +549,17 @@ void DefaultMetavoxelRendererImplementation::init() {
_heightfieldProgram.setUniformValue("diffuseMap", 1);
_heightScaleLocation = _heightfieldProgram.uniformLocation("heightScale");
_heightfieldProgram.release();
_heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
"shaders/metavoxel_heightfield_cursor.vert");
_heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
"shaders/metavoxel_heightfield_cursor.frag");
_heightfieldCursorProgram.link();
_heightfieldCursorProgram.bind();
_heightfieldCursorProgram.setUniformValue("heightMap", 0);
_cursorHeightScaleLocation = _heightfieldCursorProgram.uniformLocation("heightScale");
_heightfieldCursorProgram.release();
}
}
@ -771,6 +856,8 @@ ProgramObject DefaultMetavoxelRendererImplementation::_pointProgram;
int DefaultMetavoxelRendererImplementation::_pointScaleLocation;
ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldProgram;
int DefaultMetavoxelRendererImplementation::_heightScaleLocation;
ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldCursorProgram;
int DefaultMetavoxelRendererImplementation::_cursorHeightScaleLocation;
static void enableClipPlane(GLenum plane, float x, float y, float z, float w) {
GLdouble coefficients[] = { x, y, z, w };

View file

@ -43,6 +43,8 @@ public:
void simulate(float deltaTime);
void render();
void renderHeightfieldCursor(const glm::vec3& position, float radius);
Q_INVOKABLE void deleteTextures(int heightID, int colorID);
protected:
@ -105,7 +107,7 @@ public:
virtual ~BufferData();
virtual void render() = 0;
virtual void render(bool cursor = false) = 0;
};
typedef QExplicitlySharedDataPointer<BufferData> BufferDataPointer;
@ -116,7 +118,7 @@ public:
PointBuffer(const BufferPointVector& points);
virtual void render();
virtual void render(bool cursor = false);
private:
@ -140,7 +142,7 @@ public:
const QByteArray& getHeight() const { return _height; }
const QByteArray& getColor() const { return _color; }
virtual void render();
virtual void render(bool cursor = false);
private:
@ -193,6 +195,9 @@ public:
static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; }
static int getHeightScaleLocation() { return _heightScaleLocation; }
static ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; }
static int getCursorHeightScaleLocation() { return _cursorHeightScaleLocation; }
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod);
@ -206,6 +211,9 @@ private:
static ProgramObject _heightfieldProgram;
static int _heightScaleLocation;
static ProgramObject _heightfieldCursorProgram;
static int _cursorHeightScaleLocation;
};
/// Base class for spanner renderers; provides clipping.

View file

@ -118,6 +118,8 @@ MetavoxelEditor::MetavoxelEditor() :
addTool(new SetSpannerTool(this));
addTool(new ImportHeightfieldTool(this));
addTool(new EraseHeightfieldTool(this));
addTool(new HeightfieldHeightBrushTool(this));
addTool(new HeightfieldColorBrushTool(this));
updateAttributes();
@ -1080,3 +1082,72 @@ void EraseHeightfieldTool::apply() {
message.edit = QVariant::fromValue(edit);
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name) :
MetavoxelTool(editor, name, false) {
QWidget* widget = new QWidget();
widget->setLayout(_form = new QFormLayout());
layout()->addWidget(widget);
_form->addRow("Radius:", _radius = new QDoubleSpinBox());
_radius->setSingleStep(0.01);
_radius->setMaximum(FLT_MAX);
_radius->setValue(1.0);
}
void HeightfieldBrushTool::render() {
if (Application::getInstance()->isMouseHidden()) {
return;
}
// find the intersection with the heightfield
glm::vec3 origin = Application::getInstance()->getMouseRayOrigin();
glm::vec3 direction = Application::getInstance()->getMouseRayDirection();
float distance;
if (!Application::getInstance()->getMetavoxels()->findFirstRayHeightfieldIntersection(origin, direction, distance)) {
return;
}
Application::getInstance()->getMetavoxels()->renderHeightfieldCursor(
_position = origin + distance * direction, _radius->value());
}
bool HeightfieldBrushTool::eventFilter(QObject* watched, QEvent* event) {
if (event->type() == QEvent::Wheel) {
float angle = static_cast<QWheelEvent*>(event)->angleDelta().y();
const float ANGLE_SCALE = 1.0f / 1000.0f;
_radius->setValue(_radius->value() * glm::pow(2.0f, angle * ANGLE_SCALE));
return true;
} else if (event->type() == QEvent::MouseButtonPress) {
MetavoxelEditMessage message = { createEdit(static_cast<QMouseEvent*>(event)->button() == Qt::RightButton) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
return true;
}
return false;
}
HeightfieldHeightBrushTool::HeightfieldHeightBrushTool(MetavoxelEditor* editor) :
HeightfieldBrushTool(editor, "Height Brush") {
_form->addRow("Height:", _height = new QDoubleSpinBox());
_height->setMinimum(-FLT_MAX);
_height->setMaximum(FLT_MAX);
_height->setValue(1.0);
}
QVariant HeightfieldHeightBrushTool::createEdit(bool alternate) {
return QVariant::fromValue(PaintHeightfieldHeightEdit(_position, _radius->value(),
alternate ? -_height->value() : _height->value()));
}
HeightfieldColorBrushTool::HeightfieldColorBrushTool(MetavoxelEditor* editor) :
HeightfieldBrushTool(editor, "Color Brush") {
_form->addRow("Color:", _color = new QColorEditor(this));
}
QVariant HeightfieldColorBrushTool::createEdit(bool alternate) {
return QVariant::fromValue(PaintHeightfieldColorEdit(_position, _radius->value(), _color->getColor()));
}

View file

@ -18,6 +18,7 @@
#include "MetavoxelSystem.h"
#include "renderer/ProgramObject.h"
class QColorEditor;
class QComboBox;
class QDoubleSpinBox;
class QGroupBox;
@ -282,7 +283,7 @@ private:
HeightfieldPreview _preview;
};
// Allows clearing heighfield blocks.
/// Allows clearing heighfield blocks.
class EraseHeightfieldTool : public HeightfieldTool {
Q_OBJECT
@ -302,4 +303,60 @@ private:
QSpinBox* _length;
};
/// Base class for tools that allow painting on heightfields.
class HeightfieldBrushTool : public MetavoxelTool {
Q_OBJECT
public:
HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name);
virtual void render();
virtual bool eventFilter(QObject* watched, QEvent* event);
protected:
virtual QVariant createEdit(bool alternate) = 0;
QFormLayout* _form;
QDoubleSpinBox* _radius;
glm::vec3 _position;
};
/// Allows raising or lowering parts of the heightfield.
class HeightfieldHeightBrushTool : public HeightfieldBrushTool {
Q_OBJECT
public:
HeightfieldHeightBrushTool(MetavoxelEditor* editor);
protected:
virtual QVariant createEdit(bool alternate);
private:
QDoubleSpinBox* _height;
};
/// Allows coloring parts of the heightfield.
class HeightfieldColorBrushTool : public HeightfieldBrushTool {
Q_OBJECT
public:
HeightfieldColorBrushTool(MetavoxelEditor* editor);
protected:
virtual QVariant createEdit(bool alternate);
private:
QColorEditor* _color;
};
#endif // hifi_MetavoxelEditor_h

View file

@ -61,6 +61,180 @@ SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(cons
return closestSpanner;
}
class RayHeightfieldIntersectionVisitor : public RayIntersectionVisitor {
public:
float intersectionDistance;
RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod);
virtual int visit(MetavoxelInfo& info, float distance);
};
RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm::vec3& origin,
const glm::vec3& direction, const MetavoxelLOD& lod) :
RayIntersectionVisitor(origin, direction, QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getHeightfieldAttribute(), QVector<AttributePointer>(), lod),
intersectionDistance(FLT_MAX) {
}
static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / 255.0f;
int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance) {
if (!info.isLeaf) {
return _order;
}
HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
if (!pointer) {
return STOP_RECURSION;
}
const QByteArray& contents = pointer->getContents();
const uchar* src = (const uchar*)contents.constData();
int size = glm::sqrt((float)contents.size());
int highest = size - 1;
float heightScale = highest * EIGHT_BIT_MAXIMUM_RECIPROCAL;
// find the initial location in heightfield coordinates
glm::vec3 entry = (_origin + distance * _direction - info.minimum) * (float)highest / info.size;
glm::vec3 floors = glm::floor(entry);
glm::vec3 ceils = glm::ceil(entry);
if (floors.x == ceils.x) {
if (_direction.x > 0.0f) {
ceils.x += 1.0f;
} else {
floors.x -= 1.0f;
}
}
if (floors.z == ceils.z) {
if (_direction.z > 0.0f) {
ceils.z += 1.0f;
} else {
floors.z -= 1.0f;
}
}
bool withinBounds = true;
float accumulatedDistance = 0.0f;
while (withinBounds) {
// find the heights at the corners of the current cell
int floorX = qMin(qMax((int)floors.x, 0), highest);
int floorZ = qMin(qMax((int)floors.z, 0), highest);
int ceilX = qMin(qMax((int)ceils.x, 0), highest);
int ceilZ = qMin(qMax((int)ceils.z, 0), highest);
float upperLeft = src[floorZ * size + floorX] * heightScale;
float upperRight = src[floorZ * size + ceilX] * heightScale;
float lowerLeft = src[ceilZ * size + floorX] * heightScale;
float lowerRight = src[ceilZ * size + ceilX] * heightScale;
// find the distance to the next x coordinate
float xDistance = FLT_MAX;
if (_direction.x > 0.0f) {
xDistance = (ceils.x - entry.x) / _direction.x;
} else if (_direction.x < 0.0f) {
xDistance = (floors.x - entry.x) / _direction.x;
}
// and the distance to the next z coordinate
float zDistance = FLT_MAX;
if (_direction.z > 0.0f) {
zDistance = (ceils.z - entry.z) / _direction.z;
} else if (_direction.z < 0.0f) {
zDistance = (floors.z - entry.z) / _direction.z;
}
// the exit distance is the lower of those two
float exitDistance = qMin(xDistance, zDistance);
glm::vec3 exit, nextFloors = floors, nextCeils = ceils;
if (exitDistance == FLT_MAX) {
if (_direction.y > 0.0f) {
return SHORT_CIRCUIT; // line points upwards; no collisions possible
}
withinBounds = false; // line points downwards; check this cell only
} else {
// find the exit point and the next cell, and determine whether it's still within the bounds
exit = entry + exitDistance * _direction;
withinBounds = (exit.y >= 0.0f && exit.y <= highest);
if (exitDistance == xDistance) {
if (_direction.x > 0.0f) {
nextFloors.x += 1.0f;
withinBounds &= (nextCeils.x += 1.0f) <= highest;
} else {
withinBounds &= (nextFloors.x -= 1.0f) >= 0.0f;
nextCeils.x -= 1.0f;
}
}
if (exitDistance == zDistance) {
if (_direction.z > 0.0f) {
nextFloors.z += 1.0f;
withinBounds &= (nextCeils.z += 1.0f) <= highest;
} else {
withinBounds &= (nextFloors.z -= 1.0f) >= 0.0f;
nextCeils.z -= 1.0f;
}
}
// check the vertical range of the ray against the ranges of the cell heights
if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) ||
qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) {
entry = exit;
floors = nextFloors;
ceils = nextCeils;
accumulatedDistance += exitDistance;
continue;
}
}
// having passed the bounds check, we must check against the planes
glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z);
// first check the triangle including the Z+ segment
glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft);
float lowerProduct = glm::dot(lowerNormal, _direction);
if (lowerProduct < 0.0f) {
float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct;
glm::vec3 intersection = relativeEntry + planeDistance * _direction;
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
intersection.z >= intersection.x) {
intersectionDistance = qMin(intersectionDistance, distance +
(accumulatedDistance + planeDistance) * (info.size / highest));
return SHORT_CIRCUIT;
}
}
// then the one with the X+ segment
glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight);
float upperProduct = glm::dot(upperNormal, _direction);
if (upperProduct < 0.0f) {
float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct;
glm::vec3 intersection = relativeEntry + planeDistance * _direction;
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
intersection.x >= intersection.z) {
intersectionDistance = qMin(intersectionDistance, distance +
(accumulatedDistance + planeDistance) * (info.size / highest));
return SHORT_CIRCUIT;
}
}
// no joy; continue on our way
entry = exit;
floors = nextFloors;
ceils = nextCeils;
accumulatedDistance += exitDistance;
}
return STOP_RECURSION;
}
bool MetavoxelClientManager::findFirstRayHeightfieldIntersection(const glm::vec3& origin,
const glm::vec3& direction, float& distance) {
RayHeightfieldIntersectionVisitor visitor(origin, direction, getLOD());
guide(visitor);
if (visitor.intersectionDistance == FLT_MAX) {
return false;
}
distance = visitor.intersectionDistance;
return true;
}
void MetavoxelClientManager::setSphere(const glm::vec3& center, float radius, const QColor& color) {
Sphere* sphere = new Sphere();
sphere->setTranslation(center);
@ -78,6 +252,84 @@ void MetavoxelClientManager::applyEdit(const MetavoxelEditMessage& edit, bool re
QMetaObject::invokeMethod(_updater, "applyEdit", Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, reliable));
}
class HeightfieldHeightVisitor : public MetavoxelVisitor {
public:
float height;
HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location);
virtual int visit(MetavoxelInfo& info);
private:
glm::vec3 _location;
};
HeightfieldHeightVisitor::HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute(),
QVector<AttributePointer>(), lod),
height(-FLT_MAX),
_location(location) {
}
static const int REVERSE_ORDER = MetavoxelVisitor::encodeOrder(7, 6, 5, 4, 3, 2, 1, 0);
int HeightfieldHeightVisitor::visit(MetavoxelInfo& info) {
glm::vec3 relative = _location - info.minimum;
if (relative.x < 0.0f || relative.z < 0.0f || relative.x > info.size || relative.z > info.size ||
height >= info.minimum.y + info.size) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return REVERSE_ORDER;
}
HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
if (!pointer) {
return STOP_RECURSION;
}
const QByteArray& contents = pointer->getContents();
const uchar* src = (const uchar*)contents.constData();
int size = glm::sqrt((float)contents.size());
int highest = size - 1;
relative *= highest / info.size;
// find the bounds of the cell containing the point and the shared vertex heights
glm::vec3 floors = glm::floor(relative);
glm::vec3 ceils = glm::ceil(relative);
glm::vec3 fracts = glm::fract(relative);
int floorX = qMin(qMax((int)floors.x, 0), highest);
int floorZ = qMin(qMax((int)floors.z, 0), highest);
int ceilX = qMin(qMax((int)ceils.x, 0), highest);
int ceilZ = qMin(qMax((int)ceils.z, 0), highest);
float upperLeft = src[floorZ * size + floorX];
float lowerRight = src[ceilZ * size + ceilX];
float interpolatedHeight;
// the final vertex (and thus which triangle we check) depends on which half we're on
if (fracts.x > fracts.z) {
float upperRight = src[floorZ * size + ceilX];
interpolatedHeight = glm::mix(glm::mix(upperLeft, upperRight, fracts.x), lowerRight, fracts.z);
} else {
float lowerLeft = src[ceilZ * size + floorX];
interpolatedHeight = glm::mix(upperLeft, glm::mix(lowerLeft, lowerRight, fracts.x), fracts.z);
}
if (interpolatedHeight == 0.0f) {
return STOP_RECURSION; // ignore zero values
}
// convert the interpolated height into world space
height = qMax(height, info.minimum.y + interpolatedHeight * info.size * EIGHT_BIT_MAXIMUM_RECIPROCAL);
return SHORT_CIRCUIT;
}
float MetavoxelClientManager::getHeightfieldHeight(const glm::vec3& location) {
HeightfieldHeightVisitor visitor(getLOD(), location);
guide(visitor);
return visitor.height;
}
MetavoxelLOD MetavoxelClientManager::getLOD() {
return MetavoxelLOD();
}

View file

@ -37,12 +37,16 @@ public:
SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction,
const AttributePointer& attribute, float& distance);
bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance);
Q_INVOKABLE void setSphere(const glm::vec3& center, float radius, const QColor& color = QColor(Qt::gray));
Q_INVOKABLE void setSpanner(const SharedObjectPointer& object, bool reliable = false);
Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false);
Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location);
/// Returns the current LOD. This must be thread-safe, as it will be called from the updater thread.
virtual MetavoxelLOD getLOD();

View file

@ -318,3 +318,169 @@ SetDataEdit::SetDataEdit(const glm::vec3& minimum, const MetavoxelData& data, bo
void SetDataEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
data.set(minimum, this->data, blend);
}
PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position, float radius, float height) :
position(position),
radius(radius),
height(height) {
}
class PaintHeightfieldHeightEditVisitor : public MetavoxelVisitor {
public:
PaintHeightfieldHeightEditVisitor(const PaintHeightfieldHeightEdit& edit);
virtual int visit(MetavoxelInfo& info);
private:
PaintHeightfieldHeightEdit _edit;
Box _bounds;
};
PaintHeightfieldHeightEditVisitor::PaintHeightfieldHeightEditVisitor(const PaintHeightfieldHeightEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute(),
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute()),
_edit(edit) {
glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius);
_bounds = Box(_edit.position - extents, _edit.position + extents);
}
int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
if (!pointer) {
return STOP_RECURSION;
}
QByteArray contents(pointer->getContents());
int size = glm::sqrt((float)contents.size());
int highest = size - 1;
float heightScale = highest / info.size;
glm::vec3 center = (_edit.position - info.minimum) * heightScale;
float scaledRadius = _edit.radius * heightScale;
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
glm::vec3 start = glm::floor(center - extents);
glm::vec3 end = glm::ceil(center + extents);
// raise/lower all points within the radius
float z = qMax(start.z, 0.0f);
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX;
float squaredRadius = scaledRadius * scaledRadius;
float squaredRadiusReciprocal = 1.0f / squaredRadius;
const int EIGHT_BIT_MAXIMUM = 255;
float scaledHeight = _edit.height * EIGHT_BIT_MAXIMUM / info.size;
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
uchar* dest = lineDest;
for (float x = startX; x <= endX; x += 1.0f, dest++) {
float dx = x - center.x, dz = z - center.z;
float distanceSquared = dx * dx + dz * dz;
if (distanceSquared <= squaredRadius) {
// height falls off towards edges
int value = *dest + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal;
*dest = qMin(qMax(value, 0), EIGHT_BIT_MAXIMUM);
}
}
lineDest += size;
}
HeightfieldDataPointer newPointer(new HeightfieldData(contents));
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer));
return STOP_RECURSION;
}
void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldHeightEditVisitor visitor(*this);
data.guide(visitor);
}
PaintHeightfieldColorEdit::PaintHeightfieldColorEdit(const glm::vec3& position, float radius, const QColor& color) :
position(position),
radius(radius),
color(color) {
}
class PaintHeightfieldColorEditVisitor : public MetavoxelVisitor {
public:
PaintHeightfieldColorEditVisitor(const PaintHeightfieldColorEdit& edit);
virtual int visit(MetavoxelInfo& info);
private:
PaintHeightfieldColorEdit _edit;
Box _bounds;
};
PaintHeightfieldColorEditVisitor::PaintHeightfieldColorEditVisitor(const PaintHeightfieldColorEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute(),
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute()),
_edit(edit) {
glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius);
_bounds = Box(_edit.position - extents, _edit.position + extents);
}
int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
if (!pointer) {
return STOP_RECURSION;
}
QByteArray contents(pointer->getContents());
const int BYTES_PER_PIXEL = 3;
int size = glm::sqrt((float)contents.size() / BYTES_PER_PIXEL);
int highest = size - 1;
float heightScale = highest / info.size;
glm::vec3 center = (_edit.position - info.minimum) * heightScale;
float scaledRadius = _edit.radius * heightScale;
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
glm::vec3 start = glm::floor(center - extents);
glm::vec3 end = glm::ceil(center + extents);
// paint all points within the radius
float z = qMax(start.z, 0.0f);
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
int stride = size * BYTES_PER_PIXEL;
char* lineDest = contents.data() + (int)z * stride + (int)startX * BYTES_PER_PIXEL;
float squaredRadius = scaledRadius * scaledRadius;
char red = _edit.color.red(), green = _edit.color.green(), blue = _edit.color.blue();
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
char* dest = lineDest;
for (float x = startX; x <= endX; x += 1.0f, dest += BYTES_PER_PIXEL) {
float dx = x - center.x, dz = z - center.z;
if (dx * dx + dz * dz <= squaredRadius) {
dest[0] = red;
dest[1] = green;
dest[2] = blue;
}
}
lineDest += stride;
}
HeightfieldDataPointer newPointer(new HeightfieldData(contents));
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer));
return STOP_RECURSION;
}
void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldColorEditVisitor visitor(*this);
data.guide(visitor);
}

View file

@ -207,4 +207,38 @@ public:
DECLARE_STREAMABLE_METATYPE(SetDataEdit)
/// An edit that sets a region of a heightfield height.
class PaintHeightfieldHeightEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM glm::vec3 position;
STREAM float radius;
STREAM float height;
PaintHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, float height = 0.0f);
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(PaintHeightfieldHeightEdit)
/// An edit that sets a region of a heightfield color.
class PaintHeightfieldColorEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM glm::vec3 position;
STREAM float radius;
STREAM QColor color;
PaintHeightfieldColorEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, const QColor& color = QColor());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(PaintHeightfieldColorEdit)
#endif // hifi_MetavoxelMessages_h

View file

@ -138,6 +138,8 @@ public:
QColorEditor(QWidget* parent);
const QColor& getColor() const { return _color; }
signals:
void colorChanged(const QColor& color);

View file

@ -80,6 +80,8 @@ PacketVersion versionForPacketType(PacketType type) {
return 1;
case PacketTypeAudioStreamStats:
return 1;
case PacketTypeMetavoxelData:
return 1;
default:
return 0;
}