mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 15:17:42 +02:00
Working on fetching/clearing heightfield chunks.
This commit is contained in:
parent
632f3bfa7f
commit
a0f75c990e
5 changed files with 214 additions and 269 deletions
|
@ -363,6 +363,17 @@ void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius)
|
||||||
|
|
||||||
DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().release();
|
DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().release();
|
||||||
|
|
||||||
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
|
||||||
|
DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().bind();
|
||||||
|
|
||||||
|
SpannerCursorRenderVisitor spannerVisitor(bounds);
|
||||||
|
guide(spannerVisitor);
|
||||||
|
|
||||||
|
DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release();
|
||||||
|
|
||||||
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
|
||||||
glDisableClientState(GL_VERTEX_ARRAY);
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
|
|
||||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||||
|
|
|
@ -344,6 +344,10 @@ int SpannerReplaceVisitor::visit(MetavoxelInfo& info) {
|
||||||
void MetavoxelData::replace(const AttributePointer& attribute, const Box& bounds, float granularity,
|
void MetavoxelData::replace(const AttributePointer& attribute, const Box& bounds, float granularity,
|
||||||
const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject) {
|
const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject) {
|
||||||
Spanner* newSpanner = static_cast<Spanner*>(newObject.data());
|
Spanner* newSpanner = static_cast<Spanner*>(newObject.data());
|
||||||
|
if (!newSpanner) {
|
||||||
|
remove(attribute, bounds, granularity, oldObject);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (bounds != newSpanner->getBounds() || granularity != newSpanner->getPlacementGranularity()) {
|
if (bounds != newSpanner->getBounds() || granularity != newSpanner->getPlacementGranularity()) {
|
||||||
// if the bounds have changed, we must remove and reinsert
|
// if the bounds have changed, we must remove and reinsert
|
||||||
remove(attribute, bounds, granularity, oldObject);
|
remove(attribute, bounds, granularity, oldObject);
|
||||||
|
|
|
@ -155,7 +155,9 @@ PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position
|
||||||
}
|
}
|
||||||
|
|
||||||
void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||||
glm::vec3 extents(radius, radius, radius);
|
// increase the extents slightly to include neighboring tiles
|
||||||
|
const float RADIUS_EXTENSION = 1.1f;
|
||||||
|
glm::vec3 extents = glm::vec3(radius, radius, radius) * RADIUS_EXTENSION;
|
||||||
QVector<SharedObjectPointer> results;
|
QVector<SharedObjectPointer> results;
|
||||||
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
|
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
|
||||||
Box(position - extents, position + extents), results);
|
Box(position - extents, position + extents), results);
|
||||||
|
@ -492,262 +494,6 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) {
|
||||||
return STOP_RECURSION;
|
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
|
|
||||||
TempHeightfield* spanner = static_cast<TempHeightfield*>(_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);
|
|
||||||
_heightfieldHeight = (int)glm::round((_spannerBounds.maximum.z - _spannerBounds.minimum.z) / increment);
|
|
||||||
int heightfieldArea = _heightfieldWidth * _heightfieldHeight;
|
|
||||||
Box innerBounds = _spannerBounds;
|
|
||||||
innerBounds.maximum.x -= increment;
|
|
||||||
innerBounds.maximum.z -= increment;
|
|
||||||
_spanner = spanner = new TempHeightfield(innerBounds, 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) {
|
|
||||||
HeightfieldHeightDataPointer newHeightPointer(new HeightfieldHeightData(contents));
|
|
||||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldHeightDataPointer>(newHeightPointer));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
info.outputValues[0] = AttributeValue(_outputs.at(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// allow a border for what we clear in terms of color/material
|
|
||||||
innerBounds.minimum.x += increment;
|
|
||||||
innerBounds.minimum.z += increment;
|
|
||||||
innerBounds.maximum.x -= increment;
|
|
||||||
innerBounds.maximum.z -= increment;
|
|
||||||
innerOverlap = bounds.getIntersection(innerBounds);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foundNonZero) {
|
|
||||||
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);
|
|
||||||
if (destWidth > 0 && destHeight > 0) {
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
info.outputValues[1] = AttributeValue(_outputs.at(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foundNonZero) {
|
|
||||||
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);
|
|
||||||
if (destWidth > 0 && destHeight > 0) {
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
info.outputValues[2] = AttributeValue(_outputs.at(2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return STOP_RECURSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||||
// expand to fit the entire edit
|
// expand to fit the entire edit
|
||||||
Spanner* spanner = static_cast<Spanner*>(this->spanner.data());
|
Spanner* spanner = static_cast<Spanner*>(this->spanner.data());
|
||||||
|
@ -758,14 +504,34 @@ void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObject
|
||||||
QColor color = averageColor;
|
QColor color = averageColor;
|
||||||
color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f);
|
color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f);
|
||||||
|
|
||||||
// clear/fetch any heightfield data
|
// find the bounds of all voxel nodes intersected
|
||||||
HeightfieldClearFetchVisitor heightfieldVisitor(spanner->getBounds(), spanner->getVoxelizationGranularity());
|
float nodeSize = VOXEL_BLOCK_SIZE * glm::pow(2.0f, glm::floor(
|
||||||
data.guide(heightfieldVisitor);
|
glm::log(spanner->getVoxelizationGranularity()) / glm::log(2.0f)));
|
||||||
|
Box bounds(glm::floor(spanner->getBounds().minimum / nodeSize) * nodeSize,
|
||||||
|
glm::ceil(spanner->getBounds().maximum / nodeSize) * nodeSize);
|
||||||
|
|
||||||
|
// expand to include edges
|
||||||
|
Box expandedBounds = bounds;
|
||||||
|
float increment = nodeSize / VOXEL_BLOCK_SIZE;
|
||||||
|
expandedBounds.maximum.x += increment;
|
||||||
|
expandedBounds.maximum.z += increment;
|
||||||
|
|
||||||
|
// get all intersecting spanners
|
||||||
|
QVector<SharedObjectPointer> results;
|
||||||
|
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), expandedBounds, results);
|
||||||
|
|
||||||
|
// clear/voxelize as appropriate
|
||||||
|
SharedObjectPointer heightfield;
|
||||||
|
foreach (const SharedObjectPointer& result, results) {
|
||||||
|
Spanner* newSpanner = static_cast<Spanner*>(result.data())->clearAndFetchHeight(bounds, heightfield);
|
||||||
|
if (newSpanner != result) {
|
||||||
|
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), result, newSpanner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// voxelize the fetched heightfield, if any
|
// voxelize the fetched heightfield, if any
|
||||||
if (heightfieldVisitor.getSpanner()) {
|
if (heightfield) {
|
||||||
VoxelMaterialSpannerEditVisitor visitor(static_cast<Spanner*>(heightfieldVisitor.getSpanner().data()),
|
VoxelMaterialSpannerEditVisitor visitor(static_cast<Spanner*>(heightfield.data()), material, color);
|
||||||
material, color);
|
|
||||||
data.guide(visitor);
|
data.guide(visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -117,6 +117,10 @@ Spanner* Spanner::paintHeight(const glm::vec3& position, float radius, float hei
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Spanner* Spanner::clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
bool Spanner::hasOwnColors() const {
|
bool Spanner::hasOwnColors() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1814,6 +1818,159 @@ Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float
|
||||||
return newHeightfield;
|
return newHeightfield;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Spanner* Heightfield::clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield) {
|
||||||
|
if (!_height) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
int heightWidth = _height->getWidth();
|
||||||
|
int heightHeight = _height->getContents().size() / heightWidth;
|
||||||
|
int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION;
|
||||||
|
int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION;
|
||||||
|
float heightIncrementX = getScale() / innerHeightWidth;
|
||||||
|
float heightIncrementZ = (getScale() * _aspectZ) / innerHeightHeight;
|
||||||
|
|
||||||
|
int colorWidth = heightWidth;
|
||||||
|
int colorHeight = heightHeight;
|
||||||
|
if (_color) {
|
||||||
|
colorWidth = _color->getWidth();
|
||||||
|
colorHeight = _color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES);
|
||||||
|
}
|
||||||
|
int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE;
|
||||||
|
int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE;
|
||||||
|
float colorIncrementX = getScale() / innerColorWidth;
|
||||||
|
float colorIncrementZ = (getScale() * _aspectZ) / innerColorHeight;
|
||||||
|
|
||||||
|
int materialWidth = colorWidth;
|
||||||
|
int materialHeight = colorHeight;
|
||||||
|
if (_material) {
|
||||||
|
materialWidth = _material->getWidth();
|
||||||
|
materialHeight = _material->getContents().size() / materialWidth;
|
||||||
|
}
|
||||||
|
int innerMaterialWidth = materialWidth - HeightfieldData::SHARED_EDGE;
|
||||||
|
int innerMaterialHeight = materialHeight - HeightfieldData::SHARED_EDGE;
|
||||||
|
float materialIncrementX = getScale() / innerMaterialWidth;
|
||||||
|
float materialIncrementZ = (getScale() * _aspectZ) / innerMaterialHeight;
|
||||||
|
|
||||||
|
float largestIncrementX = qMax(heightIncrementX, qMax(colorIncrementX, materialIncrementX));
|
||||||
|
float largestIncrementZ = qMax(heightIncrementZ, qMax(colorIncrementZ, materialIncrementZ));
|
||||||
|
|
||||||
|
glm::vec3 minimum(glm::floor(bounds.minimum.x / largestIncrementX) * largestIncrementX, getBounds().minimum.y,
|
||||||
|
glm::floor(bounds.minimum.z / largestIncrementZ) * largestIncrementZ);
|
||||||
|
glm::vec3 maximum(glm::ceil(bounds.maximum.x / largestIncrementX) * largestIncrementX, getBounds().maximum.y,
|
||||||
|
glm::ceil(bounds.maximum.z / largestIncrementZ) * largestIncrementZ);
|
||||||
|
Box largestBounds(minimum, maximum);
|
||||||
|
|
||||||
|
glm::mat4 baseTransform = glm::mat4_cast(glm::inverse(getRotation())) * glm::translate(-getTranslation());
|
||||||
|
glm::vec3 inverseScale(innerHeightWidth / getScale(), 1.0f, innerHeightHeight / (getScale() * _aspectZ));
|
||||||
|
glm::mat4 transform = glm::scale(inverseScale) * baseTransform;
|
||||||
|
Box transformedBounds = transform * largestBounds;
|
||||||
|
|
||||||
|
// make sure there are values to clear
|
||||||
|
int startX = glm::clamp((int)glm::ceil(transformedBounds.minimum.x) + HeightfieldHeight::HEIGHT_BORDER,
|
||||||
|
0, heightWidth - 1);
|
||||||
|
int startZ = glm::clamp((int)glm::ceil(transformedBounds.minimum.z) + HeightfieldHeight::HEIGHT_BORDER,
|
||||||
|
0, heightHeight - 1);
|
||||||
|
int endX = glm::clamp((int)glm::floor(transformedBounds.maximum.x) + HeightfieldHeight::HEIGHT_BORDER, 0, heightWidth - 1);
|
||||||
|
int endZ = glm::clamp((int)glm::floor(transformedBounds.maximum.z) + HeightfieldHeight::HEIGHT_BORDER,
|
||||||
|
0, heightHeight - 1);
|
||||||
|
const quint16* src = _height->getContents().constData() + startZ * heightWidth + startX;
|
||||||
|
for (int z = startZ; z <= endZ; z++, src += heightWidth) {
|
||||||
|
const quint16* lineSrc = src;
|
||||||
|
for (int x = startX; x <= endX; x++) {
|
||||||
|
if (*lineSrc++ != 0) {
|
||||||
|
goto clearableBreak;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
clearableBreak:
|
||||||
|
|
||||||
|
// create heightfield if necessary
|
||||||
|
Heightfield* spanner = static_cast<Heightfield*>(heightfield.data());
|
||||||
|
if (!spanner) {
|
||||||
|
int spannerHeightWidth = (int)((maximum.x - minimum.x) / heightIncrementX) + HeightfieldHeight::HEIGHT_EXTENSION;
|
||||||
|
int spannerHeightHeight = (int)((maximum.z - minimum.z) / heightIncrementZ) + HeightfieldHeight::HEIGHT_EXTENSION;
|
||||||
|
int spannerColorWidth = (int)((maximum.x - bounds.minimum.x) / colorIncrementX) + HeightfieldData::SHARED_EDGE;
|
||||||
|
int spannerColorHeight = (int)((maximum.z - minimum.z) / colorIncrementZ) + HeightfieldData::SHARED_EDGE;
|
||||||
|
int spannerMaterialWidth = (int)((maximum.x - minimum.x) / materialIncrementX) + HeightfieldData::SHARED_EDGE;
|
||||||
|
int spannerMaterialHeight = (int)((maximum.z - minimum.z) / materialIncrementZ) + HeightfieldData::SHARED_EDGE;
|
||||||
|
|
||||||
|
heightfield = spanner = new Heightfield();
|
||||||
|
spanner->setTranslation(minimum);
|
||||||
|
spanner->setScale(maximum.x - minimum.x);
|
||||||
|
spanner->setAspectY((maximum.y - minimum.y) / spanner->getScale());
|
||||||
|
spanner->setAspectZ((maximum.z - minimum.z) / spanner->getScale());
|
||||||
|
spanner->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(spannerHeightWidth,
|
||||||
|
QVector<quint16>(spannerHeightWidth * spannerHeightHeight))));
|
||||||
|
spanner->setColor(HeightfieldColorPointer(new HeightfieldColor(spannerColorWidth,
|
||||||
|
QByteArray(spannerColorWidth * spannerColorHeight * DataBlock::COLOR_BYTES, 0xFF))));
|
||||||
|
spanner->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(spannerMaterialWidth,
|
||||||
|
QByteArray(spannerMaterialWidth * spannerMaterialHeight, 0), QVector<SharedObjectPointer>())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear the height
|
||||||
|
QVector<quint16> newHeightContents = _height->getContents();
|
||||||
|
quint16* dest = newHeightContents.data() + startZ * heightWidth + startX;
|
||||||
|
for (int z = startZ; z <= endZ; z++, dest += heightWidth) {
|
||||||
|
memset(dest, 0, (endX - startX + 1) * sizeof(quint16));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we've cleared all the inner height, we can remove the spanner entirely
|
||||||
|
src = newHeightContents.constData() + heightWidth + HeightfieldHeight::HEIGHT_BORDER;
|
||||||
|
for (int z = 0; z < innerHeightHeight; z++, src += heightWidth) {
|
||||||
|
const quint16* lineSrc = src;
|
||||||
|
for (int x = 0; x < innerHeightWidth; x++) {
|
||||||
|
if (*lineSrc++ != 0) {
|
||||||
|
goto nonEmptyBreak;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
nonEmptyBreak:
|
||||||
|
|
||||||
|
Heightfield* newHeightfield = static_cast<Heightfield*>(clone(true));
|
||||||
|
newHeightfield->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)));
|
||||||
|
|
||||||
|
// and the color
|
||||||
|
if (_color) {
|
||||||
|
inverseScale = glm::vec3(innerColorWidth / getScale(), 1.0f, innerColorHeight / (getScale() * _aspectZ));
|
||||||
|
transform = glm::scale(inverseScale) * baseTransform;
|
||||||
|
transformedBounds = transform * largestBounds;
|
||||||
|
startX = glm::clamp((int)glm::ceil(transformedBounds.minimum.x), 0, colorWidth - 1);
|
||||||
|
startZ = glm::clamp((int)glm::ceil(transformedBounds.minimum.z), 0, colorHeight - 1);
|
||||||
|
endX = glm::clamp((int)glm::floor(transformedBounds.maximum.x), 0, colorWidth - 1);
|
||||||
|
endZ = glm::clamp((int)glm::floor(transformedBounds.maximum.z), 0, colorHeight - 1);
|
||||||
|
QByteArray newColorContents = _color->getContents();
|
||||||
|
char* dest = newColorContents.data() + (startZ * colorWidth + startX) * DataBlock::COLOR_BYTES;
|
||||||
|
for (int z = startZ; z <= endZ; z++, dest += colorWidth * DataBlock::COLOR_BYTES) {
|
||||||
|
memset(dest, 0, (endX - startX + 1) * DataBlock::COLOR_BYTES);
|
||||||
|
}
|
||||||
|
newHeightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(colorWidth, newColorContents)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// and the material
|
||||||
|
if (_material) {
|
||||||
|
inverseScale = glm::vec3(innerMaterialWidth / getScale(), 1.0f, innerMaterialHeight / (getScale() * _aspectZ));
|
||||||
|
transform = glm::scale(inverseScale) * baseTransform;
|
||||||
|
transformedBounds = transform * largestBounds;
|
||||||
|
startX = glm::clamp((int)glm::ceil(transformedBounds.minimum.x), 0, materialWidth - 1);
|
||||||
|
startZ = glm::clamp((int)glm::ceil(transformedBounds.minimum.z), 0, materialHeight - 1);
|
||||||
|
endX = glm::clamp((int)glm::floor(transformedBounds.maximum.x), 0, materialWidth - 1);
|
||||||
|
endZ = glm::clamp((int)glm::floor(transformedBounds.maximum.z), 0, materialHeight - 1);
|
||||||
|
QByteArray newMaterialContents = _material->getContents();
|
||||||
|
QVector<SharedObjectPointer> newMaterials = _material->getMaterials();
|
||||||
|
char* dest = newMaterialContents.data() + startZ * materialWidth + startX;
|
||||||
|
for (int z = startZ; z <= endZ; z++, dest += materialWidth) {
|
||||||
|
memset(dest, 0, endX - startX + 1);
|
||||||
|
}
|
||||||
|
clearUnusedMaterials(newMaterials, newMaterialContents);
|
||||||
|
newHeightfield->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(
|
||||||
|
materialWidth, newMaterialContents, newMaterials)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return newHeightfield;
|
||||||
|
}
|
||||||
|
|
||||||
bool Heightfield::hasOwnColors() const {
|
bool Heightfield::hasOwnColors() const {
|
||||||
return _color;
|
return _color;
|
||||||
}
|
}
|
||||||
|
@ -1953,32 +2110,32 @@ bool Heightfield::intersects(const glm::vec3& start, const glm::vec3& end, float
|
||||||
|
|
||||||
const float DISTANCE_THRESHOLD = 0.001f;
|
const float DISTANCE_THRESHOLD = 0.001f;
|
||||||
if (glm::abs(entry.x - 0.0f) < DISTANCE_THRESHOLD) {
|
if (glm::abs(entry.x - 0.0f) < DISTANCE_THRESHOLD) {
|
||||||
normal = glm::vec3(-1.0f, 0.0f, 0.0f);
|
normal = getRotation() * glm::vec3(-1.0f, 0.0f, 0.0f);
|
||||||
distance = boundsDistance;
|
distance = boundsDistance;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} else if (glm::abs(entry.x - innerWidth) < DISTANCE_THRESHOLD) {
|
} else if (glm::abs(entry.x - innerWidth) < DISTANCE_THRESHOLD) {
|
||||||
normal = glm::vec3(1.0f, 0.0f, 0.0f);
|
normal = getRotation() * glm::vec3(1.0f, 0.0f, 0.0f);
|
||||||
distance = boundsDistance;
|
distance = boundsDistance;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} else if (glm::abs(entry.y - 0.0f) < DISTANCE_THRESHOLD) {
|
} else if (glm::abs(entry.y - 0.0f) < DISTANCE_THRESHOLD) {
|
||||||
normal = glm::vec3(0.0f, -1.0f, 0.0f);
|
normal = getRotation() * glm::vec3(0.0f, -1.0f, 0.0f);
|
||||||
distance = boundsDistance;
|
distance = boundsDistance;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} else if (glm::abs(entry.y - numeric_limits<quint16>::max()) < DISTANCE_THRESHOLD) {
|
} else if (glm::abs(entry.y - numeric_limits<quint16>::max()) < DISTANCE_THRESHOLD) {
|
||||||
normal = glm::vec3(0.0f, 1.0f, 0.0f);
|
normal = getRotation() * glm::vec3(0.0f, 1.0f, 0.0f);
|
||||||
distance = boundsDistance;
|
distance = boundsDistance;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} else if (glm::abs(entry.z - 0.0f) < DISTANCE_THRESHOLD) {
|
} else if (glm::abs(entry.z - 0.0f) < DISTANCE_THRESHOLD) {
|
||||||
normal = glm::vec3(0.0f, 0.0f, -1.0f);
|
normal = getRotation() * glm::vec3(0.0f, 0.0f, -1.0f);
|
||||||
distance = boundsDistance;
|
distance = boundsDistance;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} else if (glm::abs(entry.z - innerHeight) < DISTANCE_THRESHOLD) {
|
} else if (glm::abs(entry.z - innerHeight) < DISTANCE_THRESHOLD) {
|
||||||
normal = glm::vec3(0.0f, 0.0f, 1.0f);
|
normal = getRotation() * glm::vec3(0.0f, 0.0f, 1.0f);
|
||||||
distance = boundsDistance;
|
distance = boundsDistance;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,11 @@ public:
|
||||||
/// \return the modified spanner, or this if no modification was performed
|
/// \return the modified spanner, or this if no modification was performed
|
||||||
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height);
|
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height);
|
||||||
|
|
||||||
|
/// Attempts to clear and fetch part of the spanner's height.
|
||||||
|
/// \param heightfield the heightfield to populate
|
||||||
|
/// \return the modified spanner, or this if no modification was performed
|
||||||
|
virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield);
|
||||||
|
|
||||||
/// Checks whether this spanner has its own colors.
|
/// Checks whether this spanner has its own colors.
|
||||||
virtual bool hasOwnColors() const;
|
virtual bool hasOwnColors() const;
|
||||||
|
|
||||||
|
@ -525,6 +530,8 @@ public:
|
||||||
|
|
||||||
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height);
|
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height);
|
||||||
|
|
||||||
|
virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield);
|
||||||
|
|
||||||
virtual bool hasOwnColors() const;
|
virtual bool hasOwnColors() const;
|
||||||
virtual bool hasOwnMaterials() const;
|
virtual bool hasOwnMaterials() const;
|
||||||
virtual QRgb getColorAt(const glm::vec3& point);
|
virtual QRgb getColorAt(const glm::vec3& point);
|
||||||
|
|
Loading…
Reference in a new issue