mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 11:45:36 +02:00
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:
commit
8d9df8570c
28 changed files with 4104 additions and 5817 deletions
|
@ -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.";
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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()));
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "DatagramSequencer.h"
|
||||
#include "MetavoxelData.h"
|
||||
#include "Spanner.h"
|
||||
|
||||
class PacketRecord;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
2126
libraries/metavoxels/src/Spanner.cpp
Normal file
2126
libraries/metavoxels/src/Spanner.cpp
Normal file
File diff suppressed because it is too large
Load diff
536
libraries/metavoxels/src/Spanner.h
Normal file
536
libraries/metavoxels/src/Spanner.h
Normal 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
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue