Merge pull request #3847 from ey6es/metavoxels

Big metavoxel update: removed unused pieces, converted heightfields to "spanners" in order to allow arbitrary transforms, switched to 16 bit heightfields with lossless compression, some rendering improvements.
This commit is contained in:
AndrewMeadows 2014-11-21 15:57:00 -08:00
commit 8d9df8570c
28 changed files with 4104 additions and 5817 deletions

View file

@ -9,7 +9,9 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QCoreApplication>
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QSaveFile>
#include <QThread>
@ -33,14 +35,16 @@ void MetavoxelServer::applyEdit(const MetavoxelEditMessage& edit) {
setData(data);
}
void MetavoxelServer::setData(const MetavoxelData& data) {
void MetavoxelServer::setData(const MetavoxelData& data, bool loaded) {
if (_data == data) {
return;
}
emit dataChanged(_data = data);
if (!_savedDataInitialized) {
if (loaded) {
_savedData = data;
} else if (!_savedDataInitialized) {
_savedDataInitialized = true;
// start the save timer
@ -304,38 +308,58 @@ MetavoxelPersister::MetavoxelPersister(MetavoxelServer* server) :
_server(server) {
}
const char* SAVE_FILE = "metavoxels.dat";
const char* SAVE_FILE = "/resources/metavoxels.dat";
const int FILE_MAGIC = 0xDADAFACE;
const int FILE_VERSION = 1;
void MetavoxelPersister::load() {
QFile file(SAVE_FILE);
QString path = QCoreApplication::applicationDirPath() + SAVE_FILE;
QFile file(path);
if (!file.exists()) {
return;
}
MetavoxelData data;
{
QDebug debug = qDebug() << "Reading from" << SAVE_FILE << "...";
QDebug debug = qDebug() << "Reading from" << path << "...";
file.open(QIODevice::ReadOnly);
QDataStream inStream(&file);
Bitstream in(inStream);
int magic, version;
in >> magic;
if (magic != FILE_MAGIC) {
debug << "wrong file magic: " << magic;
return;
}
in >> version;
if (version != FILE_VERSION) {
debug << "wrong file version: " << version;
return;
}
try {
in >> data;
} catch (const BitstreamException& e) {
debug << "failed, " << e.getDescription();
return;
}
QMetaObject::invokeMethod(_server, "setData", Q_ARG(const MetavoxelData&, data));
QMetaObject::invokeMethod(_server, "setData", Q_ARG(const MetavoxelData&, data), Q_ARG(bool, true));
debug << "done.";
}
data.dumpStats();
}
void MetavoxelPersister::save(const MetavoxelData& data) {
QDebug debug = qDebug() << "Writing to" << SAVE_FILE << "...";
QSaveFile file(SAVE_FILE);
QString path = QCoreApplication::applicationDirPath() + SAVE_FILE;
QDir directory = QFileInfo(path).dir();
if (!directory.exists()) {
directory.mkpath(".");
}
QDebug debug = qDebug() << "Writing to" << path << "...";
QSaveFile file(path);
file.open(QIODevice::WriteOnly);
QDataStream outStream(&file);
Bitstream out(outStream);
out << data;
out << FILE_MAGIC << FILE_VERSION << data;
out.flush();
file.commit();
debug << "done.";

View file

@ -36,7 +36,7 @@ public:
const MetavoxelData& getData() const { return _data; }
Q_INVOKABLE void setData(const MetavoxelData& data);
Q_INVOKABLE void setData(const MetavoxelData& data, bool loaded = false);
virtual void run();

View file

@ -15,10 +15,10 @@
uniform sampler2D heightMap;
// the distance between height points in texture space
uniform float heightScale;
uniform vec4 heightScale;
// the scale between height and color textures
uniform float colorScale;
uniform vec2 colorScale;
// the interpolated normal
varying vec4 normal;
@ -26,14 +26,14 @@ varying vec4 normal;
void main(void) {
// transform and store the normal for interpolation
vec2 heightCoord = gl_MultiTexCoord0.st;
vec4 neighborHeights = vec4(texture2D(heightMap, heightCoord - vec2(heightScale, 0.0)).r,
texture2D(heightMap, heightCoord + vec2(heightScale, 0.0)).r,
texture2D(heightMap, heightCoord - vec2(0.0, heightScale)).r,
texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r);
vec4 neighborsZero = step(1.0 / 255.0, neighborHeights);
normal = normalize(gl_ModelViewMatrix * vec4(
(neighborHeights.x - neighborHeights.y) * neighborsZero.x * neighborsZero.y, heightScale,
(neighborHeights.z - neighborHeights.w) * neighborsZero.z * neighborsZero.w, 0.0));
vec4 neighborHeights = vec4(texture2D(heightMap, heightCoord - vec2(heightScale.s, 0.0)).r,
texture2D(heightMap, heightCoord + vec2(heightScale.s, 0.0)).r,
texture2D(heightMap, heightCoord - vec2(0.0, heightScale.t)).r,
texture2D(heightMap, heightCoord + vec2(0.0, heightScale.t)).r);
vec4 neighborsZero = step(1.0 / 65535.0, neighborHeights);
normal = vec4(normalize(gl_NormalMatrix * vec3(
heightScale.p * (neighborHeights.y - neighborHeights.x) * neighborsZero.x * neighborsZero.y, 1.0,
heightScale.q * (neighborHeights.w - neighborHeights.z) * neighborsZero.z * neighborsZero.w)), 0.0);
// add the height to the position
float height = texture2D(heightMap, heightCoord).r;
@ -43,5 +43,5 @@ void main(void) {
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0));
// pass along the scaled/offset texture coordinates
gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * colorScale;
gl_TexCoord[0] = vec4((heightCoord - heightScale.st) * colorScale, 0.0, 1.0);
}

View file

@ -18,10 +18,10 @@ uniform sampler2D heightMap;
uniform sampler2D textureMap;
// the distance between height points in texture space
uniform float heightScale;
uniform vec2 heightScale;
// the scale between height and texture textures
uniform float textureScale;
uniform vec2 textureScale;
// the splat texture offset
uniform vec2 splatTextureOffset;
@ -58,7 +58,7 @@ void main(void) {
gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], 0.0, 1.0);
// compute the alpha values for each texture
float value = texture2D(textureMap, (gl_MultiTexCoord0.st - vec2(heightScale, heightScale)) * textureScale).r;
float value = texture2D(textureMap, (gl_MultiTexCoord0.st - heightScale) * textureScale).r;
vec4 valueVector = vec4(value, value, value, value);
alphaValues = step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima);
}

View file

@ -1,31 +0,0 @@
#version 120
//
// metavoxel_point.vert
// vertex shader
//
// Created by Andrzej Kapolka on 12/12/13.
// Copyright 2013 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
//
uniform float pointScale;
// the interpolated normal
varying vec4 normal;
void main(void) {
// transform and store the normal for interpolation
normal = vec4(normalize(gl_NormalMatrix * gl_Normal), 0.0);
// extract the first three components of the vertex for position
gl_Position = gl_ModelViewProjectionMatrix * vec4(gl_Vertex.xyz, 1.0);
// the final component is the size in world space
gl_PointSize = pointScale * gl_Vertex.w / gl_Position.w;
// copy the color for interpolation
gl_FrontColor = vec4(gl_Color.rgb, 0.0);
}

View file

@ -464,7 +464,7 @@ Menu::Menu() :
QMenu* metavoxelOptionsMenu = developerMenu->addMenu("Metavoxels");
addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::DisplayHermiteData, 0, false,
Application::getInstance()->getMetavoxels(), SLOT(refreshVoxelData()));
addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderHeightfields, 0, true);
addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderSpanners, 0, true);
addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderDualContourSurfaces, 0, true);
addActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::NetworkSimulator, 0, this,
SLOT(showMetavoxelNetworkSimulator()));

View file

@ -446,9 +446,9 @@ namespace MenuOption {
const QString RenderEntitiesAsScene = "Render Entities as Scene";
const QString RenderFocusIndicator = "Show Eye Focus";
const QString RenderHeadCollisionShapes = "Show Head Collision Shapes";
const QString RenderHeightfields = "Render Heightfields";
const QString RenderLookAtVectors = "Show Look-at Vectors";
const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes";
const QString RenderSpanners = "Render Spanners";
const QString RenderTargetFramerate = "Framerate";
const QString RenderTargetFramerateUnlimited = "Unlimited";
const QString RenderTargetFramerate60 = "60";

File diff suppressed because it is too large Load diff

View file

@ -23,7 +23,11 @@
#include "renderer/ProgramObject.h"
class HeightfieldBaseLayerBatch;
class HeightfieldSplatBatch;
class Model;
class VoxelBatch;
class VoxelSplatBatch;
/// Renders a metavoxel tree.
class MetavoxelSystem : public MetavoxelClientManager {
@ -54,7 +58,6 @@ public:
void setNetworkSimulation(const NetworkSimulation& simulation);
NetworkSimulation getNetworkSimulation();
const AttributePointer& getPointBufferAttribute() { return _pointBufferAttribute; }
const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; }
const AttributePointer& getVoxelBufferAttribute() { return _voxelBufferAttribute; }
@ -65,12 +68,8 @@ public:
void renderVoxelCursor(const glm::vec3& position, float radius);
bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance);
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);
@ -82,9 +81,13 @@ public:
Q_INVOKABLE void setVoxelColor(const SharedObjectPointer& spanner, const QColor& color);
Q_INVOKABLE void setVoxelMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material);
Q_INVOKABLE void deleteTextures(int heightID, int colorID, int textureID);
void addHeightfieldBaseBatch(const HeightfieldBaseLayerBatch& batch) { _heightfieldBaseBatches.append(batch); }
void addHeightfieldSplatBatch(const HeightfieldSplatBatch& batch) { _heightfieldSplatBatches.append(batch); }
void addVoxelBaseBatch(const VoxelBatch& batch) { _voxelBaseBatches.append(batch); }
void addVoxelSplatBatch(const VoxelSplatBatch& batch) { _voxelSplatBatches.append(batch); }
signals:
void rendering();
@ -103,7 +106,6 @@ private:
void guideToAugmented(MetavoxelVisitor& visitor, bool render = false);
AttributePointer _pointBufferAttribute;
AttributePointer _heightfieldBufferAttribute;
AttributePointer _voxelBufferAttribute;
@ -113,6 +115,100 @@ private:
NetworkSimulation _networkSimulation;
QReadWriteLock _networkSimulationLock;
QVector<HeightfieldBaseLayerBatch> _heightfieldBaseBatches;
QVector<HeightfieldSplatBatch> _heightfieldSplatBatches;
QVector<VoxelBatch> _voxelBaseBatches;
QVector<VoxelSplatBatch> _voxelSplatBatches;
ProgramObject _baseHeightfieldProgram;
int _baseHeightScaleLocation;
int _baseColorScaleLocation;
class SplatLocations {
public:
int heightScale;
int textureScale;
int splatTextureOffset;
int splatTextureScalesS;
int splatTextureScalesT;
int textureValueMinima;
int textureValueMaxima;
int materials;
int materialWeights;
};
ProgramObject _splatHeightfieldProgram;
SplatLocations _splatHeightfieldLocations;
int _splatHeightScaleLocation;
int _splatTextureScaleLocation;
int _splatTextureOffsetLocation;
int _splatTextureScalesSLocation;
int _splatTextureScalesTLocation;
int _splatTextureValueMinimaLocation;
int _splatTextureValueMaximaLocation;
ProgramObject _heightfieldCursorProgram;
ProgramObject _baseVoxelProgram;
ProgramObject _splatVoxelProgram;
SplatLocations _splatVoxelLocations;
ProgramObject _voxelCursorProgram;
static void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations);
};
/// Base class for heightfield batches.
class HeightfieldBatch {
public:
QOpenGLBuffer* vertexBuffer;
QOpenGLBuffer* indexBuffer;
glm::vec3 translation;
glm::quat rotation;
glm::vec3 scale;
int vertexCount;
int indexCount;
GLuint heightTextureID;
glm::vec4 heightScale;
};
/// A batch containing a heightfield base layer.
class HeightfieldBaseLayerBatch : public HeightfieldBatch {
public:
GLuint colorTextureID;
glm::vec2 colorScale;
};
/// A batch containing a heightfield splat.
class HeightfieldSplatBatch : public HeightfieldBatch {
public:
GLuint materialTextureID;
glm::vec2 textureScale;
glm::vec2 splatTextureOffset;
int splatTextureIDs[4];
glm::vec4 splatTextureScalesS;
glm::vec4 splatTextureScalesT;
int materialIndex;
};
/// Base class for voxel batches.
class VoxelBatch {
public:
QOpenGLBuffer* vertexBuffer;
QOpenGLBuffer* indexBuffer;
int vertexCount;
int indexCount;
};
/// A batch containing a voxel splat.
class VoxelSplatBatch : public VoxelBatch {
public:
int splatTextureIDs[4];
glm::vec4 splatTextureScalesS;
glm::vec4 splatTextureScalesT;
int materialIndex;
};
/// Generic abstract base class for objects that handle a signal.
@ -124,18 +220,6 @@ public slots:
virtual void handle() = 0;
};
/// Describes contents of a point in a point buffer.
class BufferPoint {
public:
glm::vec4 vertex;
quint8 color[3];
quint8 normal[3];
};
typedef QVector<BufferPoint> BufferPointVector;
Q_DECLARE_METATYPE(BufferPointVector)
/// Simple throttle for limiting bandwidth on a per-second basis.
class Throttle {
public:
@ -203,107 +287,6 @@ public:
typedef QExplicitlySharedDataPointer<BufferData> BufferDataPointer;
/// Contains the information necessary to render a group of points.
class PointBuffer : public BufferData {
public:
PointBuffer(const BufferPointVector& points);
virtual void render(bool cursor = false);
private:
BufferPointVector _points;
QOpenGLBuffer _buffer;
int _pointCount;
};
/// Contains the information necessary to render a heightfield block.
class HeightfieldBuffer : public BufferData {
public:
static const int HEIGHT_BORDER;
static const int SHARED_EDGE;
static const int HEIGHT_EXTENSION;
HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height,
const QByteArray& color, const QByteArray& material = QByteArray(),
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
~HeightfieldBuffer();
const glm::vec3& getTranslation() const { return _translation; }
float getScale() const { return _scale; }
const Box& getHeightBounds() const { return _heightBounds; }
const Box& getColorBounds() const { return _colorBounds; }
const Box& getMaterialBounds() const { return _materialBounds; }
QByteArray& getHeight() { return _height; }
const QByteArray& getHeight() const { return _height; }
QByteArray& getColor() { return _color; }
const QByteArray& getColor() const { return _color; }
QByteArray& getMaterial() { return _material; }
const QByteArray& getMaterial() const { return _material; }
QVector<SharedObjectPointer>& getMaterials() { return _materials; }
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
QByteArray getUnextendedHeight() const;
QByteArray getUnextendedColor(int x = 0, int y = 0) const;
int getHeightSize() const { return _heightSize; }
float getHeightIncrement() const { return _heightIncrement; }
int getColorSize() const { return _colorSize; }
float getColorIncrement() const { return _colorIncrement; }
int getMaterialSize() const { return _materialSize; }
float getMaterialIncrement() const { return _materialIncrement; }
virtual void render(bool cursor = false);
private:
glm::vec3 _translation;
float _scale;
Box _heightBounds;
Box _colorBounds;
Box _materialBounds;
QByteArray _height;
QByteArray _color;
QByteArray _material;
QVector<SharedObjectPointer> _materials;
GLuint _heightTextureID;
GLuint _colorTextureID;
GLuint _materialTextureID;
QVector<NetworkTexturePointer> _networkTextures;
int _heightSize;
float _heightIncrement;
int _colorSize;
float _colorIncrement;
int _materialSize;
float _materialIncrement;
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
static QHash<int, BufferPair> _bufferPairs;
};
/// Convenience class for rendering a preview of a heightfield.
class HeightfieldPreview {
public:
void setBuffers(const QVector<BufferDataPointer>& buffers) { _buffers = buffers; }
const QVector<BufferDataPointer>& getBuffers() const { return _buffers; }
void render(const glm::vec3& translation, float scale) const;
private:
QVector<BufferDataPointer> _buffers;
};
/// Describes contents of a vertex in a voxel buffer.
class VoxelPoint {
public:
@ -316,12 +299,27 @@ public:
void setNormal(const glm::vec3& normal);
};
/// A container for a coordinate within a voxel block.
class VoxelCoord {
public:
QRgb encoded;
VoxelCoord(QRgb encoded) : encoded(encoded) { }
bool operator==(const VoxelCoord& other) const { return encoded == other.encoded; }
};
inline uint qHash(const VoxelCoord& coord, uint seed) {
// multiply by prime numbers greater than the possible size
return qHash(qRed(coord.encoded) + 257 * (qGreen(coord.encoded) + 263 * qBlue(coord.encoded)), seed);
}
/// Contains the information necessary to render a voxel block.
class VoxelBuffer : public BufferData {
public:
VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>& indices, const QVector<glm::vec3>& hermite,
const QMultiHash<QRgb, int>& quadIndices, int size, const QVector<SharedObjectPointer>& materials =
const QMultiHash<VoxelCoord, int>& quadIndices, int size, const QVector<SharedObjectPointer>& materials =
QVector<SharedObjectPointer>());
/// Finds the first intersection between the described ray and the voxel data.
@ -336,7 +334,7 @@ private:
QVector<VoxelPoint> _vertices;
QVector<int> _indices;
QVector<glm::vec3> _hermite;
QMultiHash<QRgb, int> _quadIndices;
QMultiHash<VoxelCoord, int> _quadIndices;
int _size;
int _vertexCount;
int _indexCount;
@ -367,120 +365,37 @@ class DefaultMetavoxelRendererImplementation : public MetavoxelRendererImplement
public:
static void init();
static ProgramObject& getPointProgram() { return _pointProgram; }
static int getPointScaleLocation() { return _pointScaleLocation; }
static ProgramObject& getBaseHeightfieldProgram() { return _baseHeightfieldProgram; }
static int getBaseHeightScaleLocation() { return _baseHeightScaleLocation; }
static int getBaseColorScaleLocation() { return _baseColorScaleLocation; }
class SplatLocations {
public:
int heightScale;
int textureScale;
int splatTextureOffset;
int splatTextureScalesS;
int splatTextureScalesT;
int textureValueMinima;
int textureValueMaxima;
int materials;
int materialWeights;
};
static ProgramObject& getSplatHeightfieldProgram() { return _splatHeightfieldProgram; }
static const SplatLocations& getSplatHeightfieldLocations() { return _splatHeightfieldLocations; }
static ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; }
static ProgramObject& getBaseVoxelProgram() { return _baseVoxelProgram; }
static ProgramObject& getSplatVoxelProgram() { return _splatVoxelProgram; }
static const SplatLocations& getSplatVoxelLocations() { return _splatVoxelLocations; }
static ProgramObject& getVoxelCursorProgram() { return _voxelCursorProgram; }
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod);
virtual void simulate(MetavoxelData& data, float deltaTime, MetavoxelInfo& info, const MetavoxelLOD& lod);
virtual void render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod);
private:
static void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations);
static ProgramObject _pointProgram;
static int _pointScaleLocation;
static ProgramObject _baseHeightfieldProgram;
static int _baseHeightScaleLocation;
static int _baseColorScaleLocation;
static ProgramObject _splatHeightfieldProgram;
static SplatLocations _splatHeightfieldLocations;
static int _splatHeightScaleLocation;
static int _splatTextureScaleLocation;
static int _splatTextureOffsetLocation;
static int _splatTextureScalesSLocation;
static int _splatTextureScalesTLocation;
static int _splatTextureValueMinimaLocation;
static int _splatTextureValueMaximaLocation;
static ProgramObject _heightfieldCursorProgram;
static ProgramObject _baseVoxelProgram;
static ProgramObject _splatVoxelProgram;
static SplatLocations _splatVoxelLocations;
static ProgramObject _voxelCursorProgram;
};
/// Base class for spanner renderers; provides clipping.
class ClippedRenderer : public SpannerRenderer {
Q_OBJECT
public:
virtual void render(const glm::vec4& color, Mode mode, const glm::vec3& clipMinimum, float clipSize);
protected:
virtual void renderUnclipped(const glm::vec4& color, Mode mode) = 0;
};
/// Renders spheres.
class SphereRenderer : public ClippedRenderer {
class SphereRenderer : public SpannerRenderer {
Q_OBJECT
public:
Q_INVOKABLE SphereRenderer();
virtual void render(const glm::vec4& color, Mode mode, const glm::vec3& clipMinimum, float clipSize);
protected:
virtual void renderUnclipped(const glm::vec4& color, Mode mode);
virtual void render(bool cursor = false);
};
/// Renders cuboids.
class CuboidRenderer : public ClippedRenderer {
class CuboidRenderer : public SpannerRenderer {
Q_OBJECT
public:
Q_INVOKABLE CuboidRenderer();
protected:
virtual void renderUnclipped(const glm::vec4& color, Mode mode);
virtual void render(bool cursor = false);
};
/// Renders static models.
class StaticModelRenderer : public ClippedRenderer {
class StaticModelRenderer : public SpannerRenderer {
Q_OBJECT
public:
@ -489,12 +404,8 @@ public:
virtual void init(Spanner* spanner);
virtual void simulate(float deltaTime);
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const;
protected:
virtual void renderUnclipped(const glm::vec4& color, Mode mode);
virtual void render(bool cursor = false);
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
private slots:
@ -508,4 +419,34 @@ private:
Model* _model;
};
/// Renders heightfields.
class HeightfieldRenderer : public SpannerRenderer {
Q_OBJECT
public:
Q_INVOKABLE HeightfieldRenderer();
virtual ~HeightfieldRenderer();
virtual void init(Spanner* spanner);
virtual void render(bool cursor = false);
private slots:
void applyHeight(const HeightfieldHeightPointer& height);
void applyColor(const HeightfieldColorPointer& color);
void applyMaterial(const HeightfieldMaterialPointer& material);
private:
GLuint _heightTextureID;
GLuint _colorTextureID;
GLuint _materialTextureID;
QVector<NetworkTexturePointer> _networkTextures;
typedef QPair<int, int> IntPair;
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
static QHash<IntPair, BufferPair> _bufferPairs;
};
#endif // hifi_MetavoxelSystem_h

View file

@ -15,6 +15,14 @@
ProgramObject::ProgramObject(QObject* parent) : QGLShaderProgram(parent) {
}
void ProgramObject::setUniform(int location, const glm::vec2& value) {
setUniformValue(location, value.x, value.y);
}
void ProgramObject::setUniform(const char* name, const glm::vec2& value) {
setUniformValue(name, value.x, value.y);
}
void ProgramObject::setUniform(int location, const glm::vec3& value) {
setUniformValue(location, value.x, value.y, value.z);
}
@ -23,6 +31,14 @@ void ProgramObject::setUniform(const char* name, const glm::vec3& value) {
setUniformValue(name, value.x, value.y, value.z);
}
void ProgramObject::setUniform(int location, const glm::vec4& value) {
setUniformValue(location, value.x, value.y, value.z, value.w);
}
void ProgramObject::setUniform(const char* name, const glm::vec4& value) {
setUniformValue(name, value.x, value.y, value.z, value.w);
}
void ProgramObject::setUniformArray(const char* name, const glm::vec3* values, int count) {
GLfloat* floatVal = new GLfloat[count*3];
int index = 0;

View file

@ -21,8 +21,12 @@ public:
ProgramObject(QObject* parent = 0);
void setUniform(int location, const glm::vec2& value);
void setUniform(const char* name, const glm::vec2& value);
void setUniform(int location, const glm::vec3& value);
void setUniform(const char* name, const glm::vec3& value);
void setUniform(int location, const glm::vec4& value);
void setUniform(const char* name, const glm::vec4& value);
void setUniformArray(const char* name, const glm::vec3* values, int count);
};

View file

@ -48,7 +48,7 @@ enum GridPlane {
const glm::vec2 INVALID_VECTOR(FLT_MAX, FLT_MAX);
MetavoxelEditor::MetavoxelEditor() :
QWidget(Application::getInstance()->getGLWidget(), Qt::Tool) {
QWidget(Application::getInstance()->getWindow(), Qt::Tool) {
setWindowTitle("Metavoxel Editor");
setAttribute(Qt::WA_DeleteOnClose);
@ -122,11 +122,9 @@ MetavoxelEditor::MetavoxelEditor() :
addTool(new InsertSpannerTool(this));
addTool(new RemoveSpannerTool(this));
addTool(new ClearSpannersTool(this));
addTool(new SetSpannerTool(this));
addTool(new HeightfieldHeightBrushTool(this));
addTool(new HeightfieldMaterialBrushTool(this));
addTool(new ImportHeightfieldTool(this));
addTool(new EraseHeightfieldTool(this));
addTool(new VoxelMaterialBoxTool(this));
addTool(new VoxelMaterialSpannerTool(this));
addTool(new VoxelMaterialBrushTool(this));
@ -136,6 +134,8 @@ MetavoxelEditor::MetavoxelEditor() :
connect(Application::getInstance(), SIGNAL(simulating(float)), SLOT(simulate(float)));
connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render()));
connect(Application::getInstance()->getMetavoxels(), &MetavoxelSystem::rendering,
this, &MetavoxelEditor::renderPreview);
Application::getInstance()->getGLWidget()->installEventFilter(this);
@ -241,7 +241,7 @@ void MetavoxelEditor::createNewAttribute() {
form.addRow("Name:", &name);
SharedObjectEditor editor(&Attribute::staticMetaObject, false);
editor.setObject(new QRgbAttribute());
editor.setObject(new FloatAttribute());
layout.addWidget(&editor);
QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
@ -370,6 +370,13 @@ void MetavoxelEditor::render() {
glDepthMask(GL_TRUE);
}
void MetavoxelEditor::renderPreview() {
MetavoxelTool* tool = getActiveTool();
if (tool) {
tool->renderPreview();
}
}
void MetavoxelEditor::addTool(MetavoxelTool* tool) {
_tools.append(tool);
layout()->addWidget(tool);
@ -407,6 +414,10 @@ void MetavoxelTool::render() {
// nothing by default
}
void MetavoxelTool::renderPreview() {
// nothing by default
}
BoxTool::BoxTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) :
MetavoxelTool(editor, name, usesValue, userFacing) {
@ -587,6 +598,16 @@ void GlobalSetTool::apply() {
PlaceSpannerTool::PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, const QString& placeText, bool usesValue) :
MetavoxelTool(editor, name, usesValue) {
QWidget* widget = new QWidget(this);
layout()->addWidget(widget);
QHBoxLayout* box = new QHBoxLayout();
widget->setLayout(box);
box->setContentsMargins(QMargins());
box->addStretch(1);
box->addWidget(_followMouse = new QCheckBox("Follow Mouse"));
_followMouse->setChecked(true);
box->addStretch(1);
if (!placeText.isEmpty()) {
QPushButton* button = new QPushButton(placeText);
layout()->addWidget(button);
@ -595,12 +616,9 @@ PlaceSpannerTool::PlaceSpannerTool(MetavoxelEditor* editor, const QString& name,
}
void PlaceSpannerTool::simulate(float deltaTime) {
if (Application::getInstance()->isMouseHidden()) {
return;
}
Spanner* spanner = static_cast<Spanner*>(getSpanner(true).data());
Transformable* transformable = qobject_cast<Transformable*>(spanner);
if (transformable) {
if (transformable && _followMouse->isChecked() && !Application::getInstance()->isMouseHidden()) {
// find the intersection of the mouse ray with the grid and place the transformable there
glm::quat rotation = _editor->getGridRotation();
glm::quat inverseRotation = glm::inverse(rotation);
@ -614,15 +632,9 @@ void PlaceSpannerTool::simulate(float deltaTime) {
spanner->getRenderer()->simulate(deltaTime);
}
void PlaceSpannerTool::render() {
if (Application::getInstance()->isMouseHidden()) {
return;
}
void PlaceSpannerTool::renderPreview() {
Spanner* spanner = static_cast<Spanner*>(getSpanner().data());
const float SPANNER_ALPHA = 0.25f;
QColor color = getColor();
spanner->getRenderer()->render(glm::vec4(color.redF(), color.greenF(), color.blueF(), SPANNER_ALPHA),
SpannerRenderer::DEFAULT_MODE, glm::vec3(), 0.0f);
spanner->getRenderer()->render();
}
bool PlaceSpannerTool::appliesTo(const AttributePointer& attribute) const {
@ -661,7 +673,7 @@ InsertSpannerTool::InsertSpannerTool(MetavoxelEditor* editor) :
void InsertSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, spanner)) };
Application::getInstance()->getMetavoxels()->applyEdit(message);
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
RemoveSpannerTool::RemoveSpannerTool(MetavoxelEditor* editor) :
@ -712,246 +724,6 @@ void ClearSpannersTool::clear() {
Application::getInstance()->getMetavoxels()->applyEdit(message);
}
SetSpannerTool::SetSpannerTool(MetavoxelEditor* editor) :
PlaceSpannerTool(editor, "Set Spanner", "Set") {
}
bool SetSpannerTool::appliesTo(const AttributePointer& attribute) const {
return attribute == AttributeRegistry::getInstance()->getSpannersAttribute();
}
glm::quat DIRECTION_ROTATIONS[] = {
rotationBetween(glm::vec3(-1.0f, 0.0f, 0.0f), IDENTITY_FRONT),
rotationBetween(glm::vec3(1.0f, 0.0f, 0.0f), IDENTITY_FRONT),
rotationBetween(glm::vec3(0.0f, -1.0f, 0.0f), IDENTITY_FRONT),
rotationBetween(glm::vec3(0.0f, 1.0f, 0.0f), IDENTITY_FRONT),
rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), IDENTITY_FRONT),
rotationBetween(glm::vec3(0.0f, 0.0f, 1.0f), IDENTITY_FRONT) };
/// Represents a view from one direction of the spanner to be voxelized.
class DirectionImages {
public:
QImage color;
QVector<float> depth;
glm::vec3 minima;
glm::vec3 maxima;
glm::vec3 scale;
};
class Voxelizer : public QRunnable {
public:
Voxelizer(float size, const Box& bounds, float granularity, const QVector<DirectionImages>& directionImages);
virtual void run();
private:
void voxelize(const glm::vec3& center);
float _size;
Box _bounds;
float _granularity;
QVector<DirectionImages> _directionImages;
};
Voxelizer::Voxelizer(float size, const Box& bounds, float granularity, const QVector<DirectionImages>& directionImages) :
_size(size),
_bounds(bounds),
_granularity(granularity),
_directionImages(directionImages) {
}
void Voxelizer::run() {
// voxelize separately each cell within the bounds
float halfSize = _size * 0.5f;
for (float x = _bounds.minimum.x + halfSize; x < _bounds.maximum.x; x += _size) {
for (float y = _bounds.minimum.y + halfSize; y < _bounds.maximum.y; y += _size) {
for (float z = _bounds.minimum.z + halfSize; z < _bounds.maximum.z; z += _size) {
voxelize(glm::vec3(x, y, z));
}
}
}
}
class VoxelizationVisitor : public MetavoxelVisitor {
public:
VoxelizationVisitor(const QVector<DirectionImages>& directionImages, const glm::vec3& center, float granularity);
virtual int visit(MetavoxelInfo& info);
private:
QVector<DirectionImages> _directionImages;
glm::vec3 _center;
float _granularity;
};
VoxelizationVisitor::VoxelizationVisitor(const QVector<DirectionImages>& directionImages,
const glm::vec3& center, float granularity) :
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getColorAttribute()),
_directionImages(directionImages),
_center(center),
_granularity(granularity) {
}
bool checkDisjoint(const DirectionImages& images, const glm::vec3& minimum, const glm::vec3& maximum, float extent) {
for (int x = qMax(0, (int)minimum.x), xmax = qMin(images.color.width(), (int)maximum.x); x < xmax; x++) {
for (int y = qMax(0, (int)minimum.y), ymax = qMin(images.color.height(), (int)maximum.y); y < ymax; y++) {
float depth = 1.0f - images.depth.at(y * images.color.width() + x);
if (depth - minimum.z >= -extent - EPSILON) {
return false;
}
}
}
return true;
}
int VoxelizationVisitor::visit(MetavoxelInfo& info) {
float halfSize = info.size * 0.5f;
glm::vec3 center = info.minimum + _center + glm::vec3(halfSize, halfSize, halfSize);
const float EXTENT_SCALE = 2.0f;
if (info.size > _granularity) {
for (unsigned int i = 0; i < sizeof(DIRECTION_ROTATIONS) / sizeof(DIRECTION_ROTATIONS[0]); i++) {
glm::vec3 rotated = DIRECTION_ROTATIONS[i] * center;
const DirectionImages& images = _directionImages.at(i);
glm::vec3 relative = (rotated - images.minima) * images.scale;
glm::vec3 extents = images.scale * halfSize;
glm::vec3 minimum = relative - extents;
glm::vec3 maximum = relative + extents;
if (checkDisjoint(images, minimum, maximum, extents.z * EXTENT_SCALE)) {
info.outputValues[0] = AttributeValue(_outputs.at(0));
return STOP_RECURSION;
}
}
return DEFAULT_ORDER;
}
QRgb closestColor = QRgb();
float closestDistance = FLT_MAX;
for (unsigned int i = 0; i < sizeof(DIRECTION_ROTATIONS) / sizeof(DIRECTION_ROTATIONS[0]); i++) {
glm::vec3 rotated = DIRECTION_ROTATIONS[i] * center;
const DirectionImages& images = _directionImages.at(i);
glm::vec3 relative = (rotated - images.minima) * images.scale;
int x = qMax(qMin((int)glm::round(relative.x), images.color.width() - 1), 0);
int y = qMax(qMin((int)glm::round(relative.y), images.color.height() - 1), 0);
float depth = 1.0f - images.depth.at(y * images.color.width() + x);
float distance = depth - relative.z;
float extent = images.scale.z * halfSize * EXTENT_SCALE;
if (distance < -extent - EPSILON) {
info.outputValues[0] = AttributeValue(_outputs.at(0));
return STOP_RECURSION;
}
QRgb color = images.color.pixel(x, y);
if (distance < extent + EPSILON) {
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<QRgb>(color));
return STOP_RECURSION;
}
if (distance < closestDistance) {
closestColor = color;
closestDistance = distance;
}
}
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<QRgb>(closestColor));
return STOP_RECURSION;
}
void Voxelizer::voxelize(const glm::vec3& center) {
MetavoxelData data;
data.setSize(_size);
VoxelizationVisitor visitor(_directionImages, center, _granularity);
data.guide(visitor);
MetavoxelEditMessage edit = { QVariant::fromValue(SetDataEdit(
center - glm::vec3(_size, _size, _size) * 0.5f, data, true)) };
QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels(), "applyEdit",
Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, true));
}
void SetSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
Spanner* spannerData = static_cast<Spanner*>(spanner.data());
Box bounds = spannerData->getBounds();
float longestSide(qMax(bounds.getLongestSide(), spannerData->getPlacementGranularity()));
float size = powf(2.0f, floorf(logf(longestSide) / logf(2.0f)));
Box cellBounds(glm::floor(bounds.minimum / size) * size, glm::ceil(bounds.maximum / size) * size);
Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject()->bind();
glEnable(GL_SCISSOR_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
QVector<DirectionImages> directionImages;
for (unsigned int i = 0; i < sizeof(DIRECTION_ROTATIONS) / sizeof(DIRECTION_ROTATIONS[0]); i++) {
glm::vec3 minima(FLT_MAX, FLT_MAX, FLT_MAX);
glm::vec3 maxima(-FLT_MAX, -FLT_MAX, -FLT_MAX);
for (int j = 0; j < Box::VERTEX_COUNT; j++) {
glm::vec3 rotated = DIRECTION_ROTATIONS[i] * cellBounds.getVertex(j);
minima = glm::min(minima, rotated);
maxima = glm::max(maxima, rotated);
}
float renderGranularity = spannerData->getVoxelizationGranularity() / 4.0f;
int width = glm::round((maxima.x - minima.x) / renderGranularity);
int height = glm::round((maxima.y - minima.y) / renderGranularity);
glViewport(0, 0, width, height);
glScissor(0, 0, width, height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glOrtho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glm::vec3 axis = glm::axis(DIRECTION_ROTATIONS[i]);
glRotatef(glm::degrees(glm::angle(DIRECTION_ROTATIONS[i])), axis.x, axis.y, axis.z);
Application::getInstance()->setupWorldLight();
Application::getInstance()->updateUntranslatedViewMatrix();
// TODO: assign an equivalent viewTransform object to the application to match the current path which uses glMatrixStack
// setViewTransform(viewTransform);
const glm::vec4 OPAQUE_WHITE(1.0f, 1.0f, 1.0f, 1.0f);
spannerData->getRenderer()->render(OPAQUE_WHITE, SpannerRenderer::DIFFUSE_MODE, glm::vec3(), 0.0f);
DirectionImages images = { QImage(width, height, QImage::Format_ARGB32),
QVector<float>(width * height), minima, maxima, glm::vec3(width / (maxima.x - minima.x),
height / (maxima.y - minima.y), 1.0f / (maxima.z - minima.z)) };
glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, images.color.bits());
glReadPixels(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, images.depth.data());
directionImages.append(images);
glMatrixMode(GL_PROJECTION);
}
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glEnable(GL_BLEND);
glDisable(GL_SCISSOR_TEST);
Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject()->release();
glViewport(0, 0, Application::getInstance()->getGLWidget()->getDeviceWidth(),
Application::getInstance()->getGLWidget()->getDeviceHeight());
// send the images off to the lab for processing
QThreadPool::globalInstance()->start(new Voxelizer(size, cellBounds,
spannerData->getVoxelizationGranularity(), directionImages));
}
HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) :
MetavoxelTool(editor, name, false) {
@ -964,7 +736,7 @@ HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) :
_scale->setMinimum(-FLT_MAX);
_scale->setMaximum(FLT_MAX);
_scale->setPrefix("2^");
_scale->setValue(3.0);
_scale->setValue(2.0);
QPushButton* applyButton = new QPushButton("Apply");
layout()->addWidget(applyButton);
@ -972,7 +744,7 @@ HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) :
}
bool HeightfieldTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("HeightfieldAttribute");
return attribute->inherits("SpannerSetAttribute");
}
void HeightfieldTool::render() {
@ -984,274 +756,131 @@ void HeightfieldTool::render() {
ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) :
HeightfieldTool(editor, "Import Heightfield"),
_loadingImage(false) {
_spanner(new Heightfield()) {
_form->addRow("Block Size:", _blockSize = new QSpinBox());
_blockSize->setPrefix("2^");
_blockSize->setMinimum(1);
_blockSize->setValue(5);
connect(_blockSize, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
&ImportHeightfieldTool::updatePreview);
_form->addRow("Height:", _height = new QPushButton());
connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile);
_form->addRow(_rawOptions = new QWidget());
QHBoxLayout* rawLayout = new QHBoxLayout();
_rawOptions->setLayout(rawLayout);
_rawOptions->setVisible(false);
rawLayout->addStretch(1);
rawLayout->addWidget(new QLabel("Scale:"));
rawLayout->addWidget(_heightScale = new QDoubleSpinBox());
_form->addRow("Height Scale:", _heightScale = new QDoubleSpinBox());
const double MAX_OFFSET_SCALE = 100000.0;
_heightScale->setMaximum(MAX_OFFSET_SCALE);
_heightScale->setSingleStep(0.0001);
_heightScale->setDecimals(4);
_heightScale->setValue(1.0);
_heightScale->setSingleStep(0.01);
_heightScale->setValue(8.0);
connect(_heightScale, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
&ImportHeightfieldTool::updateHeightImage);
rawLayout->addSpacing(15);
rawLayout->addWidget(new QLabel("Offset:"));
rawLayout->addWidget(_heightOffset = new QDoubleSpinBox());
&ImportHeightfieldTool::updateSpanner);
_form->addRow("Height Offset:", _heightOffset = new QDoubleSpinBox());
_heightOffset->setMinimum(-MAX_OFFSET_SCALE);
_heightOffset->setMaximum(MAX_OFFSET_SCALE);
_heightOffset->setDecimals(4);
_heightOffset->setSingleStep(0.01);
connect(_heightOffset, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
&ImportHeightfieldTool::updateHeightImage);
&ImportHeightfieldTool::updateSpanner);
_form->addRow("Color:", _color = new QPushButton());
connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile);
_form->addRow("Height:", _height = new HeightfieldHeightEditor(this));
connect(_height, &HeightfieldHeightEditor::heightChanged, this, &ImportHeightfieldTool::updateSpanner);
connect(Application::getInstance()->getMetavoxels(), &MetavoxelSystem::rendering,
this, &ImportHeightfieldTool::renderPreview);
_form->addRow("Color:", _color = new HeightfieldColorEditor(this));
connect(_color, &HeightfieldColorEditor::colorChanged, this, &ImportHeightfieldTool::updateSpanner);
connect(_translation, &Vec3Editor::valueChanged, this, &ImportHeightfieldTool::updateSpanner);
connect(_scale, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
&ImportHeightfieldTool::updateSpanner);
}
void ImportHeightfieldTool::apply() {
float scale = _translation->getSingleStep();
foreach (const BufferDataPointer& bufferData, _preview.getBuffers()) {
HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(bufferData.data());
MetavoxelData data;
data.setSize(scale);
QByteArray height = buffer->getUnextendedHeight();
HeightfieldHeightDataPointer heightPointer(new HeightfieldHeightData(height));
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue(
AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer))));
MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit(
_translation->getValue() + buffer->getTranslation() * scale, data)) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
int colorUnits = buffer->getColor().isEmpty() ? 1 : (buffer->getColorSize() - HeightfieldBuffer::SHARED_EDGE) /
(buffer->getHeightSize() - HeightfieldBuffer::HEIGHT_EXTENSION);
float colorScale = scale / colorUnits;
for (int y = 0; y < colorUnits; y++) {
for (int x = 0; x < colorUnits; x++) {
MetavoxelData data;
data.setSize(colorScale);
QByteArray color;
if (buffer->getColor().isEmpty()) {
const unsigned char WHITE_VALUE = 0xFF;
color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE);
} else {
color = buffer->getUnextendedColor(x, y);
}
HeightfieldColorDataPointer colorPointer(new HeightfieldColorData(color));
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(
AttributeValue(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(),
encodeInline(colorPointer))));
QByteArray material(height.size(), 0);
HeightfieldMaterialDataPointer materialPointer(new HeightfieldMaterialData(material));
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), new MetavoxelNode(
AttributeValue(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(),
encodeInline(materialPointer))));
MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit(
_translation->getValue() + buffer->getTranslation() * scale + glm::vec3(x, 0.0f, y) * colorScale, data)) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
}
}
}
const float EIGHT_BIT_MAXIMUM = 255.0f;
void ImportHeightfieldTool::selectHeightFile() {
QString filename = QFileDialog::getOpenFileName(this, "Select Height Image", QString(),
"Images (*.png *.jpg *.bmp *.raw)");
if (filename.isNull()) {
return;
}
if (filename.toLower().endsWith(".raw")) {
QFile input(filename);
input.open(QIODevice::ReadOnly);
QDataStream in(&input);
in.setByteOrder(QDataStream::LittleEndian);
_rawHeight.clear();
int minHeight = numeric_limits<quint16>::max();
int maxHeight = numeric_limits<quint16>::min();
while (!in.atEnd()) {
quint16 height;
in >> height;
_rawHeight.append(height);
minHeight = qMin(minHeight, (int)height);
maxHeight = qMax(maxHeight, (int)height);
}
_height->setText(filename);
_rawOptions->setVisible(true);
_loadingImage = true;
_heightScale->setValue((EIGHT_BIT_MAXIMUM - 1.0f) / (maxHeight - minHeight));
_heightOffset->setValue(-minHeight * _heightScale->value() + 1.0);
_loadingImage = false;
updateHeightImage();
return;
}
if (!_heightImage.load(filename)) {
QMessageBox::warning(this, "Invalid Image", "The selected image could not be read.");
return;
}
_rawOptions->setVisible(false);
_heightImage = _heightImage.convertToFormat(QImage::Format_RGB888);
_height->setText(filename);
updatePreview();
}
void ImportHeightfieldTool::selectColorFile() {
QString filename = QFileDialog::getOpenFileName(this, "Select Color Image", QString(), "Images (*.png *.jpg *.bmp)");
if (filename.isNull()) {
return;
}
if (!_colorImage.load(filename)) {
QMessageBox::warning(this, "Invalid Image", "The selected image could not be read.");
return;
}
_colorImage = _colorImage.convertToFormat(QImage::Format_RGB888);
_color->setText(filename);
updatePreview();
}
void ImportHeightfieldTool::updateHeightImage() {
if (_loadingImage) {
return;
}
int size = glm::sqrt(float(_rawHeight.size()));
_heightImage = QImage(size, size, QImage::Format_RGB888);
const quint16* src = _rawHeight.constData();
float scale = _heightScale->value(), offset = _heightOffset->value();
for (int y = 0; y < size; y++) {
uchar* dest = _heightImage.scanLine(y);
for (const quint16* end = src + size; src != end; src++) {
uchar height = glm::clamp(*src * scale + offset, 1.0f, EIGHT_BIT_MAXIMUM);
*dest++ = height;
*dest++ = height;
*dest++ = height;
}
}
updatePreview();
}
void ImportHeightfieldTool::updatePreview() {
QVector<BufferDataPointer> buffers;
if (_heightImage.width() > 0 && _heightImage.height() > 0) {
float z = 0.0f;
int blockSize = pow(2.0, _blockSize->value());
int heightSize = blockSize + HeightfieldBuffer::HEIGHT_EXTENSION;
int colorScale = glm::round(glm::log(_colorImage.height() / (float)_heightImage.height()) / glm::log(2.0f));
int colorBlockSize = blockSize * pow(2.0, qMax(colorScale, 0));
int colorSize = colorBlockSize + HeightfieldBuffer::SHARED_EDGE;
for (int i = 0; i < _heightImage.height(); i += blockSize, z++) {
float x = 0.0f;
for (int j = 0; j < _heightImage.width(); j += blockSize, x++) {
QByteArray height(heightSize * heightSize, 0);
int extendedI = qMax(i - HeightfieldBuffer::HEIGHT_BORDER, 0);
int extendedJ = qMax(j - HeightfieldBuffer::HEIGHT_BORDER, 0);
int offsetY = extendedI - i + HeightfieldBuffer::HEIGHT_BORDER;
int offsetX = extendedJ - j + HeightfieldBuffer::HEIGHT_BORDER;
int rows = qMin(heightSize - offsetY, _heightImage.height() - extendedI);
int columns = qMin(heightSize - offsetX, _heightImage.width() - extendedJ);
for (int y = 0; y < rows; y++) {
uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * DataBlock::COLOR_BYTES;
char* dest = height.data() + (y + offsetY) * heightSize + offsetX;
for (int x = 0; x < columns; x++) {
*dest++ = qMax((uchar)1, *src);
src += DataBlock::COLOR_BYTES;
}
}
QByteArray color;
if (!_colorImage.isNull()) {
int colorI = (i / blockSize) * colorBlockSize;
int colorJ = (j / blockSize) * colorBlockSize;
color = QByteArray(colorSize * colorSize * DataBlock::COLOR_BYTES, 0);
rows = qMax(0, qMin(colorSize, _colorImage.height() - colorI));
columns = qMax(0, qMin(colorSize, _colorImage.width() - colorJ));
for (int y = 0; y < rows; y++) {
memcpy(color.data() + y * colorSize * DataBlock::COLOR_BYTES,
_colorImage.scanLine(colorI + y) + colorJ * DataBlock::COLOR_BYTES,
columns * DataBlock::COLOR_BYTES);
}
}
buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color)));
}
}
}
_preview.setBuffers(buffers);
void ImportHeightfieldTool::simulate(float deltaTime) {
static_cast<Heightfield*>(_spanner.data())->getRenderer()->simulate(deltaTime);
}
void ImportHeightfieldTool::renderPreview() {
if (isVisible()) {
_preview.render(_translation->getValue(), _translation->getSingleStep());
static_cast<Heightfield*>(_spanner.data())->getRenderer()->render();
}
const int HEIGHTFIELD_BLOCK_SIZE = 256;
void ImportHeightfieldTool::apply() {
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute());
if (!(_height->getHeight() && attribute)) {
return;
}
int width = _height->getHeight()->getWidth();
const QVector<quint16>& contents = _height->getHeight()->getContents();
int height = contents.size() / width;
int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION;
int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION;
float scale = pow(2.0, _scale->value());
for (int i = 0; i < innerHeight; i += HEIGHTFIELD_BLOCK_SIZE) {
for (int j = 0; j < innerWidth; j += HEIGHTFIELD_BLOCK_SIZE) {
Heightfield* heightfield = new Heightfield();
int extendedHeightSize = HEIGHTFIELD_BLOCK_SIZE + HeightfieldHeight::HEIGHT_EXTENSION;
QVector<quint16> heightContents(extendedHeightSize * extendedHeightSize);
quint16* dest = heightContents.data();
const quint16* src = contents.constData() + i * width + j;
int copyWidth = qMin(width - j, extendedHeightSize);
int copyHeight = qMin(height - i, extendedHeightSize);
for (int z = 0; z < copyHeight; z++, src += width, dest += extendedHeightSize) {
memcpy(dest, src, copyWidth * sizeof(quint16));
}
heightfield->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(extendedHeightSize, heightContents)));
int materialWidth = HEIGHTFIELD_BLOCK_SIZE + HeightfieldData::SHARED_EDGE;
int materialHeight = materialWidth;
if (_color->getColor()) {
int colorWidth = _color->getColor()->getWidth();
const QByteArray& contents = _color->getColor()->getContents();
int colorHeight = contents.size() / (colorWidth * DataBlock::COLOR_BYTES);
int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE;
int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE;
materialWidth = HEIGHTFIELD_BLOCK_SIZE * innerColorWidth / innerWidth + HeightfieldData::SHARED_EDGE;
materialHeight = HEIGHTFIELD_BLOCK_SIZE * innerColorHeight / innerHeight + HeightfieldData::SHARED_EDGE;
QByteArray colorContents(materialWidth * materialHeight * DataBlock::COLOR_BYTES, 0);
int colorI = i * (materialWidth - HeightfieldData::SHARED_EDGE) / HEIGHTFIELD_BLOCK_SIZE;
int colorJ = j * (materialHeight - HeightfieldData::SHARED_EDGE) / HEIGHTFIELD_BLOCK_SIZE;
char* dest = colorContents.data();
const char* src = contents.constData() + (colorI * colorWidth + colorJ) * DataBlock::COLOR_BYTES;
int copyWidth = qMin(colorWidth - colorJ, materialWidth);
int copyHeight = qMin(colorHeight - colorI, materialHeight);
for (int z = 0; z < copyHeight; z++, src += colorWidth * DataBlock::COLOR_BYTES,
dest += materialWidth * DataBlock::COLOR_BYTES) {
memcpy(dest, src, copyWidth * DataBlock::COLOR_BYTES);
}
heightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(materialWidth, colorContents)));
} else {
heightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(materialWidth,
QByteArray(materialWidth * materialHeight * DataBlock::COLOR_BYTES, 0xFF))));
}
heightfield->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth,
QByteArray(materialWidth * materialHeight, 0), QVector<SharedObjectPointer>())));
heightfield->setScale(scale);
heightfield->setAspectY(_heightScale->value() / scale);
heightfield->setTranslation(_translation->getValue() + glm::vec3((j / HEIGHTFIELD_BLOCK_SIZE) * scale,
_heightOffset->value(), (i / HEIGHTFIELD_BLOCK_SIZE) * scale));
MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, heightfield)) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
}
}
EraseHeightfieldTool::EraseHeightfieldTool(MetavoxelEditor* editor) :
HeightfieldTool(editor, "Erase Heightfield") {
void ImportHeightfieldTool::updateSpanner() {
Heightfield* heightfield = static_cast<Heightfield*>(_spanner.data());
heightfield->setHeight(_height->getHeight());
heightfield->setColor(_color->getColor());
_form->addRow("Width:", _width = new QSpinBox());
_width->setMinimum(1);
_width->setMaximum(INT_MAX);
_form->addRow("Length:", _length = new QSpinBox());
_length->setMinimum(1);
_length->setMaximum(INT_MAX);
}
void EraseHeightfieldTool::render() {
HeightfieldTool::render();
glColor3f(1.0f, 0.0f, 0.0f);
glLineWidth(4.0f);
glPushMatrix();
glm::vec3 translation = _translation->getValue();
glTranslatef(translation.x, translation.y, translation.z);
float scale = _translation->getSingleStep();
glScalef(scale * _width->value(), scale, scale * _length->value());
glTranslatef(0.5f, 0.5f, 0.5f);
glutWireCube(1.0);
glPopMatrix();
glLineWidth(1.0f);
}
void EraseHeightfieldTool::apply() {
// clear the heightfield
float scale = _translation->getSingleStep();
BoxSetEdit edit(Box(_translation->getValue(), _translation->getValue() +
glm::vec3(_width->value() * scale, scale, _length->value() * scale)), scale,
OwnedAttributeValue(AttributeRegistry::getInstance()->getHeightfieldAttribute()));
MetavoxelEditMessage message = { QVariant::fromValue(edit) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
// and the color
edit.value = OwnedAttributeValue(AttributeRegistry::getInstance()->getHeightfieldColorAttribute());
message.edit = QVariant::fromValue(edit);
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
float scale = pow(2.0, _scale->value());
float aspectZ = 1.0f;
if (_height->getHeight()) {
int width = _height->getHeight()->getWidth();
int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION;
int innerHeight = _height->getHeight()->getContents().size() / width - HeightfieldHeight::HEIGHT_EXTENSION;
float widthBlocks = glm::ceil((float)innerWidth / HEIGHTFIELD_BLOCK_SIZE);
scale *= widthBlocks;
aspectZ = glm::ceil((float)innerHeight / HEIGHTFIELD_BLOCK_SIZE) / widthBlocks;
}
heightfield->setScale(scale);
heightfield->setAspectY(_heightScale->value() / scale);
heightfield->setAspectZ(aspectZ);
heightfield->setTranslation(_translation->getValue() + glm::vec3(0.0f, _heightOffset->value(), 0.0f));
}
HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name) :
@ -1268,7 +897,7 @@ HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QStrin
}
bool HeightfieldBrushTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("HeightfieldAttribute");
return attribute->inherits("SpannerSetAttribute");
}
void HeightfieldBrushTool::render() {

View file

@ -62,6 +62,7 @@ private slots:
void simulate(float deltaTime);
void render();
void renderPreview();
private:
@ -104,6 +105,9 @@ public:
/// Renders the tool's interface, if any.
virtual void render();
/// Renders the tool's metavoxel preview, if any.
virtual void renderPreview();
protected:
MetavoxelEditor* _editor;
@ -184,7 +188,7 @@ public:
virtual void simulate(float deltaTime);
virtual void render();
virtual void renderPreview();
virtual bool appliesTo(const AttributePointer& attribute) const;
@ -199,6 +203,10 @@ protected:
protected slots:
void place();
private:
QCheckBox* _followMouse;
};
/// Allows inserting a spanner into the scene.
@ -242,21 +250,6 @@ private slots:
void clear();
};
/// Allows setting the value by placing a spanner.
class SetSpannerTool : public PlaceSpannerTool {
Q_OBJECT
public:
SetSpannerTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
protected:
virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner);
};
/// Base class for heightfield tools.
class HeightfieldTool : public MetavoxelTool {
Q_OBJECT
@ -288,55 +281,27 @@ public:
ImportHeightfieldTool(MetavoxelEditor* editor);
virtual void simulate(float deltaTime);
virtual void renderPreview();
protected:
virtual void apply();
private slots:
void selectHeightFile();
void selectColorFile();
void updateHeightImage();
void updatePreview();
void renderPreview();
void updateSpanner();
private:
QSpinBox* _blockSize;
QPushButton* _height;
QWidget* _rawOptions;
QDoubleSpinBox* _heightScale;
QDoubleSpinBox* _heightOffset;
bool _loadingImage;
QPushButton* _color;
HeightfieldHeightEditor* _height;
HeightfieldColorEditor* _color;
QVector<quint16> _rawHeight;
QImage _heightImage;
QImage _colorImage;
HeightfieldPreview _preview;
};
/// Allows clearing heighfield blocks.
class EraseHeightfieldTool : public HeightfieldTool {
Q_OBJECT
public:
EraseHeightfieldTool(MetavoxelEditor* editor);
virtual void render();
protected:
virtual void apply();
private:
QSpinBox* _width;
QSpinBox* _length;
SharedObjectPointer _spanner;
};
/// Base class for tools that allow painting on heightfields.

File diff suppressed because it is too large Load diff

View file

@ -88,21 +88,6 @@ public:
/// Returns a reference to the standard SharedObjectSet "spanners" attribute.
const AttributePointer& getSpannersAttribute() const { return _spannersAttribute; }
/// Returns a reference to the standard QRgb "color" attribute.
const AttributePointer& getColorAttribute() const { return _colorAttribute; }
/// Returns a reference to the standard packed normal "normal" attribute.
const AttributePointer& getNormalAttribute() const { return _normalAttribute; }
/// Returns a reference to the standard QRgb "spannerColor" attribute.
const AttributePointer& getSpannerColorAttribute() const { return _spannerColorAttribute; }
/// Returns a reference to the standard packed normal "spannerNormal" attribute.
const AttributePointer& getSpannerNormalAttribute() const { return _spannerNormalAttribute; }
/// Returns a reference to the standard "spannerMask" attribute.
const AttributePointer& getSpannerMaskAttribute() const { return _spannerMaskAttribute; }
/// Returns a reference to the standard HeightfieldHeightDataPointer "heightfield" attribute.
const AttributePointer& getHeightfieldAttribute() const { return _heightfieldAttribute; }
@ -131,11 +116,6 @@ private:
AttributePointer _guideAttribute;
AttributePointer _rendererAttribute;
AttributePointer _spannersAttribute;
AttributePointer _colorAttribute;
AttributePointer _normalAttribute;
AttributePointer _spannerColorAttribute;
AttributePointer _spannerNormalAttribute;
AttributePointer _spannerMaskAttribute;
AttributePointer _heightfieldAttribute;
AttributePointer _heightfieldColorAttribute;
AttributePointer _heightfieldMaterialAttribute;
@ -366,51 +346,13 @@ template<class T, int bits> inline bool SimpleInlineAttribute<T, bits>::merge(
return allChildrenEqual;
}
/// Simple float attribute.
/// A simple float attribute.
class FloatAttribute : public SimpleInlineAttribute<float> {
Q_OBJECT
Q_PROPERTY(float defaultValue MEMBER _defaultValue)
public:
Q_INVOKABLE FloatAttribute(const QString& name = QString(), float defaultValue = 0.0f);
};
/// Provides appropriate averaging for RGBA values.
class QRgbAttribute : public InlineAttribute<QRgb> {
Q_OBJECT
Q_PROPERTY(uint defaultValue MEMBER _defaultValue)
public:
Q_INVOKABLE QRgbAttribute(const QString& name = QString(), QRgb defaultValue = QRgb());
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual void* mix(void* first, void* second, float alpha) const;
virtual void* blend(void* source, void* dest) const;
virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const;
virtual void* createFromVariant(const QVariant& value) const;
virtual QWidget* createEditor(QWidget* parent = NULL) const;
};
/// Provides appropriate averaging for packed normals.
class PackedNormalAttribute : public QRgbAttribute {
Q_OBJECT
public:
Q_INVOKABLE PackedNormalAttribute(const QString& name = QString(), QRgb defaultValue = QRgb());
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual void* mix(void* first, void* second, float alpha) const;
virtual void* blend(void* source, void* dest) const;
Q_INVOKABLE FloatAttribute(const QString& name = QString());
};
/// Packs a normal into an RGB value.
@ -422,42 +364,6 @@ QRgb packNormal(const glm::vec3& normal, int alpha);
/// Unpacks a normal from an RGB value.
glm::vec3 unpackNormal(QRgb value);
/// RGBA values for voxelized spanners.
class SpannerQRgbAttribute : public QRgbAttribute {
Q_OBJECT
public:
Q_INVOKABLE SpannerQRgbAttribute(const QString& name = QString(), QRgb defaultValue = QRgb());
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
};
/// Packed normals for voxelized spanners.
class SpannerPackedNormalAttribute : public PackedNormalAttribute {
Q_OBJECT
public:
Q_INVOKABLE SpannerPackedNormalAttribute(const QString& name = QString(), QRgb defaultValue = QRgb());
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
};
typedef QExplicitlySharedDataPointer<DataBlock> DataBlockPointer;
/// Base class for blocks of data.
@ -494,124 +400,6 @@ protected:
QMutex _encodedSubdivisionsMutex;
};
typedef QExplicitlySharedDataPointer<HeightfieldHeightData> HeightfieldHeightDataPointer;
/// Contains a block of heightfield height data.
class HeightfieldHeightData : public DataBlock {
public:
HeightfieldHeightData(const QByteArray& contents);
HeightfieldHeightData(Bitstream& in, int bytes);
HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& reference);
HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& ancestor,
const glm::vec3& minimum, float size);
const QByteArray& getContents() const { return _contents; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldHeightDataPointer& reference);
void writeSubdivided(Bitstream& out, const HeightfieldHeightDataPointer& ancestor,
const glm::vec3& minimum, float size);
private:
void read(Bitstream& in, int bytes);
void set(const QImage& image);
QByteArray _contents;
};
/// An attribute that stores heightfield data.
class HeightfieldAttribute : public InlineAttribute<HeightfieldHeightDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE HeightfieldAttribute(const QString& name = QString());
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
};
typedef QExplicitlySharedDataPointer<HeightfieldColorData> HeightfieldColorDataPointer;
/// Contains a block of heightfield color data.
class HeightfieldColorData : public DataBlock {
public:
HeightfieldColorData(const QByteArray& contents);
HeightfieldColorData(Bitstream& in, int bytes);
HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& reference);
HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& ancestor,
const glm::vec3& minimum, float size);
const QByteArray& getContents() const { return _contents; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldColorDataPointer& reference);
void writeSubdivided(Bitstream& out, const HeightfieldColorDataPointer& ancestor,
const glm::vec3& minimum, float size);
private:
void read(Bitstream& in, int bytes);
void set(const QImage& image);
QByteArray _contents;
};
/// An attribute that stores heightfield colors.
class HeightfieldColorAttribute : public InlineAttribute<HeightfieldColorDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE HeightfieldColorAttribute(const QString& name = QString());
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
};
typedef QExplicitlySharedDataPointer<HeightfieldMaterialData> HeightfieldMaterialDataPointer;
/// Contains a block of heightfield material data.
class HeightfieldMaterialData : public DataBlock {
public:
HeightfieldMaterialData(const QByteArray& contents,
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
HeightfieldMaterialData(Bitstream& in, int bytes);
HeightfieldMaterialData(Bitstream& in, int bytes, const HeightfieldMaterialDataPointer& reference);
const QByteArray& getContents() const { return _contents; }
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldMaterialDataPointer& reference);
private:
void read(Bitstream& in, int bytes);
QByteArray _contents;
QVector<SharedObjectPointer> _materials;
};
/// Contains the description of a material.
class MaterialObject : public SharedObject {
Q_OBJECT
@ -635,25 +423,6 @@ private:
float _scaleT;
};
/// An attribute that stores heightfield materials.
class HeightfieldMaterialAttribute : public InlineAttribute<HeightfieldMaterialDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE HeightfieldMaterialAttribute(const QString& name = QString());
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
};
/// Utility method for editing: given a material pointer and a list of materials, returns the corresponding material index,
/// creating a new entry in the list if necessary.
uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials, QByteArray& contents);

View file

@ -16,6 +16,7 @@
#include "DatagramSequencer.h"
#include "MetavoxelData.h"
#include "Spanner.h"
class PacketRecord;

View file

@ -63,17 +63,85 @@ SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(cons
return closestSpanner;
}
void MetavoxelClientManager::setSphere(const glm::vec3& center, float radius, const QColor& color) {
Sphere* sphere = new Sphere();
sphere->setTranslation(center);
sphere->setScale(radius);
sphere->setColor(color);
setSpanner(sphere);
class RayHeightfieldIntersectionVisitor : public RaySpannerIntersectionVisitor {
public:
float intersectionDistance;
RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod);
virtual bool visitSpanner(Spanner* spanner, float distance);
};
RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm::vec3& origin,
const glm::vec3& direction, const MetavoxelLOD& lod) :
RaySpannerIntersectionVisitor(origin, direction, QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getSpannersAttribute(),
QVector<AttributePointer>(), QVector<AttributePointer>(), lod),
intersectionDistance(FLT_MAX) {
}
void MetavoxelClientManager::setSpanner(const SharedObjectPointer& object, bool reliable) {
MetavoxelEditMessage edit = { QVariant::fromValue(SetSpannerEdit(object)) };
applyEdit(edit, reliable);
bool RayHeightfieldIntersectionVisitor::visitSpanner(Spanner* spanner, float distance) {
if (spanner->isHeightfield()) {
intersectionDistance = distance;
return false;
}
return true;
}
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;
}
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) {

View file

@ -36,11 +36,11 @@ 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 float getHeightfieldHeight(const glm::vec3& location);
Q_INVOKABLE void setSpanner(const SharedObjectPointer& object, bool reliable = false);
Q_INVOKABLE void paintHeightfieldHeight(const glm::vec3& position, float radius, float height);
Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false);

File diff suppressed because it is too large Load diff

View file

@ -12,13 +12,10 @@
#ifndef hifi_MetavoxelData_h
#define hifi_MetavoxelData_h
#include <QBitArray>
#include <QHash>
#include <QMutex>
#include <QSharedData>
#include <QSharedPointer>
#include <QScriptString>
#include <QScriptValue>
#include <QVector>
#include <glm/glm.hpp>
@ -26,16 +23,12 @@
#include "AttributeRegistry.h"
#include "MetavoxelUtil.h"
class QScriptContext;
class MetavoxelInfo;
class MetavoxelNode;
class MetavoxelRendererImplementation;
class MetavoxelVisitation;
class MetavoxelVisitor;
class NetworkValue;
class Spanner;
class SpannerRenderer;
/// Determines whether to subdivide each node when traversing. Contains the position (presumed to be of the viewer) and a
/// threshold value, where lower thresholds cause smaller/more distant voxels to be subdivided.
@ -109,6 +102,9 @@ public:
void replace(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& oldObject,
const SharedObjectPointer& newObject);
/// Retrieves all spanners that intersect the specified bounds.
void getIntersecting(const AttributePointer& attribute, const Box& bounds, QVector<SharedObjectPointer>& results);
/// Clears all data in the specified attribute layer.
void clear(const AttributePointer& attribute);
@ -373,16 +369,14 @@ class SpannerVisitor : public MetavoxelVisitor {
public:
SpannerVisitor(const QVector<AttributePointer>& spannerInputs,
const QVector<AttributePointer>& spannerMasks = QVector<AttributePointer>(),
const QVector<AttributePointer>& inputs = QVector<AttributePointer>(),
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
const MetavoxelLOD& lod = MetavoxelLOD(),
int order = DEFAULT_ORDER);
/// Visits a spanner (or part thereof).
/// \param clipSize the size of the clip volume, or zero if unclipped
/// Visits a spanner.
/// \return true to continue, false to short-circuit the tour
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) = 0;
virtual bool visit(Spanner* spanner) = 0;
virtual void prepare(MetavoxelData* data);
virtual int visit(MetavoxelInfo& info);
@ -390,7 +384,6 @@ public:
protected:
int _spannerInputCount;
int _spannerMaskCount;
int _order;
int _visit;
};
@ -422,12 +415,11 @@ public:
RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction,
const QVector<AttributePointer>& spannerInputs,
const QVector<AttributePointer>& spannerMasks = QVector<AttributePointer>(),
const QVector<AttributePointer>& inputs = QVector<AttributePointer>(),
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
const MetavoxelLOD& lod = MetavoxelLOD());
/// Visits a spannerthat the ray intersects.
/// Visits a spanner that the ray intersects.
/// \return true to continue, false to short-circuit the tour
virtual bool visitSpanner(Spanner* spanner, float distance) = 0;
@ -437,7 +429,6 @@ public:
protected:
int _spannerInputCount;
int _spannerMaskCount;
int _visit;
};
@ -468,62 +459,6 @@ public:
virtual bool guideToDifferent(MetavoxelVisitation& visitation);
};
/// A temporary test guide that just makes the existing voxels throb with delight.
class ThrobbingMetavoxelGuide : public DefaultMetavoxelGuide {
Q_OBJECT
Q_PROPERTY(float rate MEMBER _rate)
public:
Q_INVOKABLE ThrobbingMetavoxelGuide();
virtual bool guide(MetavoxelVisitation& visitation);
private:
float _rate;
};
/// Represents a guide implemented in Javascript.
class ScriptedMetavoxelGuide : public DefaultMetavoxelGuide {
Q_OBJECT
Q_PROPERTY(ParameterizedURL url MEMBER _url WRITE setURL)
public:
Q_INVOKABLE ScriptedMetavoxelGuide();
virtual bool guide(MetavoxelVisitation& visitation);
public slots:
void setURL(const ParameterizedURL& url);
private:
static QScriptValue getInputs(QScriptContext* context, QScriptEngine* engine);
static QScriptValue getOutputs(QScriptContext* context, QScriptEngine* engine);
static QScriptValue visit(QScriptContext* context, QScriptEngine* engine);
ParameterizedURL _url;
QSharedPointer<NetworkValue> _guideFunction;
QScriptString _minimumHandle;
QScriptString _sizeHandle;
QScriptString _inputValuesHandle;
QScriptString _outputValuesHandle;
QScriptString _isLeafHandle;
QScriptValueList _arguments;
QScriptValue _getInputsFunction;
QScriptValue _getOutputsFunction;
QScriptValue _visitFunction;
QScriptValue _info;
QScriptValue _minimum;
MetavoxelVisitation* _visitation;
};
/// Contains the state associated with a visit to a metavoxel system.
class MetavoxelVisitation {
public:
@ -592,310 +527,4 @@ public:
virtual QByteArray getImplementationClassName() const;
};
/// An object that spans multiple octree cells.
class Spanner : public SharedObject {
Q_OBJECT
Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false)
Q_PROPERTY(float placementGranularity MEMBER _placementGranularity DESIGNABLE false)
Q_PROPERTY(float voxelizationGranularity MEMBER _voxelizationGranularity DESIGNABLE false)
Q_PROPERTY(float masked MEMBER _masked DESIGNABLE false)
public:
/// Returns the value of the global visit counter and increments it.
static int getAndIncrementNextVisit() { return _nextVisit.fetchAndAddOrdered(1); }
Spanner();
void setBounds(const Box& bounds);
const Box& getBounds() const { return _bounds; }
void setPlacementGranularity(float granularity) { _placementGranularity = granularity; }
float getPlacementGranularity() const { return _placementGranularity; }
void setVoxelizationGranularity(float granularity) { _voxelizationGranularity = granularity; }
float getVoxelizationGranularity() const { return _voxelizationGranularity; }
void setMasked(bool masked) { _masked = masked; }
bool isMasked() const { return _masked; }
/// Returns a reference to the list of attributes associated with this spanner.
virtual const QVector<AttributePointer>& getAttributes() const;
/// Returns a reference to the list of corresponding attributes that we voxelize the spanner into.
virtual const QVector<AttributePointer>& getVoxelizedAttributes() const;
/// Sets the attribute values associated with this spanner in the supplied info.
/// \return true to recurse, false to stop
virtual bool getAttributeValues(MetavoxelInfo& info, bool force = false) const;
/// Blends the attribute values associated with this spanner into the supplied info.
/// \param force if true, blend even if we would normally subdivide
/// \return true to recurse, false to stop
virtual bool blendAttributeValues(MetavoxelInfo& info, bool force = false) const;
/// Checks whether we've visited this object on the current traversal. If we have, returns false.
/// If we haven't, sets the last visit identifier and returns true.
bool testAndSetVisited(int visit);
/// Returns a pointer to the renderer, creating it if necessary.
SpannerRenderer* getRenderer();
/// Finds the intersection between the described ray and this spanner.
/// \param clipSize the size of the clip region, or zero if unclipped
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const;
/// Checks whether this spanner has its own colors.
virtual bool hasOwnColors() const;
/// Checks whether this spanner has its own materials.
virtual bool hasOwnMaterials() const;
/// Checks whether the spanner contains the specified point.
virtual bool contains(const glm::vec3& point);
/// Retrieves the color at the specified point.
virtual QRgb getColorAt(const glm::vec3& point);
/// Retrieves the material at the specified point.
virtual int getMaterialAt(const glm::vec3& point);
/// Retrieves a reference to the list of materials.
virtual QVector<SharedObjectPointer>& getMaterials();
/// Finds the intersection, if any, between the specified line segment and the spanner.
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
signals:
void boundsWillChange();
void boundsChanged(const Box& bounds);
protected:
SpannerRenderer* _renderer;
/// Returns the name of the class to instantiate in order to render this spanner.
virtual QByteArray getRendererClassName() const;
private:
Box _bounds;
float _placementGranularity;
float _voxelizationGranularity;
bool _masked;
QHash<QThread*, int> _lastVisits; ///< last visit identifiers for each thread
QMutex _lastVisitsMutex;
static QAtomicInt _nextVisit; ///< the global visit counter
};
/// Base class for objects that can render spanners.
class SpannerRenderer : public QObject {
Q_OBJECT
public:
enum Mode { DEFAULT_MODE, DIFFUSE_MODE, NORMAL_MODE };
Q_INVOKABLE SpannerRenderer();
virtual void init(Spanner* spanner);
virtual void simulate(float deltaTime);
virtual void render(const glm::vec4& color, Mode mode, const glm::vec3& clipMinimum, float clipSize);
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const;
protected:
Spanner* _spanner;
};
/// An object with a 3D transform.
class Transformable : public Spanner {
Q_OBJECT
Q_PROPERTY(glm::vec3 translation MEMBER _translation WRITE setTranslation NOTIFY translationChanged)
Q_PROPERTY(glm::quat rotation MEMBER _rotation WRITE setRotation NOTIFY rotationChanged)
Q_PROPERTY(float scale MEMBER _scale WRITE setScale NOTIFY scaleChanged)
public:
Transformable();
void setTranslation(const glm::vec3& translation);
const glm::vec3& getTranslation() const { return _translation; }
void setRotation(const glm::quat& rotation);
const glm::quat& getRotation() const { return _rotation; }
void setScale(float scale);
float getScale() const { return _scale; }
signals:
void translationChanged(const glm::vec3& translation);
void rotationChanged(const glm::quat& rotation);
void scaleChanged(float scale);
private:
glm::vec3 _translation;
glm::quat _rotation;
float _scale;
};
/// A transformable object with a color.
class ColorTransformable : public Transformable {
Q_OBJECT
Q_PROPERTY(QColor color MEMBER _color WRITE setColor NOTIFY colorChanged DESIGNABLE false)
public:
ColorTransformable();
void setColor(const QColor& color);
const QColor& getColor() const { return _color; }
signals:
void colorChanged(const QColor& color);
protected:
QColor _color;
};
/// A sphere.
class Sphere : public ColorTransformable {
Q_OBJECT
public:
Q_INVOKABLE Sphere();
virtual const QVector<AttributePointer>& getAttributes() const;
virtual const QVector<AttributePointer>& getVoxelizedAttributes() const;
virtual bool getAttributeValues(MetavoxelInfo& info, bool force = false) const;
virtual bool blendAttributeValues(MetavoxelInfo& info, bool force = false) const;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const;
virtual bool contains(const glm::vec3& point);
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
protected:
virtual QByteArray getRendererClassName() const;
private slots:
void updateBounds();
private:
AttributeValue getNormal(MetavoxelInfo& info, int alpha) const;
};
/// A cuboid.
class Cuboid : public ColorTransformable {
Q_OBJECT
Q_PROPERTY(float aspectY MEMBER _aspectY WRITE setAspectY NOTIFY aspectYChanged)
Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged)
public:
Q_INVOKABLE Cuboid();
void setAspectY(float aspectY);
float getAspectY() const { return _aspectY; }
void setAspectZ(float aspectZ);
float getAspectZ() const { return _aspectZ; }
virtual bool contains(const glm::vec3& point);
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
signals:
void aspectYChanged(float aspectY);
void aspectZChanged(float aspectZ);
protected:
virtual QByteArray getRendererClassName() const;
private slots:
void updateBoundsAndPlanes();
private:
float _aspectY;
float _aspectZ;
static const int PLANE_COUNT = 6;
glm::vec4 _planes[PLANE_COUNT];
};
/// A static 3D model loaded from the network.
class StaticModel : public Transformable {
Q_OBJECT
Q_PROPERTY(QUrl url MEMBER _url WRITE setURL NOTIFY urlChanged)
public:
Q_INVOKABLE StaticModel();
void setURL(const QUrl& url);
const QUrl& getURL() const { return _url; }
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const;
signals:
void urlChanged(const QUrl& url);
protected:
virtual QByteArray getRendererClassName() const;
private:
QUrl _url;
};
/// A heightfield represented as a spanner.
class Heightfield : public Transformable {
Q_OBJECT
public:
Heightfield(const Box& bounds, float increment, const QByteArray& height, const QByteArray& color,
const QByteArray& material, const QVector<SharedObjectPointer>& materials);
QByteArray& getHeight() { return _height; }
QByteArray& getColor() { return _color; }
QByteArray& getMaterial() { return _material; }
virtual bool hasOwnColors() const;
virtual bool hasOwnMaterials() const;
virtual QRgb getColorAt(const glm::vec3& point);
virtual int getMaterialAt(const glm::vec3& point);
virtual QVector<SharedObjectPointer>& getMaterials();
virtual bool contains(const glm::vec3& point);
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
private:
float _increment;
int _width;
float _heightScale;
QByteArray _height;
QByteArray _color;
QByteArray _material;
QVector<SharedObjectPointer> _materials;
};
#endif // hifi_MetavoxelData_h

View file

@ -10,6 +10,7 @@
//
#include "MetavoxelMessages.h"
#include "Spanner.h"
void MetavoxelEditMessage::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
static_cast<const MetavoxelEdit*>(edit.data())->apply(data, objects);
@ -39,8 +40,7 @@ private:
};
BoxSetEditVisitor::BoxSetEditVisitor(const BoxSetEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() << edit.value.getAttribute() <<
AttributeRegistry::getInstance()->getSpannerMaskAttribute()),
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() << edit.value.getAttribute()),
_edit(edit) {
}
@ -55,57 +55,17 @@ int BoxSetEditVisitor::visit(MetavoxelInfo& info) {
float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size);
if (volume >= 1.0f) {
info.outputValues[0] = _edit.value;
info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline<float>(1.0f));
return STOP_RECURSION; // entirely contained
}
if (info.size <= _edit.granularity) {
if (volume >= 0.5f) {
info.outputValues[0] = _edit.value;
info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline<float>(1.0f));
}
return STOP_RECURSION; // reached granularity limit; take best guess
}
return DEFAULT_ORDER; // subdivide
}
class GatherUnmaskedSpannersVisitor : public SpannerVisitor {
public:
GatherUnmaskedSpannersVisitor(const Box& bounds);
const QList<SharedObjectPointer>& getUnmaskedSpanners() const { return _unmaskedSpanners; }
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
private:
Box _bounds;
QList<SharedObjectPointer> _unmaskedSpanners;
};
GatherUnmaskedSpannersVisitor::GatherUnmaskedSpannersVisitor(const Box& bounds) :
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute()),
_bounds(bounds) {
}
bool GatherUnmaskedSpannersVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
if (!spanner->isMasked() && spanner->getBounds().intersects(_bounds)) {
_unmaskedSpanners.append(spanner);
}
return true;
}
static void setIntersectingMasked(const Box& bounds, MetavoxelData& data) {
GatherUnmaskedSpannersVisitor visitor(bounds);
data.guide(visitor);
foreach (const SharedObjectPointer& object, visitor.getUnmaskedSpanners()) {
Spanner* newSpanner = static_cast<Spanner*>(object->clone(true));
newSpanner->setMasked(true);
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), object, newSpanner);
}
}
void BoxSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
while (!data.getBounds().contains(region)) {
@ -114,9 +74,6 @@ void BoxSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects)
BoxSetEditVisitor setVisitor(*this);
data.guide(setVisitor);
// flip the mask flag of all intersecting spanners
setIntersectingMasked(region, data);
}
GlobalSetEdit::GlobalSetEdit(const OwnedAttributeValue& value) :
@ -155,56 +112,8 @@ InsertSpannerEdit::InsertSpannerEdit(const AttributePointer& attribute, const Sh
spanner(spanner) {
}
class UpdateSpannerVisitor : public MetavoxelVisitor {
public:
UpdateSpannerVisitor(const QVector<AttributePointer>& attributes, Spanner* spanner);
virtual int visit(MetavoxelInfo& info);
private:
Spanner* _spanner;
float _voxelizationSize;
int _steps;
};
UpdateSpannerVisitor::UpdateSpannerVisitor(const QVector<AttributePointer>& attributes, Spanner* spanner) :
MetavoxelVisitor(QVector<AttributePointer>() << attributes << AttributeRegistry::getInstance()->getSpannersAttribute(),
attributes),
_spanner(spanner),
_voxelizationSize(qMax(spanner->getBounds().getLongestSide(), spanner->getPlacementGranularity()) * 2.0f /
AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()),
_steps(glm::round(logf(AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()) /
logf(2.0f) - 2.0f)) {
}
int UpdateSpannerVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_spanner->getBounds())) {
return STOP_RECURSION;
}
MetavoxelInfo* parentInfo = info.parentInfo;
for (int i = 0; i < _steps && parentInfo; i++) {
parentInfo = parentInfo->parentInfo;
}
for (int i = 0; i < _outputs.size(); i++) {
info.outputValues[i] = AttributeValue(_outputs.at(i));
}
if (parentInfo) {
foreach (const SharedObjectPointer& object,
parentInfo->inputValues.at(_outputs.size()).getInlineValue<SharedObjectSet>()) {
static_cast<const Spanner*>(object.data())->blendAttributeValues(info, true);
}
}
return (info.size > _voxelizationSize) ? DEFAULT_ORDER : STOP_RECURSION;
}
void InsertSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
data.insert(attribute, this->spanner);
Spanner* spanner = static_cast<Spanner*>(this->spanner.data());
UpdateSpannerVisitor visitor(spanner->getVoxelizedAttributes(), spanner);
data.guide(visitor);
data.insert(attribute, spanner);
}
RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id) :
@ -218,99 +127,15 @@ void RemoveSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& o
qDebug() << "Missing object to remove" << id;
return;
}
// keep a strong reference to the object
SharedObjectPointer sharedPointer = object;
data.remove(attribute, object);
Spanner* spanner = static_cast<Spanner*>(object);
UpdateSpannerVisitor visitor(spanner->getVoxelizedAttributes(), spanner);
data.guide(visitor);
}
ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) :
attribute(attribute) {
}
class GatherSpannerAttributesVisitor : public SpannerVisitor {
public:
GatherSpannerAttributesVisitor(const AttributePointer& attribute);
const QSet<AttributePointer>& getAttributes() const { return _attributes; }
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
protected:
QSet<AttributePointer> _attributes;
};
GatherSpannerAttributesVisitor::GatherSpannerAttributesVisitor(const AttributePointer& attribute) :
SpannerVisitor(QVector<AttributePointer>() << attribute) {
}
bool GatherSpannerAttributesVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
foreach (const AttributePointer& attribute, spanner->getVoxelizedAttributes()) {
_attributes.insert(attribute);
}
return true;
}
void ClearSpannersEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// find all the spanner attributes
GatherSpannerAttributesVisitor visitor(attribute);
data.guide(visitor);
data.clear(attribute);
foreach (const AttributePointer& attribute, visitor.getAttributes()) {
data.clear(attribute);
}
}
SetSpannerEdit::SetSpannerEdit(const SharedObjectPointer& spanner) :
spanner(spanner) {
}
class SetSpannerEditVisitor : public MetavoxelVisitor {
public:
SetSpannerEditVisitor(const QVector<AttributePointer>& attributes, Spanner* spanner);
virtual int visit(MetavoxelInfo& info);
private:
Spanner* _spanner;
};
SetSpannerEditVisitor::SetSpannerEditVisitor(const QVector<AttributePointer>& attributes, Spanner* spanner) :
MetavoxelVisitor(attributes, QVector<AttributePointer>() << attributes <<
AttributeRegistry::getInstance()->getSpannerMaskAttribute()),
_spanner(spanner) {
}
int SetSpannerEditVisitor::visit(MetavoxelInfo& info) {
if (_spanner->blendAttributeValues(info)) {
return DEFAULT_ORDER;
}
if (info.outputValues.at(0).getAttribute()) {
info.outputValues.last() = AttributeValue(_outputs.last(), encodeInline<float>(1.0f));
}
return STOP_RECURSION;
}
void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
Spanner* spanner = static_cast<Spanner*>(this->spanner.data());
// expand to fit the entire spanner
while (!data.getBounds().contains(spanner->getBounds())) {
data.expand();
}
SetSpannerEditVisitor visitor(spanner->getAttributes(), spanner);
data.guide(visitor);
setIntersectingMasked(spanner->getBounds(), data);
}
SetDataEdit::SetDataEdit(const glm::vec3& minimum, const MetavoxelData& data, bool blend) :
@ -329,87 +154,20 @@ PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position
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);
}
const int EIGHT_BIT_MAXIMUM = 255;
int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
HeightfieldHeightDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldHeightDataPointer>();
if (!pointer) {
return STOP_RECURSION;
}
QByteArray contents(pointer->getContents());
int size = glm::sqrt((float)contents.size());
int highest = size - 1;
float heightScale = size / 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;
float scaledHeight = _edit.height * EIGHT_BIT_MAXIMUM / info.size;
bool changed = false;
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
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;
if (value != *dest) {
*dest = qMin(qMax(value, 0), EIGHT_BIT_MAXIMUM);
changed = true;
}
}
}
lineDest += size;
}
if (changed) {
HeightfieldHeightDataPointer newPointer(new HeightfieldHeightData(contents));
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldHeightDataPointer>(newPointer));
}
return STOP_RECURSION;
}
void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldHeightEditVisitor visitor(*this);
data.guide(visitor);
// increase the extents slightly to include neighboring tiles
const float RADIUS_EXTENSION = 1.1f;
glm::vec3 extents = glm::vec3(radius, radius, radius) * RADIUS_EXTENSION;
QVector<SharedObjectPointer> results;
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
Box(position - extents, position + extents), results);
foreach (const SharedObjectPointer& spanner, results) {
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->paintHeight(position, radius, height);
if (newSpanner != spanner) {
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner);
}
}
}
MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& averageColor) :
@ -417,130 +175,6 @@ MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& av
averageColor(averageColor) {
}
class PaintHeightfieldMaterialEditVisitor : public MetavoxelVisitor {
public:
PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& color);
virtual int visit(MetavoxelInfo& info);
private:
glm::vec3 _position;
float _radius;
SharedObjectPointer _material;
QColor _color;
Box _bounds;
};
PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& color) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute()),
_position(position),
_radius(radius),
_material(material),
_color(color) {
const float LARGE_EXTENT = 100000.0f;
glm::vec3 extents(_radius, LARGE_EXTENT, _radius);
_bounds = Box(_position - extents, _position + extents);
}
int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
HeightfieldColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<HeightfieldColorDataPointer>();
if (colorPointer) {
QByteArray contents(colorPointer->getContents());
int size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES);
int highest = size - 1;
float heightScale = size / info.size;
glm::vec3 center = (_position - info.minimum) * heightScale;
float scaledRadius = _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 * DataBlock::COLOR_BYTES;
char* lineDest = contents.data() + (int)z * stride + (int)startX * DataBlock::COLOR_BYTES;
float squaredRadius = scaledRadius * scaledRadius;
char red = _color.red(), green = _color.green(), blue = _color.blue();
bool changed = false;
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 += DataBlock::COLOR_BYTES) {
float dx = x - center.x, dz = z - center.z;
if (dx * dx + dz * dz <= squaredRadius) {
dest[0] = red;
dest[1] = green;
dest[2] = blue;
changed = true;
}
}
lineDest += stride;
}
if (changed) {
HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents));
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
encodeInline<HeightfieldColorDataPointer>(newPointer));
}
}
HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue<HeightfieldMaterialDataPointer>();
if (materialPointer) {
QVector<SharedObjectPointer> materials = materialPointer->getMaterials();
QByteArray contents(materialPointer->getContents());
uchar materialIndex = getMaterialIndex(_material, materials, contents);
int size = glm::sqrt((float)contents.size());
int highest = size - 1;
float heightScale = highest / info.size;
glm::vec3 center = (_position - info.minimum) * heightScale;
float scaledRadius = _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);
uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX;
float squaredRadius = scaledRadius * scaledRadius;
bool changed = false;
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;
if (dx * dx + dz * dz <= squaredRadius) {
*dest = materialIndex;
changed = true;
}
}
lineDest += size;
}
if (changed) {
clearUnusedMaterials(materials, contents);
HeightfieldMaterialDataPointer newPointer(new HeightfieldMaterialData(contents, materials));
info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline<HeightfieldMaterialDataPointer>(newPointer));
}
}
return STOP_RECURSION;
}
PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& averageColor) :
MaterialEdit(material, averageColor),
@ -549,8 +183,17 @@ PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& posi
}
void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldMaterialEditVisitor visitor(position, radius, material, averageColor);
data.guide(visitor);
glm::vec3 extents(radius, radius, radius);
QVector<SharedObjectPointer> results;
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
Box(position - extents, position + extents), results);
foreach (const SharedObjectPointer& spanner, results) {
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->paintMaterial(position, radius, material, averageColor);
if (newSpanner != spanner) {
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner);
}
}
}
const int VOXEL_BLOCK_SIZE = 16;
@ -709,7 +352,8 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) {
if (minZ > 0) {
hermiteMinZ--;
hermiteSizeZ++;
}
}
const int EIGHT_BIT_MAXIMUM = 255;
QRgb* hermiteDestZ = hermiteContents.data() + hermiteMinZ * hermiteArea + hermiteMinY * hermiteSamples +
hermiteMinX * VoxelHermiteData::EDGE_COUNT;
for (int z = hermiteMinZ, hermiteMaxZ = z + hermiteSizeZ - 1; z <= hermiteMaxZ; z++, hermiteDestZ += hermiteArea) {
@ -850,262 +494,6 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) {
return STOP_RECURSION;
}
class HeightfieldClearFetchVisitor : public MetavoxelVisitor {
public:
HeightfieldClearFetchVisitor(const Box& bounds, float granularity);
const SharedObjectPointer& getSpanner() const { return _spanner; }
virtual int visit(MetavoxelInfo& info);
private:
Box _bounds;
Box _expandedBounds;
SharedObjectPointer _spanner;
Box _spannerBounds;
int _heightfieldWidth;
int _heightfieldHeight;
};
HeightfieldClearFetchVisitor::HeightfieldClearFetchVisitor(const Box& bounds, float granularity) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute()) {
// find the bounds of all voxel nodes intersected
float nodeSize = VOXEL_BLOCK_SIZE * glm::pow(2.0f, glm::floor(glm::log(granularity) / glm::log(2.0f)));
_bounds.minimum = glm::floor(bounds.minimum / nodeSize) * nodeSize;
_bounds.maximum = glm::ceil(bounds.maximum / nodeSize) * nodeSize;
// expand to include edges
_expandedBounds = _bounds;
float increment = nodeSize / VOXEL_BLOCK_SIZE;
_expandedBounds.maximum.x += increment;
_expandedBounds.maximum.z += increment;
}
int HeightfieldClearFetchVisitor::visit(MetavoxelInfo& info) {
Box bounds = info.getBounds();
if (!bounds.intersects(_expandedBounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
HeightfieldHeightDataPointer heightPointer = info.inputValues.at(0).getInlineValue<HeightfieldHeightDataPointer>();
if (!heightPointer) {
return STOP_RECURSION;
}
QByteArray contents(heightPointer->getContents());
int size = glm::sqrt((float)contents.size());
float heightScale = size / info.size;
Box overlap = bounds.getIntersection(_expandedBounds);
int srcX = (overlap.minimum.x - info.minimum.x) * heightScale;
int srcY = (overlap.minimum.z - info.minimum.z) * heightScale;
int srcWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) * heightScale);
int srcHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) * heightScale);
char* src = contents.data() + srcY * size + srcX;
// check for non-zero values
bool foundNonZero = false;
for (int y = 0; y < srcHeight; y++, src += (size - srcWidth)) {
for (char* end = src + srcWidth; src != end; src++) {
if (*src != 0) {
foundNonZero = true;
goto outerBreak;
}
}
}
outerBreak:
// if everything is zero, we're done
if (!foundNonZero) {
return STOP_RECURSION;
}
// create spanner if necessary
Heightfield* spanner = static_cast<Heightfield*>(_spanner.data());
float increment = 1.0f / heightScale;
if (!spanner) {
_spannerBounds.minimum = glm::floor(_bounds.minimum / increment) * increment;
_spannerBounds.maximum = (glm::ceil(_bounds.maximum / increment) + glm::vec3(1.0f, 0.0f, 1.0f)) * increment;
_spannerBounds.minimum.y = bounds.minimum.y;
_spannerBounds.maximum.y = bounds.maximum.y;
_heightfieldWidth = (int)glm::round((_spannerBounds.maximum.x - _spannerBounds.minimum.x) / increment);
_heightfieldHeight = (int)glm::round((_spannerBounds.maximum.z - _spannerBounds.minimum.z) / increment);
int heightfieldArea = _heightfieldWidth * _heightfieldHeight;
Box innerBounds = _spannerBounds;
innerBounds.maximum.x -= increment;
innerBounds.maximum.z -= increment;
_spanner = spanner = new Heightfield(innerBounds, increment, QByteArray(heightfieldArea, 0),
QByteArray(heightfieldArea * DataBlock::COLOR_BYTES, 0), QByteArray(heightfieldArea, 0),
QVector<SharedObjectPointer>());
}
// copy the inner area
overlap = bounds.getIntersection(_spannerBounds);
int destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale;
int destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale;
int destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale);
int destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale);
char* dest = spanner->getHeight().data() + destY * _heightfieldWidth + destX;
srcX = (overlap.minimum.x - info.minimum.x) * heightScale;
srcY = (overlap.minimum.z - info.minimum.z) * heightScale;
src = contents.data() + srcY * size + srcX;
for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth, src += size) {
memcpy(dest, src, destWidth);
}
// clear the inner area
Box innerBounds = _spannerBounds;
innerBounds.minimum.x += increment;
innerBounds.minimum.z += increment;
innerBounds.maximum.x -= increment;
innerBounds.maximum.z -= increment;
Box innerOverlap = bounds.getIntersection(innerBounds);
destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale;
destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale;
destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale);
destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale);
dest = contents.data() + destY * size + destX;
for (int y = 0; y < destHeight; y++, dest += size) {
memset(dest, 0, destWidth);
}
// see if there are any non-zero values left
foundNonZero = false;
dest = contents.data();
for (char* end = dest + contents.size(); dest != end; dest++) {
if (*dest != 0) {
foundNonZero = true;
break;
}
}
// if all is gone, clear the node
if (foundNonZero) {
HeightfieldHeightDataPointer newHeightPointer(new HeightfieldHeightData(contents));
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldHeightDataPointer>(newHeightPointer));
} else {
info.outputValues[0] = AttributeValue(_outputs.at(0));
}
// allow a border for what we clear in terms of color/material
innerBounds.minimum.x += increment;
innerBounds.minimum.z += increment;
innerBounds.maximum.x -= increment;
innerBounds.maximum.z -= increment;
innerOverlap = bounds.getIntersection(innerBounds);
HeightfieldColorDataPointer colorPointer = info.inputValues.at(1).getInlineValue<HeightfieldColorDataPointer>();
if (colorPointer) {
contents = colorPointer->getContents();
size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES);
heightScale = size / info.size;
// copy the inner area
destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale;
destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale;
destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale);
destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale);
dest = spanner->getColor().data() + (destY * _heightfieldWidth + destX) * DataBlock::COLOR_BYTES;
srcX = (overlap.minimum.x - info.minimum.x) * heightScale;
srcY = (overlap.minimum.z - info.minimum.z) * heightScale;
src = contents.data() + (srcY * size + srcX) * DataBlock::COLOR_BYTES;
for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth * DataBlock::COLOR_BYTES,
src += size * DataBlock::COLOR_BYTES) {
memcpy(dest, src, destWidth * DataBlock::COLOR_BYTES);
}
if (foundNonZero) {
destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale;
destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale;
destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale);
destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale);
if (destWidth > 0 && destHeight > 0) {
dest = contents.data() + (destY * size + destX) * DataBlock::COLOR_BYTES;
for (int y = 0; y < destHeight; y++, dest += size * DataBlock::COLOR_BYTES) {
memset(dest, 0, destWidth * DataBlock::COLOR_BYTES);
}
HeightfieldColorDataPointer newColorPointer(new HeightfieldColorData(contents));
info.outputValues[1] = AttributeValue(_outputs.at(1),
encodeInline<HeightfieldColorDataPointer>(newColorPointer));
}
} else {
info.outputValues[1] = AttributeValue(_outputs.at(1));
}
}
HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue<HeightfieldMaterialDataPointer>();
if (materialPointer) {
contents = materialPointer->getContents();
QVector<SharedObjectPointer> materials = materialPointer->getMaterials();
size = glm::sqrt((float)contents.size());
heightScale = size / info.size;
// copy the inner area
destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale;
destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale;
destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale);
destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale);
uchar* dest = (uchar*)spanner->getMaterial().data() + destY * _heightfieldWidth + destX;
srcX = (overlap.minimum.x - info.minimum.x) * heightScale;
srcY = (overlap.minimum.z - info.minimum.z) * heightScale;
uchar* src = (uchar*)contents.data() + srcY * size + srcX;
QHash<int, int> materialMap;
for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth, src += size) {
for (uchar* lineSrc = src, *lineDest = dest, *end = src + destWidth; lineSrc != end; lineSrc++, lineDest++) {
int material = *lineSrc;
if (material != 0) {
int& mapping = materialMap[material];
if (mapping == 0) {
mapping = getMaterialIndex(materials.at(material - 1), spanner->getMaterials(),
spanner->getMaterial());
}
material = mapping;
}
*lineDest = material;
}
}
if (foundNonZero) {
destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale;
destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale;
destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale);
destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale);
if (destWidth > 0 && destHeight > 0) {
dest = (uchar*)contents.data() + destY * size + destX;
for (int y = 0; y < destHeight; y++, dest += size) {
memset(dest, 0, destWidth);
}
clearUnusedMaterials(materials, contents);
HeightfieldMaterialDataPointer newMaterialPointer(new HeightfieldMaterialData(contents, materials));
info.outputValues[2] = AttributeValue(_outputs.at(2),
encodeInline<HeightfieldMaterialDataPointer>(newMaterialPointer));
}
} else {
info.outputValues[2] = AttributeValue(_outputs.at(2));
}
}
return STOP_RECURSION;
}
void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
Spanner* spanner = static_cast<Spanner*>(this->spanner.data());
@ -1116,14 +504,34 @@ void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObject
QColor color = averageColor;
color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f);
// clear/fetch any heightfield data
HeightfieldClearFetchVisitor heightfieldVisitor(spanner->getBounds(), spanner->getVoxelizationGranularity());
data.guide(heightfieldVisitor);
// find the bounds of all voxel nodes intersected
float nodeSize = VOXEL_BLOCK_SIZE * glm::pow(2.0f, glm::floor(
glm::log(spanner->getVoxelizationGranularity()) / glm::log(2.0f)));
Box bounds(glm::floor(spanner->getBounds().minimum / nodeSize) * nodeSize,
glm::ceil(spanner->getBounds().maximum / nodeSize) * nodeSize);
// expand to include edges
Box expandedBounds = bounds;
float increment = nodeSize / VOXEL_BLOCK_SIZE;
expandedBounds.maximum.x += increment;
expandedBounds.maximum.z += increment;
// get all intersecting spanners
QVector<SharedObjectPointer> results;
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), expandedBounds, results);
// clear/voxelize as appropriate
SharedObjectPointer heightfield;
foreach (const SharedObjectPointer& result, results) {
Spanner* newSpanner = static_cast<Spanner*>(result.data())->clearAndFetchHeight(bounds, heightfield);
if (newSpanner != result) {
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), result, newSpanner);
}
}
// voxelize the fetched heightfield, if any
if (heightfieldVisitor.getSpanner()) {
VoxelMaterialSpannerEditVisitor visitor(static_cast<Spanner*>(heightfieldVisitor.getSpanner().data()),
material, color);
if (heightfield) {
VoxelMaterialSpannerEditVisitor visitor(static_cast<Spanner*>(heightfield.data()), material, color);
data.guide(visitor);
}

View file

@ -177,21 +177,6 @@ public:
DECLARE_STREAMABLE_METATYPE(ClearSpannersEdit)
/// An edit that sets a spanner's attributes in the voxel tree.
class SetSpannerEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM SharedObjectPointer spanner;
SetSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(SetSpannerEdit)
/// An edit that directly sets part of the metavoxel data.
class SetDataEdit : public MetavoxelEdit {
STREAMABLE

View file

@ -14,7 +14,6 @@
#include <QDoubleSpinBox>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QItemEditorCreatorBase>
#include <QItemEditorFactory>
#include <QLineEdit>
#include <QMetaType>
@ -77,32 +76,11 @@ QByteArray DelegatingItemEditorFactory::valuePropertyName(int userType) const {
return propertyName.isNull() ? _parentFactory->valuePropertyName(userType) : propertyName;
}
static QItemEditorFactory* getItemEditorFactory() {
QItemEditorFactory* getItemEditorFactory() {
static QItemEditorFactory* factory = new DelegatingItemEditorFactory();
return factory;
}
/// Because Windows doesn't necessarily have the staticMetaObject available when we want to create,
/// this class simply delays the value property name lookup until actually requested.
template<class T> class LazyItemEditorCreator : public QItemEditorCreatorBase {
public:
virtual QWidget* createWidget(QWidget* parent) const { return new T(parent); }
virtual QByteArray valuePropertyName() const;
protected:
QByteArray _valuePropertyName;
};
template<class T> QByteArray LazyItemEditorCreator<T>::valuePropertyName() const {
if (_valuePropertyName.isNull()) {
const_cast<LazyItemEditorCreator<T>*>(this)->_valuePropertyName = T::staticMetaObject.userProperty().name();
}
return _valuePropertyName;
}
static QItemEditorCreatorBase* createDoubleEditorCreator() {
QItemEditorCreatorBase* creator = new LazyItemEditorCreator<DoubleEditor>();
getItemEditorFactory()->registerEditor(qMetaTypeId<double>(), creator);

View file

@ -14,6 +14,7 @@
#include <QColor>
#include <QComboBox>
#include <QItemEditorCreatorBase>
#include <QSharedPointer>
#include <QUrl>
#include <QWidget>
@ -24,6 +25,7 @@
class QByteArray;
class QDoubleSpinBox;
class QItemEditorFactory;
class QPushButton;
class NetworkProgram;
@ -108,6 +110,30 @@ private:
AxisExtents _crossProductExtents[CROSS_PRODUCT_EXTENT_COUNT];
};
/// Returns a pointer to the singleton item editor factory.
QItemEditorFactory* getItemEditorFactory();
/// Because Windows doesn't necessarily have the staticMetaObject available when we want to create,
/// this class simply delays the value property name lookup until actually requested.
template<class T> class LazyItemEditorCreator : public QItemEditorCreatorBase {
public:
virtual QWidget* createWidget(QWidget* parent) const { return new T(parent); }
virtual QByteArray valuePropertyName() const;
protected:
QByteArray _valuePropertyName;
};
template<class T> QByteArray LazyItemEditorCreator<T>::valuePropertyName() const {
if (_valuePropertyName.isNull()) {
const_cast<LazyItemEditorCreator<T>*>(this)->_valuePropertyName = T::staticMetaObject.userProperty().name();
}
return _valuePropertyName;
}
/// Editor for meta-object values.
class QMetaObjectEditor : public QWidget {
Q_OBJECT

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,536 @@
//
// Spanner.h
// libraries/metavoxels/src
//
// Created by Andrzej Kapolka on 11/10/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
//
#ifndef hifi_Spanner_h
#define hifi_Spanner_h
#include <glm/glm.hpp>
#include "AttributeRegistry.h"
#include "MetavoxelUtil.h"
class HeightfieldColor;
class HeightfieldHeight;
class HeightfieldMaterial;
class SpannerRenderer;
/// An object that spans multiple octree cells.
class Spanner : public SharedObject {
Q_OBJECT
Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false)
Q_PROPERTY(float placementGranularity MEMBER _placementGranularity DESIGNABLE false)
Q_PROPERTY(float voxelizationGranularity MEMBER _voxelizationGranularity DESIGNABLE false)
public:
/// Returns the value of the global visit counter and increments it.
static int getAndIncrementNextVisit() { return _nextVisit.fetchAndAddOrdered(1); }
Spanner();
void setBounds(const Box& bounds);
const Box& getBounds() const { return _bounds; }
void setPlacementGranularity(float granularity) { _placementGranularity = granularity; }
float getPlacementGranularity() const { return _placementGranularity; }
void setVoxelizationGranularity(float granularity) { _voxelizationGranularity = granularity; }
float getVoxelizationGranularity() const { return _voxelizationGranularity; }
void setMerged(bool merged) { _merged = merged; }
bool isMerged() const { return _merged; }
/// Checks whether we've visited this object on the current traversal. If we have, returns false.
/// If we haven't, sets the last visit identifier and returns true.
bool testAndSetVisited(int visit);
/// Returns a pointer to the renderer, creating it if necessary.
SpannerRenderer* getRenderer();
/// Checks whether this is a heightfield.
virtual bool isHeightfield() const;
/// 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;
/// Attempts to paint on the spanner.
/// \return the modified spanner, or this if no modification was performed
virtual Spanner* paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material,
const QColor& color);
/// Attempts to modify the spanner's height.
/// \return the modified spanner, or this if no modification was performed
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height);
/// Attempts to clear and fetch part of the spanner's height.
/// \param heightfield the heightfield to populate
/// \return the modified spanner, or this if no modification was performed
virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield);
/// Checks whether this spanner has its own colors.
virtual bool hasOwnColors() const;
/// Checks whether this spanner has its own materials.
virtual bool hasOwnMaterials() const;
/// Checks whether the spanner contains the specified point.
virtual bool contains(const glm::vec3& point);
/// Retrieves the color at the specified point.
virtual QRgb getColorAt(const glm::vec3& point);
/// Retrieves the material at the specified point.
virtual int getMaterialAt(const glm::vec3& point);
/// Retrieves a reference to the list of materials.
virtual QVector<SharedObjectPointer>& getMaterials();
/// Finds the intersection, if any, between the specified line segment and the spanner.
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
signals:
void boundsWillChange();
void boundsChanged(const Box& bounds);
protected:
SpannerRenderer* _renderer;
/// Returns the name of the class to instantiate in order to render this spanner.
virtual QByteArray getRendererClassName() const;
private:
Box _bounds;
float _placementGranularity;
float _voxelizationGranularity;
bool _merged;
QHash<QThread*, int> _lastVisits; ///< last visit identifiers for each thread
QMutex _lastVisitsMutex;
static QAtomicInt _nextVisit; ///< the global visit counter
};
/// Base class for objects that can render spanners.
class SpannerRenderer : public QObject {
Q_OBJECT
public:
Q_INVOKABLE SpannerRenderer();
virtual void init(Spanner* spanner);
virtual void simulate(float deltaTime);
virtual void render(bool cursor = false);
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
protected:
Spanner* _spanner;
};
/// An object with a 3D transform.
class Transformable : public Spanner {
Q_OBJECT
Q_PROPERTY(glm::vec3 translation MEMBER _translation WRITE setTranslation NOTIFY translationChanged)
Q_PROPERTY(glm::quat rotation MEMBER _rotation WRITE setRotation NOTIFY rotationChanged)
Q_PROPERTY(float scale MEMBER _scale WRITE setScale NOTIFY scaleChanged)
public:
Transformable();
void setTranslation(const glm::vec3& translation);
const glm::vec3& getTranslation() const { return _translation; }
void setRotation(const glm::quat& rotation);
const glm::quat& getRotation() const { return _rotation; }
void setScale(float scale);
float getScale() const { return _scale; }
signals:
void translationChanged(const glm::vec3& translation);
void rotationChanged(const glm::quat& rotation);
void scaleChanged(float scale);
private:
glm::vec3 _translation;
glm::quat _rotation;
float _scale;
};
/// A transformable object with a color.
class ColorTransformable : public Transformable {
Q_OBJECT
Q_PROPERTY(QColor color MEMBER _color WRITE setColor NOTIFY colorChanged DESIGNABLE false)
public:
ColorTransformable();
void setColor(const QColor& color);
const QColor& getColor() const { return _color; }
signals:
void colorChanged(const QColor& color);
protected:
QColor _color;
};
/// A sphere.
class Sphere : public ColorTransformable {
Q_OBJECT
public:
Q_INVOKABLE Sphere();
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
virtual bool contains(const glm::vec3& point);
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
protected:
virtual QByteArray getRendererClassName() const;
private slots:
void updateBounds();
};
/// A cuboid.
class Cuboid : public ColorTransformable {
Q_OBJECT
Q_PROPERTY(float aspectY MEMBER _aspectY WRITE setAspectY NOTIFY aspectYChanged)
Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged)
public:
Q_INVOKABLE Cuboid();
void setAspectY(float aspectY);
float getAspectY() const { return _aspectY; }
void setAspectZ(float aspectZ);
float getAspectZ() const { return _aspectZ; }
virtual bool contains(const glm::vec3& point);
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
signals:
void aspectYChanged(float aspectY);
void aspectZChanged(float aspectZ);
protected:
virtual QByteArray getRendererClassName() const;
private slots:
void updateBoundsAndPlanes();
private:
float _aspectY;
float _aspectZ;
static const int PLANE_COUNT = 6;
glm::vec4 _planes[PLANE_COUNT];
};
/// A static 3D model loaded from the network.
class StaticModel : public Transformable {
Q_OBJECT
Q_PROPERTY(QUrl url MEMBER _url WRITE setURL NOTIFY urlChanged)
public:
Q_INVOKABLE StaticModel();
void setURL(const QUrl& url);
const QUrl& getURL() const { return _url; }
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
signals:
void urlChanged(const QUrl& url);
protected:
virtual QByteArray getRendererClassName() const;
private:
QUrl _url;
};
/// Base class for heightfield data blocks.
class HeightfieldData : public DataBlock {
public:
static const int SHARED_EDGE;
HeightfieldData(int width = 0);
int getWidth() const { return _width; }
protected:
int _width;
};
typedef QExplicitlySharedDataPointer<HeightfieldHeight> HeightfieldHeightPointer;
/// A block of height data associated with a heightfield.
class HeightfieldHeight : public HeightfieldData {
public:
static const int HEIGHT_BORDER;
static const int HEIGHT_EXTENSION;
HeightfieldHeight(int width, const QVector<quint16>& contents);
HeightfieldHeight(Bitstream& in, int bytes);
HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference);
QVector<quint16>& getContents() { return _contents; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldHeightPointer& reference);
private:
void read(Bitstream& in, int bytes);
QVector<quint16> _contents;
};
Q_DECLARE_METATYPE(HeightfieldHeightPointer)
Bitstream& operator<<(Bitstream& out, const HeightfieldHeightPointer& value);
Bitstream& operator>>(Bitstream& in, HeightfieldHeightPointer& value);
template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference);
template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference);
/// Allows editing heightfield height blocks.
class HeightfieldHeightEditor : public QWidget {
Q_OBJECT
Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged USER true)
public:
HeightfieldHeightEditor(QWidget* parent = NULL);
const HeightfieldHeightPointer& getHeight() const { return _height; }
signals:
void heightChanged(const HeightfieldHeightPointer& height);
public slots:
void setHeight(const HeightfieldHeightPointer& height);
private slots:
void select();
void clear();
private:
HeightfieldHeightPointer _height;
QPushButton* _select;
QPushButton* _clear;
};
typedef QExplicitlySharedDataPointer<HeightfieldColor> HeightfieldColorPointer;
/// A block of color data associated with a heightfield.
class HeightfieldColor : public HeightfieldData {
public:
HeightfieldColor(int width, const QByteArray& contents);
HeightfieldColor(Bitstream& in, int bytes);
HeightfieldColor(Bitstream& in, int bytes, const HeightfieldColorPointer& reference);
QByteArray& getContents() { return _contents; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldColorPointer& reference);
private:
void read(Bitstream& in, int bytes);
QByteArray _contents;
};
Q_DECLARE_METATYPE(HeightfieldColorPointer)
Bitstream& operator<<(Bitstream& out, const HeightfieldColorPointer& value);
Bitstream& operator>>(Bitstream& in, HeightfieldColorPointer& value);
template<> void Bitstream::writeRawDelta(const HeightfieldColorPointer& value, const HeightfieldColorPointer& reference);
template<> void Bitstream::readRawDelta(HeightfieldColorPointer& value, const HeightfieldColorPointer& reference);
/// Allows editing heightfield color blocks.
class HeightfieldColorEditor : public QWidget {
Q_OBJECT
Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged USER true)
public:
HeightfieldColorEditor(QWidget* parent = NULL);
const HeightfieldColorPointer& getColor() const { return _color; }
signals:
void colorChanged(const HeightfieldColorPointer& color);
public slots:
void setColor(const HeightfieldColorPointer& color);
private slots:
void select();
void clear();
private:
HeightfieldColorPointer _color;
QPushButton* _select;
QPushButton* _clear;
};
typedef QExplicitlySharedDataPointer<HeightfieldMaterial> HeightfieldMaterialPointer;
/// A block of material data associated with a heightfield.
class HeightfieldMaterial : public HeightfieldData {
public:
HeightfieldMaterial(int width, const QByteArray& contents, const QVector<SharedObjectPointer>& materials);
HeightfieldMaterial(Bitstream& in, int bytes);
HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference);
QByteArray& getContents() { return _contents; }
QVector<SharedObjectPointer>& getMaterials() { return _materials; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference);
private:
void read(Bitstream& in, int bytes);
QByteArray _contents;
QVector<SharedObjectPointer> _materials;
};
Q_DECLARE_METATYPE(HeightfieldMaterialPointer)
Bitstream& operator<<(Bitstream& out, const HeightfieldMaterialPointer& value);
Bitstream& operator>>(Bitstream& in, HeightfieldMaterialPointer& value);
template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference);
template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference);
/// A heightfield represented as a spanner.
class Heightfield : public Transformable {
Q_OBJECT
Q_PROPERTY(float aspectY MEMBER _aspectY WRITE setAspectY NOTIFY aspectYChanged)
Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged)
Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged)
Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged DESIGNABLE false)
public:
Q_INVOKABLE Heightfield();
void setAspectY(float aspectY);
float getAspectY() const { return _aspectY; }
void setAspectZ(float aspectZ);
float getAspectZ() const { return _aspectZ; }
void setHeight(const HeightfieldHeightPointer& height);
const HeightfieldHeightPointer& getHeight() const { return _height; }
void setColor(const HeightfieldColorPointer& color);
const HeightfieldColorPointer& getColor() const { return _color; }
void setMaterial(const HeightfieldMaterialPointer& material);
const HeightfieldMaterialPointer& getMaterial() const { return _material; }
virtual bool isHeightfield() const;
virtual float getHeight(const glm::vec3& location) const;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
virtual Spanner* paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material,
const QColor& color);
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height);
virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield);
virtual bool hasOwnColors() const;
virtual bool hasOwnMaterials() const;
virtual QRgb getColorAt(const glm::vec3& point);
virtual int getMaterialAt(const glm::vec3& point);
virtual QVector<SharedObjectPointer>& getMaterials();
virtual bool contains(const glm::vec3& point);
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
signals:
void aspectYChanged(float aspectY);
void aspectZChanged(float aspectZ);
void heightChanged(const HeightfieldHeightPointer& height);
void colorChanged(const HeightfieldColorPointer& color);
void materialChanged(const HeightfieldMaterialPointer& material);
protected:
virtual QByteArray getRendererClassName() const;
private slots:
void updateBounds();
private:
float _aspectY;
float _aspectZ;
HeightfieldHeightPointer _height;
HeightfieldColorPointer _color;
HeightfieldMaterialPointer _material;
};
#endif // hifi_Spanner_h

View file

@ -81,7 +81,7 @@ PacketVersion versionForPacketType(PacketType type) {
case PacketTypeAudioStreamStats:
return 1;
case PacketTypeMetavoxelData:
return 8;
return 9;
case PacketTypeVoxelData:
return VERSION_VOXELS_HAS_FILE_BREAKS;
default:

View file

@ -445,6 +445,9 @@ bool MetavoxelTests::run() {
// seed the random number generator so that our tests are reproducible
srand(0xBAAAAABE);
// register our test attribute
AttributePointer testAttribute = AttributeRegistry::getInstance()->registerAttribute(new FloatAttribute("testAttribute"));
// check for an optional command line argument specifying a single test
QStringList arguments = this->arguments();
int test = (arguments.size() > 1) ? arguments.at(1).toInt() : 0;
@ -582,8 +585,8 @@ public:
};
RandomVisitor::RandomVisitor() :
MetavoxelVisitor(QVector<AttributePointer>(),
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getColorAttribute()),
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getAttribute("testAttribute")),
leafCount(0) {
}
@ -594,8 +597,7 @@ int RandomVisitor::visit(MetavoxelInfo& info) {
if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) {
return DEFAULT_ORDER;
}
info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline<QRgb>(qRgb(randomColorValue(),
randomColorValue(), randomColorValue())));
info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline<float>(randFloat()));
leafCount++;
return STOP_RECURSION;
}
@ -812,8 +814,8 @@ private:
};
MutateVisitor::MutateVisitor() :
MetavoxelVisitor(QVector<AttributePointer>(),
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getColorAttribute()),
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getAttribute("testAttribute")),
_mutationsRemaining(randIntInRange(2, 4)) {
}
@ -824,8 +826,7 @@ int MutateVisitor::visit(MetavoxelInfo& info) {
if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) {
return encodeRandomOrder();
}
info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline<QRgb>(qRgb(randomColorValue(),
randomColorValue(), randomColorValue())));
info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline<float>(randFloat()));
_mutationsRemaining--;
metavoxelMutationsPerformed++;
return STOP_RECURSION;