mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 12:04:18 +02:00
More progress on voxelizing heightfields.
This commit is contained in:
parent
fc6f0afb93
commit
131486c8f7
3 changed files with 325 additions and 266 deletions
|
@ -2365,3 +2365,187 @@ 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::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;
|
||||
}
|
||||
|
|
|
@ -848,4 +848,32 @@ 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; }
|
||||
QVector<SharedObjectPointer>& getMaterials() { return _materials; }
|
||||
|
||||
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
|
||||
|
|
|
@ -625,192 +625,6 @@ VoxelMaterialSpannerEdit::VoxelMaterialSpannerEdit(const SharedObjectPointer& sp
|
|||
spanner(spanner) {
|
||||
}
|
||||
|
||||
class VoxelHeightfieldFetchVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
VoxelHeightfieldFetchVisitor();
|
||||
|
||||
void init(const Box& bounds);
|
||||
|
||||
bool isEmpty() const { return _heightContents.isEmpty(); }
|
||||
|
||||
int getContentsWidth() const { return _contentsWidth; }
|
||||
int getContentsHeight() const { return _contentsHeight; }
|
||||
|
||||
const Box& getHeightBounds() const { return _heightBounds; }
|
||||
|
||||
float getInterpolatedHeight(float x, float y) const;
|
||||
|
||||
QRgb getInterpolatedColor(float x, float y) const;
|
||||
|
||||
int getNearestMaterial(float x, float y) const;
|
||||
|
||||
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
Box _bounds;
|
||||
Box _expandedBounds;
|
||||
int _contentsWidth;
|
||||
int _contentsHeight;
|
||||
QByteArray _heightContents;
|
||||
QByteArray _colorContents;
|
||||
QByteArray _materialContents;
|
||||
QVector<SharedObjectPointer> _materials;
|
||||
Box _heightBounds;
|
||||
};
|
||||
|
||||
VoxelHeightfieldFetchVisitor::VoxelHeightfieldFetchVisitor() :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute()) {
|
||||
}
|
||||
|
||||
void VoxelHeightfieldFetchVisitor::init(const Box& bounds) {
|
||||
_expandedBounds = _bounds = bounds;
|
||||
float increment = (bounds.maximum.x - bounds.minimum.x) / VOXEL_BLOCK_SIZE;
|
||||
_expandedBounds.maximum.x += increment;
|
||||
_expandedBounds.maximum.z += increment;
|
||||
_heightContents.clear();
|
||||
_colorContents.clear();
|
||||
_materialContents.clear();
|
||||
_materials.clear();
|
||||
}
|
||||
|
||||
float VoxelHeightfieldFetchVisitor::getInterpolatedHeight(float x, float y) const {
|
||||
int floorX = glm::floor(x), floorY = glm::floor(y);
|
||||
int ceilX = glm::ceil(x), ceilY = glm::ceil(y);
|
||||
const uchar* src = (const uchar*)_heightContents.constData();
|
||||
float upperLeft = src[floorY * _contentsWidth + floorX];
|
||||
float lowerRight = src[ceilY * _contentsWidth + ceilX];
|
||||
float fractX = glm::fract(x), fractY = glm::fract(y);
|
||||
float interpolatedHeight = glm::mix(upperLeft, lowerRight, fractY);
|
||||
if (fractX >= fractY) {
|
||||
float upperRight = src[floorY * _contentsWidth + ceilX];
|
||||
interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fractY),
|
||||
(fractX - fractY) / (1.0f - fractY));
|
||||
|
||||
} else {
|
||||
float lowerLeft = src[ceilY * _contentsWidth + floorX];
|
||||
interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fractY), interpolatedHeight, fractX / fractY);
|
||||
}
|
||||
return interpolatedHeight;
|
||||
}
|
||||
|
||||
QRgb VoxelHeightfieldFetchVisitor::getInterpolatedColor(float x, float y) const {
|
||||
int floorX = glm::floor(x), floorY = glm::floor(y);
|
||||
int ceilX = glm::ceil(x), ceilY = glm::ceil(y);
|
||||
const uchar* src = (const uchar*)_colorContents.constData();
|
||||
const uchar* upperLeft = src + (floorY * _contentsWidth + floorX) * DataBlock::COLOR_BYTES;
|
||||
const uchar* lowerRight = src + (ceilY * _contentsWidth + ceilX) * DataBlock::COLOR_BYTES;
|
||||
float fractX = glm::fract(x), fractY = glm::fract(y);
|
||||
glm::vec3 interpolatedColor = glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]),
|
||||
glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fractY);
|
||||
if (fractX >= fractY) {
|
||||
const uchar* upperRight = src + (floorY * _contentsWidth + 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]), fractY), (fractX - fractY) / (1.0f - fractY));
|
||||
|
||||
} else {
|
||||
const uchar* lowerLeft = src + (ceilY * _contentsWidth + 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]), fractY), interpolatedColor, fractX / fractY);
|
||||
}
|
||||
return qRgb(interpolatedColor.r, interpolatedColor.g, interpolatedColor.b);
|
||||
}
|
||||
|
||||
int VoxelHeightfieldFetchVisitor::getNearestMaterial(float x, float y) const {
|
||||
const uchar* src = (const uchar*)_materialContents.constData();
|
||||
return src[(int)glm::round(y) * _contentsWidth + (int)glm::round(x)];
|
||||
}
|
||||
|
||||
int VoxelHeightfieldFetchVisitor::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;
|
||||
}
|
||||
const QByteArray& heightContents = heightPointer->getContents();
|
||||
int size = glm::sqrt((float)heightContents.size());
|
||||
float heightIncrement = info.size / size;
|
||||
|
||||
// the first heightfield we encounter determines the resolution
|
||||
if (_heightContents.isEmpty()) {
|
||||
_heightBounds.minimum = glm::floor(_bounds.minimum / heightIncrement) * heightIncrement;
|
||||
_heightBounds.maximum = (glm::ceil(_bounds.maximum / heightIncrement) + glm::vec3(1.0f, 0.0f, 1.0f)) * heightIncrement;
|
||||
_heightBounds.minimum.y = bounds.minimum.y;
|
||||
_heightBounds.maximum.y = bounds.maximum.y;
|
||||
_contentsWidth = (int)glm::round((_heightBounds.maximum.x - _heightBounds.minimum.x) / heightIncrement);
|
||||
_contentsHeight = (int)glm::round((_heightBounds.maximum.z - _heightBounds.minimum.z) / heightIncrement);
|
||||
_heightContents = QByteArray(_contentsWidth * _contentsHeight, 0);
|
||||
_colorContents = QByteArray(_contentsWidth * _contentsHeight * DataBlock::COLOR_BYTES, 0);
|
||||
_materialContents = QByteArray(_contentsWidth * _contentsHeight, 0);
|
||||
}
|
||||
|
||||
Box overlap = _heightBounds.getIntersection(bounds);
|
||||
if (overlap.isEmpty()) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
int srcX = (overlap.minimum.x - bounds.minimum.x) / heightIncrement;
|
||||
int srcY = (overlap.minimum.z - bounds.minimum.z) / heightIncrement;
|
||||
int destX = (overlap.minimum.x - _heightBounds.minimum.x) / heightIncrement;
|
||||
int destY = (overlap.minimum.z - _heightBounds.minimum.z) / heightIncrement;
|
||||
int width = (overlap.maximum.x - overlap.minimum.x) / heightIncrement;
|
||||
int height = (overlap.maximum.z - overlap.minimum.z) / heightIncrement;
|
||||
char* heightDest = _heightContents.data() + destY * _contentsWidth + destX;
|
||||
const char* heightSrc = heightContents.constData() + srcY * size + srcX;
|
||||
|
||||
for (int y = 0; y < height; y++, heightDest += _contentsWidth, heightSrc += size) {
|
||||
memcpy(heightDest, heightSrc, width);
|
||||
}
|
||||
|
||||
HeightfieldColorDataPointer colorPointer = info.inputValues.at(1).getInlineValue<HeightfieldColorDataPointer>();
|
||||
if (colorPointer && colorPointer->getContents().size() == heightContents.size() * DataBlock::COLOR_BYTES) {
|
||||
char* colorDest = _colorContents.data() + (destY * _contentsWidth + destX) * DataBlock::COLOR_BYTES;
|
||||
const char* colorSrc = colorPointer->getContents().constData() + (srcY * size + srcX) * DataBlock::COLOR_BYTES;
|
||||
|
||||
for (int y = 0; y < height; y++, colorDest += _contentsWidth * DataBlock::COLOR_BYTES,
|
||||
colorSrc += size * DataBlock::COLOR_BYTES) {
|
||||
memcpy(colorDest, colorSrc, width * DataBlock::COLOR_BYTES);
|
||||
}
|
||||
}
|
||||
|
||||
HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue<HeightfieldMaterialDataPointer>();
|
||||
if (materialPointer && materialPointer->getContents().size() == heightContents.size()) {
|
||||
uchar* materialDest = (uchar*)_materialContents.data() + destY * _contentsWidth + destX;
|
||||
const uchar* materialSrc = (const uchar*)materialPointer->getContents().constData() + srcY * size + srcX;
|
||||
const QVector<SharedObjectPointer>& materials = materialPointer->getMaterials();
|
||||
QHash<int, int> materialMap;
|
||||
|
||||
for (int y = 0; y < height; y++, materialDest += _contentsWidth, materialSrc += size) {
|
||||
const uchar* lineSrc = materialSrc;
|
||||
for (uchar* lineDest = materialDest, *end = lineDest + width; lineDest != end; lineDest++, lineSrc++) {
|
||||
int value = *lineSrc;
|
||||
if (value == 0) {
|
||||
continue;
|
||||
}
|
||||
int& mapping = materialMap[value];
|
||||
if (mapping == 0) {
|
||||
_materials.append(materials.at(value - 1));
|
||||
mapping = _materials.size();
|
||||
}
|
||||
*lineDest = mapping;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
class VoxelMaterialSpannerEditVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
|
@ -824,7 +638,6 @@ private:
|
|||
SharedObjectPointer _material;
|
||||
QColor _color;
|
||||
float _blockSize;
|
||||
VoxelHeightfieldFetchVisitor _heightfieldVisitor;
|
||||
};
|
||||
|
||||
VoxelMaterialSpannerEditVisitor::VoxelMaterialSpannerEditVisitor(Spanner* spanner,
|
||||
|
@ -849,14 +662,12 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) {
|
|||
if (info.size > _blockSize) {
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
bool fetchFromHeightfield = false;
|
||||
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);
|
||||
fetchFromHeightfield = true;
|
||||
}
|
||||
|
||||
QVector<QRgb> hermiteContents;
|
||||
|
@ -865,7 +676,6 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) {
|
|||
hermiteContents = hermitePointer->getContents();
|
||||
} else {
|
||||
hermiteContents = QVector<QRgb>(VOXEL_BLOCK_VOLUME * VoxelHermiteData::EDGE_COUNT);
|
||||
fetchFromHeightfield = true;
|
||||
}
|
||||
|
||||
QByteArray materialContents;
|
||||
|
@ -876,68 +686,9 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) {
|
|||
materials = materialPointer->getMaterials();
|
||||
} else {
|
||||
materialContents = QByteArray(VOXEL_BLOCK_VOLUME, 0);
|
||||
fetchFromHeightfield = true;
|
||||
}
|
||||
|
||||
float scale = VOXEL_BLOCK_SIZE / info.size;
|
||||
if (fetchFromHeightfield) {
|
||||
// the first time we touch a voxel block, we have to look up any intersecting heightfield data
|
||||
_heightfieldVisitor.init(bounds);
|
||||
_data->guide(_heightfieldVisitor);
|
||||
if (!_heightfieldVisitor.isEmpty()) {
|
||||
QRgb* colorDest = oldColorContents.data();
|
||||
QRgb* hermiteDest = hermiteContents.data();
|
||||
uchar* materialDest = (uchar*)materialContents.data();
|
||||
const Box& heightBounds = _heightfieldVisitor.getHeightBounds();
|
||||
float heightStepX = (heightBounds.maximum.x - heightBounds.minimum.x) /
|
||||
(_heightfieldVisitor.getContentsWidth() - 1);
|
||||
float heightStepY = (heightBounds.maximum.z - heightBounds.minimum.z) /
|
||||
(_heightfieldVisitor.getContentsHeight() - 1);
|
||||
float lineHeightX = (bounds.minimum.x - heightBounds.minimum.x) / heightStepX;
|
||||
float heightY = (bounds.minimum.z - heightBounds.minimum.z) / heightStepY;
|
||||
float heightScale = (heightBounds.maximum.y - heightBounds.minimum.y) / EIGHT_BIT_MAXIMUM;
|
||||
QHash<int, int> materialMap;
|
||||
|
||||
for (int z = 0; z < VOXEL_BLOCK_SAMPLES; z++, heightY += heightStepY) {
|
||||
float heightX = lineHeightX;
|
||||
for (int x = 0; x < VOXEL_BLOCK_SAMPLES; x++, colorDest++, hermiteDest += VoxelHermiteData::EDGE_COUNT,
|
||||
materialDest++, heightX += heightStepX) {
|
||||
float height = _heightfieldVisitor.getInterpolatedHeight(heightX, heightY);
|
||||
if (height == 0.0f) {
|
||||
continue;
|
||||
}
|
||||
height = heightBounds.minimum.y + height * heightScale;
|
||||
if (height < bounds.minimum.y) {
|
||||
continue;
|
||||
}
|
||||
height = (height - bounds.minimum.y) * scale;
|
||||
int clampedHeight = qMin((int)height, VOXEL_BLOCK_SIZE);
|
||||
QRgb color = _heightfieldVisitor.getInterpolatedColor(heightX, heightY);
|
||||
uchar* lineMaterialDest = materialDest;
|
||||
uchar material = _heightfieldVisitor.getNearestMaterial(heightX, heightY);
|
||||
if (material != 0) {
|
||||
int& mapping = materialMap[material];
|
||||
if (mapping == 0) {
|
||||
materials.append(_heightfieldVisitor.getMaterials().at(material - 1));
|
||||
mapping = materials.size();
|
||||
}
|
||||
material = mapping;
|
||||
}
|
||||
for (QRgb* lineColorDest = colorDest, *end = lineColorDest + clampedHeight * VOXEL_BLOCK_SAMPLES;
|
||||
lineColorDest != end; lineColorDest += VOXEL_BLOCK_SAMPLES,
|
||||
lineMaterialDest += VOXEL_BLOCK_SAMPLES) {
|
||||
*lineColorDest = color;
|
||||
*lineMaterialDest = material;
|
||||
}
|
||||
hermiteDest[clampedHeight * VOXEL_BLOCK_SAMPLES * VoxelHermiteData::EDGE_COUNT + 1] = packNormal(
|
||||
glm::vec3(0.0f, 1.0f, 0.0f), (height - clampedHeight) * EIGHT_BIT_MAXIMUM);
|
||||
}
|
||||
colorDest += VOXEL_BLOCK_AREA - VOXEL_BLOCK_SAMPLES;
|
||||
hermiteDest += (VOXEL_BLOCK_AREA - VOXEL_BLOCK_SAMPLES) * VoxelHermiteData::EDGE_COUNT;
|
||||
materialDest += VOXEL_BLOCK_AREA - VOXEL_BLOCK_SAMPLES;
|
||||
}
|
||||
}
|
||||
}
|
||||
QVector<QRgb> colorContents = oldColorContents;
|
||||
|
||||
Box overlap = info.getBounds().getIntersection(_spanner->getBounds());
|
||||
|
@ -1100,19 +851,26 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) {
|
|||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
class HeightfieldClearVisitor : public MetavoxelVisitor {
|
||||
class HeightfieldClearFetchVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
HeightfieldClearVisitor(const Box& bounds, float granularity);
|
||||
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;
|
||||
};
|
||||
|
||||
HeightfieldClearVisitor::HeightfieldClearVisitor(const Box& bounds, float granularity) :
|
||||
HeightfieldClearFetchVisitor::HeightfieldClearFetchVisitor(const Box& bounds, float granularity) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector<AttributePointer>() <<
|
||||
|
@ -1126,14 +884,15 @@ HeightfieldClearVisitor::HeightfieldClearVisitor(const Box& bounds, float granul
|
|||
_bounds.maximum = glm::ceil(bounds.maximum / nodeSize) * nodeSize;
|
||||
|
||||
// expand to include edges
|
||||
_expandedBounds = _bounds;
|
||||
float increment = nodeSize / VOXEL_BLOCK_SIZE;
|
||||
_bounds.maximum.x += increment;
|
||||
_bounds.maximum.z += increment;
|
||||
_expandedBounds.maximum.x += increment;
|
||||
_expandedBounds.maximum.z += increment;
|
||||
}
|
||||
|
||||
int HeightfieldClearVisitor::visit(MetavoxelInfo& info) {
|
||||
int HeightfieldClearFetchVisitor::visit(MetavoxelInfo& info) {
|
||||
Box bounds = info.getBounds();
|
||||
if (!bounds.intersects(_bounds)) {
|
||||
if (!bounds.intersects(_expandedBounds)) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
if (!info.isLeaf) {
|
||||
|
@ -1147,17 +906,97 @@ int HeightfieldClearVisitor::visit(MetavoxelInfo& info) {
|
|||
int size = glm::sqrt((float)contents.size());
|
||||
float heightScale = size / info.size;
|
||||
|
||||
Box overlap = bounds.getIntersection(_bounds);
|
||||
int destX = (overlap.minimum.x - info.minimum.x) * heightScale;
|
||||
int destY = (overlap.minimum.z - info.minimum.z) * heightScale;
|
||||
int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) * heightScale);
|
||||
int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) * heightScale);
|
||||
char* dest = contents.data() + destY * size + destX;
|
||||
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) * 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) + 1;
|
||||
int destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale) + 1;
|
||||
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 = _bounds;
|
||||
innerBounds.minimum.x += increment;
|
||||
innerBounds.minimum.z += increment;
|
||||
overlap = bounds.getIntersection(innerBounds);
|
||||
destX = (overlap.minimum.x - info.minimum.x) * heightScale;
|
||||
destY = (overlap.minimum.z - info.minimum.z) * heightScale;
|
||||
destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) * heightScale);
|
||||
destHeight = glm::ceil((overlap.maximum.z - overlap.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));
|
||||
|
||||
|
@ -1216,12 +1055,20 @@ 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);
|
||||
|
||||
// clear any heightfield data
|
||||
HeightfieldClearVisitor clearVisitor(spanner->getBounds(), spanner->getVoxelizationGranularity());
|
||||
data.guide(clearVisitor);
|
||||
}
|
||||
|
||||
PaintVoxelMaterialEdit::PaintVoxelMaterialEdit(const glm::vec3& position, float radius,
|
||||
|
|
Loading…
Reference in a new issue