mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 06:53:01 +02:00
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:
commit
0e72c6a0b6
12 changed files with 758 additions and 12 deletions
|
@ -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);
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
|
@ -118,6 +118,73 @@ void MetavoxelSystem::render() {
|
||||||
guideToAugmented(renderVisitor);
|
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) {
|
void MetavoxelSystem::deleteTextures(int heightID, int colorID) {
|
||||||
glDeleteTextures(1, (GLuint*)&heightID);
|
glDeleteTextures(1, (GLuint*)&heightID);
|
||||||
glDeleteTextures(1, (GLuint*)&colorID);
|
glDeleteTextures(1, (GLuint*)&colorID);
|
||||||
|
@ -239,7 +306,7 @@ PointBuffer::PointBuffer(const BufferPointVector& points) :
|
||||||
_points(points) {
|
_points(points) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PointBuffer::render() {
|
void PointBuffer::render(bool cursor) {
|
||||||
// initialize buffer, etc. on first render
|
// initialize buffer, etc. on first render
|
||||||
if (!_buffer.isCreated()) {
|
if (!_buffer.isCreated()) {
|
||||||
_buffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
|
_buffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
|
||||||
|
@ -294,7 +361,7 @@ public:
|
||||||
glm::vec3 vertex;
|
glm::vec3 vertex;
|
||||||
};
|
};
|
||||||
|
|
||||||
void HeightfieldBuffer::render() {
|
void HeightfieldBuffer::render(bool cursor) {
|
||||||
// initialize textures, etc. on first render
|
// initialize textures, etc. on first render
|
||||||
if (_heightTextureID == 0) {
|
if (_heightTextureID == 0) {
|
||||||
glGenTextures(1, &_heightTextureID);
|
glGenTextures(1, &_heightTextureID);
|
||||||
|
@ -385,17 +452,24 @@ void HeightfieldBuffer::render() {
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, _heightTextureID);
|
glBindTexture(GL_TEXTURE_2D, _heightTextureID);
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE1);
|
int heightScaleLocation;
|
||||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
if (cursor) {
|
||||||
|
heightScaleLocation = DefaultMetavoxelRendererImplementation::getCursorHeightScaleLocation();
|
||||||
|
} else {
|
||||||
|
heightScaleLocation = DefaultMetavoxelRendererImplementation::getHeightScaleLocation();
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||||
|
}
|
||||||
|
|
||||||
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().setUniformValue(
|
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().setUniformValue(heightScaleLocation, 1.0f / _heightSize);
|
||||||
DefaultMetavoxelRendererImplementation::getHeightScaleLocation(), 1.0f / _heightSize);
|
|
||||||
|
|
||||||
glDrawRangeElements(GL_QUADS, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
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);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
|
@ -475,6 +549,17 @@ void DefaultMetavoxelRendererImplementation::init() {
|
||||||
_heightfieldProgram.setUniformValue("diffuseMap", 1);
|
_heightfieldProgram.setUniformValue("diffuseMap", 1);
|
||||||
_heightScaleLocation = _heightfieldProgram.uniformLocation("heightScale");
|
_heightScaleLocation = _heightfieldProgram.uniformLocation("heightScale");
|
||||||
_heightfieldProgram.release();
|
_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;
|
int DefaultMetavoxelRendererImplementation::_pointScaleLocation;
|
||||||
ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldProgram;
|
ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldProgram;
|
||||||
int DefaultMetavoxelRendererImplementation::_heightScaleLocation;
|
int DefaultMetavoxelRendererImplementation::_heightScaleLocation;
|
||||||
|
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 };
|
||||||
|
|
|
@ -43,6 +43,8 @@ public:
|
||||||
void simulate(float deltaTime);
|
void simulate(float deltaTime);
|
||||||
void render();
|
void render();
|
||||||
|
|
||||||
|
void renderHeightfieldCursor(const glm::vec3& position, float radius);
|
||||||
|
|
||||||
Q_INVOKABLE void deleteTextures(int heightID, int colorID);
|
Q_INVOKABLE void deleteTextures(int heightID, int colorID);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -105,7 +107,7 @@ public:
|
||||||
|
|
||||||
virtual ~BufferData();
|
virtual ~BufferData();
|
||||||
|
|
||||||
virtual void render() = 0;
|
virtual void render(bool cursor = false) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef QExplicitlySharedDataPointer<BufferData> BufferDataPointer;
|
typedef QExplicitlySharedDataPointer<BufferData> BufferDataPointer;
|
||||||
|
@ -116,7 +118,7 @@ public:
|
||||||
|
|
||||||
PointBuffer(const BufferPointVector& points);
|
PointBuffer(const BufferPointVector& points);
|
||||||
|
|
||||||
virtual void render();
|
virtual void render(bool cursor = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -140,7 +142,7 @@ public:
|
||||||
const QByteArray& getHeight() const { return _height; }
|
const QByteArray& getHeight() const { return _height; }
|
||||||
const QByteArray& getColor() const { return _color; }
|
const QByteArray& getColor() const { return _color; }
|
||||||
|
|
||||||
virtual void render();
|
virtual void render(bool cursor = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -193,6 +195,9 @@ public:
|
||||||
static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; }
|
static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; }
|
||||||
static int getHeightScaleLocation() { return _heightScaleLocation; }
|
static int getHeightScaleLocation() { return _heightScaleLocation; }
|
||||||
|
|
||||||
|
static ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; }
|
||||||
|
static int getCursorHeightScaleLocation() { return _cursorHeightScaleLocation; }
|
||||||
|
|
||||||
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
|
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
|
||||||
|
|
||||||
virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod);
|
virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod);
|
||||||
|
@ -206,6 +211,9 @@ private:
|
||||||
|
|
||||||
static ProgramObject _heightfieldProgram;
|
static ProgramObject _heightfieldProgram;
|
||||||
static int _heightScaleLocation;
|
static int _heightScaleLocation;
|
||||||
|
|
||||||
|
static ProgramObject _heightfieldCursorProgram;
|
||||||
|
static int _cursorHeightScaleLocation;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Base class for spanner renderers; provides clipping.
|
/// Base class for spanner renderers; provides clipping.
|
||||||
|
|
|
@ -118,6 +118,8 @@ MetavoxelEditor::MetavoxelEditor() :
|
||||||
addTool(new SetSpannerTool(this));
|
addTool(new SetSpannerTool(this));
|
||||||
addTool(new ImportHeightfieldTool(this));
|
addTool(new ImportHeightfieldTool(this));
|
||||||
addTool(new EraseHeightfieldTool(this));
|
addTool(new EraseHeightfieldTool(this));
|
||||||
|
addTool(new HeightfieldHeightBrushTool(this));
|
||||||
|
addTool(new HeightfieldColorBrushTool(this));
|
||||||
|
|
||||||
updateAttributes();
|
updateAttributes();
|
||||||
|
|
||||||
|
@ -1080,3 +1082,72 @@ void EraseHeightfieldTool::apply() {
|
||||||
message.edit = QVariant::fromValue(edit);
|
message.edit = QVariant::fromValue(edit);
|
||||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
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()));
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "MetavoxelSystem.h"
|
#include "MetavoxelSystem.h"
|
||||||
#include "renderer/ProgramObject.h"
|
#include "renderer/ProgramObject.h"
|
||||||
|
|
||||||
|
class QColorEditor;
|
||||||
class QComboBox;
|
class QComboBox;
|
||||||
class QDoubleSpinBox;
|
class QDoubleSpinBox;
|
||||||
class QGroupBox;
|
class QGroupBox;
|
||||||
|
@ -282,7 +283,7 @@ private:
|
||||||
HeightfieldPreview _preview;
|
HeightfieldPreview _preview;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Allows clearing heighfield blocks.
|
/// Allows clearing heighfield blocks.
|
||||||
class EraseHeightfieldTool : public HeightfieldTool {
|
class EraseHeightfieldTool : public HeightfieldTool {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -302,4 +303,60 @@ private:
|
||||||
QSpinBox* _length;
|
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
|
#endif // hifi_MetavoxelEditor_h
|
||||||
|
|
|
@ -61,6 +61,180 @@ 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);
|
||||||
|
@ -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));
|
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,12 +37,16 @@ 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();
|
||||||
|
|
||||||
|
|
|
@ -318,3 +318,169 @@ SetDataEdit::SetDataEdit(const glm::vec3& minimum, const MetavoxelData& data, bo
|
||||||
void SetDataEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
void SetDataEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||||
data.set(minimum, this->data, blend);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -207,4 +207,38 @@ public:
|
||||||
|
|
||||||
DECLARE_STREAMABLE_METATYPE(SetDataEdit)
|
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
|
#endif // hifi_MetavoxelMessages_h
|
||||||
|
|
|
@ -138,6 +138,8 @@ public:
|
||||||
|
|
||||||
QColorEditor(QWidget* parent);
|
QColorEditor(QWidget* parent);
|
||||||
|
|
||||||
|
const QColor& getColor() const { return _color; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
void colorChanged(const QColor& color);
|
void colorChanged(const QColor& color);
|
||||||
|
|
|
@ -80,6 +80,8 @@ PacketVersion versionForPacketType(PacketType type) {
|
||||||
return 1;
|
return 1;
|
||||||
case PacketTypeAudioStreamStats:
|
case PacketTypeAudioStreamStats:
|
||||||
return 1;
|
return 1;
|
||||||
|
case PacketTypeMetavoxelData:
|
||||||
|
return 1;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue