Sphere tools for voxel editing.

This commit is contained in:
Andrzej Kapolka 2014-09-02 17:45:37 -07:00
parent 926f0d099c
commit 33faffd9d4
6 changed files with 490 additions and 0 deletions

View file

@ -126,6 +126,8 @@ MetavoxelEditor::MetavoxelEditor() :
addTool(new EraseHeightfieldTool(this));
addTool(new VoxelColorBoxTool(this));
addTool(new VoxelMaterialBoxTool(this));
addTool(new VoxelColorSphereTool(this));
addTool(new VoxelMaterialSphereTool(this));
updateAttributes();
@ -1282,3 +1284,110 @@ void VoxelMaterialBoxTool::updateTexture() {
MaterialObject* material = static_cast<MaterialObject*>(_materialEditor->getObject().data());
_texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE);
}
SphereTool::SphereTool(MetavoxelEditor* editor, const QString& name) :
MetavoxelTool(editor, name, false, true) {
QWidget* widget = new QWidget();
widget->setLayout(_form = new QFormLayout());
layout()->addWidget(widget);
_form->addRow("Radius:", _radius = new QDoubleSpinBox());
_radius->setSingleStep(0.01);
_radius->setMaximum(FLT_MAX);
_radius->setValue(1.0);
}
void SphereTool::render() {
if (Application::getInstance()->isMouseHidden()) {
return;
}
glm::quat rotation = _editor->getGridRotation();
glm::quat inverseRotation = glm::inverse(rotation);
glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin();
glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection();
float position = _editor->getGridPosition();
if (glm::abs(rayDirection.z) < EPSILON) {
return;
}
float distance = (position - rayOrigin.z) / rayDirection.z;
_position = Application::getInstance()->getMouseRayOrigin() +
Application::getInstance()->getMouseRayDirection() * distance;
glPushMatrix();
glTranslatef(_position.x, _position.y, _position.z);
const float CURSOR_ALPHA = 0.5f;
QColor color = getColor();
glColor4f(color.redF(), color.greenF(), color.blueF(), CURSOR_ALPHA);
glEnable(GL_CULL_FACE);
glutSolidSphere(_radius->value(), 10, 10);
glDisable(GL_CULL_FACE);
glPopMatrix();
}
bool SphereTool::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) {
applyValue(_position, _radius->value());
return true;
}
return false;
}
VoxelColorSphereTool::VoxelColorSphereTool(MetavoxelEditor* editor) :
SphereTool(editor, "Set Voxel Color (Sphere)") {
_form->addRow("Color:", _color = new QColorEditor(this));
}
bool VoxelColorSphereTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
}
QColor VoxelColorSphereTool::getColor() {
return _color->getColor();
}
void VoxelColorSphereTool::applyValue(const glm::vec3& position, float radius) {
MetavoxelEditMessage message = { QVariant::fromValue(VoxelColorSphereEdit(position, radius,
_editor->getGridSpacing(), _color->getColor())) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
VoxelMaterialSphereTool::VoxelMaterialSphereTool(MetavoxelEditor* editor) :
SphereTool(editor, "Set Voxel Material (Sphere)") {
_form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false));
connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &VoxelMaterialSphereTool::updateTexture);
}
bool VoxelMaterialSphereTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
}
QColor VoxelMaterialSphereTool::getColor() {
return _texture ? _texture->getAverageColor() : QColor();
}
void VoxelMaterialSphereTool::applyValue(const glm::vec3& position, float radius) {
SharedObjectPointer material = _materialEditor->getObject();
_materialEditor->detachObject();
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSphereEdit(position, radius,
_editor->getGridSpacing(), material, _texture ? _texture->getAverageColor() : QColor())) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
void VoxelMaterialSphereTool::updateTexture() {
MaterialObject* material = static_cast<MaterialObject*>(_materialEditor->getObject().data());
_texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE);
}

View file

@ -455,4 +455,75 @@ private:
QSharedPointer<NetworkTexture> _texture;
};
/// Base class for tools based on a sphere brush.
class SphereTool : public MetavoxelTool {
Q_OBJECT
public:
SphereTool(MetavoxelEditor* editor, const QString& name);
virtual void render();
virtual bool eventFilter(QObject* watched, QEvent* event);
protected:
virtual QColor getColor() = 0;
virtual void applyValue(const glm::vec3& position, float radius) = 0;
QFormLayout* _form;
QDoubleSpinBox* _radius;
glm::vec3 _position;
};
/// Allows setting voxel colors by moving a sphere around.
class VoxelColorSphereTool : public SphereTool {
Q_OBJECT
public:
VoxelColorSphereTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
protected:
virtual QColor getColor();
virtual void applyValue(const glm::vec3& position, float radius);
private:
QColorEditor* _color;
};
/// Allows setting voxel materials by moving a sphere around.
class VoxelMaterialSphereTool : public SphereTool {
Q_OBJECT
public:
VoxelMaterialSphereTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
protected:
virtual QColor getColor();
virtual void applyValue(const glm::vec3& position, float radius);
private slots:
void updateTexture();
private:
SharedObjectEditor* _materialEditor;
QSharedPointer<NetworkTexture> _texture;
};
#endif // hifi_MetavoxelEditor_h

View file

@ -406,6 +406,10 @@ 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);

View file

@ -416,6 +416,9 @@ public:
/// 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);

View file

@ -814,3 +814,267 @@ void VoxelMaterialBoxEdit::apply(MetavoxelData& data, const WeakSharedObjectHash
VoxelMaterialBoxEditVisitor visitor(region, granularity, material, averageColor);
data.guide(visitor);
}
VoxelColorSphereEdit::VoxelColorSphereEdit(const glm::vec3& center, float radius, float granularity, const QColor& color) :
center(center),
radius(radius),
granularity(granularity),
color(color) {
}
class VoxelMaterialSphereEditVisitor : public MetavoxelVisitor {
public:
VoxelMaterialSphereEditVisitor(const glm::vec3& center, float radius, const Box& bounds, float granularity,
const SharedObjectPointer& material, const QColor& color);
virtual int visit(MetavoxelInfo& info);
private:
glm::vec3 _center;
float _radius;
Box _bounds;
float _granularity;
SharedObjectPointer _material;
QColor _color;
float _blockSize;
};
VoxelMaterialSphereEditVisitor::VoxelMaterialSphereEditVisitor(const glm::vec3& center, float radius, const Box& bounds,
float granularity, 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()),
_center(center),
_radius(radius),
_bounds(bounds),
_granularity(granularity),
_material(material),
_color(color),
_blockSize(granularity * VOXEL_BLOCK_SIZE) {
}
int VoxelMaterialSphereEditVisitor::visit(MetavoxelInfo& info) {
Box bounds = info.getBounds();
if (!bounds.intersects(_bounds)) {
return STOP_RECURSION;
}
if (info.size > _blockSize) {
return DEFAULT_ORDER;
}
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
QVector<QRgb> colorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
colorPointer->getContents() : QVector<QRgb>(VOXEL_BLOCK_VOLUME);
Box overlap = info.getBounds().getIntersection(_bounds);
float scale = VOXEL_BLOCK_SIZE / 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;
glm::vec3 relativeCenter = (_center - info.minimum) * scale;
float relativeRadius = _radius * scale;
float relativeRadiusSquared = relativeRadius * relativeRadius;
QRgb rgb = _color.rgba();
glm::vec3 position(0.0f, 0.0f, minZ);
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++) {
position.y = minY;
for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
destY += VOXEL_BLOCK_SAMPLES, position.y++) {
position.x = minX;
for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x++) {
if (glm::distance(relativeCenter, position) <= relativeRadius) {
*destX = rgb;
}
}
}
}
VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
encodeInline<VoxelColorDataPointer>(newColorPointer));
VoxelHermiteDataPointer hermitePointer = info.inputValues.at(1).getInlineValue<VoxelHermiteDataPointer>();
QVector<QRgb> hermiteContents = (hermitePointer && hermitePointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
hermitePointer->getContents() : QVector<QRgb>(VOXEL_BLOCK_VOLUME * VoxelHermiteData::EDGE_COUNT);
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++;
}
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) {
hermiteDestX[0] = 0x0;
glm::vec3 offset(x - relativeCenter.x, y - relativeCenter.y, z - relativeCenter.z);
if (x != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[1])) {
float radicand = relativeRadiusSquared - offset.y * offset.y - offset.z * offset.z;
float parameter = 0.5f;
if (radicand >= 0.0f) {
float root = glm::sqrt(radicand);
parameter = -offset.x - root;
if (parameter < 0.0f || parameter > 1.0f) {
parameter = glm::clamp(-offset.x + root, 0.0f, 1.0f);
}
}
glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f);
float length = glm::length(normal);
if (length > EPSILON) {
normal /= length;
} else {
normal = glm::vec3(0.0f, 1.0f, 0.0f);
}
hermiteDestX[0] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM);
}
}
hermiteDestX[1] = 0x0;
if (y != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[VOXEL_BLOCK_SAMPLES])) {
float radicand = relativeRadiusSquared - offset.x * offset.x - offset.z * offset.z;
float parameter = 0.5f;
if (radicand >= 0.0f) {
float root = glm::sqrt(radicand);
parameter = -offset.y - root;
if (parameter < 0.0f || parameter > 1.0f) {
parameter = glm::clamp(-offset.y + root, 0.0f, 1.0f);
}
}
glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f);
float length = glm::length(normal);
if (length > EPSILON) {
normal /= length;
} else {
normal = glm::vec3(1.0f, 0.0f, 0.0f);
}
hermiteDestX[1] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM);
}
}
hermiteDestX[2] = 0x0;
if (z != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[VOXEL_BLOCK_AREA])) {
float radicand = relativeRadiusSquared - offset.x * offset.x - offset.y * offset.y;
float parameter = 0.5f;
if (radicand >= 0.0f) {
float root = glm::sqrt(radicand);
parameter = -offset.z - root;
if (parameter < 0.0f || parameter > 1.0f) {
parameter = glm::clamp(-offset.z + root, 0.0f, 1.0f);
}
}
glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f);
float length = glm::length(normal);
if (length > EPSILON) {
normal /= length;
} else {
normal = glm::vec3(1.0f, 0.0f, 0.0f);
}
hermiteDestX[2] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM);
}
}
}
}
}
VoxelHermiteDataPointer newHermitePointer(new VoxelHermiteData(hermiteContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[1] = AttributeValue(info.inputValues.at(1).getAttribute(),
encodeInline<VoxelHermiteDataPointer>(newHermitePointer));
VoxelMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue<VoxelMaterialDataPointer>();
QByteArray materialContents;
QVector<SharedObjectPointer> materials;
if (materialPointer && materialPointer->getSize() == VOXEL_BLOCK_SAMPLES) {
materialContents = materialPointer->getContents();
materials = materialPointer->getMaterials();
} else {
materialContents = QByteArray(VOXEL_BLOCK_VOLUME, 0);
}
uchar materialIndex = getMaterialIndex(_material, materials, materialContents);
position.z = minZ;
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++) {
position.y = minY;
for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
destY += VOXEL_BLOCK_SAMPLES, position.y++) {
position.x = minX;
for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x++) {
if (glm::distance(relativeCenter, position) <= relativeRadius) {
*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 VoxelColorSphereEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
glm::vec3 extents(radius, radius, radius);
Box bounds(center - extents, center + extents);
while (!data.getBounds().contains(bounds)) {
data.expand();
}
VoxelMaterialSphereEditVisitor visitor(center, radius, bounds, granularity, SharedObjectPointer(), color);
data.guide(visitor);
}
VoxelMaterialSphereEdit::VoxelMaterialSphereEdit(const glm::vec3& center, float radius, float granularity,
const SharedObjectPointer& material, const QColor& averageColor) :
center(center),
radius(radius),
granularity(granularity),
material(material),
averageColor(averageColor) {
}
void VoxelMaterialSphereEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
glm::vec3 extents(radius, radius, radius);
Box bounds(center - extents, center + extents);
while (!data.getBounds().contains(bounds)) {
data.expand();
}
VoxelMaterialSphereEditVisitor visitor(center, radius, bounds, granularity, material, averageColor);
data.guide(visitor);
}

View file

@ -296,4 +296,43 @@ public:
DECLARE_STREAMABLE_METATYPE(VoxelMaterialBoxEdit)
/// An edit that sets the color of voxels within a sphere to a value.
class VoxelColorSphereEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM glm::vec3 center;
STREAM float radius;
STREAM float granularity;
STREAM QColor color;
VoxelColorSphereEdit(const glm::vec3& center = glm::vec3(), float radius = 0.0f,
float granularity = 0.0f, const QColor& color = QColor());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(VoxelColorSphereEdit)
/// An edit that sets the materials of voxels within a sphere to a value.
class VoxelMaterialSphereEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM glm::vec3 center;
STREAM float radius;
STREAM float granularity;
STREAM SharedObjectPointer material;
STREAM QColor averageColor;
VoxelMaterialSphereEdit(const glm::vec3& center = glm::vec3(), float radius = 0.0f, float granularity = 0.0f,
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(VoxelMaterialSphereEdit)
#endif // hifi_MetavoxelMessages_h