Merge branch 'master' of https://github.com/highfidelity/hifi into android

This commit is contained in:
Stephen Birarda 2015-01-20 11:15:54 -08:00
commit b4d6a44fc2
26 changed files with 3603 additions and 4120 deletions

View file

@ -311,7 +311,7 @@ MetavoxelPersister::MetavoxelPersister(MetavoxelServer* server) :
const char* SAVE_FILE = "/resources/metavoxels.dat";
const int FILE_MAGIC = 0xDADAFACE;
const int FILE_VERSION = 2;
const int FILE_VERSION = 4;
void MetavoxelPersister::load() {
QString path = QCoreApplication::applicationDirPath() + SAVE_FILE;

View file

@ -22,7 +22,8 @@ varying vec4 alphaValues;
void main(void) {
// blend the splat textures
gl_FragColor = gl_Color * (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x +
gl_FragColor = vec4(gl_Color.rgb, step(1.0, gl_Color.a + 1.0 / 512.0)) *
(texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x +
texture2D(diffuseMaps[1], gl_TexCoord[1].st) * alphaValues.y +
texture2D(diffuseMaps[2], gl_TexCoord[2].st) * alphaValues.z +
texture2D(diffuseMaps[3], gl_TexCoord[3].st) * alphaValues.w);

View file

@ -11,6 +11,9 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the splat texture offset
uniform vec3 splatTextureOffset;
// the splat textures scales on the S axis
uniform vec4 splatTextureScalesS;
@ -43,7 +46,7 @@ void main(void) {
normal = gl_Normal;
// pass along the scaled/offset texture coordinates
vec4 textureSpacePosition = gl_Vertex.xyyz;
vec4 textureSpacePosition = (gl_Vertex.xyz + splatTextureOffset).xyyz;
gl_TexCoord[0] = textureSpacePosition * vec4(splatTextureScalesS[0], splatTextureScalesT[0],
splatTextureScalesS[0], splatTextureScalesT[0]);
gl_TexCoord[1] = textureSpacePosition * vec4(splatTextureScalesS[1], splatTextureScalesT[1],

View file

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

View file

@ -424,9 +424,9 @@ namespace MenuOption {
const QString RenderDualContourSurfaces = "Render Dual Contour Surfaces";
const QString RenderFocusIndicator = "Show Eye Focus";
const QString RenderHeadCollisionShapes = "Show Head Collision Shapes";
const QString RenderHeightfields = "Render Heightfields";
const QString RenderLookAtVectors = "Show Look-at Vectors";
const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes";
const QString RenderSpanners = "Render Spanners";
const QString RenderTargetFramerate = "Framerate";
const QString RenderTargetFramerateUnlimited = "Unlimited";
const QString RenderTargetFramerate60 = "60";

File diff suppressed because it is too large Load diff

View file

@ -26,8 +26,8 @@
class HeightfieldBaseLayerBatch;
class HeightfieldSplatBatch;
class HermiteBatch;
class MetavoxelBatch;
class Model;
class VoxelBatch;
class VoxelSplatBatch;
/// Renders a metavoxel tree.
@ -59,48 +59,35 @@ public:
void setNetworkSimulation(const NetworkSimulation& simulation);
NetworkSimulation getNetworkSimulation();
const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; }
const AttributePointer& getVoxelBufferAttribute() { return _voxelBufferAttribute; }
void simulate(float deltaTime);
void render();
void renderHeightfieldCursor(const glm::vec3& position, float radius);
void renderVoxelCursor(const glm::vec3& position, float radius);
bool findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance);
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);
Q_INVOKABLE void paintVoxelColor(const glm::vec3& position, float radius, const QColor& color);
Q_INVOKABLE void paintVoxelMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material);
Q_INVOKABLE void setHeightfieldColor(const SharedObjectPointer& spanner, const QColor& color, bool paint = false);
Q_INVOKABLE void setVoxelColor(const SharedObjectPointer& spanner, const QColor& color);
Q_INVOKABLE void setVoxelMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material);
Q_INVOKABLE void setHeightfieldMaterial(const SharedObjectPointer& spanner,
const SharedObjectPointer& material, bool paint = false);
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 addVoxelBaseBatch(const MetavoxelBatch& batch) { _voxelBaseBatches.append(batch); }
void addVoxelSplatBatch(const VoxelSplatBatch& batch) { _voxelSplatBatches.append(batch); }
void addHermiteBatch(const HermiteBatch& batch) { _hermiteBatches.append(batch); }
Q_INVOKABLE void deleteTextures(int heightTextureID, int colorTextureID, int materialTextureID) const;
Q_INVOKABLE void deleteBuffers(int vertexBufferID, int indexBufferID, int hermiteBufferID) const;
signals:
void rendering();
public slots:
void refreshVoxelData();
protected:
Q_INVOKABLE void applyMaterialEdit(const MetavoxelEditMessage& message, bool reliable = false);
@ -111,9 +98,6 @@ private:
void guideToAugmented(MetavoxelVisitor& visitor, bool render = false);
AttributePointer _heightfieldBufferAttribute;
AttributePointer _voxelBufferAttribute;
MetavoxelLOD _lod;
QReadWriteLock _lodLock;
Frustum _frustum;
@ -123,7 +107,7 @@ private:
QVector<HeightfieldBaseLayerBatch> _heightfieldBaseBatches;
QVector<HeightfieldSplatBatch> _heightfieldSplatBatches;
QVector<VoxelBatch> _voxelBaseBatches;
QVector<MetavoxelBatch> _voxelBaseBatches;
QVector<VoxelSplatBatch> _voxelSplatBatches;
QVector<HermiteBatch> _hermiteBatches;
@ -166,16 +150,21 @@ private:
static void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations);
};
/// Base class for heightfield batches.
class HeightfieldBatch {
/// Base class for all batches.
class MetavoxelBatch {
public:
QOpenGLBuffer* vertexBuffer;
QOpenGLBuffer* indexBuffer;
GLuint vertexBufferID;
GLuint indexBufferID;
glm::vec3 translation;
glm::quat rotation;
glm::vec3 scale;
int vertexCount;
int indexCount;
};
/// Base class for heightfield batches.
class HeightfieldBatch : public MetavoxelBatch {
public:
GLuint heightTextureID;
glm::vec4 heightScale;
};
@ -199,18 +188,10 @@ public:
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 {
class VoxelSplatBatch : public MetavoxelBatch {
public:
glm::vec3 splatTextureOffset;
int splatTextureIDs[4];
glm::vec4 splatTextureScalesS;
glm::vec4 splatTextureScalesT;
@ -220,7 +201,10 @@ public:
/// A batch containing Hermite data for debugging.
class HermiteBatch {
public:
QOpenGLBuffer* vertexBuffer;
GLuint vertexBufferID;
glm::vec3 translation;
glm::quat rotation;
glm::vec3 scale;
int vertexCount;
};
@ -271,8 +255,6 @@ public:
void setRenderedAugmentedData(const MetavoxelData& data) { _renderedAugmentedData = data; }
virtual int parseData(const QByteArray& packet);
Q_INVOKABLE void refreshVoxelData();
protected:
@ -295,7 +277,8 @@ public:
virtual ~BufferData();
virtual void render(bool cursor = false) = 0;
virtual void render(const glm::vec3& translation, const glm::quat& rotation,
const glm::vec3& scale, bool cursor = false) = 0;
};
typedef QExplicitlySharedDataPointer<BufferData> BufferDataPointer;
@ -334,44 +317,34 @@ public:
VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>& indices, const QVector<glm::vec3>& hermite,
const QMultiHash<VoxelCoord, int>& quadIndices, int size, const QVector<SharedObjectPointer>& materials =
QVector<SharedObjectPointer>());
virtual ~VoxelBuffer();
bool isHermiteEnabled() const { return _hermiteEnabled; }
/// Finds the first intersection between the described ray and the voxel data.
/// \param entry the entry point of the ray in relative coordinates, from (0, 0, 0) to (1, 1, 1)
bool findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin,
const glm::vec3& direction, float& distance) const;
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const;
virtual void render(bool cursor = false);
virtual void render(const glm::vec3& translation, const glm::quat& rotation,
const glm::vec3& scale, bool cursor = false);
private:
QVector<VoxelPoint> _vertices;
QVector<int> _indices;
QVector<glm::vec3> _hermite;
bool _hermiteEnabled;
QMultiHash<VoxelCoord, int> _quadIndices;
int _size;
int _vertexCount;
int _indexCount;
int _hermiteCount;
QOpenGLBuffer _vertexBuffer;
QOpenGLBuffer _indexBuffer;
QOpenGLBuffer _hermiteBuffer;
GLuint _vertexBufferID;
GLuint _indexBufferID;
GLuint _hermiteBufferID;
QVector<SharedObjectPointer> _materials;
QVector<NetworkTexturePointer> _networkTextures;
};
/// A client-side attribute that stores renderable buffers.
class BufferDataAttribute : public InlineAttribute<BufferDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE BufferDataAttribute(const QString& name = QString());
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
};
/// Renders metavoxels as points.
class DefaultMetavoxelRendererImplementation : public MetavoxelRendererImplementation {
Q_OBJECT
@ -380,7 +353,6 @@ public:
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);
};
@ -450,6 +422,9 @@ public:
HeightfieldNodeRenderer();
virtual ~HeightfieldNodeRenderer();
virtual bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const;
void render(const HeightfieldNodePointer& node, const glm::vec3& translation,
const glm::quat& rotation, const glm::vec3& scale, bool cursor);
@ -460,6 +435,8 @@ private:
GLuint _materialTextureID;
QVector<NetworkTexturePointer> _networkTextures;
BufferDataPointer _voxels;
typedef QPair<int, int> IntPair;
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
static QHash<IntPair, BufferPair> _bufferPairs;

View file

@ -97,7 +97,7 @@ MetavoxelEditor::MetavoxelEditor() :
_gridSpacing->setMinimum(-FLT_MAX);
_gridSpacing->setMaximum(FLT_MAX);
_gridSpacing->setPrefix("2^");
_gridSpacing->setValue(-3.0);
_gridSpacing->setValue(0.0);
connect(_gridSpacing, SIGNAL(valueChanged(double)), SLOT(alignGridPosition()));
formLayout->addRow("Grid Position:", _gridPosition = new QDoubleSpinBox());
@ -125,13 +125,13 @@ MetavoxelEditor::MetavoxelEditor() :
addTool(new InsertSpannerTool(this));
addTool(new RemoveSpannerTool(this));
addTool(new ClearSpannersTool(this));
addTool(new ImportHeightfieldTool(this));
addTool(new HeightfieldHeightBrushTool(this));
addTool(new HeightfieldMaterialBrushTool(this));
addTool(new ImportHeightfieldTool(this));
addTool(new VoxelMaterialBoxTool(this));
addTool(new VoxelMaterialSpannerTool(this));
addTool(new VoxelMaterialBrushTool(this));
addTool(new VoxelSculptBrushTool(this));
addTool(new HeightfieldSculptBrushTool(this));
addTool(new HeightfieldFillBrushTool(this));
addTool(new HeightfieldMaterialBoxTool(this));
addTool(new HeightfieldMaterialSpannerTool(this));
updateAttributes();
@ -331,6 +331,9 @@ void MetavoxelEditor::render() {
MetavoxelTool* tool = getActiveTool();
if (tool) {
tool->render();
if (!tool->getUsesGrid()) {
return;
}
}
glDepthMask(GL_FALSE);
@ -386,10 +389,11 @@ MetavoxelTool* MetavoxelEditor::getActiveTool() const {
ProgramObject MetavoxelEditor::_gridProgram;
MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) :
MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing, bool usesGrid) :
_editor(editor),
_usesValue(usesValue),
_userFacing(userFacing) {
_userFacing(userFacing),
_usesGrid(usesGrid) {
QVBoxLayout* layout = new QVBoxLayout();
setLayout(layout);
@ -670,7 +674,7 @@ void InsertSpannerTool::applyEdit(const AttributePointer& attribute, const Share
}
RemoveSpannerTool::RemoveSpannerTool(MetavoxelEditor* editor) :
MetavoxelTool(editor, "Remove Spanner", false) {
MetavoxelTool(editor, "Remove Spanner", false, true, false) {
}
bool RemoveSpannerTool::appliesTo(const AttributePointer& attribute) const {
@ -697,7 +701,7 @@ bool RemoveSpannerTool::eventFilter(QObject* watched, QEvent* event) {
}
ClearSpannersTool::ClearSpannersTool(MetavoxelEditor* editor) :
MetavoxelTool(editor, "Clear Spanners", false) {
MetavoxelTool(editor, "Clear Spanners", false, true, false) {
QPushButton* button = new QPushButton("Clear");
layout()->addWidget(button);
@ -718,7 +722,7 @@ void ClearSpannersTool::clear() {
}
HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) :
MetavoxelTool(editor, name, false) {
MetavoxelTool(editor, name, false, true, false) {
QWidget* widget = new QWidget();
widget->setLayout(_form = new QFormLayout());
@ -807,7 +811,7 @@ void ImportHeightfieldTool::updateSpanner() {
}
HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name) :
MetavoxelTool(editor, name, false),
MetavoxelTool(editor, name, false, true, false),
_positionValid(false) {
QWidget* widget = new QWidget();
@ -817,7 +821,7 @@ HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QStrin
_form->addRow("Radius:", _radius = new QDoubleSpinBox());
_radius->setSingleStep(0.01);
_radius->setMaximum(FLT_MAX);
_radius->setValue(1.0);
_radius->setValue(5.0);
}
bool HeightfieldBrushTool::appliesTo(const AttributePointer& attribute) const {
@ -865,11 +869,19 @@ HeightfieldHeightBrushTool::HeightfieldHeightBrushTool(MetavoxelEditor* editor)
_height->setMinimum(-FLT_MAX);
_height->setMaximum(FLT_MAX);
_height->setValue(1.0);
_form->addRow("Mode:", _mode = new QComboBox());
_mode->addItem("Raise/Lower");
_mode->addItem("Set");
_mode->addItem("Erase");
}
QVariant HeightfieldHeightBrushTool::createEdit(bool alternate) {
const int SET_MODE_INDEX = 1;
const int ERASE_MODE_INDEX = 2;
return QVariant::fromValue(PaintHeightfieldHeightEdit(_position, _radius->value(),
alternate ? -_height->value() : _height->value()));
alternate ? -_height->value() : _height->value(), _mode->currentIndex() == SET_MODE_INDEX,
_mode->currentIndex() == ERASE_MODE_INDEX));
}
MaterialControl::MaterialControl(QWidget* widget, QFormLayout* form, bool clearable) :
@ -939,16 +951,58 @@ HeightfieldMaterialBrushTool::HeightfieldMaterialBrushTool(MetavoxelEditor* edit
}
QVariant HeightfieldMaterialBrushTool::createEdit(bool alternate) {
Sphere* sphere = new Sphere();
sphere->setTranslation(_position);
sphere->setScale(_radius->value());
if (alternate) {
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere),
SharedObjectPointer(), QColor(0, 0, 0, 0), true));
} else {
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), _materialControl->getMaterial(),
_materialControl->getColor()));
}
return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere),
_materialControl->getMaterial(), _materialControl->getColor(), true));
}
}
VoxelMaterialBoxTool::VoxelMaterialBoxTool(MetavoxelEditor* editor) :
BoxTool(editor, "Set Voxel Material (Box)", false) {
HeightfieldSculptBrushTool::HeightfieldSculptBrushTool(MetavoxelEditor* editor) :
HeightfieldBrushTool(editor, "Sculpt Brush"),
_materialControl(new MaterialControl(this, _form, true)) {
}
QVariant HeightfieldSculptBrushTool::createEdit(bool alternate) {
Sphere* sphere = new Sphere();
sphere->setTranslation(_position);
sphere->setScale(_radius->value());
if (alternate) {
return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere),
SharedObjectPointer(), QColor(0, 0, 0, 0)));
} else {
return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere),
_materialControl->getMaterial(), _materialControl->getColor()));
}
}
HeightfieldFillBrushTool::HeightfieldFillBrushTool(MetavoxelEditor* editor) :
HeightfieldBrushTool(editor, "Fill Brush") {
_form->addRow("Mode:", _mode = new QComboBox());
_mode->addItem("Fill");
_mode->addItem("Voxelize");
}
QVariant HeightfieldFillBrushTool::createEdit(bool alternate) {
const int FILL_MODE_INDEX = 0;
if (_mode->currentIndex() == FILL_MODE_INDEX) {
return QVariant::fromValue(FillHeightfieldHeightEdit(_position, _radius->value()));
}
Sphere* sphere = new Sphere();
sphere->setTranslation(_position);
sphere->setScale(_radius->value());
return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere),
SharedObjectPointer(), QColor(), false, true));
}
HeightfieldMaterialBoxTool::HeightfieldMaterialBoxTool(MetavoxelEditor* editor) :
BoxTool(editor, "Set Material (Box)", false) {
QWidget* widget = new QWidget();
QFormLayout* form = new QFormLayout();
@ -965,32 +1019,32 @@ VoxelMaterialBoxTool::VoxelMaterialBoxTool(MetavoxelEditor* editor) :
_materialControl = new MaterialControl(this, form, true);
}
bool VoxelMaterialBoxTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
bool HeightfieldMaterialBoxTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("SpannerSetAttribute");
}
bool VoxelMaterialBoxTool::shouldSnapToGrid() {
bool HeightfieldMaterialBoxTool::shouldSnapToGrid() {
return _snapToGrid->isChecked();
}
QColor VoxelMaterialBoxTool::getColor() {
QColor HeightfieldMaterialBoxTool::getColor() {
return _materialControl->getColor();
}
void VoxelMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
void HeightfieldMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
Cuboid* cuboid = new Cuboid();
cuboid->setTranslation((maximum + minimum) * 0.5f);
glm::vec3 vector = (maximum - minimum) * 0.5f;
cuboid->setScale(vector.x);
cuboid->setAspectY(vector.y / vector.x);
cuboid->setAspectZ(vector.z / vector.x);
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(cuboid),
MetavoxelEditMessage message = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(cuboid),
_materialControl->getMaterial(), _materialControl->getColor())) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
VoxelMaterialSpannerTool::VoxelMaterialSpannerTool(MetavoxelEditor* editor) :
PlaceSpannerTool(editor, "Set Voxel Material (Spanner)", QString(), false) {
HeightfieldMaterialSpannerTool::HeightfieldMaterialSpannerTool(MetavoxelEditor* editor) :
PlaceSpannerTool(editor, "Set Material (Spanner)", QString(), false) {
QWidget* widget = new QWidget();
QFormLayout* form = new QFormLayout();
@ -1004,110 +1058,25 @@ VoxelMaterialSpannerTool::VoxelMaterialSpannerTool(MetavoxelEditor* editor) :
QPushButton* place = new QPushButton("Set");
layout()->addWidget(place);
connect(place, &QPushButton::clicked, this, &VoxelMaterialSpannerTool::place);
connect(place, &QPushButton::clicked, this, &HeightfieldMaterialSpannerTool::place);
}
bool VoxelMaterialSpannerTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
bool HeightfieldMaterialSpannerTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("SpannerSetAttribute");
}
SharedObjectPointer VoxelMaterialSpannerTool::getSpanner() {
SharedObjectPointer HeightfieldMaterialSpannerTool::getSpanner() {
return _spannerEditor->getObject();
}
QColor VoxelMaterialSpannerTool::getColor() {
QColor HeightfieldMaterialSpannerTool::getColor() {
return _materialControl->getColor();
}
void VoxelMaterialSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
void HeightfieldMaterialSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
static_cast<Spanner*>(spanner.data())->setWillBeVoxelized(true);
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner,
MetavoxelEditMessage message = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(spanner,
_materialControl->getMaterial(), _materialControl->getColor())) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
VoxelBrushTool::VoxelBrushTool(MetavoxelEditor* editor, const QString& name) :
MetavoxelTool(editor, name, false, true),
_positionValid(false) {
QWidget* widget = new QWidget();
widget->setLayout(_form = new QFormLayout());
layout()->addWidget(widget);
_form->addRow("Radius:", _radius = new QDoubleSpinBox());
_radius->setSingleStep(0.01);
_radius->setMaximum(FLT_MAX);
_radius->setValue(0.25);
}
bool VoxelBrushTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
}
void VoxelBrushTool::render() {
if (Application::getInstance()->isMouseHidden()) {
return;
}
// find the intersection with the voxels
glm::vec3 origin = Application::getInstance()->getMouseRayOrigin();
glm::vec3 direction = Application::getInstance()->getMouseRayDirection();
float heightfieldDistance = FLT_MAX, voxelDistance = FLT_MAX;
if (!(Application::getInstance()->getMetavoxels()->findFirstRayHeightfieldIntersection(
origin, direction, heightfieldDistance) |
Application::getInstance()->getMetavoxels()->findFirstRayVoxelIntersection(origin, direction, voxelDistance))) {
_positionValid = false;
return;
}
_positionValid = true;
Application::getInstance()->getMetavoxels()->renderVoxelCursor(
_position = origin + qMin(heightfieldDistance, voxelDistance) * direction, _radius->value());
}
bool VoxelBrushTool::eventFilter(QObject* watched, QEvent* event) {
if (event->type() == QEvent::Wheel) {
float angle = static_cast<QWheelEvent*>(event)->angleDelta().y();
const float ANGLE_SCALE = 1.0f / 1000.0f;
_radius->setValue(_radius->value() * glm::pow(2.0f, angle * ANGLE_SCALE));
return true;
} else if (event->type() == QEvent::MouseButtonPress && _positionValid) {
MetavoxelEditMessage message = { createEdit(static_cast<QMouseEvent*>(event)->button() == Qt::RightButton) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
return true;
}
return false;
}
VoxelMaterialBrushTool::VoxelMaterialBrushTool(MetavoxelEditor* editor) :
VoxelBrushTool(editor, "Material Brush"),
_materialControl(new MaterialControl(this, _form)) {
}
QVariant VoxelMaterialBrushTool::createEdit(bool alternate) {
if (alternate) {
return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
} else {
return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(),
_materialControl->getMaterial(), _materialControl->getColor()));
}
}
VoxelSculptBrushTool::VoxelSculptBrushTool(MetavoxelEditor* editor) :
VoxelBrushTool(editor, "Sculpt Brush"),
_materialControl(new MaterialControl(this, _form, true)) {
}
QVariant VoxelSculptBrushTool::createEdit(bool alternate) {
Sphere* sphere = new Sphere();
sphere->setTranslation(_position);
sphere->setScale(_radius->value());
if (alternate) {
return QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(sphere),
SharedObjectPointer(), QColor(0, 0, 0, 0)));
} else {
return QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(sphere),
_materialControl->getMaterial(), _materialControl->getColor()));
}
}

View file

@ -92,12 +92,15 @@ class MetavoxelTool : public QWidget {
public:
MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true, bool userFacing = true);
MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true,
bool userFacing = true, bool usesGrid = true);
bool getUsesValue() const { return _usesValue; }
bool isUserFacing() const { return _userFacing; }
bool getUsesGrid() const { return _usesGrid; }
virtual bool appliesTo(const AttributePointer& attribute) const;
virtual void simulate(float deltaTime);
@ -113,6 +116,7 @@ protected:
MetavoxelEditor* _editor;
bool _usesValue;
bool _userFacing;
bool _usesGrid;
};
/// Base class for tools that allow dragging out a 3D box.
@ -342,6 +346,7 @@ protected:
private:
QDoubleSpinBox* _height;
QComboBox* _mode;
};
/// Contains widgets for editing materials.
@ -387,13 +392,47 @@ private:
MaterialControl* _materialControl;
};
/// Allows setting voxel materials by dragging out a box.
class VoxelMaterialBoxTool : public BoxTool {
/// Allows sculpting parts of the heightfield.
class HeightfieldSculptBrushTool : public HeightfieldBrushTool {
Q_OBJECT
public:
VoxelMaterialBoxTool(MetavoxelEditor* editor);
HeightfieldSculptBrushTool(MetavoxelEditor* editor);
protected:
virtual QVariant createEdit(bool alternate);
private:
MaterialControl* _materialControl;
};
/// Allows "filling" (removing dual contour stack data) parts of the heightfield.
class HeightfieldFillBrushTool : public HeightfieldBrushTool {
Q_OBJECT
public:
HeightfieldFillBrushTool(MetavoxelEditor* editor);
protected:
virtual QVariant createEdit(bool alternate);
private:
QComboBox* _mode;
};
/// Allows setting heightfield materials by dragging out a box.
class HeightfieldMaterialBoxTool : public BoxTool {
Q_OBJECT
public:
HeightfieldMaterialBoxTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
@ -411,13 +450,13 @@ private:
MaterialControl* _materialControl;
};
/// Allows setting voxel materials by placing a spanner.
class VoxelMaterialSpannerTool : public PlaceSpannerTool {
/// Allows setting heightfield materials by placing a spanner.
class HeightfieldMaterialSpannerTool : public PlaceSpannerTool {
Q_OBJECT
public:
VoxelMaterialSpannerTool(MetavoxelEditor* editor);
HeightfieldMaterialSpannerTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
@ -433,63 +472,4 @@ private:
MaterialControl* _materialControl;
};
/// Base class for voxel brush tools.
class VoxelBrushTool : public MetavoxelTool {
Q_OBJECT
public:
VoxelBrushTool(MetavoxelEditor* editor, const QString& name);
virtual bool appliesTo(const AttributePointer& attribute) const;
virtual void render();
virtual bool eventFilter(QObject* watched, QEvent* event);
protected:
virtual QVariant createEdit(bool alternate) = 0;
QFormLayout* _form;
QDoubleSpinBox* _radius;
glm::vec3 _position;
bool _positionValid;
};
/// Allows texturing parts of the voxel field.
class VoxelMaterialBrushTool : public VoxelBrushTool {
Q_OBJECT
public:
VoxelMaterialBrushTool(MetavoxelEditor* editor);
protected:
virtual QVariant createEdit(bool alternate);
private:
MaterialControl* _materialControl;
};
/// Allows sculpting parts of the voxel field.
class VoxelSculptBrushTool : public VoxelBrushTool {
Q_OBJECT
public:
VoxelSculptBrushTool(MetavoxelEditor* editor);
protected:
virtual QVariant createEdit(bool alternate);
private:
MaterialControl* _materialControl;
};
#endif // hifi_MetavoxelEditor_h

View file

@ -20,13 +20,9 @@
#include "Spanner.h"
REGISTER_META_OBJECT(FloatAttribute)
REGISTER_META_OBJECT(MaterialObject)
REGISTER_META_OBJECT(SharedObjectAttribute)
REGISTER_META_OBJECT(SharedObjectSetAttribute)
REGISTER_META_OBJECT(SpannerSetAttribute)
REGISTER_META_OBJECT(VoxelColorAttribute)
REGISTER_META_OBJECT(VoxelHermiteAttribute)
REGISTER_META_OBJECT(VoxelMaterialAttribute)
static int attributePointerMetaTypeId = qRegisterMetaType<AttributePointer>();
static int ownedAttributeValueMetaTypeId = qRegisterMetaType<OwnedAttributeValue>();
@ -41,21 +37,12 @@ AttributeRegistry::AttributeRegistry() :
new DefaultMetavoxelGuide()))),
_rendererAttribute(registerAttribute(new SharedObjectAttribute("renderer", &MetavoxelRenderer::staticMetaObject,
new DefaultMetavoxelRenderer()))),
_spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))),
_voxelColorAttribute(registerAttribute(new VoxelColorAttribute("voxelColor"))),
_voxelMaterialAttribute(registerAttribute(new VoxelMaterialAttribute("voxelMaterial"))),
_voxelHermiteAttribute(registerAttribute(new VoxelHermiteAttribute("voxelHermite"))) {
_spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))) {
// our baseline LOD threshold is for voxels; spanners and heightfields are a different story
const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f;
// our baseline LOD threshold is for voxels; spanners are a different story
const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 16.0f;
_spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER);
_spannersAttribute->setUserFacing(true);
const float VOXEL_LOD_THRESHOLD_MULTIPLIER = 16.0f;
_voxelColorAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER);
_voxelColorAttribute->setUserFacing(true);
_voxelMaterialAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER);
_voxelHermiteAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER);
}
static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) {
@ -275,846 +262,6 @@ FloatAttribute::FloatAttribute(const QString& name) :
SimpleInlineAttribute(name) {
}
const float CHAR_SCALE = 127.0f;
const float INVERSE_CHAR_SCALE = 1.0f / CHAR_SCALE;
QRgb packNormal(const glm::vec3& normal) {
return qRgb((char)(normal.x * CHAR_SCALE), (char)(normal.y * CHAR_SCALE), (char)(normal.z * CHAR_SCALE));
}
QRgb packNormal(const glm::vec3& normal, int alpha) {
return qRgba((char)(normal.x * CHAR_SCALE), (char)(normal.y * CHAR_SCALE), (char)(normal.z * CHAR_SCALE), alpha);
}
glm::vec3 unpackNormal(QRgb value) {
return glm::vec3((char)qRed(value) * INVERSE_CHAR_SCALE, (char)qGreen(value) * INVERSE_CHAR_SCALE,
(char)qBlue(value) * INVERSE_CHAR_SCALE);
}
DataBlock::~DataBlock() {
}
MaterialObject::MaterialObject() :
_scaleS(1.0f),
_scaleT(1.0f) {
}
static QHash<uchar, int> countIndices(const QByteArray& contents) {
QHash<uchar, int> counts;
for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) {
if (*src != 0) {
counts[*src]++;
}
}
return counts;
}
const float EIGHT_BIT_MAXIMUM = 255.0f;
uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials, QByteArray& contents) {
if (!(material && static_cast<MaterialObject*>(material.data())->getDiffuse().isValid())) {
return 0;
}
// first look for a matching existing material, noting the first reusable slot
int firstEmptyIndex = -1;
for (int i = 0; i < materials.size(); i++) {
const SharedObjectPointer& existingMaterial = materials.at(i);
if (existingMaterial) {
if (existingMaterial->equals(material.data())) {
return i + 1;
}
} else if (firstEmptyIndex == -1) {
firstEmptyIndex = i;
}
}
// if nothing found, use the first empty slot or append
if (firstEmptyIndex != -1) {
materials[firstEmptyIndex] = material;
return firstEmptyIndex + 1;
}
if (materials.size() < EIGHT_BIT_MAXIMUM) {
materials.append(material);
return materials.size();
}
// last resort: find the least-used material and remove it
QHash<uchar, int> counts = countIndices(contents);
uchar materialIndex = 0;
int lowestCount = INT_MAX;
for (QHash<uchar, int>::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) {
if (it.value() < lowestCount) {
materialIndex = it.key();
lowestCount = it.value();
}
}
contents.replace((char)materialIndex, (char)0);
return materialIndex;
}
void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, const QByteArray& contents) {
QHash<uchar, int> counts = countIndices(contents);
for (int i = 0; i < materials.size(); i++) {
if (counts.value(i + 1) == 0) {
materials[i] = SharedObjectPointer();
}
}
while (!(materials.isEmpty() || materials.last())) {
materials.removeLast();
}
}
const int VOXEL_COLOR_HEADER_SIZE = sizeof(qint32) * 6;
static QByteArray encodeVoxelColor(int offsetX, int offsetY, int offsetZ,
int sizeX, int sizeY, int sizeZ, const QVector<QRgb>& contents) {
QByteArray inflated(VOXEL_COLOR_HEADER_SIZE, 0);
qint32* header = (qint32*)inflated.data();
*header++ = offsetX;
*header++ = offsetY;
*header++ = offsetZ;
*header++ = sizeX;
*header++ = sizeY;
*header++ = sizeZ;
inflated.append((const char*)contents.constData(), contents.size() * sizeof(QRgb));
return qCompress(inflated);
}
static QVector<QRgb> decodeVoxelColor(const QByteArray& encoded, int& offsetX, int& offsetY, int& offsetZ,
int& sizeX, int& sizeY, int& sizeZ) {
QByteArray inflated = qUncompress(encoded);
const qint32* header = (const qint32*)inflated.constData();
offsetX = *header++;
offsetY = *header++;
offsetZ = *header++;
sizeX = *header++;
sizeY = *header++;
sizeZ = *header++;
int payloadSize = inflated.size() - VOXEL_COLOR_HEADER_SIZE;
QVector<QRgb> contents(payloadSize / sizeof(QRgb));
memcpy(contents.data(), inflated.constData() + VOXEL_COLOR_HEADER_SIZE, payloadSize);
return contents;
}
VoxelColorData::VoxelColorData(const QVector<QRgb>& contents, int size) :
_contents(contents),
_size(size) {
}
VoxelColorData::VoxelColorData(Bitstream& in, int bytes) {
read(in, bytes);
}
VoxelColorData::VoxelColorData(Bitstream& in, int bytes, const VoxelColorDataPointer& reference) {
if (!reference) {
read(in, bytes);
return;
}
QMutexLocker locker(&reference->getEncodedDeltaMutex());
reference->setEncodedDelta(in.readAligned(bytes));
reference->setDeltaData(DataBlockPointer(this));
_contents = reference->getContents();
_size = reference->getSize();
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
QVector<QRgb> delta = decodeVoxelColor(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
if (delta.isEmpty()) {
return;
}
if (offsetX == 0) {
_contents = delta;
_size = sizeX;
return;
}
int minX = offsetX - 1;
int minY = offsetY - 1;
int minZ = offsetZ - 1;
const QRgb* src = delta.constData();
int size2 = _size * _size;
QRgb* planeDest = _contents.data() + minZ * size2 + minY * _size + minX;
int length = sizeX * sizeof(QRgb);
for (int z = 0; z < sizeZ; z++, planeDest += size2) {
QRgb* dest = planeDest;
for (int y = 0; y < sizeY; y++, src += sizeX, dest += _size) {
memcpy(dest, src, length);
}
}
}
void VoxelColorData::write(Bitstream& out) {
QMutexLocker locker(&_encodedMutex);
if (_encoded.isEmpty()) {
_encoded = encodeVoxelColor(0, 0, 0, _size, _size, _size, _contents);
}
out << _encoded.size();
out.writeAligned(_encoded);
}
void VoxelColorData::writeDelta(Bitstream& out, const VoxelColorDataPointer& reference) {
if (!reference || reference->getSize() != _size) {
write(out);
return;
}
QMutexLocker locker(&reference->getEncodedDeltaMutex());
if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
int minX = _size, minY = _size, minZ = _size;
int maxX = -1, maxY = -1, maxZ = -1;
const QRgb* src = _contents.constData();
const QRgb* ref = reference->getContents().constData();
for (int z = 0; z < _size; z++) {
bool differenceZ = false;
for (int y = 0; y < _size; y++) {
bool differenceY = false;
for (int x = 0; x < _size; x++) {
if (*src++ != *ref++) {
minX = qMin(minX, x);
maxX = qMax(maxX, x);
differenceY = differenceZ = true;
}
}
if (differenceY) {
minY = qMin(minY, y);
maxY = qMax(maxY, y);
}
}
if (differenceZ) {
minZ = qMin(minZ, z);
maxZ = qMax(maxZ, z);
}
}
QVector<QRgb> delta;
int sizeX = 0, sizeY = 0, sizeZ = 0;
if (maxX >= minX) {
sizeX = maxX - minX + 1;
sizeY = maxY - minY + 1;
sizeZ = maxZ - minZ + 1;
delta = QVector<QRgb>(sizeX * sizeY * sizeZ, 0);
QRgb* dest = delta.data();
int size2 = _size * _size;
const QRgb* planeSrc = _contents.constData() + minZ * size2 + minY * _size + minX;
int length = sizeX * sizeof(QRgb);
for (int z = 0; z < sizeZ; z++, planeSrc += size2) {
src = planeSrc;
for (int y = 0; y < sizeY; y++, src += _size, dest += sizeX) {
memcpy(dest, src, length);
}
}
}
reference->setEncodedDelta(encodeVoxelColor(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta));
reference->setDeltaData(DataBlockPointer(this));
}
out << reference->getEncodedDelta().size();
out.writeAligned(reference->getEncodedDelta());
}
void VoxelColorData::read(Bitstream& in, int bytes) {
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
_contents = decodeVoxelColor(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
_size = sizeX;
}
VoxelColorAttribute::VoxelColorAttribute(const QString& name) :
InlineAttribute<VoxelColorDataPointer>(name) {
}
void VoxelColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
if (!isLeaf) {
return;
}
int size;
in >> size;
if (size == 0) {
*(VoxelColorDataPointer*)&value = VoxelColorDataPointer();
} else {
*(VoxelColorDataPointer*)&value = VoxelColorDataPointer(new VoxelColorData(in, size));
}
}
void VoxelColorAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
if (!isLeaf) {
return;
}
VoxelColorDataPointer data = decodeInline<VoxelColorDataPointer>(value);
if (data) {
data->write(out);
} else {
out << 0;
}
}
void VoxelColorAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const {
if (!isLeaf) {
return;
}
int size;
in >> size;
if (size == 0) {
*(VoxelColorDataPointer*)&value = VoxelColorDataPointer();
} else {
*(VoxelColorDataPointer*)&value = VoxelColorDataPointer(new VoxelColorData(
in, size, decodeInline<VoxelColorDataPointer>(reference)));
}
}
void VoxelColorAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const {
if (!isLeaf) {
return;
}
VoxelColorDataPointer data = decodeInline<VoxelColorDataPointer>(value);
if (data) {
data->writeDelta(out, decodeInline<VoxelColorDataPointer>(reference));
} else {
out << 0;
}
}
bool VoxelColorAttribute::merge(void*& parent, void* children[], bool postRead) const {
int maxSize = 0;
for (int i = 0; i < MERGE_COUNT; i++) {
VoxelColorDataPointer pointer = decodeInline<VoxelColorDataPointer>(children[i]);
if (pointer) {
maxSize = qMax(maxSize, pointer->getSize());
}
}
if (maxSize == 0) {
*(VoxelColorDataPointer*)&parent = VoxelColorDataPointer();
return true;
}
int size = maxSize;
int area = size * size;
QVector<QRgb> contents(area * size);
int halfSize = size / 2;
int halfSizeComplement = size - halfSize;
for (int i = 0; i < MERGE_COUNT; i++) {
VoxelColorDataPointer child = decodeInline<VoxelColorDataPointer>(children[i]);
if (!child) {
continue;
}
const QVector<QRgb>& childContents = child->getContents();
int childSize = child->getSize();
int childArea = childSize * childSize;
const int INDEX_MASK = 1;
int xIndex = i & INDEX_MASK;
const int Y_SHIFT = 1;
int yIndex = (i >> Y_SHIFT) & INDEX_MASK;
int Z_SHIFT = 2;
int zIndex = (i >> Z_SHIFT) & INDEX_MASK;
QRgb* dest = contents.data() + (zIndex * halfSize * area) + (yIndex * halfSize * size) + (xIndex * halfSize);
const QRgb* src = childContents.data();
const int MAX_ALPHA = 255;
if (childSize == size) {
// simple case: one destination value for four child values
for (int z = 0; z < halfSizeComplement; z++) {
int offset4 = (z == halfSize) ? 0 : childArea;
for (int y = 0; y < halfSizeComplement; y++) {
int offset2 = (y == halfSize) ? 0 : childSize;
int offset6 = offset4 + offset2;
for (QRgb* end = dest + halfSizeComplement; dest != end; ) {
int offset1 = (dest == end - 1) ? 0 : 1;
QRgb v0 = src[0], v1 = src[offset1], v2 = src[offset2], v3 = src[offset2 + offset1], v4 = src[offset4],
v5 = src[offset4 + offset1], v6 = src[offset6], v7 = src[offset6 + offset1];
src += (1 + offset1);
int a0 = qAlpha(v0), a1 = qAlpha(v1), a2 = qAlpha(v2), a3 = qAlpha(v3),
a4 = qAlpha(v4), a5 = qAlpha(v5), a6 = qAlpha(v6), a7 = qAlpha(v7);
if (a0 == 0) {
*dest++ = qRgba(0, 0, 0, 0);
continue;
}
int alphaTotal = a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7;
*dest++ = qRgba(
(qRed(v0) * a0 + qRed(v1) * a1 + qRed(v2) * a2 + qRed(v3) * a3 +
qRed(v4) * a4 + qRed(v5) * a5 + qRed(v6) * a6 + qRed(v7) * a7) / alphaTotal,
(qGreen(v0) * a0 + qGreen(v1) * a1 + qGreen(v2) * a2 + qGreen(v3) * a3 +
qGreen(v4) * a4 + qGreen(v5) * a5 + qGreen(v6) * a6 + qGreen(v7) * a7) / alphaTotal,
(qBlue(v0) * a0 + qBlue(v1) * a1 + qBlue(v2) * a2 + qBlue(v3) * a3 +
qBlue(v4) * a4 + qBlue(v5) * a5 + qBlue(v6) * a6 + qBlue(v7) * a7) / alphaTotal,
MAX_ALPHA);
}
dest += halfSize;
src += offset2;
}
dest += halfSize * size;
src += offset4;
}
} else {
// more complex: N destination values for four child values
// ...
}
}
*(VoxelColorDataPointer*)&parent = VoxelColorDataPointer(new VoxelColorData(contents, size));
return false;
}
const int VOXEL_MATERIAL_HEADER_SIZE = sizeof(qint32) * 6;
static QByteArray encodeVoxelMaterial(int offsetX, int offsetY, int offsetZ,
int sizeX, int sizeY, int sizeZ, const QByteArray& contents) {
QByteArray inflated(VOXEL_MATERIAL_HEADER_SIZE, 0);
qint32* header = (qint32*)inflated.data();
*header++ = offsetX;
*header++ = offsetY;
*header++ = offsetZ;
*header++ = sizeX;
*header++ = sizeY;
*header++ = sizeZ;
inflated.append(contents);
return qCompress(inflated);
}
static QByteArray decodeVoxelMaterial(const QByteArray& encoded, int& offsetX, int& offsetY, int& offsetZ,
int& sizeX, int& sizeY, int& sizeZ) {
QByteArray inflated = qUncompress(encoded);
const qint32* header = (const qint32*)inflated.constData();
offsetX = *header++;
offsetY = *header++;
offsetZ = *header++;
sizeX = *header++;
sizeY = *header++;
sizeZ = *header++;
return inflated.mid(VOXEL_MATERIAL_HEADER_SIZE);
}
VoxelMaterialData::VoxelMaterialData(const QByteArray& contents, int size, const QVector<SharedObjectPointer>& materials) :
_contents(contents),
_size(size),
_materials(materials) {
}
VoxelMaterialData::VoxelMaterialData(Bitstream& in, int bytes) {
read(in, bytes);
}
VoxelMaterialData::VoxelMaterialData(Bitstream& in, int bytes, const VoxelMaterialDataPointer& reference) {
if (!reference) {
read(in, bytes);
return;
}
QMutexLocker locker(&reference->getEncodedDeltaMutex());
reference->setEncodedDelta(in.readAligned(bytes));
in.readDelta(_materials, reference->getMaterials());
reference->setDeltaData(DataBlockPointer(this));
_contents = reference->getContents();
_size = reference->getSize();
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
QByteArray delta = decodeVoxelMaterial(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
if (delta.isEmpty()) {
return;
}
if (offsetX == 0) {
_contents = delta;
_size = sizeX;
return;
}
int minX = offsetX - 1;
int minY = offsetY - 1;
int minZ = offsetZ - 1;
const char* src = delta.constData();
int size2 = _size * _size;
char* planeDest = _contents.data() + minZ * size2 + minY * _size + minX;
for (int z = 0; z < sizeZ; z++, planeDest += size2) {
char* dest = planeDest;
for (int y = 0; y < sizeY; y++, src += sizeX, dest += _size) {
memcpy(dest, src, sizeX);
}
}
}
void VoxelMaterialData::write(Bitstream& out) {
QMutexLocker locker(&_encodedMutex);
if (_encoded.isEmpty()) {
_encoded = encodeVoxelMaterial(0, 0, 0, _size, _size, _size, _contents);
}
out << _encoded.size();
out.writeAligned(_encoded);
out << _materials;
}
void VoxelMaterialData::writeDelta(Bitstream& out, const VoxelMaterialDataPointer& reference) {
if (!reference || reference->getSize() != _size) {
write(out);
return;
}
QMutexLocker locker(&reference->getEncodedDeltaMutex());
if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
int minX = _size, minY = _size, minZ = _size;
int maxX = -1, maxY = -1, maxZ = -1;
const char* src = _contents.constData();
const char* ref = reference->getContents().constData();
for (int z = 0; z < _size; z++) {
bool differenceZ = false;
for (int y = 0; y < _size; y++) {
bool differenceY = false;
for (int x = 0; x < _size; x++) {
if (*src++ != *ref++) {
minX = qMin(minX, x);
maxX = qMax(maxX, x);
differenceY = differenceZ = true;
}
}
if (differenceY) {
minY = qMin(minY, y);
maxY = qMax(maxY, y);
}
}
if (differenceZ) {
minZ = qMin(minZ, z);
maxZ = qMax(maxZ, z);
}
}
QByteArray delta;
int sizeX = 0, sizeY = 0, sizeZ = 0;
if (maxX >= minX) {
sizeX = maxX - minX + 1;
sizeY = maxY - minY + 1;
sizeZ = maxZ - minZ + 1;
delta = QByteArray(sizeX * sizeY * sizeZ, 0);
char* dest = delta.data();
int size2 = _size * _size;
const char* planeSrc = _contents.constData() + minZ * size2 + minY * _size + minX;
for (int z = 0; z < sizeZ; z++, planeSrc += size2) {
src = planeSrc;
for (int y = 0; y < sizeY; y++, src += _size, dest += sizeX) {
memcpy(dest, src, sizeX);
}
}
}
reference->setEncodedDelta(encodeVoxelMaterial(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta));
reference->setDeltaData(DataBlockPointer(this));
}
out << reference->getEncodedDelta().size();
out.writeAligned(reference->getEncodedDelta());
out.writeDelta(_materials, reference->getMaterials());
}
void VoxelMaterialData::read(Bitstream& in, int bytes) {
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
_contents = decodeVoxelMaterial(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
_size = sizeX;
in >> _materials;
}
VoxelMaterialAttribute::VoxelMaterialAttribute(const QString& name) :
InlineAttribute<VoxelMaterialDataPointer>(name) {
}
void VoxelMaterialAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
if (!isLeaf) {
return;
}
int size;
in >> size;
if (size == 0) {
*(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer();
} else {
*(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer(new VoxelMaterialData(in, size));
}
}
void VoxelMaterialAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
if (!isLeaf) {
return;
}
VoxelMaterialDataPointer data = decodeInline<VoxelMaterialDataPointer>(value);
if (data) {
data->write(out);
} else {
out << 0;
}
}
void VoxelMaterialAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const {
if (!isLeaf) {
return;
}
int size;
in >> size;
if (size == 0) {
*(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer();
} else {
*(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer(new VoxelMaterialData(
in, size, decodeInline<VoxelMaterialDataPointer>(reference)));
}
}
void VoxelMaterialAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const {
if (!isLeaf) {
return;
}
VoxelMaterialDataPointer data = decodeInline<VoxelMaterialDataPointer>(value);
if (data) {
data->writeDelta(out, decodeInline<VoxelMaterialDataPointer>(reference));
} else {
out << 0;
}
}
bool VoxelMaterialAttribute::merge(void*& parent, void* children[], bool postRead) const {
int maxSize = 0;
for (int i = 0; i < MERGE_COUNT; i++) {
VoxelMaterialDataPointer pointer = decodeInline<VoxelMaterialDataPointer>(children[i]);
if (pointer) {
maxSize = qMax(maxSize, pointer->getSize());
}
}
*(VoxelMaterialDataPointer*)&parent = VoxelMaterialDataPointer();
return maxSize == 0;
}
VoxelHermiteData::VoxelHermiteData(const QVector<QRgb>& contents, int size) :
_contents(contents),
_size(size) {
}
VoxelHermiteData::VoxelHermiteData(Bitstream& in, int bytes) {
read(in, bytes);
}
VoxelHermiteData::VoxelHermiteData(Bitstream& in, int bytes, const VoxelHermiteDataPointer& reference) {
if (!reference) {
read(in, bytes);
return;
}
QMutexLocker locker(&reference->getEncodedDeltaMutex());
reference->setEncodedDelta(in.readAligned(bytes));
reference->setDeltaData(DataBlockPointer(this));
_contents = reference->getContents();
_size = reference->getSize();
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
QVector<QRgb> delta = decodeVoxelColor(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
if (delta.isEmpty()) {
return;
}
if (offsetX == 0) {
_contents = delta;
_size = sizeX;
return;
}
int minX = offsetX - 1;
int minY = offsetY - 1;
int minZ = offsetZ - 1;
const QRgb* src = delta.constData();
int destStride = _size * EDGE_COUNT;
int destStride2 = _size * destStride;
QRgb* planeDest = _contents.data() + minZ * destStride2 + minY * destStride + minX * EDGE_COUNT;
int srcStride = sizeX * EDGE_COUNT;
int length = srcStride * sizeof(QRgb);
for (int z = 0; z < sizeZ; z++, planeDest += destStride2) {
QRgb* dest = planeDest;
for (int y = 0; y < sizeY; y++, src += srcStride, dest += destStride) {
memcpy(dest, src, length);
}
}
}
void VoxelHermiteData::write(Bitstream& out) {
QMutexLocker locker(&_encodedMutex);
if (_encoded.isEmpty()) {
_encoded = encodeVoxelColor(0, 0, 0, _size, _size, _size, _contents);
}
out << _encoded.size();
out.writeAligned(_encoded);
}
void VoxelHermiteData::writeDelta(Bitstream& out, const VoxelHermiteDataPointer& reference) {
if (!reference || reference->getSize() != _size) {
write(out);
return;
}
QMutexLocker locker(&reference->getEncodedDeltaMutex());
if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
int minX = _size, minY = _size, minZ = _size;
int maxX = -1, maxY = -1, maxZ = -1;
const QRgb* src = _contents.constData();
const QRgb* ref = reference->getContents().constData();
for (int z = 0; z < _size; z++) {
bool differenceZ = false;
for (int y = 0; y < _size; y++) {
bool differenceY = false;
for (int x = 0; x < _size; x++, src += EDGE_COUNT, ref += EDGE_COUNT) {
if (src[0] != ref[0] || src[1] != ref[1] || src[2] != ref[2]) {
minX = qMin(minX, x);
maxX = qMax(maxX, x);
differenceY = differenceZ = true;
}
}
if (differenceY) {
minY = qMin(minY, y);
maxY = qMax(maxY, y);
}
}
if (differenceZ) {
minZ = qMin(minZ, z);
maxZ = qMax(maxZ, z);
}
}
QVector<QRgb> delta;
int sizeX = 0, sizeY = 0, sizeZ = 0;
if (maxX >= minX) {
sizeX = maxX - minX + 1;
sizeY = maxY - minY + 1;
sizeZ = maxZ - minZ + 1;
delta = QVector<QRgb>(sizeX * sizeY * sizeZ * EDGE_COUNT, 0);
QRgb* dest = delta.data();
int srcStride = _size * EDGE_COUNT;
int srcStride2 = _size * srcStride;
const QRgb* planeSrc = _contents.constData() + minZ * srcStride2 + minY * srcStride + minX * EDGE_COUNT;
int destStride = sizeX * EDGE_COUNT;
int length = destStride * sizeof(QRgb);
for (int z = 0; z < sizeZ; z++, planeSrc += srcStride2) {
src = planeSrc;
for (int y = 0; y < sizeY; y++, src += srcStride, dest += destStride) {
memcpy(dest, src, length);
}
}
}
reference->setEncodedDelta(encodeVoxelColor(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta));
reference->setDeltaData(DataBlockPointer(this));
}
out << reference->getEncodedDelta().size();
out.writeAligned(reference->getEncodedDelta());
}
void VoxelHermiteData::read(Bitstream& in, int bytes) {
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
_contents = decodeVoxelColor(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
_size = sizeX;
}
VoxelHermiteAttribute::VoxelHermiteAttribute(const QString& name) :
InlineAttribute<VoxelHermiteDataPointer>(name) {
}
void VoxelHermiteAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
if (!isLeaf) {
return;
}
int size;
in >> size;
if (size == 0) {
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer();
} else {
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(new VoxelHermiteData(in, size));
}
}
void VoxelHermiteAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
if (!isLeaf) {
return;
}
VoxelHermiteDataPointer data = decodeInline<VoxelHermiteDataPointer>(value);
if (data) {
data->write(out);
} else {
out << 0;
}
}
void VoxelHermiteAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const {
if (!isLeaf) {
return;
}
int size;
in >> size;
if (size == 0) {
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer();
} else {
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(new VoxelHermiteData(
in, size, decodeInline<VoxelHermiteDataPointer>(reference)));
}
}
void VoxelHermiteAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const {
if (!isLeaf) {
return;
}
VoxelHermiteDataPointer data = decodeInline<VoxelHermiteDataPointer>(value);
if (data) {
data->writeDelta(out, decodeInline<VoxelHermiteDataPointer>(reference));
} else {
out << 0;
}
}
bool VoxelHermiteAttribute::merge(void*& parent, void* children[], bool postRead) const {
int maxSize = 0;
for (int i = 0; i < MERGE_COUNT; i++) {
VoxelHermiteDataPointer pointer = decodeInline<VoxelHermiteDataPointer>(children[i]);
if (pointer) {
maxSize = qMax(maxSize, pointer->getSize());
}
}
if (maxSize == 0) {
*(VoxelHermiteDataPointer*)&parent = VoxelHermiteDataPointer();
return true;
}
int size = maxSize;
int area = size * size;
QVector<QRgb> contents(area * size * VoxelHermiteData::EDGE_COUNT);
int halfSize = size / 2;
int halfSizeComplement = size - halfSize;
for (int i = 0; i < MERGE_COUNT; i++) {
VoxelHermiteDataPointer child = decodeInline<VoxelHermiteDataPointer>(children[i]);
if (!child) {
continue;
}
const QVector<QRgb>& childContents = child->getContents();
int childSize = child->getSize();
int childArea = childSize * childSize;
const int INDEX_MASK = 1;
int xIndex = i & INDEX_MASK;
const int Y_SHIFT = 1;
int yIndex = (i >> Y_SHIFT) & INDEX_MASK;
int Z_SHIFT = 2;
int zIndex = (i >> Z_SHIFT) & INDEX_MASK;
QRgb* dest = contents.data() + ((zIndex * halfSize * area) + (yIndex * halfSize * size) + (xIndex * halfSize)) *
VoxelHermiteData::EDGE_COUNT;
const QRgb* src = childContents.data();
int offsets[VoxelHermiteData::EDGE_COUNT];
if (childSize == size) {
// simple case: one destination value for four child values
for (int z = 0; z < halfSizeComplement; z++) {
offsets[2] = (z == halfSize) ? 0 : (childArea * VoxelHermiteData::EDGE_COUNT);
for (int y = 0; y < halfSizeComplement; y++) {
offsets[1] = (y == halfSize) ? 0 : (childSize * VoxelHermiteData::EDGE_COUNT);
for (QRgb* end = dest + halfSizeComplement * VoxelHermiteData::EDGE_COUNT; dest != end;
dest += VoxelHermiteData::EDGE_COUNT) {
offsets[0] = (dest == end - VoxelHermiteData::EDGE_COUNT) ? 0 : VoxelHermiteData::EDGE_COUNT;
for (int i = 0; i < VoxelHermiteData::EDGE_COUNT; i++) {
QRgb v0 = src[i], v1 = src[i + offsets[i]];
glm::vec3 n0 = unpackNormal(v0), n1 = unpackNormal(v1);
float l0 = glm::length(n0), l1 = glm::length(n1);
float lengthTotal = l0 + l1;
if (lengthTotal == 0.0f) {
dest[i] = qRgba(0, 0, 0, 0);
continue;
}
glm::vec3 combinedNormal = n0 + n1;
float combinedLength = glm::length(combinedNormal);
if (combinedLength > 0.0f) {
combinedNormal /= combinedLength;
}
float combinedOffset = qAlpha(v0) * 0.5f * l0 + (qAlpha(v1) + EIGHT_BIT_MAXIMUM) * 0.5f * l1;
dest[i] = packNormal(combinedNormal, combinedOffset / lengthTotal);
}
src += (VoxelHermiteData::EDGE_COUNT + offsets[0]);
}
dest += (halfSize * VoxelHermiteData::EDGE_COUNT);
src += offsets[1];
}
dest += (halfSize * size * VoxelHermiteData::EDGE_COUNT);
src += offsets[2];
}
} else {
// more complex: N destination values for four child values
// ...
}
}
*(VoxelHermiteDataPointer*)&parent = VoxelHermiteDataPointer(new VoxelHermiteData(contents, size));
return false;
}
SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject,
const SharedObjectPointer& defaultValue) :
InlineAttribute<SharedObjectPointer>(name, defaultValue),

View file

@ -30,16 +30,10 @@ class QScriptValue;
class Attribute;
class DataBlock;
class HeightfieldColorData;
class HeightfieldHeightData;
class HeightfieldMaterialData;
class MetavoxelData;
class MetavoxelLOD;
class MetavoxelNode;
class MetavoxelStreamState;
class VoxelColorData;
class VoxelHermiteData;
class VoxelMaterialData;
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
@ -88,24 +82,6 @@ public:
/// Returns a reference to the standard SharedObjectSet "spanners" attribute.
const AttributePointer& getSpannersAttribute() const { return _spannersAttribute; }
/// Returns a reference to the standard HeightfieldHeightDataPointer "heightfield" attribute.
const AttributePointer& getHeightfieldAttribute() const { return _heightfieldAttribute; }
/// Returns a reference to the standard HeightfieldColorDataPointer "heightfieldColor" attribute.
const AttributePointer& getHeightfieldColorAttribute() const { return _heightfieldColorAttribute; }
/// Returns a reference to the standard HeightfieldMaterialDataPointer "heightfieldMaterial" attribute.
const AttributePointer& getHeightfieldMaterialAttribute() const { return _heightfieldMaterialAttribute; }
/// Returns a reference to the standard VoxelColorDataPointer "voxelColor" attribute.
const AttributePointer& getVoxelColorAttribute() const { return _voxelColorAttribute; }
/// Returns a reference to the standard VoxelMaterialDataPointer "voxelMaterial" attribute.
const AttributePointer& getVoxelMaterialAttribute() const { return _voxelMaterialAttribute; }
/// Returns a reference to the standard VoxelHermiteDataPointer "voxelHermite" attribute.
const AttributePointer& getVoxelHermiteAttribute() const { return _voxelHermiteAttribute; }
private:
static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine);
@ -116,12 +92,6 @@ private:
AttributePointer _guideAttribute;
AttributePointer _rendererAttribute;
AttributePointer _spannersAttribute;
AttributePointer _heightfieldAttribute;
AttributePointer _heightfieldColorAttribute;
AttributePointer _heightfieldMaterialAttribute;
AttributePointer _voxelColorAttribute;
AttributePointer _voxelMaterialAttribute;
AttributePointer _voxelHermiteAttribute;
};
/// Converts a value to a void pointer.
@ -355,213 +325,6 @@ public:
Q_INVOKABLE FloatAttribute(const QString& name = QString());
};
/// Packs a normal into an RGB value.
QRgb packNormal(const glm::vec3& normal);
/// Packs a normal (plus extra alpha value) into an RGBA value.
QRgb packNormal(const glm::vec3& normal, int alpha);
/// Unpacks a normal from an RGB value.
glm::vec3 unpackNormal(QRgb value);
typedef QExplicitlySharedDataPointer<DataBlock> DataBlockPointer;
/// Base class for blocks of data.
class DataBlock : public QSharedData {
public:
static const int COLOR_BYTES = 3;
virtual ~DataBlock();
void setDeltaData(const DataBlockPointer& deltaData) { _deltaData = deltaData; }
const DataBlockPointer& getDeltaData() const { return _deltaData; }
void setEncodedDelta(const QByteArray& encodedDelta) { _encodedDelta = encodedDelta; }
const QByteArray& getEncodedDelta() const { return _encodedDelta; }
QMutex& getEncodedDeltaMutex() { return _encodedDeltaMutex; }
protected:
QByteArray _encoded;
QMutex _encodedMutex;
DataBlockPointer _deltaData;
QByteArray _encodedDelta;
QMutex _encodedDeltaMutex;
class EncodedSubdivision {
public:
DataBlockPointer ancestor;
QByteArray data;
};
QVector<EncodedSubdivision> _encodedSubdivisions;
QMutex _encodedSubdivisionsMutex;
};
/// Contains the description of a material.
class MaterialObject : public SharedObject {
Q_OBJECT
Q_PROPERTY(QUrl diffuse MEMBER _diffuse)
Q_PROPERTY(float scaleS MEMBER _scaleS)
Q_PROPERTY(float scaleT MEMBER _scaleT)
public:
Q_INVOKABLE MaterialObject();
const QUrl& getDiffuse() const { return _diffuse; }
float getScaleS() const { return _scaleS; }
float getScaleT() const { return _scaleT; }
private:
QUrl _diffuse;
float _scaleS;
float _scaleT;
};
/// 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);
/// Utility method for editing: removes any unused materials from the supplied list.
void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, const QByteArray& contents);
typedef QExplicitlySharedDataPointer<VoxelColorData> VoxelColorDataPointer;
/// Contains a block of voxel color data.
class VoxelColorData : public DataBlock {
public:
VoxelColorData(const QVector<QRgb>& contents, int size);
VoxelColorData(Bitstream& in, int bytes);
VoxelColorData(Bitstream& in, int bytes, const VoxelColorDataPointer& reference);
const QVector<QRgb>& getContents() const { return _contents; }
int getSize() const { return _size; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const VoxelColorDataPointer& reference);
private:
void read(Bitstream& in, int bytes);
QVector<QRgb> _contents;
int _size;
};
/// An attribute that stores voxel colors.
class VoxelColorAttribute : public InlineAttribute<VoxelColorDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE VoxelColorAttribute(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;
};
typedef QExplicitlySharedDataPointer<VoxelMaterialData> VoxelMaterialDataPointer;
/// Contains a block of voxel material data.
class VoxelMaterialData : public DataBlock {
public:
VoxelMaterialData(const QByteArray& contents, int size,
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
VoxelMaterialData(Bitstream& in, int bytes);
VoxelMaterialData(Bitstream& in, int bytes, const VoxelMaterialDataPointer& reference);
const QByteArray& getContents() const { return _contents; }
int getSize() const { return _size; }
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const VoxelMaterialDataPointer& reference);
private:
void read(Bitstream& in, int bytes);
QByteArray _contents;
int _size;
QVector<SharedObjectPointer> _materials;
};
/// An attribute that stores voxel materials.
class VoxelMaterialAttribute : public InlineAttribute<VoxelMaterialDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE VoxelMaterialAttribute(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;
};
typedef QExplicitlySharedDataPointer<VoxelHermiteData> VoxelHermiteDataPointer;
/// Contains a block of voxel Hermite data (positions and normals at edge crossings).
class VoxelHermiteData : public DataBlock {
public:
static const int EDGE_COUNT = 3;
VoxelHermiteData(const QVector<QRgb>& contents, int size);
VoxelHermiteData(Bitstream& in, int bytes);
VoxelHermiteData(Bitstream& in, int bytes, const VoxelHermiteDataPointer& reference);
const QVector<QRgb>& getContents() const { return _contents; }
int getSize() const { return _size; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const VoxelHermiteDataPointer& reference);
private:
void read(Bitstream& in, int bytes);
QVector<QRgb> _contents;
int _size;
};
/// An attribute that stores voxel Hermite data.
class VoxelHermiteAttribute : public InlineAttribute<VoxelHermiteDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE VoxelHermiteAttribute(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;
};
/// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject).
class SharedObjectAttribute : public InlineAttribute<SharedObjectPointer> {
Q_OBJECT

View file

@ -2432,12 +2432,13 @@ void MappedObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object,
}
QObject* MappedObjectStreamer::read(Bitstream& in, QObject* object) const {
bool reread = (object != NULL);
if (!object && _metaObject) {
object = _metaObject->newInstance();
}
foreach (const StreamerPropertyPair& property, _properties) {
QVariant value = property.first->read(in);
if (property.second.isValid() && object) {
if (property.second.isValid() && object && !reread) {
property.second.write(object, value);
}
}
@ -2445,6 +2446,7 @@ QObject* MappedObjectStreamer::read(Bitstream& in, QObject* object) const {
}
QObject* MappedObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const {
bool reread = (object != NULL);
if (!object && _metaObject) {
object = _metaObject->newInstance();
}
@ -2452,7 +2454,7 @@ QObject* MappedObjectStreamer::readRawDelta(Bitstream& in, const QObject* refere
QVariant value;
property.first->readDelta(in, value, (property.second.isValid() && reference &&
reference->metaObject() == _metaObject) ? property.second.read(reference) : QVariant());
if (property.second.isValid() && object) {
if (property.second.isValid() && object && !reread) {
property.second.write(object, value);
}
}
@ -2475,13 +2477,13 @@ void SharedObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object,
QObject* SharedObjectStreamer::read(Bitstream& in, QObject* object) const {
QObject* result = MappedObjectStreamer::read(in, object);
static_cast<SharedObject*>(result)->readExtra(in);
static_cast<SharedObject*>(result)->readExtra(in, object != NULL);
return result;
}
QObject* SharedObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const {
QObject* result = MappedObjectStreamer::readRawDelta(in, reference, object);
static_cast<SharedObject*>(result)->readExtraDelta(in, static_cast<const SharedObject*>(reference));
static_cast<SharedObject*>(result)->readExtraDelta(in, static_cast<const SharedObject*>(reference), object != NULL);
return result;
}
@ -2592,6 +2594,7 @@ void GenericObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object,
}
QObject* GenericObjectStreamer::read(Bitstream& in, QObject* object) const {
bool reread = (object != NULL);
if (!object) {
object = new GenericSharedObject(_weakSelf);
}
@ -2599,11 +2602,14 @@ QObject* GenericObjectStreamer::read(Bitstream& in, QObject* object) const {
foreach (const StreamerNamePair& property, _properties) {
values.append(property.first->read(in));
}
static_cast<GenericSharedObject*>(object)->setValues(values);
if (!reread) {
static_cast<GenericSharedObject*>(object)->setValues(values);
}
return object;
}
QObject* GenericObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const {
bool reread = (object != NULL);
if (!object) {
object = new GenericSharedObject(_weakSelf);
}
@ -2615,7 +2621,9 @@ QObject* GenericObjectStreamer::readRawDelta(Bitstream& in, const QObject* refer
static_cast<const GenericSharedObject*>(reference)->getValues().at(i) : QVariant());
values.append(value);
}
static_cast<GenericSharedObject*>(object)->setValues(values);
if (!reread) {
static_cast<GenericSharedObject*>(object)->setValues(values);
}
return object;
}

View file

@ -715,8 +715,9 @@ void ReliableChannel::endMessage() {
quint32 length = _buffer.pos() - _messageLengthPlaceholder;
_buffer.writeBytes(_messageLengthPlaceholder, sizeof(quint32), (const char*)&length);
_messageReceivedOffset = getBytesWritten();
_messageSize = length;
pruneOutgoingMessageStats();
_outgoingMessageStats.append(OffsetSizePair(getBytesWritten(), length));
}
void ReliableChannel::sendMessage(const QVariant& message) {
@ -725,12 +726,14 @@ void ReliableChannel::sendMessage(const QVariant& message) {
endMessage();
}
bool ReliableChannel::getMessageSendProgress(int& sent, int& total) const {
if (!_messagesEnabled || _offset >= _messageReceivedOffset) {
bool ReliableChannel::getMessageSendProgress(int& sent, int& total) {
pruneOutgoingMessageStats();
if (!_messagesEnabled || _outgoingMessageStats.isEmpty()) {
return false;
}
sent = qMax(0, _messageSize - (_messageReceivedOffset - _offset));
total = _messageSize;
const OffsetSizePair& stat = _outgoingMessageStats.first();
sent = qMax(0, stat.second - (stat.first - _offset));
total = stat.second;
return true;
}
@ -770,8 +773,7 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool o
_offset(0),
_writePosition(0),
_writePositionResetPacketNumber(0),
_messagesEnabled(true),
_messageReceivedOffset(0) {
_messagesEnabled(true) {
_buffer.open(output ? QIODevice::WriteOnly : QIODevice::ReadOnly);
_dataStream.setByteOrder(QDataStream::LittleEndian);
@ -942,3 +944,9 @@ void ReliableChannel::readData(QDataStream& in) {
}
}
void ReliableChannel::pruneOutgoingMessageStats() {
while (!_outgoingMessageStats.isEmpty() && _offset >= _outgoingMessageStats.first().first) {
_outgoingMessageStats.removeFirst();
}
}

View file

@ -386,7 +386,7 @@ public:
/// Determines the number of bytes uploaded towards the currently pending message.
/// \return true if there is a message pending, in which case the sent and total arguments will be set
bool getMessageSendProgress(int& sent, int& total) const;
bool getMessageSendProgress(int& sent, int& total);
/// Determines the number of bytes downloaded towards the currently pending message.
/// \return true if there is a message pending, in which case the received and total arguments will be set
@ -418,6 +418,8 @@ private:
void readData(QDataStream& in);
void pruneOutgoingMessageStats();
int _index;
bool _output;
CircularBuffer _buffer;
@ -432,6 +434,10 @@ private:
SpanList _acknowledged;
bool _messagesEnabled;
int _messageLengthPlaceholder; ///< the location in the buffer of the message length for the current message
typedef QPair<int, int> OffsetSizePair;
QVector<OffsetSizePair> _outgoingMessageStats;
int _messageReceivedOffset; ///< when reached, indicates that the most recent sent message has been received
int _messageSize; ///< the size of the most recent sent message; only valid when _messageReceivedOffset has been set
};

View file

@ -29,7 +29,9 @@ MetavoxelLOD::MetavoxelLOD(const glm::vec3& position, float threshold) :
}
bool MetavoxelLOD::shouldSubdivide(const glm::vec3& minimum, float size, float multiplier) const {
return size >= glm::distance(position, minimum + glm::vec3(size, size, size) * 0.5f) * threshold * multiplier;
float halfSize = size * 0.5f;
return size >= (glm::distance(position, minimum + glm::vec3(halfSize, halfSize, halfSize)) - halfSize) *
threshold * multiplier;
}
bool MetavoxelLOD::becameSubdivided(const glm::vec3& minimum, float size,
@ -57,7 +59,9 @@ bool MetavoxelLOD::becameSubdividedOrCollapsed(const glm::vec3& minimum, float s
}
bool MetavoxelLOD::shouldSubdivide(const glm::vec2& minimum, float size, float multiplier) const {
return size >= glm::distance(glm::vec2(position), minimum + glm::vec2(size, size) * 0.5f) * threshold * multiplier;
float halfSize = size * 0.5f;
return size >= (glm::distance(glm::vec2(position), minimum + glm::vec2(halfSize, halfSize)) - halfSize) *
threshold * multiplier;
}
bool MetavoxelLOD::becameSubdivided(const glm::vec2& minimum, float size,
@ -1188,67 +1192,6 @@ void MetavoxelNode::writeSpanners(MetavoxelStreamState& state) const {
}
}
void MetavoxelNode::writeSpannerDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const {
SharedObjectSet oldSet = decodeInline<SharedObjectSet>(reference.getAttributeValue());
SharedObjectSet newSet = decodeInline<SharedObjectSet>(_attributeValue);
foreach (const SharedObjectPointer& object, oldSet) {
if (static_cast<Spanner*>(object.data())->testAndSetVisited(state.base.visit) && !newSet.contains(object)) {
state.base.stream << object;
}
}
foreach (const SharedObjectPointer& object, newSet) {
if (static_cast<Spanner*>(object.data())->testAndSetVisited(state.base.visit) && !oldSet.contains(object)) {
state.base.stream << object;
}
}
if (isLeaf() || !state.shouldSubdivide()) {
if (!reference.isLeaf() && state.shouldSubdivideReference()) {
MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i);
reference._children[i]->writeSpanners(nextState);
}
}
return;
}
MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
if (reference.isLeaf() || !state.shouldSubdivideReference()) {
for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i);
_children[i]->writeSpanners(nextState);
}
return;
}
for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i);
if (_children[i] != reference._children[i]) {
_children[i]->writeSpannerDelta(*reference._children[i], nextState);
} else if (nextState.becameSubdivided()) {
_children[i]->writeSpannerSubdivision(nextState);
}
}
}
void MetavoxelNode::writeSpannerSubdivision(MetavoxelStreamState& state) const {
if (!isLeaf()) {
MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
if (!state.shouldSubdivideReference()) {
for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i);
_children[i]->writeSpanners(nextState);
}
} else {
for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i);
if (nextState.becameSubdivided()) {
_children[i]->writeSpannerSubdivision(nextState);
}
}
}
}
}
void MetavoxelNode::decrementReferenceCount(const AttributePointer& attribute) {
if (!_referenceCount.deref()) {
destroy(attribute);
@ -1621,8 +1564,9 @@ static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, int e
bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute
visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
visitation.visitor->getLOD().threshold;
float halfSize = visitation.info.size * 0.5f;
visitation.info.lodBase = (glm::distance(visitation.visitor->getLOD().position, visitation.info.minimum +
glm::vec3(halfSize, halfSize, halfSize)) - halfSize) * visitation.visitor->getLOD().threshold;
visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase *
visitation.visitor->getMinimumLODThresholdMultiplier());
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();
@ -1651,8 +1595,9 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute
visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
visitation.visitor->getLOD().threshold;
float halfSize = visitation.info.size * 0.5f;
visitation.info.lodBase = (glm::distance(visitation.visitor->getLOD().position, visitation.info.minimum +
glm::vec3(halfSize, halfSize, halfSize)) - halfSize) * visitation.visitor->getLOD().threshold;
visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase *
visitation.visitor->getMinimumLODThresholdMultiplier());
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();

View file

@ -239,9 +239,7 @@ public:
void writeSubdivided(MetavoxelStreamState& state, const MetavoxelStreamState& ancestorState, void* ancestorValue) const;
void writeSpanners(MetavoxelStreamState& state) const;
void writeSpannerDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const;
void writeSpannerSubdivision(MetavoxelStreamState& state) const;
/// Increments the node's reference count.
void incrementReferenceCount() { _referenceCount.ref(); }

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <glm/gtx/transform.hpp>
#include "MetavoxelMessages.h"
#include "Spanner.h"
@ -146,10 +148,13 @@ void SetDataEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects
data.set(minimum, this->data, blend);
}
PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position, float radius, float height) :
PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position, float radius,
float height, bool set, bool erase) :
position(position),
radius(radius),
height(height) {
height(height),
set(set),
erase(erase) {
}
void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
@ -161,7 +166,7 @@ void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObje
Box(position - extents, position + extents), results);
foreach (const SharedObjectPointer& spanner, results) {
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->paintHeight(position, radius, height);
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->paintHeight(position, radius, height, set, erase);
if (newSpanner != spanner) {
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner);
}
@ -173,473 +178,100 @@ MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& av
averageColor(averageColor) {
}
PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& averageColor) :
HeightfieldMaterialSpannerEdit::HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner,
const SharedObjectPointer& material, const QColor& averageColor, bool paint, bool voxelize) :
MaterialEdit(material, averageColor),
spanner(spanner),
paint(paint),
voxelize(voxelize) {
}
class SpannerProjectionFetchVisitor : public SpannerVisitor {
public:
SpannerProjectionFetchVisitor(const Box& bounds, QVector<SharedObjectPointer>& results);
virtual bool visit(Spanner* spanner);
private:
const Box& _bounds;
QVector<SharedObjectPointer>& _results;
float _closestDistance;
};
SpannerProjectionFetchVisitor::SpannerProjectionFetchVisitor(const Box& bounds, QVector<SharedObjectPointer>& results) :
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute()),
_bounds(bounds),
_results(results),
_closestDistance(FLT_MAX) {
}
bool SpannerProjectionFetchVisitor::visit(Spanner* spanner) {
Heightfield* heightfield = qobject_cast<Heightfield*>(spanner);
if (!heightfield) {
return true;
}
glm::mat4 transform = glm::scale(1.0f / glm::vec3(heightfield->getScale(),
heightfield->getScale() * heightfield->getAspectY(),
heightfield->getScale() * heightfield->getAspectZ())) *
glm::mat4_cast(glm::inverse(heightfield->getRotation())) * glm::translate(-heightfield->getTranslation());
Box transformedBounds = transform * _bounds;
if (transformedBounds.maximum.x < 0.0f || transformedBounds.maximum.z < 0.0f ||
transformedBounds.minimum.x > 1.0f || transformedBounds.minimum.z > 1.0f) {
return true;
}
float distance = qMin(glm::abs(transformedBounds.minimum.y), glm::abs(transformedBounds.maximum.y));
if (distance < _closestDistance) {
_results.clear();
_results.append(spanner);
_closestDistance = distance;
}
return true;
}
void HeightfieldMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// make sure the color meets our transparency requirements
QColor color = averageColor;
if (paint) {
color.setAlphaF(1.0f);
} else if (color.alphaF() < 0.5f) {
color = QColor(0, 0, 0, 0);
}
QVector<SharedObjectPointer> results;
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
static_cast<Spanner*>(spanner.data())->getBounds(), results);
// if there's nothing intersecting directly, find the closest heightfield that intersects the projection
if (results.isEmpty()) {
SpannerProjectionFetchVisitor visitor(static_cast<Spanner*>(spanner.data())->getBounds(), results);
data.guide(visitor);
}
foreach (const SharedObjectPointer& result, results) {
Spanner* newResult = static_cast<Spanner*>(result.data())->setMaterial(spanner, material, color, paint, voxelize);
if (newResult != result) {
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), result, newResult);
}
}
}
FillHeightfieldHeightEdit::FillHeightfieldHeightEdit(const glm::vec3& position, float radius) :
position(position),
radius(radius) {
}
void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
glm::vec3 extents(radius, radius, radius);
void FillHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
glm::vec3 extents = glm::vec3(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);
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->fillHeight(position, radius);
if (newSpanner != spanner) {
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner);
}
}
}
const int VOXEL_BLOCK_SIZE = 16;
const int VOXEL_BLOCK_SAMPLES = VOXEL_BLOCK_SIZE + 1;
const int VOXEL_BLOCK_AREA = VOXEL_BLOCK_SAMPLES * VOXEL_BLOCK_SAMPLES;
const int VOXEL_BLOCK_VOLUME = VOXEL_BLOCK_AREA * VOXEL_BLOCK_SAMPLES;
VoxelMaterialSpannerEdit::VoxelMaterialSpannerEdit(const SharedObjectPointer& spanner,
const SharedObjectPointer& material, const QColor& averageColor) :
MaterialEdit(material, averageColor),
spanner(spanner) {
}
class VoxelMaterialSpannerEditVisitor : public MetavoxelVisitor {
public:
VoxelMaterialSpannerEditVisitor(Spanner* spanner, const SharedObjectPointer& material, const QColor& color);
virtual int visit(MetavoxelInfo& info);
private:
Spanner* _spanner;
SharedObjectPointer _material;
QColor _color;
float _blockSize;
};
VoxelMaterialSpannerEditVisitor::VoxelMaterialSpannerEditVisitor(Spanner* spanner,
const SharedObjectPointer& material, const QColor& color) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
AttributeRegistry::getInstance()->getVoxelHermiteAttribute() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
AttributeRegistry::getInstance()->getVoxelHermiteAttribute() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute()),
_spanner(spanner),
_material(material),
_color(color),
_blockSize(spanner->getVoxelizationGranularity() * VOXEL_BLOCK_SIZE) {
}
int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) {
Box bounds = info.getBounds();
if (!bounds.intersects(_spanner->getBounds())) {
return STOP_RECURSION;
}
if (info.size > _blockSize) {
return DEFAULT_ORDER;
}
QVector<QRgb> oldColorContents;
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
if (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) {
oldColorContents = colorPointer->getContents();
} else {
oldColorContents = QVector<QRgb>(VOXEL_BLOCK_VOLUME);
}
QVector<QRgb> hermiteContents;
VoxelHermiteDataPointer hermitePointer = info.inputValues.at(1).getInlineValue<VoxelHermiteDataPointer>();
if (hermitePointer && hermitePointer->getSize() == VOXEL_BLOCK_SAMPLES) {
hermiteContents = hermitePointer->getContents();
} else {
hermiteContents = QVector<QRgb>(VOXEL_BLOCK_VOLUME * VoxelHermiteData::EDGE_COUNT);
}
QByteArray materialContents;
QVector<SharedObjectPointer> materials;
VoxelMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue<VoxelMaterialDataPointer>();
if (materialPointer && materialPointer->getSize() == VOXEL_BLOCK_SAMPLES) {
materialContents = materialPointer->getContents();
materials = materialPointer->getMaterials();
} else {
materialContents = QByteArray(VOXEL_BLOCK_VOLUME, 0);
}
float scale = VOXEL_BLOCK_SIZE / info.size;
QVector<QRgb> colorContents = oldColorContents;
Box overlap = info.getBounds().getIntersection(_spanner->getBounds());
overlap.minimum = (overlap.minimum - info.minimum) * scale;
overlap.maximum = (overlap.maximum - info.minimum) * scale;
int minX = glm::ceil(overlap.minimum.x);
int minY = glm::ceil(overlap.minimum.y);
int minZ = glm::ceil(overlap.minimum.z);
int sizeX = (int)overlap.maximum.x - minX + 1;
int sizeY = (int)overlap.maximum.y - minY + 1;
int sizeZ = (int)overlap.maximum.z - minZ + 1;
bool flipped = false;
float step = 1.0f / scale;
glm::vec3 position(0.0f, 0.0f, info.minimum.z + minZ * step);
if (_spanner->hasOwnColors()) {
for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) {
position.y = info.minimum.y + minY * step;
for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
destY += VOXEL_BLOCK_SAMPLES, position.y += step) {
position.x = info.minimum.x + minX * step;
for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) {
if (_spanner->contains(position)) {
*destX = _spanner->getColorAt(position);
}
}
}
}
} else {
QRgb rgb = _color.rgba();
flipped = (qAlpha(rgb) == 0);
for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) {
position.y = info.minimum.y + minY * step;
for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
destY += VOXEL_BLOCK_SAMPLES, position.y += step) {
position.x = info.minimum.x + minX * step;
for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) {
if (_spanner->contains(position)) {
*destX = rgb;
}
}
}
}
}
// if there are no visible colors, we can clear everything
bool foundOpaque = false;
for (const QRgb* src = colorContents.constData(), *end = src + colorContents.size(); src != end; src++) {
if (qAlpha(*src) != 0) {
foundOpaque = true;
break;
}
}
if (!foundOpaque) {
info.outputValues[0] = AttributeValue(_outputs.at(0));
info.outputValues[1] = AttributeValue(_outputs.at(1));
info.outputValues[2] = AttributeValue(_outputs.at(2));
return STOP_RECURSION;
}
VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
encodeInline<VoxelColorDataPointer>(newColorPointer));
int hermiteArea = VOXEL_BLOCK_AREA * VoxelHermiteData::EDGE_COUNT;
int hermiteSamples = VOXEL_BLOCK_SAMPLES * VoxelHermiteData::EDGE_COUNT;
int hermiteMinX = minX, hermiteMinY = minY, hermiteMinZ = minZ;
int hermiteSizeX = sizeX, hermiteSizeY = sizeY, hermiteSizeZ = sizeZ;
if (minX > 0) {
hermiteMinX--;
hermiteSizeX++;
}
if (minY > 0) {
hermiteMinY--;
hermiteSizeY++;
}
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) {
QRgb* hermiteDestY = hermiteDestZ;
for (int y = hermiteMinY, hermiteMaxY = y + hermiteSizeY - 1; y <= hermiteMaxY; y++, hermiteDestY += hermiteSamples) {
QRgb* hermiteDestX = hermiteDestY;
for (int x = hermiteMinX, hermiteMaxX = x + hermiteSizeX - 1; x <= hermiteMaxX; x++,
hermiteDestX += VoxelHermiteData::EDGE_COUNT) {
// at each intersected non-terminal edge, we check for a transition and, if one is detected, we assign the
// crossing and normal values based on intersection with the sphere
float distance;
glm::vec3 normal;
if (x != VOXEL_BLOCK_SIZE) {
int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
const QRgb* color = colorContents.constData() + offset;
int alpha0 = qAlpha(color[0]);
int alpha1 = qAlpha(color[1]);
if (alpha0 != alpha1) {
if (_spanner->intersects(info.minimum + glm::vec3(x, y, z) * step,
info.minimum + glm::vec3(x + 1, y, z) * step, distance, normal)) {
const QRgb* oldColor = oldColorContents.constData() + offset;
if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[1]) == alpha1) {
int alpha = distance * EIGHT_BIT_MAXIMUM;
if (normal.x < 0.0f ? alpha <= qAlpha(hermiteDestX[0]) : alpha >= qAlpha(hermiteDestX[0])) {
hermiteDestX[0] = packNormal(flipped ? -normal : normal, alpha);
}
} else {
hermiteDestX[0] = packNormal(flipped ? -normal : normal, distance * EIGHT_BIT_MAXIMUM);
}
}
} else {
hermiteDestX[0] = 0x0;
}
} else {
hermiteDestX[0] = 0x0;
}
if (y != VOXEL_BLOCK_SIZE) {
int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
const QRgb* color = colorContents.constData() + offset;
int alpha0 = qAlpha(color[0]);
int alpha2 = qAlpha(color[VOXEL_BLOCK_SAMPLES]);
if (alpha0 != alpha2) {
if (_spanner->intersects(info.minimum + glm::vec3(x, y, z) * step,
info.minimum + glm::vec3(x, y + 1, z) * step, distance, normal)) {
const QRgb* oldColor = oldColorContents.constData() + offset;
if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_SAMPLES]) == alpha2) {
int alpha = distance * EIGHT_BIT_MAXIMUM;
if (normal.y < 0.0f ? alpha <= qAlpha(hermiteDestX[1]) : alpha >= qAlpha(hermiteDestX[1])) {
hermiteDestX[1] = packNormal(flipped ? -normal : normal, alpha);
}
} else {
hermiteDestX[1] = packNormal(flipped ? -normal : normal, distance * EIGHT_BIT_MAXIMUM);
}
}
} else {
hermiteDestX[1] = 0x0;
}
} else {
hermiteDestX[1] = 0x0;
}
if (z != VOXEL_BLOCK_SIZE) {
int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
const QRgb* color = colorContents.constData() + offset;
int alpha0 = qAlpha(color[0]);
int alpha4 = qAlpha(color[VOXEL_BLOCK_AREA]);
if (alpha0 != alpha4) {
if (_spanner->intersects(info.minimum + glm::vec3(x, y, z) * step,
info.minimum + glm::vec3(x, y, z + 1) * step, distance, normal)) {
const QRgb* oldColor = oldColorContents.constData() + offset;
if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_AREA]) == alpha4) {
int alpha = distance * EIGHT_BIT_MAXIMUM;
if (normal.z < 0.0f ? alpha <= qAlpha(hermiteDestX[2]) : alpha >= qAlpha(hermiteDestX[2])) {
hermiteDestX[2] = packNormal(flipped ? -normal : normal, alpha);
}
} else {
hermiteDestX[2] = packNormal(flipped ? -normal : normal, distance * EIGHT_BIT_MAXIMUM);
}
}
} else {
hermiteDestX[2] = 0x0;
}
} else {
hermiteDestX[2] = 0x0;
}
}
}
}
VoxelHermiteDataPointer newHermitePointer(new VoxelHermiteData(hermiteContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[1] = AttributeValue(info.inputValues.at(1).getAttribute(),
encodeInline<VoxelHermiteDataPointer>(newHermitePointer));
if (_spanner->hasOwnMaterials()) {
QHash<int, int> materialMap;
position.z = info.minimum.z + minZ * step;
for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) {
position.y = info.minimum.y + minY * step;
for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
destY += VOXEL_BLOCK_SAMPLES, position.y += step) {
position.x = info.minimum.x + minX * step;
for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) {
if (_spanner->contains(position)) {
int material = _spanner->getMaterialAt(position);
if (material != 0) {
int& mapping = materialMap[material];
if (mapping == 0) {
mapping = getMaterialIndex(_spanner->getMaterials().at(material - 1), materials,
materialContents);
}
material = mapping;
}
*destX = material;
}
}
}
}
} else {
uchar materialIndex = getMaterialIndex(_material, materials, materialContents);
position.z = info.minimum.z + minZ * step;
for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) {
position.y = info.minimum.y + minY * step;
for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
destY += VOXEL_BLOCK_SAMPLES, position.y += step) {
position.x = info.minimum.x + minX * step;
for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) {
if (_spanner->contains(position)) {
*destX = materialIndex;
}
}
}
}
}
clearUnusedMaterials(materials, materialContents);
VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, VOXEL_BLOCK_SAMPLES, materials));
info.outputValues[2] = AttributeValue(_inputs.at(2), encodeInline<VoxelMaterialDataPointer>(newMaterialPointer));
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());
while (!data.getBounds().contains(spanner->getBounds())) {
data.expand();
}
// make sure it's either 100% transparent or 100% opaque
QColor color = averageColor;
color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f);
// 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 (heightfield) {
VoxelMaterialSpannerEditVisitor visitor(static_cast<Spanner*>(heightfield.data()), material, color);
data.guide(visitor);
}
VoxelMaterialSpannerEditVisitor visitor(spanner, material, color);
data.guide(visitor);
}
PaintVoxelMaterialEdit::PaintVoxelMaterialEdit(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& averageColor) :
MaterialEdit(material, averageColor),
position(position),
radius(radius) {
}
class PaintVoxelMaterialEditVisitor : public MetavoxelVisitor {
public:
PaintVoxelMaterialEditVisitor(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;
};
PaintVoxelMaterialEditVisitor::PaintVoxelMaterialEditVisitor(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& color) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute()),
_position(position),
_radius(radius),
_material(material),
_color(color) {
glm::vec3 extents(_radius, _radius, _radius);
_bounds = Box(_position - extents, _position + extents);
}
int PaintVoxelMaterialEditVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
VoxelMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue<VoxelMaterialDataPointer>();
if (!(colorPointer && materialPointer && colorPointer->getSize() == materialPointer->getSize())) {
return STOP_RECURSION;
}
QVector<QRgb> colorContents = colorPointer->getContents();
QByteArray materialContents = materialPointer->getContents();
QVector<SharedObjectPointer> materials = materialPointer->getMaterials();
Box overlap = info.getBounds().getIntersection(_bounds);
int size = colorPointer->getSize();
int area = size * size;
float scale = (size - 1.0f) / info.size;
overlap.minimum = (overlap.minimum - info.minimum) * scale;
overlap.maximum = (overlap.maximum - info.minimum) * scale;
int minX = glm::ceil(overlap.minimum.x);
int minY = glm::ceil(overlap.minimum.y);
int minZ = glm::ceil(overlap.minimum.z);
int sizeX = (int)overlap.maximum.x - minX + 1;
int sizeY = (int)overlap.maximum.y - minY + 1;
int sizeZ = (int)overlap.maximum.z - minZ + 1;
QRgb rgb = _color.rgba();
float step = 1.0f / scale;
glm::vec3 position(0.0f, 0.0f, info.minimum.z + minZ * step);
uchar materialIndex = getMaterialIndex(_material, materials, materialContents);
QRgb* colorData = colorContents.data();
uchar* materialData = (uchar*)materialContents.data();
for (int destZ = minZ * area + minY * size + minX, endZ = destZ + sizeZ * area; destZ != endZ;
destZ += area, position.z += step) {
position.y = info.minimum.y + minY * step;
for (int destY = destZ, endY = destY + sizeY * size; destY != endY; destY += size, position.y += step) {
position.x = info.minimum.x + minX * step;
for (int destX = destY, endX = destX + sizeX; destX != endX; destX++, position.x += step) {
QRgb& color = colorData[destX];
if (qAlpha(color) != 0 && glm::distance(position, _position) <= _radius) {
color = rgb;
materialData[destX] = materialIndex;
}
}
}
}
VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, size));
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
encodeInline<VoxelColorDataPointer>(newColorPointer));
clearUnusedMaterials(materials, materialContents);
VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, size, materials));
info.outputValues[1] = AttributeValue(_inputs.at(1), encodeInline<VoxelMaterialDataPointer>(newMaterialPointer));
return STOP_RECURSION;
}
void PaintVoxelMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// make sure it's 100% opaque
QColor color = averageColor;
color.setAlphaF(1.0f);
PaintVoxelMaterialEditVisitor visitor(position, radius, material, color);
data.guide(visitor);
}

View file

@ -203,8 +203,11 @@ public:
STREAM glm::vec3 position;
STREAM float radius;
STREAM float height;
STREAM bool set;
STREAM bool erase;
PaintHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, float height = 0.0f);
PaintHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
float height = 0.0f, bool set = false, bool erase = false);
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
@ -225,54 +228,39 @@ public:
DECLARE_STREAMABLE_METATYPE(MaterialEdit)
/// An edit that sets a region of a heightfield material.
class PaintHeightfieldMaterialEdit : STREAM public MaterialEdit {
STREAMABLE
public:
STREAM glm::vec3 position;
STREAM float radius;
PaintHeightfieldMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(PaintHeightfieldMaterialEdit)
/// An edit that sets the materials of voxels within a spanner to a value.
class VoxelMaterialSpannerEdit : STREAM public MaterialEdit {
/// An edit that sets the materials of a heightfield within a spanner to a value.
class HeightfieldMaterialSpannerEdit : STREAM public MaterialEdit {
STREAMABLE
public:
STREAM SharedObjectPointer spanner;
STREAM bool paint;
STREAM bool voxelize;
VoxelMaterialSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer(),
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer(),
const SharedObjectPointer& material = SharedObjectPointer(),
const QColor& averageColor = QColor(), bool paint = false, bool voxelize = false);
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(VoxelMaterialSpannerEdit)
DECLARE_STREAMABLE_METATYPE(HeightfieldMaterialSpannerEdit)
/// An edit that sets a region of a voxel material.
class PaintVoxelMaterialEdit : STREAM public MaterialEdit {
/// An edit that fills a region of a heightfield height.
class FillHeightfieldHeightEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM glm::vec3 position;
STREAM float radius;
PaintVoxelMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
FillHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f);
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(PaintVoxelMaterialEdit)
DECLARE_STREAMABLE_METATYPE(FillHeightfieldHeightEdit)
#endif // hifi_MetavoxelMessages_h

View file

@ -135,7 +135,7 @@ void SharedObject::writeExtra(Bitstream& out) const {
// nothing by default
}
void SharedObject::readExtra(Bitstream& in) {
void SharedObject::readExtra(Bitstream& in, bool reread) {
// nothing by default
}
@ -143,7 +143,7 @@ void SharedObject::writeExtraDelta(Bitstream& out, const SharedObject* reference
// nothing by default
}
void SharedObject::readExtraDelta(Bitstream& in, const SharedObject* reference) {
void SharedObject::readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread) {
// nothing by default
}

View file

@ -84,13 +84,15 @@ public:
virtual void writeExtra(Bitstream& out) const;
/// Reads the non-property contents of this object from the specified stream.
virtual void readExtra(Bitstream& in);
/// \param reread if true, reread the contents from the stream but don't reapply them
virtual void readExtra(Bitstream& in, bool reread = false);
/// Writes the delta-encoded non-property contents of this object to the specified stream.
virtual void writeExtraDelta(Bitstream& out, const SharedObject* reference) const;
/// Reads the delta-encoded non-property contents of this object from the specified stream.
virtual void readExtraDelta(Bitstream& in, const SharedObject* reference);
/// \param reread if true, reread the contents from the stream but don't reapply them
virtual void readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread = false);
/// Writes the subdivision of the contents of this object (preceeded by a
/// reference to the object itself) to the specified stream if necessary.

File diff suppressed because it is too large Load diff

View file

@ -18,11 +18,13 @@
#include "MetavoxelUtil.h"
class AbstractHeightfieldNodeRenderer;
class DataBlock;
class Heightfield;
class HeightfieldColor;
class HeightfieldHeight;
class HeightfieldMaterial;
class HeightfieldNode;
class HeightfieldStack;
class SpannerRenderer;
/// An object that spans multiple octree cells.
@ -71,19 +73,20 @@ public:
/// 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.
/// \param set whether to set the height as opposed to raising/lowering it
/// \param erase whether to erase height values
/// \return the modified spanner, or this if no modification was performed
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height);
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase);
/// Attempts to clear and fetch part of the spanner's height.
/// \param heightfield the heightfield to populate
/// Attempts to fill the spanner's height (adding removing volumetric information).
/// \return the modified spanner, or this if no modification was performed
virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield);
virtual Spanner* fillHeight(const glm::vec3& position, float radius);
/// Attempts to "sculpt" or "paint," etc., with the supplied spanner.
/// \return the modified spanner, or this if no modification was performed
virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material,
const QColor& color, bool paint, bool voxelize);
/// Checks whether this spanner has its own colors.
virtual bool hasOwnColors() const;
@ -292,6 +295,42 @@ private:
QUrl _url;
};
typedef QExplicitlySharedDataPointer<DataBlock> DataBlockPointer;
/// Base class for blocks of data.
class DataBlock : public QSharedData {
public:
static const int COLOR_BYTES = 3;
virtual ~DataBlock();
void setDeltaData(const DataBlockPointer& deltaData) { _deltaData = deltaData; }
const DataBlockPointer& getDeltaData() const { return _deltaData; }
void setEncodedDelta(const QByteArray& encodedDelta) { _encodedDelta = encodedDelta; }
const QByteArray& getEncodedDelta() const { return _encodedDelta; }
QMutex& getEncodedDeltaMutex() { return _encodedDeltaMutex; }
protected:
QByteArray _encoded;
QMutex _encodedMutex;
DataBlockPointer _deltaData;
QByteArray _encodedDelta;
QMutex _encodedDeltaMutex;
class EncodedSubdivision {
public:
DataBlockPointer ancestor;
QByteArray data;
};
QVector<EncodedSubdivision> _encodedSubdivisions;
QMutex _encodedSubdivisionsMutex;
};
/// Base class for heightfield data blocks.
class HeightfieldData : public DataBlock {
public:
@ -466,6 +505,126 @@ 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);
/// Contains the description of a material.
class MaterialObject : public SharedObject {
Q_OBJECT
Q_PROPERTY(QUrl diffuse MEMBER _diffuse)
Q_PROPERTY(float scaleS MEMBER _scaleS)
Q_PROPERTY(float scaleT MEMBER _scaleT)
public:
Q_INVOKABLE MaterialObject();
const QUrl& getDiffuse() const { return _diffuse; }
float getScaleS() const { return _scaleS; }
float getScaleT() const { return _scaleT; }
private:
QUrl _diffuse;
float _scaleS;
float _scaleT;
};
/// Finds a material index for the supplied material in the provided list, adding an entry if necessary. Returns -1
/// on failure (no room to add new material).
int getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials);
typedef QExplicitlySharedDataPointer<HeightfieldStack> HeightfieldStackPointer;
/// A single column within a stack block.
class StackArray : public QByteArray {
public:
#pragma pack(push, 1)
/// A single entry within the array.
class Entry {
public:
quint32 color;
uchar material;
quint32 hermiteX;
quint32 hermiteY;
quint32 hermiteZ;
Entry();
bool isSet() const { return qAlpha(color) != 0; }
bool isZero() const;
bool isMergeable(const Entry& other) const;
void setHermiteX(const glm::vec3& normal, float position);
float getHermiteX(glm::vec3& normal) const;
void setHermiteY(const glm::vec3& normal, float position);
float getHermiteY(glm::vec3& normal) const;
void setHermiteZ(const glm::vec3& normal, float position);
float getHermiteZ(glm::vec3& normal) const;
};
#pragma pack(pop)
static int getSize(int entries) { return (entries == 0) ? 0 : sizeof(quint16) + sizeof(Entry) * entries; }
StackArray() : QByteArray() { }
StackArray(int entries) : QByteArray(getSize(entries), 0) { }
StackArray(const QByteArray& other) : QByteArray(other) { }
StackArray(const char* src, int bytes) : QByteArray(src, bytes) { }
int getPosition() const { return *(const quint16*)constData(); }
void setPosition(int position) { *(quint16*)data() = position; }
quint16& getPositionRef() { return *(quint16*)data(); }
int getEntryCount() const { return isEmpty() ? 0 : (size() - sizeof(quint16)) / sizeof(Entry); }
Entry* getEntryData() { return (Entry*)(data() + sizeof(quint16)); }
const Entry* getEntryData() const { return (const Entry*)(constData() + sizeof(quint16)); }
int getEntryAlpha(int y, float heightfieldHeight = 0.0f) const;
Entry& getEntry(int y, float heightfieldHeight = 0.0f);
const Entry& getEntry(int y, float heightfieldHeight = 0.0f) const;
void getExtents(int& minimumY, int& maximumY) const;
bool hasSetEntries() const;
void removeEntries(int position, int count) { remove(sizeof(quint16) + position * sizeof(Entry), count * sizeof(Entry)); }
};
/// A block of stack data associated with a heightfield.
class HeightfieldStack : public HeightfieldData {
public:
HeightfieldStack(int width, const QVector<StackArray>& contents, const QVector<SharedObjectPointer>& materials);
HeightfieldStack(Bitstream& in, int bytes);
HeightfieldStack(Bitstream& in, int bytes, const HeightfieldStackPointer& reference);
QVector<StackArray>& getContents() { return _contents; }
QVector<SharedObjectPointer>& getMaterials() { return _materials; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldStackPointer& reference);
private:
void read(Bitstream& in, int bytes);
QVector<StackArray> _contents;
QVector<SharedObjectPointer> _materials;
};
Q_DECLARE_METATYPE(HeightfieldStackPointer)
Bitstream& operator<<(Bitstream& out, const HeightfieldStackPointer& value);
Bitstream& operator>>(Bitstream& in, HeightfieldStackPointer& value);
template<> void Bitstream::writeRawDelta(const HeightfieldStackPointer& value, const HeightfieldStackPointer& reference);
template<> void Bitstream::readRawDelta(HeightfieldStackPointer& value, const HeightfieldStackPointer& reference);
typedef QExplicitlySharedDataPointer<HeightfieldNode> HeightfieldNodePointer;
/// Holds the base state used in streaming heightfield data.
@ -499,14 +658,15 @@ public:
HeightfieldNode(const HeightfieldHeightPointer& height = HeightfieldHeightPointer(),
const HeightfieldColorPointer& color = HeightfieldColorPointer(),
const HeightfieldMaterialPointer& material = HeightfieldMaterialPointer());
const HeightfieldMaterialPointer& material = HeightfieldMaterialPointer(),
const HeightfieldStackPointer& stack = HeightfieldStackPointer());
HeightfieldNode(const HeightfieldNode& other);
~HeightfieldNode();
void setContents(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color,
const HeightfieldMaterialPointer& material);
const HeightfieldMaterialPointer& material, const HeightfieldStackPointer& stack);
void setHeight(const HeightfieldHeightPointer& height) { _height = height; }
const HeightfieldHeightPointer& getHeight() const { return _height; }
@ -517,6 +677,9 @@ public:
void setMaterial(const HeightfieldMaterialPointer& material) { _material = material; }
const HeightfieldMaterialPointer& getMaterial() const { return _material; }
void setStack(const HeightfieldStackPointer& stack) { _stack = stack; }
const HeightfieldStackPointer& getStack() const { return _stack; }
void setRenderer(AbstractHeightfieldNodeRenderer* renderer) { _renderer = renderer; }
AbstractHeightfieldNodeRenderer* getRenderer() const { return _renderer; }
@ -525,21 +688,25 @@ public:
void setChild(int index, const HeightfieldNodePointer& child) { _children[index] = child; }
const HeightfieldNodePointer& getChild(int index) const { return _children[index]; }
float getHeight(const glm::vec3& location) const;
bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
void getRangeAfterHeightPaint(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const glm::vec3& position, float radius, float height, float& minimum, float& maximum) const;
HeightfieldNode* paintMaterial(const glm::vec3& position, const glm::vec3& radius, const SharedObjectPointer& material,
const QColor& color);
void getRangeAfterHeightPaint(const glm::vec3& position, const glm::vec3& radius,
float height, int& minimum, int& maximum) const;
HeightfieldNode* paintHeight(const glm::vec3& position, const glm::vec3& radius, float height,
HeightfieldNode* paintHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const glm::vec3& position, float radius, float height, bool set, bool erase,
float normalizeScale, float normalizeOffset);
HeightfieldNode* fillHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const glm::vec3& position, float radius);
void getRangeAfterEdit(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const Box& editBounds, float& minimum, float& maximum) const;
HeightfieldNode* setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
Spanner* spanner, const SharedObjectPointer& material, const QColor& color, bool paint, bool voxelize,
float normalizeScale, float normalizeOffset);
HeightfieldNode* clearAndFetchHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const Box& bounds, SharedObjectPointer& heightfield);
void read(HeightfieldStreamState& state);
void write(HeightfieldStreamState& state) const;
@ -563,9 +730,16 @@ private:
QRgb getColorAt(const glm::vec3& location) const;
int getMaterialAt(const glm::vec3& location) const;
void maybeRenormalize(const glm::vec3& scale, float normalizeScale, float normalizeOffset, int innerStackWidth,
QVector<quint16>& heightContents, QVector<StackArray>& stackContents);
bool findHeightfieldRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
float boundsDistance, float& distance) const;
HeightfieldHeightPointer _height;
HeightfieldColorPointer _color;
HeightfieldMaterialPointer _material;
HeightfieldStackPointer _stack;
HeightfieldNodePointer _children[CHILD_COUNT];
@ -577,6 +751,9 @@ class AbstractHeightfieldNodeRenderer {
public:
virtual ~AbstractHeightfieldNodeRenderer();
virtual bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const;
};
/// A heightfield represented as a spanner.
@ -588,8 +765,9 @@ class Heightfield : public Transformable {
Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged STORED false)
Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged STORED false
DESIGNABLE false)
Q_PROPERTY(HeightfieldNodePointer root MEMBER _root WRITE setRoot NOTIFY rootChanged STORED false DESIGNABLE false)
Q_PROPERTY(HeightfieldStackPointer stack MEMBER _stack WRITE setStack NOTIFY stackChanged STORED false
DESIGNABLE false)
public:
Q_INVOKABLE Heightfield();
@ -609,7 +787,10 @@ public:
void setMaterial(const HeightfieldMaterialPointer& material);
const HeightfieldMaterialPointer& getMaterial() const { return _material; }
void setRoot(const HeightfieldNodePointer& root);
void setStack(const HeightfieldStackPointer& stack);
const HeightfieldStackPointer& getStack() const { return _stack; }
void setRoot(const HeightfieldNodePointer& root) { _root = root; }
const HeightfieldNodePointer& getRoot() const { return _root; }
MetavoxelLOD transformLOD(const MetavoxelLOD& lod) const;
@ -622,13 +803,13 @@ public:
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* paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase);
virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield);
virtual Spanner* fillHeight(const glm::vec3& position, float radius);
virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material,
const QColor& color, bool paint, bool voxelize);
virtual bool hasOwnColors() const;
virtual bool hasOwnMaterials() const;
virtual QRgb getColorAt(const glm::vec3& point);
@ -639,9 +820,9 @@ public:
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
virtual void writeExtra(Bitstream& out) const;
virtual void readExtra(Bitstream& in);
virtual void readExtra(Bitstream& in, bool reread);
virtual void writeExtraDelta(Bitstream& out, const SharedObject* reference) const;
virtual void readExtraDelta(Bitstream& in, const SharedObject* reference);
virtual void readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread);
virtual void maybeWriteSubdivision(Bitstream& out);
virtual SharedObject* readSubdivision(Bitstream& in);
@ -652,7 +833,7 @@ signals:
void heightChanged(const HeightfieldHeightPointer& height);
void colorChanged(const HeightfieldColorPointer& color);
void materialChanged(const HeightfieldMaterialPointer& material);
void rootChanged(const HeightfieldNodePointer& root);
void stackChanged(const HeightfieldStackPointer& stack);
protected:
@ -665,12 +846,15 @@ private slots:
private:
Heightfield* prepareEdit(float minimumValue, float maximumValue, float& normalizeScale, float& normalizeOffset);
float _aspectY;
float _aspectZ;
HeightfieldHeightPointer _height;
HeightfieldColorPointer _color;
HeightfieldMaterialPointer _material;
HeightfieldStackPointer _stack;
HeightfieldNodePointer _root;
};

View file

@ -389,6 +389,8 @@ void AddressManager::setDomainInfo(const QString& hostname, quint16 port) {
_rootPlaceName = hostname;
_rootPlaceID = QUuid();
qDebug() << "Possible domain change required to connect to domain at" << hostname << "on" << port;
emit possibleDomainChangeRequired(hostname, port);
}

View file

@ -78,7 +78,7 @@ PacketVersion versionForPacketType(PacketType type) {
case PacketTypeAudioStreamStats:
return 1;
case PacketTypeMetavoxelData:
return 10;
return 12;
default:
return 0;
}

View file

@ -66,7 +66,7 @@ void PhysicsEngine::addEntityInternal(EntityItem* entity) {
_entityMotionStates.insert(motionState);
} else {
// We failed to add the entity to the simulation. Probably because we couldn't create a shape for it.
qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine";
//qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine";
delete motionState;
}
}

View file

@ -262,7 +262,7 @@ bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direc
return false; // origin below plane
}
float divisor = glm::dot(normal, direction);
if (divisor > -EPSILON) {
if (divisor >= 0.0f) {
return false;
}
float t = dividend / divisor;
@ -490,4 +490,4 @@ void PolygonClip::copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& len
vertexArrayA[i] = vertexArrayB[i];
}
}
}
}