Height painting.

This commit is contained in:
Andrzej Kapolka 2014-11-14 18:06:38 -08:00
parent 479bea1eb2
commit f192f2428e
3 changed files with 85 additions and 86 deletions

View file

@ -154,87 +154,18 @@ PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position
height(height) {
}
class PaintHeightfieldHeightEditVisitor : public MetavoxelVisitor {
public:
PaintHeightfieldHeightEditVisitor(const PaintHeightfieldHeightEdit& edit);
virtual int visit(MetavoxelInfo& info);
private:
PaintHeightfieldHeightEdit _edit;
Box _bounds;
};
PaintHeightfieldHeightEditVisitor::PaintHeightfieldHeightEditVisitor(const PaintHeightfieldHeightEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute(),
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute()),
_edit(edit) {
glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius);
_bounds = Box(_edit.position - extents, _edit.position + extents);
}
const int EIGHT_BIT_MAXIMUM = 255;
int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
HeightfieldHeightDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldHeightDataPointer>();
if (!pointer) {
return STOP_RECURSION;
}
QByteArray contents(pointer->getContents());
int size = glm::sqrt((float)contents.size());
int highest = size - 1;
float heightScale = size / info.size;
glm::vec3 center = (_edit.position - info.minimum) * heightScale;
float scaledRadius = _edit.radius * heightScale;
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
glm::vec3 start = glm::floor(center - extents);
glm::vec3 end = glm::ceil(center + extents);
// raise/lower all points within the radius
float z = qMax(start.z, 0.0f);
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX;
float squaredRadius = scaledRadius * scaledRadius;
float squaredRadiusReciprocal = 1.0f / squaredRadius;
float scaledHeight = _edit.height * EIGHT_BIT_MAXIMUM / info.size;
bool changed = false;
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
uchar* dest = lineDest;
for (float x = startX; x <= endX; x += 1.0f, dest++) {
float dx = x - center.x, dz = z - center.z;
float distanceSquared = dx * dx + dz * dz;
if (distanceSquared <= squaredRadius) {
// height falls off towards edges
int value = *dest + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal;
if (value != *dest) {
*dest = qMin(qMax(value, 0), EIGHT_BIT_MAXIMUM);
changed = true;
}
}
}
lineDest += size;
}
if (changed) {
HeightfieldHeightDataPointer newPointer(new HeightfieldHeightData(contents));
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldHeightDataPointer>(newPointer));
}
return STOP_RECURSION;
}
void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldHeightEditVisitor visitor(*this);
data.guide(visitor);
glm::vec3 extents(radius, radius, radius);
QVector<SharedObjectPointer> results;
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
Box(position - extents, position + extents), results);
foreach (const SharedObjectPointer& spanner, results) {
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->paintHeight(position, radius, height);
if (newSpanner != spanner) {
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner);
}
}
}
MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& averageColor) :
@ -256,7 +187,7 @@ void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedOb
Box(position - extents, position + extents), results);
foreach (const SharedObjectPointer& spanner, results) {
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->paint(position, radius, material, averageColor);
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->paintMaterial(position, radius, material, averageColor);
if (newSpanner != spanner) {
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner);
}
@ -419,7 +350,8 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) {
if (minZ > 0) {
hermiteMinZ--;
hermiteSizeZ++;
}
}
const int EIGHT_BIT_MAXIMUM = 255;
QRgb* hermiteDestZ = hermiteContents.data() + hermiteMinZ * hermiteArea + hermiteMinY * hermiteSamples +
hermiteMinX * VoxelHermiteData::EDGE_COUNT;
for (int z = hermiteMinZ, hermiteMaxZ = z + hermiteSizeZ - 1; z <= hermiteMaxZ; z++, hermiteDestZ += hermiteArea) {

View file

@ -108,7 +108,12 @@ bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& dire
return _bounds.findRayIntersection(origin, direction, distance);
}
Spanner* Spanner::paint(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color) {
Spanner* Spanner::paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material,
const QColor& color) {
return this;
}
Spanner* Spanner::paintHeight(const glm::vec3& position, float radius, float height) {
return this;
}
@ -1608,7 +1613,7 @@ bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3&
return false;
}
Spanner* Heightfield::paint(const glm::vec3& position, float radius,
Spanner* Heightfield::paintMaterial(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& color) {
if (!_height) {
return this;
@ -1714,6 +1719,60 @@ Spanner* Heightfield::paint(const glm::vec3& position, float radius,
return newHeightfield;
}
Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float height) {
if (!_height) {
return this;
}
int heightWidth = _height->getWidth();
int heightHeight = _height->getContents().size() / heightWidth;
QVector<quint16> contents = _height->getContents();
int innerWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION;
int innerHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION;
int highestX = heightWidth - 1;
int highestZ = heightHeight - 1;
Heightfield* newHeightfield = static_cast<Heightfield*>(clone(true));
glm::vec3 inverseScale(innerWidth / getScale(), 1.0f, innerHeight / (getScale() * _aspectZ));
glm::vec3 center = glm::inverse(getRotation()) * (position - getTranslation()) * inverseScale;
center.x += 1.0f;
center.z += 1.0f;
glm::vec3 extents = glm::vec3(radius, radius, radius) * inverseScale;
glm::vec3 start = glm::floor(center - extents);
glm::vec3 end = glm::ceil(center + extents);
// paint all points within the radius
float z = qMax(start.z, 0.0f);
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highestX);
quint16* lineDest = contents.data() + (int)z * heightWidth + (int)startX;
float squaredRadius = extents.x * extents.x;
float squaredRadiusReciprocal = 1.0f / squaredRadius;
float scaledHeight = height * numeric_limits<quint16>::max() / (getScale() * _aspectY);
float multiplierZ = inverseScale.x / inverseScale.z;
bool changed = false;
for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) {
quint16* dest = lineDest;
for (float x = startX; x <= endX; x += 1.0f, dest++) {
float dx = x - center.x, dz = (z - center.z) * multiplierZ;
float distanceSquared = dx * dx + dz * dz;
if (distanceSquared <= squaredRadius) {
// height falls off towards edges
int value = *dest + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal;
if (value != *dest) {
*dest = qMin(qMax(value, 0), (int)numeric_limits<quint16>::max());
changed = true;
}
}
}
lineDest += heightWidth;
}
if (changed) {
newHeightfield->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, contents)));
}
return newHeightfield;
}
QByteArray Heightfield::getRendererClassName() const {
return "HeightfieldRenderer";
}

View file

@ -66,7 +66,12 @@ public:
/// Attempts to paint on the spanner.
/// \return the modified spanner, or this if no modification was performed
virtual Spanner* paint(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color);
virtual Spanner* paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material,
const QColor& color);
/// Attempts to modify the spanner's height.
/// \return the modified spanner, or this if no modification was performed
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height);
/// Checks whether this spanner has its own colors.
virtual bool hasOwnColors() const;
@ -515,8 +520,11 @@ public:
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
virtual Spanner* paint(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color);
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);
signals:
void aspectYChanged(float aspectY);