mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 17:14:59 +02:00
Merge pull request #3621 from ey6es/metavoxels
Sculpt brush, very rough conversion of heightfield patches to dual contour surfaces on edit.
This commit is contained in:
commit
2e958599aa
9 changed files with 784 additions and 283 deletions
|
@ -1,7 +1,7 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// metavoxel_voxel_cursor.frag
|
||||
// metavoxel_cursor.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 10/10/14.
|
|
@ -1,32 +0,0 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// metavoxel_heightfield_cursor.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 8/7/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// the inner radius of the outline, squared
|
||||
const float SQUARED_OUTLINE_INNER_RADIUS = 0.81;
|
||||
|
||||
// the outer radius of the outline, squared
|
||||
const float SQUARED_OUTLINE_OUTER_RADIUS = 1.0;
|
||||
|
||||
// the inner radius of the inset, squared
|
||||
const float SQUARED_INSET_INNER_RADIUS = 0.855625;
|
||||
|
||||
// the outer radius of the inset, squared
|
||||
const float SQUARED_INSET_OUTER_RADIUS = 0.950625;
|
||||
|
||||
void main(void) {
|
||||
// use the distance to compute the ring color, then multiply it by the varying color
|
||||
float squaredDistance = dot(gl_TexCoord[0].st, gl_TexCoord[0].st);
|
||||
float alpha = step(SQUARED_OUTLINE_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_OUTLINE_OUTER_RADIUS);
|
||||
float white = step(SQUARED_INSET_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_INSET_OUTER_RADIUS);
|
||||
gl_FragColor = gl_Color * vec4(white, white, white, alpha);
|
||||
}
|
|
@ -21,7 +21,8 @@ void main(void) {
|
|||
gl_Position = gl_ProjectionMatrix * viewPosition;
|
||||
|
||||
// generate the texture coordinates from the view position
|
||||
gl_TexCoord[0] = vec4(dot(viewPosition, gl_EyePlaneS[4]), dot(viewPosition, gl_EyePlaneT[4]), 0.0, 1.0);
|
||||
gl_TexCoord[0] = vec4(dot(viewPosition, gl_EyePlaneS[4]), dot(viewPosition, gl_EyePlaneT[4]),
|
||||
dot(viewPosition, gl_EyePlaneR[4]), 1.0);
|
||||
|
||||
// the zero height should be invisible
|
||||
gl_FrontColor = vec4(1.0, 1.0, 1.0, 1.0 - step(height, 0.0));
|
||||
|
|
|
@ -533,8 +533,10 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r
|
|||
float scale = 1.0f / radius;
|
||||
glm::vec4 sCoefficients(scale, 0.0f, 0.0f, -scale * position.x);
|
||||
glm::vec4 tCoefficients(0.0f, 0.0f, scale, -scale * position.z);
|
||||
glm::vec4 rCoefficients(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&sCoefficients);
|
||||
glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&tCoefficients);
|
||||
glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&rCoefficients);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glm::vec3 extents(radius, radius, radius);
|
||||
|
@ -575,12 +577,23 @@ void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius)
|
|||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glm::vec3 extents(radius, radius, radius);
|
||||
CursorRenderVisitor visitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(),
|
||||
Box(position - extents, position + extents));
|
||||
guideToAugmented(visitor);
|
||||
Box bounds(position - extents, position + extents);
|
||||
CursorRenderVisitor voxelVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), bounds);
|
||||
guideToAugmented(voxelVisitor);
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().release();
|
||||
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().bind();
|
||||
|
||||
CursorRenderVisitor heightfieldVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(),
|
||||
bounds);
|
||||
guideToAugmented(heightfieldVisitor);
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release();
|
||||
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
|
@ -1450,7 +1463,7 @@ void DefaultMetavoxelRendererImplementation::init() {
|
|||
_heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
"shaders/metavoxel_heightfield_cursor.vert");
|
||||
_heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
"shaders/metavoxel_heightfield_cursor.frag");
|
||||
"shaders/metavoxel_cursor.frag");
|
||||
_heightfieldCursorProgram.link();
|
||||
|
||||
_heightfieldCursorProgram.bind();
|
||||
|
@ -1468,7 +1481,7 @@ void DefaultMetavoxelRendererImplementation::init() {
|
|||
_voxelCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
"shaders/metavoxel_voxel_cursor.vert");
|
||||
_voxelCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
"shaders/metavoxel_voxel_cursor.frag");
|
||||
"shaders/metavoxel_cursor.frag");
|
||||
_voxelCursorProgram.link();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -130,6 +130,7 @@ MetavoxelEditor::MetavoxelEditor() :
|
|||
addTool(new VoxelMaterialBoxTool(this));
|
||||
addTool(new VoxelMaterialSpannerTool(this));
|
||||
addTool(new VoxelMaterialBrushTool(this));
|
||||
addTool(new VoxelSculptBrushTool(this));
|
||||
|
||||
updateAttributes();
|
||||
|
||||
|
@ -1048,7 +1049,7 @@ void ImportHeightfieldTool::apply() {
|
|||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue(
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer))));
|
||||
|
||||
int size = glm::sqrt(float(height.size())) + HeightfieldBuffer::SHARED_EDGE;
|
||||
int size = glm::sqrt(float(height.size()));
|
||||
QByteArray material(size * size, 0);
|
||||
HeightfieldMaterialDataPointer materialPointer(new HeightfieldMaterialData(material));
|
||||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), new MetavoxelNode(AttributeValue(
|
||||
|
@ -1294,35 +1295,44 @@ QVariant HeightfieldHeightBrushTool::createEdit(bool alternate) {
|
|||
alternate ? -_height->value() : _height->value()));
|
||||
}
|
||||
|
||||
HeightfieldMaterialBrushTool::HeightfieldMaterialBrushTool(MetavoxelEditor* editor) :
|
||||
HeightfieldBrushTool(editor, "Material Brush") {
|
||||
MaterialControl::MaterialControl(QWidget* widget, QFormLayout* form, bool clearable) :
|
||||
QObject(widget) {
|
||||
|
||||
_form->addRow("Color:", _color = new QColorEditor(this));
|
||||
connect(_color, &QColorEditor::colorChanged, this, &HeightfieldMaterialBrushTool::clearTexture);
|
||||
_form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false));
|
||||
connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &HeightfieldMaterialBrushTool::updateTexture);
|
||||
QHBoxLayout* colorLayout = new QHBoxLayout();
|
||||
form->addRow(colorLayout);
|
||||
colorLayout->addWidget(new QLabel("Color:"));
|
||||
colorLayout->addWidget(_color = new QColorEditor(widget), 1);
|
||||
connect(_color, &QColorEditor::colorChanged, this, &MaterialControl::clearTexture);
|
||||
if (clearable) {
|
||||
QPushButton* eraseButton = new QPushButton("Erase");
|
||||
colorLayout->addWidget(eraseButton);
|
||||
connect(eraseButton, &QPushButton::clicked, this, &MaterialControl::clearColor);
|
||||
}
|
||||
|
||||
form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false));
|
||||
connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &MaterialControl::updateTexture);
|
||||
}
|
||||
|
||||
QVariant HeightfieldMaterialBrushTool::createEdit(bool alternate) {
|
||||
if (alternate) {
|
||||
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
|
||||
SharedObjectPointer MaterialControl::getMaterial() {
|
||||
SharedObjectPointer material = _materialEditor->getObject();
|
||||
if (static_cast<MaterialObject*>(material.data())->getDiffuse().isValid()) {
|
||||
_materialEditor->detachObject();
|
||||
} else {
|
||||
SharedObjectPointer material = _materialEditor->getObject();
|
||||
if (static_cast<MaterialObject*>(material.data())->getDiffuse().isValid()) {
|
||||
_materialEditor->detachObject();
|
||||
} else {
|
||||
material = SharedObjectPointer();
|
||||
}
|
||||
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), material,
|
||||
_color->getColor()));
|
||||
}
|
||||
material = SharedObjectPointer();
|
||||
}
|
||||
return material;
|
||||
}
|
||||
|
||||
void HeightfieldMaterialBrushTool::clearTexture() {
|
||||
void MaterialControl::clearColor() {
|
||||
_color->setColor(QColor(0, 0, 0, 0));
|
||||
clearTexture();
|
||||
}
|
||||
|
||||
void MaterialControl::clearTexture() {
|
||||
_materialEditor->setObject(new MaterialObject());
|
||||
}
|
||||
|
||||
void HeightfieldMaterialBrushTool::updateTexture() {
|
||||
void MaterialControl::updateTexture() {
|
||||
if (_texture) {
|
||||
_texture->disconnect(this);
|
||||
}
|
||||
|
@ -1336,15 +1346,29 @@ void HeightfieldMaterialBrushTool::updateTexture() {
|
|||
if (_texture->isLoaded()) {
|
||||
textureLoaded();
|
||||
} else {
|
||||
connect(_texture.data(), &Resource::loaded, this, &HeightfieldMaterialBrushTool::textureLoaded);
|
||||
connect(_texture.data(), &Resource::loaded, this, &MaterialControl::textureLoaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HeightfieldMaterialBrushTool::textureLoaded() {
|
||||
void MaterialControl::textureLoaded() {
|
||||
_color->setColor(_texture->getAverageColor());
|
||||
}
|
||||
|
||||
HeightfieldMaterialBrushTool::HeightfieldMaterialBrushTool(MetavoxelEditor* editor) :
|
||||
HeightfieldBrushTool(editor, "Material Brush"),
|
||||
_materialControl(new MaterialControl(this, _form)) {
|
||||
}
|
||||
|
||||
QVariant HeightfieldMaterialBrushTool::createEdit(bool alternate) {
|
||||
if (alternate) {
|
||||
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
|
||||
} else {
|
||||
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), _materialControl->getMaterial(),
|
||||
_materialControl->getColor()));
|
||||
}
|
||||
}
|
||||
|
||||
VoxelMaterialBoxTool::VoxelMaterialBoxTool(MetavoxelEditor* editor) :
|
||||
BoxTool(editor, "Set Voxel Material (Box)", false) {
|
||||
|
||||
|
@ -1360,17 +1384,7 @@ VoxelMaterialBoxTool::VoxelMaterialBoxTool(MetavoxelEditor* editor) :
|
|||
form->addRow(gridLayout);
|
||||
_snapToGrid->setChecked(true);
|
||||
|
||||
QHBoxLayout* colorLayout = new QHBoxLayout();
|
||||
form->addRow(colorLayout);
|
||||
colorLayout->addWidget(new QLabel("Color:"));
|
||||
colorLayout->addWidget(_color = new QColorEditor(this), 1);
|
||||
connect(_color, &QColorEditor::colorChanged, this, &VoxelMaterialBoxTool::clearTexture);
|
||||
QPushButton* eraseButton = new QPushButton("Erase");
|
||||
colorLayout->addWidget(eraseButton);
|
||||
connect(eraseButton, &QPushButton::clicked, this, &VoxelMaterialBoxTool::clearColor);
|
||||
|
||||
form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false));
|
||||
connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &VoxelMaterialBoxTool::updateTexture);
|
||||
_materialControl = new MaterialControl(this, form, true);
|
||||
}
|
||||
|
||||
bool VoxelMaterialBoxTool::appliesTo(const AttributePointer& attribute) const {
|
||||
|
@ -1382,16 +1396,10 @@ bool VoxelMaterialBoxTool::shouldSnapToGrid() {
|
|||
}
|
||||
|
||||
QColor VoxelMaterialBoxTool::getColor() {
|
||||
return _color->getColor();
|
||||
return _materialControl->getColor();
|
||||
}
|
||||
|
||||
void VoxelMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
|
||||
SharedObjectPointer material = _materialEditor->getObject();
|
||||
if (static_cast<MaterialObject*>(material.data())->getDiffuse().isValid()) {
|
||||
_materialEditor->detachObject();
|
||||
} else {
|
||||
material = SharedObjectPointer();
|
||||
}
|
||||
Cuboid* cuboid = new Cuboid();
|
||||
cuboid->setTranslation((maximum + minimum) * 0.5f);
|
||||
glm::vec3 vector = (maximum - minimum) * 0.5f;
|
||||
|
@ -1399,42 +1407,10 @@ void VoxelMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3&
|
|||
cuboid->setAspectY(vector.y / vector.x);
|
||||
cuboid->setAspectZ(vector.z / vector.x);
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(cuboid),
|
||||
material, _color->getColor())) };
|
||||
_materialControl->getMaterial(), _materialControl->getColor())) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||
}
|
||||
|
||||
void VoxelMaterialBoxTool::clearColor() {
|
||||
_color->setColor(QColor(0, 0, 0, 0));
|
||||
clearTexture();
|
||||
}
|
||||
|
||||
void VoxelMaterialBoxTool::clearTexture() {
|
||||
_materialEditor->setObject(new MaterialObject());
|
||||
}
|
||||
|
||||
void VoxelMaterialBoxTool::updateTexture() {
|
||||
if (_texture) {
|
||||
_texture->disconnect(this);
|
||||
}
|
||||
MaterialObject* material = static_cast<MaterialObject*>(_materialEditor->getObject().data());
|
||||
if (!material->getDiffuse().isValid()) {
|
||||
_texture.clear();
|
||||
return;
|
||||
}
|
||||
_texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE);
|
||||
if (_texture) {
|
||||
if (_texture->isLoaded()) {
|
||||
textureLoaded();
|
||||
} else {
|
||||
connect(_texture.data(), &Resource::loaded, this, &VoxelMaterialBoxTool::textureLoaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelMaterialBoxTool::textureLoaded() {
|
||||
_color->setColor(_texture->getAverageColor());
|
||||
}
|
||||
|
||||
VoxelMaterialSpannerTool::VoxelMaterialSpannerTool(MetavoxelEditor* editor) :
|
||||
PlaceSpannerTool(editor, "Set Voxel Material (Spanner)", QString(), false) {
|
||||
|
||||
|
@ -1446,17 +1422,7 @@ VoxelMaterialSpannerTool::VoxelMaterialSpannerTool(MetavoxelEditor* editor) :
|
|||
form->addRow(_spannerEditor = new SharedObjectEditor(&Spanner::staticMetaObject, false, this));
|
||||
_spannerEditor->setObject(new Sphere());
|
||||
|
||||
QHBoxLayout* colorLayout = new QHBoxLayout();
|
||||
form->addRow(colorLayout);
|
||||
colorLayout->addWidget(new QLabel("Color:"));
|
||||
colorLayout->addWidget(_color = new QColorEditor(this), 1);
|
||||
connect(_color, &QColorEditor::colorChanged, this, &VoxelMaterialSpannerTool::clearTexture);
|
||||
QPushButton* eraseButton = new QPushButton("Erase");
|
||||
colorLayout->addWidget(eraseButton);
|
||||
connect(eraseButton, &QPushButton::clicked, this, &VoxelMaterialSpannerTool::clearColor);
|
||||
|
||||
form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false));
|
||||
connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &VoxelMaterialSpannerTool::updateTexture);
|
||||
_materialControl = new MaterialControl(this, form, true);
|
||||
|
||||
QPushButton* place = new QPushButton("Set");
|
||||
layout()->addWidget(place);
|
||||
|
@ -1475,54 +1441,16 @@ SharedObjectPointer VoxelMaterialSpannerTool::getSpanner(bool detach) {
|
|||
}
|
||||
|
||||
QColor VoxelMaterialSpannerTool::getColor() {
|
||||
return _color->getColor();
|
||||
return _materialControl->getColor();
|
||||
}
|
||||
|
||||
void VoxelMaterialSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
|
||||
_spannerEditor->detachObject();
|
||||
|
||||
SharedObjectPointer material = _materialEditor->getObject();
|
||||
if (static_cast<MaterialObject*>(material.data())->getDiffuse().isValid()) {
|
||||
_materialEditor->detachObject();
|
||||
} else {
|
||||
material = SharedObjectPointer();
|
||||
}
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, material, _color->getColor())) };
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner,
|
||||
_materialControl->getMaterial(), _materialControl->getColor())) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||
}
|
||||
|
||||
void VoxelMaterialSpannerTool::clearColor() {
|
||||
_color->setColor(QColor(0, 0, 0, 0));
|
||||
clearTexture();
|
||||
}
|
||||
|
||||
void VoxelMaterialSpannerTool::clearTexture() {
|
||||
_materialEditor->setObject(new MaterialObject());
|
||||
}
|
||||
|
||||
void VoxelMaterialSpannerTool::updateTexture() {
|
||||
if (_texture) {
|
||||
_texture->disconnect(this);
|
||||
}
|
||||
MaterialObject* material = static_cast<MaterialObject*>(_materialEditor->getObject().data());
|
||||
if (!material->getDiffuse().isValid()) {
|
||||
_texture.clear();
|
||||
return;
|
||||
}
|
||||
_texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE);
|
||||
if (_texture) {
|
||||
if (_texture->isLoaded()) {
|
||||
textureLoaded();
|
||||
} else {
|
||||
connect(_texture.data(), &Resource::loaded, this, &VoxelMaterialSpannerTool::textureLoaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelMaterialSpannerTool::textureLoaded() {
|
||||
_color->setColor(_texture->getAverageColor());
|
||||
}
|
||||
|
||||
VoxelBrushTool::VoxelBrushTool(MetavoxelEditor* editor, const QString& name) :
|
||||
MetavoxelTool(editor, name, false, true) {
|
||||
|
||||
|
@ -1549,12 +1477,14 @@ void VoxelBrushTool::render() {
|
|||
glm::vec3 origin = Application::getInstance()->getMouseRayOrigin();
|
||||
glm::vec3 direction = Application::getInstance()->getMouseRayDirection();
|
||||
|
||||
float distance;
|
||||
if (!Application::getInstance()->getMetavoxels()->findFirstRayVoxelIntersection(origin, direction, distance)) {
|
||||
float heightfieldDistance = FLT_MAX, voxelDistance = FLT_MAX;
|
||||
if (!(Application::getInstance()->getMetavoxels()->findFirstRayHeightfieldIntersection(
|
||||
origin, direction, heightfieldDistance) |
|
||||
Application::getInstance()->getMetavoxels()->findFirstRayVoxelIntersection(origin, direction, voxelDistance))) {
|
||||
return;
|
||||
}
|
||||
Application::getInstance()->getMetavoxels()->renderVoxelCursor(
|
||||
_position = origin + distance * direction, _radius->value());
|
||||
_position = origin + qMin(heightfieldDistance, voxelDistance) * direction, _radius->value());
|
||||
}
|
||||
|
||||
bool VoxelBrushTool::eventFilter(QObject* watched, QEvent* event) {
|
||||
|
@ -1573,51 +1503,33 @@ bool VoxelBrushTool::eventFilter(QObject* watched, QEvent* event) {
|
|||
}
|
||||
|
||||
VoxelMaterialBrushTool::VoxelMaterialBrushTool(MetavoxelEditor* editor) :
|
||||
VoxelBrushTool(editor, "Material Brush") {
|
||||
|
||||
_form->addRow("Color:", _color = new QColorEditor(this));
|
||||
connect(_color, &QColorEditor::colorChanged, this, &VoxelMaterialBrushTool::clearTexture);
|
||||
_form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false));
|
||||
connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &VoxelMaterialBrushTool::updateTexture);
|
||||
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 {
|
||||
SharedObjectPointer material = _materialEditor->getObject();
|
||||
if (static_cast<MaterialObject*>(material.data())->getDiffuse().isValid()) {
|
||||
_materialEditor->detachObject();
|
||||
} else {
|
||||
material = SharedObjectPointer();
|
||||
}
|
||||
return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(), material, _color->getColor()));
|
||||
return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(),
|
||||
_materialControl->getMaterial(), _materialControl->getColor()));
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelMaterialBrushTool::clearTexture() {
|
||||
_materialEditor->setObject(new MaterialObject());
|
||||
VoxelSculptBrushTool::VoxelSculptBrushTool(MetavoxelEditor* editor) :
|
||||
VoxelBrushTool(editor, "Sculpt Brush"),
|
||||
_materialControl(new MaterialControl(this, _form, true)) {
|
||||
}
|
||||
|
||||
void VoxelMaterialBrushTool::updateTexture() {
|
||||
if (_texture) {
|
||||
_texture->disconnect(this);
|
||||
}
|
||||
MaterialObject* material = static_cast<MaterialObject*>(_materialEditor->getObject().data());
|
||||
if (!material->getDiffuse().isValid()) {
|
||||
_texture.clear();
|
||||
return;
|
||||
}
|
||||
_texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE);
|
||||
if (_texture) {
|
||||
if (_texture->isLoaded()) {
|
||||
textureLoaded();
|
||||
} else {
|
||||
connect(_texture.data(), &Resource::loaded, this, &VoxelMaterialBrushTool::textureLoaded);
|
||||
}
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelMaterialBrushTool::textureLoaded() {
|
||||
_color->setColor(_texture->getAverageColor());
|
||||
}
|
||||
|
|
|
@ -380,6 +380,32 @@ private:
|
|||
QDoubleSpinBox* _height;
|
||||
};
|
||||
|
||||
/// Contains widgets for editing materials.
|
||||
class MaterialControl : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
MaterialControl(QWidget* widget, QFormLayout* form, bool clearable = false);
|
||||
|
||||
SharedObjectPointer getMaterial();
|
||||
|
||||
const QColor& getColor() const { return _color->getColor(); }
|
||||
|
||||
private slots:
|
||||
|
||||
void clearColor();
|
||||
void clearTexture();
|
||||
void updateTexture();
|
||||
void textureLoaded();
|
||||
|
||||
private:
|
||||
|
||||
QColorEditor* _color;
|
||||
SharedObjectEditor* _materialEditor;
|
||||
QSharedPointer<NetworkTexture> _texture;
|
||||
};
|
||||
|
||||
/// Allows texturing parts of the heightfield.
|
||||
class HeightfieldMaterialBrushTool : public HeightfieldBrushTool {
|
||||
Q_OBJECT
|
||||
|
@ -391,18 +417,10 @@ public:
|
|||
protected:
|
||||
|
||||
virtual QVariant createEdit(bool alternate);
|
||||
|
||||
private slots:
|
||||
|
||||
void clearTexture();
|
||||
void updateTexture();
|
||||
void textureLoaded();
|
||||
|
||||
private:
|
||||
|
||||
QColorEditor* _color;
|
||||
SharedObjectEditor* _materialEditor;
|
||||
QSharedPointer<NetworkTexture> _texture;
|
||||
MaterialControl* _materialControl;
|
||||
};
|
||||
|
||||
/// Allows setting voxel materials by dragging out a box.
|
||||
|
@ -423,19 +441,10 @@ protected:
|
|||
|
||||
virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum);
|
||||
|
||||
private slots:
|
||||
|
||||
void clearColor();
|
||||
void clearTexture();
|
||||
void updateTexture();
|
||||
void textureLoaded();
|
||||
|
||||
private:
|
||||
|
||||
QCheckBox* _snapToGrid;
|
||||
QColorEditor* _color;
|
||||
SharedObjectEditor* _materialEditor;
|
||||
QSharedPointer<NetworkTexture> _texture;
|
||||
MaterialControl* _materialControl;
|
||||
};
|
||||
|
||||
/// Allows setting voxel materials by placing a spanner.
|
||||
|
@ -454,19 +463,10 @@ protected:
|
|||
virtual QColor getColor();
|
||||
virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner);
|
||||
|
||||
private slots:
|
||||
|
||||
void clearColor();
|
||||
void clearTexture();
|
||||
void updateTexture();
|
||||
void textureLoaded();
|
||||
|
||||
private:
|
||||
|
||||
SharedObjectEditor* _spannerEditor;
|
||||
QColorEditor* _color;
|
||||
SharedObjectEditor* _materialEditor;
|
||||
QSharedPointer<NetworkTexture> _texture;
|
||||
MaterialControl* _materialControl;
|
||||
};
|
||||
|
||||
/// Base class for voxel brush tools.
|
||||
|
@ -505,17 +505,26 @@ protected:
|
|||
|
||||
virtual QVariant createEdit(bool alternate);
|
||||
|
||||
private slots:
|
||||
|
||||
void clearTexture();
|
||||
void updateTexture();
|
||||
void textureLoaded();
|
||||
|
||||
private:
|
||||
|
||||
QColorEditor* _color;
|
||||
SharedObjectEditor* _materialEditor;
|
||||
QSharedPointer<NetworkTexture> _texture;
|
||||
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
|
||||
|
|
|
@ -2045,6 +2045,27 @@ bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& dire
|
|||
return _bounds.findRayIntersection(origin, direction, distance);
|
||||
}
|
||||
|
||||
bool Spanner::hasOwnColors() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Spanner::hasOwnMaterials() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
QRgb Spanner::getColor(const glm::vec3& point) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Spanner::getMaterial(const glm::vec3& point) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
QVector<SharedObjectPointer>& Spanner::getMaterials() {
|
||||
static QVector<SharedObjectPointer> emptyMaterials;
|
||||
return emptyMaterials;
|
||||
}
|
||||
|
||||
bool Spanner::contains(const glm::vec3& point) {
|
||||
return false;
|
||||
}
|
||||
|
@ -2365,3 +2386,234 @@ bool StaticModel::findRayIntersection(const glm::vec3& origin, const glm::vec3&
|
|||
QByteArray StaticModel::getRendererClassName() const {
|
||||
return "StaticModelRenderer";
|
||||
}
|
||||
|
||||
const float EIGHT_BIT_MAXIMUM = 255.0f;
|
||||
|
||||
Heightfield::Heightfield(const Box& bounds, float increment, const QByteArray& height, const QByteArray& color,
|
||||
const QByteArray& material, const QVector<SharedObjectPointer>& materials) :
|
||||
_increment(increment),
|
||||
_width((int)glm::round((bounds.maximum.x - bounds.minimum.x) / increment) + 1),
|
||||
_heightScale((bounds.maximum.y - bounds.minimum.y) / EIGHT_BIT_MAXIMUM),
|
||||
_height(height),
|
||||
_color(color),
|
||||
_material(material),
|
||||
_materials(materials) {
|
||||
|
||||
setBounds(bounds);
|
||||
}
|
||||
|
||||
bool Heightfield::hasOwnColors() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Heightfield::hasOwnMaterials() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
QRgb Heightfield::getColor(const glm::vec3& point) {
|
||||
glm::vec3 relative = (point - getBounds().minimum) / _increment;
|
||||
glm::vec3 floors = glm::floor(relative);
|
||||
glm::vec3 ceils = glm::ceil(relative);
|
||||
glm::vec3 fracts = glm::fract(relative);
|
||||
int floorX = (int)floors.x;
|
||||
int floorZ = (int)floors.z;
|
||||
int ceilX = (int)ceils.x;
|
||||
int ceilZ = (int)ceils.z;
|
||||
const uchar* src = (const uchar*)_color.constData();
|
||||
const uchar* upperLeft = src + (floorZ * _width + floorX) * DataBlock::COLOR_BYTES;
|
||||
const uchar* lowerRight = src + (ceilZ * _width + ceilX) * DataBlock::COLOR_BYTES;
|
||||
glm::vec3 interpolatedColor = glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]),
|
||||
glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z);
|
||||
|
||||
// the final vertex (and thus which triangle we check) depends on which half we're on
|
||||
if (fracts.x >= fracts.z) {
|
||||
const uchar* upperRight = src + (floorZ * _width + ceilX) * DataBlock::COLOR_BYTES;
|
||||
interpolatedColor = glm::mix(interpolatedColor, glm::mix(glm::vec3(upperRight[0], upperRight[1], upperRight[2]),
|
||||
glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z), (fracts.x - fracts.z) / (1.0f - fracts.z));
|
||||
|
||||
} else {
|
||||
const uchar* lowerLeft = src + (ceilZ * _width + floorX) * DataBlock::COLOR_BYTES;
|
||||
interpolatedColor = glm::mix(glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]),
|
||||
glm::vec3(lowerLeft[0], lowerLeft[1], lowerLeft[2]), fracts.z), interpolatedColor, fracts.x / fracts.z);
|
||||
}
|
||||
return qRgb(interpolatedColor.r, interpolatedColor.g, interpolatedColor.b);
|
||||
}
|
||||
|
||||
int Heightfield::getMaterial(const glm::vec3& point) {
|
||||
glm::vec3 relative = (point - getBounds().minimum) / _increment;
|
||||
const uchar* src = (const uchar*)_material.constData();
|
||||
return src[(int)glm::round(relative.z) * _width + (int)glm::round(relative.x)];
|
||||
}
|
||||
|
||||
QVector<SharedObjectPointer>& Heightfield::getMaterials() {
|
||||
return _materials;
|
||||
}
|
||||
|
||||
bool Heightfield::contains(const glm::vec3& point) {
|
||||
if (!getBounds().contains(point)) {
|
||||
return false;
|
||||
}
|
||||
glm::vec3 relative = (point - getBounds().minimum) / _increment;
|
||||
glm::vec3 floors = glm::floor(relative);
|
||||
glm::vec3 ceils = glm::ceil(relative);
|
||||
glm::vec3 fracts = glm::fract(relative);
|
||||
int floorX = (int)floors.x;
|
||||
int floorZ = (int)floors.z;
|
||||
int ceilX = (int)ceils.x;
|
||||
int ceilZ = (int)ceils.z;
|
||||
const uchar* src = (const uchar*)_height.constData();
|
||||
float upperLeft = src[floorZ * _width + floorX];
|
||||
float lowerRight = src[ceilZ * _width + ceilX];
|
||||
float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z);
|
||||
|
||||
// the final vertex (and thus which triangle we check) depends on which half we're on
|
||||
if (fracts.x >= fracts.z) {
|
||||
float upperRight = src[floorZ * _width + ceilX];
|
||||
interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z),
|
||||
(fracts.x - fracts.z) / (1.0f - fracts.z));
|
||||
|
||||
} else {
|
||||
float lowerLeft = src[ceilZ * _width + floorX];
|
||||
interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z);
|
||||
}
|
||||
return interpolatedHeight != 0.0f && point.y <= interpolatedHeight * _heightScale + getBounds().minimum.y;
|
||||
}
|
||||
|
||||
bool Heightfield::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) {
|
||||
// find the initial location in heightfield coordinates
|
||||
float rayDistance;
|
||||
glm::vec3 direction = end - start;
|
||||
if (!getBounds().findRayIntersection(start, direction, rayDistance) || rayDistance > 1.0f) {
|
||||
return false;
|
||||
}
|
||||
glm::vec3 entry = (start + direction * rayDistance - getBounds().minimum) / _increment;
|
||||
direction /= _increment;
|
||||
glm::vec3 floors = glm::floor(entry);
|
||||
glm::vec3 ceils = glm::ceil(entry);
|
||||
if (floors.x == ceils.x) {
|
||||
if (direction.x > 0.0f) {
|
||||
ceils.x += 1.0f;
|
||||
} else {
|
||||
floors.x -= 1.0f;
|
||||
}
|
||||
}
|
||||
if (floors.z == ceils.z) {
|
||||
if (direction.z > 0.0f) {
|
||||
ceils.z += 1.0f;
|
||||
} else {
|
||||
floors.z -= 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool withinBounds = true;
|
||||
float accumulatedDistance = 0.0f;
|
||||
const uchar* src = (const uchar*)_height.constData();
|
||||
int highestX = _width - 1;
|
||||
float highestY = (getBounds().maximum.y - getBounds().minimum.y) / _increment;
|
||||
int highestZ = (int)glm::round((getBounds().maximum.z - getBounds().minimum.z) / _increment);
|
||||
float heightScale = _heightScale / _increment;
|
||||
while (withinBounds && accumulatedDistance <= 1.0f) {
|
||||
// find the heights at the corners of the current cell
|
||||
int floorX = qMin(qMax((int)floors.x, 0), highestX);
|
||||
int floorZ = qMin(qMax((int)floors.z, 0), highestZ);
|
||||
int ceilX = qMin(qMax((int)ceils.x, 0), highestX);
|
||||
int ceilZ = qMin(qMax((int)ceils.z, 0), highestZ);
|
||||
float upperLeft = src[floorZ * _width + floorX] * heightScale;
|
||||
float upperRight = src[floorZ * _width + ceilX] * heightScale;
|
||||
float lowerLeft = src[ceilZ * _width + floorX] * heightScale;
|
||||
float lowerRight = src[ceilZ * _width + ceilX] * heightScale;
|
||||
|
||||
// find the distance to the next x coordinate
|
||||
float xDistance = FLT_MAX;
|
||||
if (direction.x > 0.0f) {
|
||||
xDistance = (ceils.x - entry.x) / direction.x;
|
||||
} else if (direction.x < 0.0f) {
|
||||
xDistance = (floors.x - entry.x) / direction.x;
|
||||
}
|
||||
|
||||
// and the distance to the next z coordinate
|
||||
float zDistance = FLT_MAX;
|
||||
if (direction.z > 0.0f) {
|
||||
zDistance = (ceils.z - entry.z) / direction.z;
|
||||
} else if (direction.z < 0.0f) {
|
||||
zDistance = (floors.z - entry.z) / direction.z;
|
||||
}
|
||||
|
||||
// the exit distance is the lower of those two
|
||||
float exitDistance = qMin(xDistance, zDistance);
|
||||
glm::vec3 exit, nextFloors = floors, nextCeils = ceils;
|
||||
if (exitDistance == FLT_MAX) {
|
||||
withinBounds = false; // line points upwards/downwards; check this cell only
|
||||
|
||||
} else {
|
||||
// find the exit point and the next cell, and determine whether it's still within the bounds
|
||||
exit = entry + exitDistance * direction;
|
||||
withinBounds = (exit.y >= 0.0f && exit.y <= highestY);
|
||||
if (exitDistance == xDistance) {
|
||||
if (direction.x > 0.0f) {
|
||||
nextFloors.x += 1.0f;
|
||||
withinBounds &= (nextCeils.x += 1.0f) <= highestX;
|
||||
} else {
|
||||
withinBounds &= (nextFloors.x -= 1.0f) >= 0.0f;
|
||||
nextCeils.x -= 1.0f;
|
||||
}
|
||||
}
|
||||
if (exitDistance == zDistance) {
|
||||
if (direction.z > 0.0f) {
|
||||
nextFloors.z += 1.0f;
|
||||
withinBounds &= (nextCeils.z += 1.0f) <= highestZ;
|
||||
} else {
|
||||
withinBounds &= (nextFloors.z -= 1.0f) >= 0.0f;
|
||||
nextCeils.z -= 1.0f;
|
||||
}
|
||||
}
|
||||
// check the vertical range of the ray against the ranges of the cell heights
|
||||
if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) ||
|
||||
qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) {
|
||||
entry = exit;
|
||||
floors = nextFloors;
|
||||
ceils = nextCeils;
|
||||
accumulatedDistance += exitDistance;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// having passed the bounds check, we must check against the planes
|
||||
glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z);
|
||||
|
||||
// first check the triangle including the Z+ segment
|
||||
glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft);
|
||||
float lowerProduct = glm::dot(lowerNormal, direction);
|
||||
if (lowerProduct != 0.0f) {
|
||||
float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct;
|
||||
glm::vec3 intersection = relativeEntry + planeDistance * direction;
|
||||
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
|
||||
intersection.z >= intersection.x) {
|
||||
distance = rayDistance + (accumulatedDistance + planeDistance) * _increment;
|
||||
normal = glm::normalize(lowerNormal);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// then the one with the X+ segment
|
||||
glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight);
|
||||
float upperProduct = glm::dot(upperNormal, direction);
|
||||
if (upperProduct != 0.0f) {
|
||||
float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct;
|
||||
glm::vec3 intersection = relativeEntry + planeDistance * direction;
|
||||
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
|
||||
intersection.x >= intersection.z) {
|
||||
distance = rayDistance + (accumulatedDistance + planeDistance) * _increment;
|
||||
normal = glm::normalize(upperNormal);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// no joy; continue on our way
|
||||
entry = exit;
|
||||
floors = nextFloors;
|
||||
ceils = nextCeils;
|
||||
accumulatedDistance += exitDistance;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -644,9 +644,24 @@ public:
|
|||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
const glm::vec3& clipMinimum, float clipSize, float& distance) const;
|
||||
|
||||
/// Checks whether this spanner has its own colors.
|
||||
virtual bool hasOwnColors() const;
|
||||
|
||||
/// Checks whether this spanner has its own materials.
|
||||
virtual bool hasOwnMaterials() const;
|
||||
|
||||
/// Checks whether the spanner contains the specified point.
|
||||
virtual bool contains(const glm::vec3& point);
|
||||
|
||||
/// Retrieves the color at the specified point.
|
||||
virtual QRgb getColor(const glm::vec3& point);
|
||||
|
||||
/// Retrieves the material at the specified point.
|
||||
virtual int getMaterial(const glm::vec3& point);
|
||||
|
||||
/// Retrieves a reference to the list of materials.
|
||||
virtual QVector<SharedObjectPointer>& getMaterials();
|
||||
|
||||
/// Finds the intersection, if any, between the specified line segment and the spanner.
|
||||
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
|
||||
|
||||
|
@ -848,4 +863,37 @@ private:
|
|||
QUrl _url;
|
||||
};
|
||||
|
||||
/// A heightfield represented as a spanner.
|
||||
class Heightfield : public Transformable {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Heightfield(const Box& bounds, float increment, const QByteArray& height, const QByteArray& color,
|
||||
const QByteArray& material, const QVector<SharedObjectPointer>& materials);
|
||||
|
||||
QByteArray& getHeight() { return _height; }
|
||||
QByteArray& getColor() { return _color; }
|
||||
QByteArray& getMaterial() { return _material; }
|
||||
|
||||
virtual bool hasOwnColors() const;
|
||||
virtual bool hasOwnMaterials() const;
|
||||
virtual QRgb getColor(const glm::vec3& point);
|
||||
virtual int getMaterial(const glm::vec3& point);
|
||||
virtual QVector<SharedObjectPointer>& getMaterials();
|
||||
|
||||
virtual bool contains(const glm::vec3& point);
|
||||
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
|
||||
|
||||
private:
|
||||
|
||||
float _increment;
|
||||
int _width;
|
||||
float _heightScale;
|
||||
QByteArray _height;
|
||||
QByteArray _color;
|
||||
QByteArray _material;
|
||||
QVector<SharedObjectPointer> _materials;
|
||||
};
|
||||
|
||||
#endif // hifi_MetavoxelData_h
|
||||
|
|
|
@ -662,13 +662,36 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) {
|
|||
if (info.size > _blockSize) {
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
QVector<QRgb> oldColorContents;
|
||||
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
|
||||
QVector<QRgb> oldColorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
|
||||
colorPointer->getContents() : QVector<QRgb>(VOXEL_BLOCK_VOLUME);
|
||||
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());
|
||||
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);
|
||||
|
@ -682,30 +705,41 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) {
|
|||
bool flipped = (qAlpha(rgb) == 0);
|
||||
float step = 1.0f / scale;
|
||||
glm::vec3 position(0.0f, 0.0f, info.minimum.z + minZ * step);
|
||||
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 (_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->getColor(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -806,39 +840,53 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
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();
|
||||
|
||||
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->getMaterial(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 {
|
||||
materialContents = QByteArray(VOXEL_BLOCK_VOLUME, 0);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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));
|
||||
|
@ -846,6 +894,244 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) {
|
|||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
class HeightfieldClearFetchVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
HeightfieldClearFetchVisitor(const Box& bounds, float granularity);
|
||||
|
||||
const SharedObjectPointer& getSpanner() const { return _spanner; }
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
Box _bounds;
|
||||
Box _expandedBounds;
|
||||
SharedObjectPointer _spanner;
|
||||
Box _spannerBounds;
|
||||
int _heightfieldWidth;
|
||||
int _heightfieldHeight;
|
||||
};
|
||||
|
||||
HeightfieldClearFetchVisitor::HeightfieldClearFetchVisitor(const Box& bounds, float granularity) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector<AttributePointer>() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute()) {
|
||||
|
||||
// find the bounds of all voxel nodes intersected
|
||||
float nodeSize = VOXEL_BLOCK_SIZE * glm::pow(2.0f, glm::floor(glm::log(granularity) / glm::log(2.0f)));
|
||||
_bounds.minimum = glm::floor(bounds.minimum / nodeSize) * nodeSize;
|
||||
_bounds.maximum = glm::ceil(bounds.maximum / nodeSize) * nodeSize;
|
||||
|
||||
// expand to include edges
|
||||
_expandedBounds = _bounds;
|
||||
float increment = nodeSize / VOXEL_BLOCK_SIZE;
|
||||
_expandedBounds.maximum.x += increment;
|
||||
_expandedBounds.maximum.z += increment;
|
||||
}
|
||||
|
||||
int HeightfieldClearFetchVisitor::visit(MetavoxelInfo& info) {
|
||||
Box bounds = info.getBounds();
|
||||
if (!bounds.intersects(_expandedBounds)) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
if (!info.isLeaf) {
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
HeightfieldHeightDataPointer heightPointer = info.inputValues.at(0).getInlineValue<HeightfieldHeightDataPointer>();
|
||||
if (!heightPointer) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
QByteArray contents(heightPointer->getContents());
|
||||
int size = glm::sqrt((float)contents.size());
|
||||
float heightScale = size / info.size;
|
||||
|
||||
Box overlap = bounds.getIntersection(_expandedBounds);
|
||||
int srcX = (overlap.minimum.x - info.minimum.x) * heightScale;
|
||||
int srcY = (overlap.minimum.z - info.minimum.z) * heightScale;
|
||||
int srcWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) * heightScale);
|
||||
int srcHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) * heightScale);
|
||||
char* src = contents.data() + srcY * size + srcX;
|
||||
|
||||
// check for non-zero values
|
||||
bool foundNonZero = false;
|
||||
for (int y = 0; y < srcHeight; y++, src += (size - srcWidth)) {
|
||||
for (char* end = src + srcWidth; src != end; src++) {
|
||||
if (*src != 0) {
|
||||
foundNonZero = true;
|
||||
goto outerBreak;
|
||||
}
|
||||
}
|
||||
}
|
||||
outerBreak:
|
||||
|
||||
// if everything is zero, we're done
|
||||
if (!foundNonZero) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
// create spanner if necessary
|
||||
Heightfield* spanner = static_cast<Heightfield*>(_spanner.data());
|
||||
float increment = 1.0f / heightScale;
|
||||
if (!spanner) {
|
||||
_spannerBounds.minimum = glm::floor(_bounds.minimum / increment) * increment;
|
||||
_spannerBounds.maximum = (glm::ceil(_bounds.maximum / increment) + glm::vec3(1.0f, 0.0f, 1.0f)) * increment;
|
||||
_spannerBounds.minimum.y = bounds.minimum.y;
|
||||
_spannerBounds.maximum.y = bounds.maximum.y;
|
||||
_heightfieldWidth = (int)glm::round((_spannerBounds.maximum.x - _spannerBounds.minimum.x) / increment) + 1;
|
||||
_heightfieldHeight = (int)glm::round((_spannerBounds.maximum.z - _spannerBounds.minimum.z) / increment) + 1;
|
||||
int heightfieldArea = _heightfieldWidth * _heightfieldHeight;
|
||||
_spanner = spanner = new Heightfield(_spannerBounds, increment, QByteArray(heightfieldArea, 0),
|
||||
QByteArray(heightfieldArea * DataBlock::COLOR_BYTES, 0), QByteArray(heightfieldArea, 0),
|
||||
QVector<SharedObjectPointer>());
|
||||
}
|
||||
|
||||
// copy the inner area
|
||||
overlap = bounds.getIntersection(_spannerBounds);
|
||||
int destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale;
|
||||
int destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale;
|
||||
int destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale);
|
||||
int destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale);
|
||||
char* dest = spanner->getHeight().data() + destY * _heightfieldWidth + destX;
|
||||
srcX = (overlap.minimum.x - info.minimum.x) * heightScale;
|
||||
srcY = (overlap.minimum.z - info.minimum.z) * heightScale;
|
||||
src = contents.data() + srcY * size + srcX;
|
||||
|
||||
for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth, src += size) {
|
||||
memcpy(dest, src, destWidth);
|
||||
}
|
||||
|
||||
// clear the inner area
|
||||
Box innerBounds = _spannerBounds;
|
||||
innerBounds.minimum.x += increment;
|
||||
innerBounds.minimum.z += increment;
|
||||
innerBounds.maximum.x -= increment;
|
||||
innerBounds.maximum.z -= increment;
|
||||
Box innerOverlap = bounds.getIntersection(innerBounds);
|
||||
destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale;
|
||||
destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale;
|
||||
destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale);
|
||||
destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale);
|
||||
dest = contents.data() + destY * size + destX;
|
||||
|
||||
for (int y = 0; y < destHeight; y++, dest += size) {
|
||||
memset(dest, 0, destWidth);
|
||||
}
|
||||
|
||||
// see if there are any non-zero values left
|
||||
foundNonZero = false;
|
||||
dest = contents.data();
|
||||
for (char* end = dest + contents.size(); dest != end; dest++) {
|
||||
if (*dest != 0) {
|
||||
foundNonZero = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if all is gone, clear the node
|
||||
if (!foundNonZero) {
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0),
|
||||
encodeInline<HeightfieldHeightDataPointer>(HeightfieldHeightDataPointer()));
|
||||
info.outputValues[1] = AttributeValue(_outputs.at(1),
|
||||
encodeInline<HeightfieldColorDataPointer>(HeightfieldColorDataPointer()));
|
||||
info.outputValues[2] = AttributeValue(_outputs.at(2),
|
||||
encodeInline<HeightfieldMaterialDataPointer>(HeightfieldMaterialDataPointer()));
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
HeightfieldHeightDataPointer newHeightPointer(new HeightfieldHeightData(contents));
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldHeightDataPointer>(newHeightPointer));
|
||||
|
||||
HeightfieldColorDataPointer colorPointer = info.inputValues.at(1).getInlineValue<HeightfieldColorDataPointer>();
|
||||
if (colorPointer) {
|
||||
contents = colorPointer->getContents();
|
||||
size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES);
|
||||
heightScale = size / info.size;
|
||||
|
||||
// copy the inner area
|
||||
destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale;
|
||||
destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale;
|
||||
destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale);
|
||||
destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale);
|
||||
dest = spanner->getColor().data() + (destY * _heightfieldWidth + destX) * DataBlock::COLOR_BYTES;
|
||||
srcX = (overlap.minimum.x - info.minimum.x) * heightScale;
|
||||
srcY = (overlap.minimum.z - info.minimum.z) * heightScale;
|
||||
src = contents.data() + (srcY * size + srcX) * DataBlock::COLOR_BYTES;
|
||||
|
||||
for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth * DataBlock::COLOR_BYTES,
|
||||
src += size * DataBlock::COLOR_BYTES) {
|
||||
memcpy(dest, src, destWidth * DataBlock::COLOR_BYTES);
|
||||
}
|
||||
|
||||
destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale;
|
||||
destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale;
|
||||
destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale);
|
||||
destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale);
|
||||
dest = contents.data() + (destY * size + destX) * DataBlock::COLOR_BYTES;
|
||||
|
||||
for (int y = 0; y < destHeight; y++, dest += size * DataBlock::COLOR_BYTES) {
|
||||
memset(dest, 0, destWidth * DataBlock::COLOR_BYTES);
|
||||
}
|
||||
|
||||
HeightfieldColorDataPointer newColorPointer(new HeightfieldColorData(contents));
|
||||
info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline<HeightfieldColorDataPointer>(newColorPointer));
|
||||
}
|
||||
|
||||
HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue<HeightfieldMaterialDataPointer>();
|
||||
if (materialPointer) {
|
||||
contents = materialPointer->getContents();
|
||||
QVector<SharedObjectPointer> materials = materialPointer->getMaterials();
|
||||
size = glm::sqrt((float)contents.size());
|
||||
heightScale = size / info.size;
|
||||
|
||||
// copy the inner area
|
||||
destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale;
|
||||
destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale;
|
||||
destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale);
|
||||
destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale);
|
||||
uchar* dest = (uchar*)spanner->getMaterial().data() + destY * _heightfieldWidth + destX;
|
||||
srcX = (overlap.minimum.x - info.minimum.x) * heightScale;
|
||||
srcY = (overlap.minimum.z - info.minimum.z) * heightScale;
|
||||
uchar* src = (uchar*)contents.data() + srcY * size + srcX;
|
||||
QHash<int, int> materialMap;
|
||||
|
||||
for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth, src += size) {
|
||||
for (uchar* lineSrc = src, *lineDest = dest, *end = src + destWidth; lineSrc != end; lineSrc++, lineDest++) {
|
||||
int material = *lineSrc;
|
||||
if (material != 0) {
|
||||
int& mapping = materialMap[material];
|
||||
if (mapping == 0) {
|
||||
mapping = getMaterialIndex(materials.at(material - 1), spanner->getMaterials(),
|
||||
spanner->getMaterial());
|
||||
}
|
||||
material = mapping;
|
||||
}
|
||||
*lineDest = material;
|
||||
}
|
||||
}
|
||||
|
||||
destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale;
|
||||
destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale;
|
||||
destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale);
|
||||
destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale);
|
||||
dest = (uchar*)contents.data() + destY * size + destX;
|
||||
|
||||
for (int y = 0; y < destHeight; y++, dest += size) {
|
||||
memset(dest, 0, destWidth);
|
||||
}
|
||||
|
||||
clearUnusedMaterials(materials, contents);
|
||||
HeightfieldMaterialDataPointer newMaterialPointer(new HeightfieldMaterialData(contents, materials));
|
||||
info.outputValues[2] = AttributeValue(_outputs.at(2),
|
||||
encodeInline<HeightfieldMaterialDataPointer>(newMaterialPointer));
|
||||
}
|
||||
|
||||
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());
|
||||
|
@ -855,6 +1141,18 @@ void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObject
|
|||
// make sure it's either 100% transparent or 100% opaque
|
||||
QColor color = averageColor;
|
||||
color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f);
|
||||
|
||||
// clear/fetch any heightfield data
|
||||
HeightfieldClearFetchVisitor heightfieldVisitor(spanner->getBounds(), spanner->getVoxelizationGranularity());
|
||||
data.guide(heightfieldVisitor);
|
||||
|
||||
// voxelize the fetched heightfield, if any
|
||||
if (heightfieldVisitor.getSpanner()) {
|
||||
VoxelMaterialSpannerEditVisitor visitor(static_cast<Spanner*>(heightfieldVisitor.getSpanner().data()),
|
||||
material, color);
|
||||
data.guide(visitor);
|
||||
}
|
||||
|
||||
VoxelMaterialSpannerEditVisitor visitor(spanner, material, color);
|
||||
data.guide(visitor);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue