mirror of
https://github.com/overte-org/overte.git
synced 2025-08-12 18:55:08 +02:00
Material/ray testing bits.
This commit is contained in:
parent
808cb2e4bc
commit
5a46f3e20d
5 changed files with 302 additions and 178 deletions
|
@ -1044,16 +1044,17 @@ VoxelBuffer::VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>
|
|||
_materials(materials) {
|
||||
}
|
||||
|
||||
bool VoxelBuffer::findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin,
|
||||
const glm::vec3& direction, float& distance) const {
|
||||
bool VoxelBuffer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float boundsDistance, float& distance) const {
|
||||
float highest = _size - 1.0f;
|
||||
glm::vec3 position = entry * highest;
|
||||
glm::vec3 position = (origin + direction * boundsDistance) * highest;
|
||||
glm::vec3 floors = glm::floor(position);
|
||||
int max = _size - 2;
|
||||
int x = qMin((int)floors.x, max), y = qMin((int)floors.y, max), z = qMin((int)floors.z, max);
|
||||
forever {
|
||||
for (QMultiHash<VoxelCoord, int>::const_iterator it = _quadIndices.constFind(qRgb(x + 1, y + 1, z + 1));
|
||||
it != _quadIndices.constEnd(); it++) {
|
||||
VoxelCoord key(qRgb(x, y, z));
|
||||
for (QMultiHash<VoxelCoord, int>::const_iterator it = _quadIndices.constFind(key);
|
||||
it != _quadIndices.constEnd() && it.key() == key; it++) {
|
||||
const int* indices = _indices.constData() + *it;
|
||||
if (findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex,
|
||||
_vertices.at(indices[1]).vertex, _vertices.at(indices[2]).vertex, distance) ||
|
||||
|
@ -1397,6 +1398,19 @@ HeightfieldNodeRenderer::~HeightfieldNodeRenderer() {
|
|||
Q_ARG(int, _colorTextureID), Q_ARG(int, _materialTextureID));
|
||||
}
|
||||
|
||||
bool HeightfieldNodeRenderer::findRayIntersection(const glm::vec3& translation, const glm::quat& rotation,
|
||||
const glm::vec3& scale, const glm::vec3& origin, const glm::vec3& direction,
|
||||
float boundsDistance, float& distance) const {
|
||||
if (!_voxels) {
|
||||
return false;
|
||||
}
|
||||
glm::quat inverseRotation = glm::inverse(rotation);
|
||||
float inverseScale = 1.0f / scale.x;
|
||||
return static_cast<const VoxelBuffer*>(_voxels.data())->findRayIntersection(
|
||||
inverseRotation * (origin - translation) * inverseScale, inverseRotation * direction * inverseScale,
|
||||
boundsDistance, distance);
|
||||
}
|
||||
|
||||
class EdgeCrossing {
|
||||
public:
|
||||
glm::vec3 point;
|
||||
|
@ -2030,8 +2044,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
|||
}
|
||||
indicesZ.swap(lastIndicesZ);
|
||||
}
|
||||
|
||||
_voxels = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, width, node->getStack()->getMaterials());
|
||||
_voxels = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, stackWidth,
|
||||
node->getStack()->getMaterials());
|
||||
}
|
||||
|
||||
if (_voxels) {
|
||||
|
|
|
@ -323,9 +323,7 @@ public:
|
|||
bool isHermiteEnabled() const { return _hermiteEnabled; }
|
||||
|
||||
/// Finds the first intersection between the described ray and the voxel data.
|
||||
/// \param entry the entry point of the ray in relative coordinates, from (0, 0, 0) to (1, 1, 1)
|
||||
bool findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin,
|
||||
const glm::vec3& direction, float& distance) const;
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const;
|
||||
|
||||
virtual void render(const glm::vec3& translation, const glm::quat& rotation,
|
||||
const glm::vec3& scale, bool cursor = false);
|
||||
|
@ -425,6 +423,9 @@ public:
|
|||
HeightfieldNodeRenderer();
|
||||
virtual ~HeightfieldNodeRenderer();
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
|
||||
const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const;
|
||||
|
||||
void render(const HeightfieldNodePointer& node, const glm::vec3& translation,
|
||||
const glm::quat& rotation, const glm::vec3& scale, bool cursor);
|
||||
|
||||
|
|
|
@ -1134,9 +1134,8 @@ static QHash<uchar, int> countIndices(const QByteArray& contents) {
|
|||
return counts;
|
||||
}
|
||||
|
||||
const float EIGHT_BIT_MAXIMUM = 255.0f;
|
||||
|
||||
uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials, QByteArray& contents) {
|
||||
static uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials,
|
||||
QByteArray& contents) {
|
||||
if (!(material && static_cast<MaterialObject*>(material.data())->getDiffuse().isValid())) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -1157,7 +1156,7 @@ uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObject
|
|||
materials[firstEmptyIndex] = material;
|
||||
return firstEmptyIndex + 1;
|
||||
}
|
||||
if (materials.size() < EIGHT_BIT_MAXIMUM) {
|
||||
if (materials.size() < numeric_limits<quint8>::max()) {
|
||||
materials.append(material);
|
||||
return materials.size();
|
||||
}
|
||||
|
@ -1175,7 +1174,85 @@ uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObject
|
|||
return materialIndex;
|
||||
}
|
||||
|
||||
void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, const QByteArray& contents) {
|
||||
static void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, const QByteArray& contents) {
|
||||
QHash<uchar, int> counts = countIndices(contents);
|
||||
for (int i = 0; i < materials.size(); i++) {
|
||||
if (counts.value(i + 1) == 0) {
|
||||
materials[i] = SharedObjectPointer();
|
||||
}
|
||||
}
|
||||
while (!(materials.isEmpty() || materials.last())) {
|
||||
materials.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
static QHash<uchar, int> countIndices(const QVector<StackArray>& contents) {
|
||||
QHash<uchar, int> counts;
|
||||
foreach (const StackArray& array, contents) {
|
||||
if (array.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
for (const StackArray::Entry* entry = array.getEntryData(), *end = entry + array.getEntryCount();
|
||||
entry != end; entry++) {
|
||||
if (entry->material != 0) {
|
||||
counts[entry->material]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
|
||||
static uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials,
|
||||
QVector<StackArray>& contents) {
|
||||
if (!(material && static_cast<MaterialObject*>(material.data())->getDiffuse().isValid())) {
|
||||
return 0;
|
||||
}
|
||||
// first look for a matching existing material, noting the first reusable slot
|
||||
int firstEmptyIndex = -1;
|
||||
for (int i = 0; i < materials.size(); i++) {
|
||||
const SharedObjectPointer& existingMaterial = materials.at(i);
|
||||
if (existingMaterial) {
|
||||
if (existingMaterial->equals(material.data())) {
|
||||
return i + 1;
|
||||
}
|
||||
} else if (firstEmptyIndex == -1) {
|
||||
firstEmptyIndex = i;
|
||||
}
|
||||
}
|
||||
// if nothing found, use the first empty slot or append
|
||||
if (firstEmptyIndex != -1) {
|
||||
materials[firstEmptyIndex] = material;
|
||||
return firstEmptyIndex + 1;
|
||||
}
|
||||
if (materials.size() < numeric_limits<quint8>::max()) {
|
||||
materials.append(material);
|
||||
return materials.size();
|
||||
}
|
||||
// last resort: find the least-used material and remove it
|
||||
QHash<uchar, int> counts = countIndices(contents);
|
||||
uchar materialIndex = 0;
|
||||
int lowestCount = INT_MAX;
|
||||
for (QHash<uchar, int>::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) {
|
||||
if (it.value() < lowestCount) {
|
||||
materialIndex = it.key();
|
||||
lowestCount = it.value();
|
||||
}
|
||||
}
|
||||
for (StackArray* array = contents.data(), *end = array + contents.size(); array != end; array++) {
|
||||
if (array->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
for (StackArray::Entry* entry = array->getEntryData(), *end = entry + array->getEntryCount();
|
||||
entry != end; entry++) {
|
||||
if (entry->material == materialIndex) {
|
||||
entry->material = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return materialIndex;
|
||||
}
|
||||
|
||||
static void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, const QVector<StackArray>& contents) {
|
||||
QHash<uchar, int> counts = countIndices(contents);
|
||||
for (int i = 0; i < materials.size(); i++) {
|
||||
if (counts.value(i + 1) == 0) {
|
||||
|
@ -1688,18 +1765,26 @@ float HeightfieldNode::getHeight(const glm::vec3& location) const {
|
|||
return interpolatedHeight / numeric_limits<quint16>::max();
|
||||
}
|
||||
|
||||
bool HeightfieldNode::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
bool HeightfieldNode::findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
|
||||
const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
glm::quat inverseRotation = glm::inverse(rotation);
|
||||
glm::vec3 inverseScale = 1.0f / scale;
|
||||
glm::vec3 transformedOrigin = inverseRotation * (origin - translation) * inverseScale;
|
||||
glm::vec3 transformedDirection = inverseRotation * direction * inverseScale;
|
||||
float boundsDistance;
|
||||
if (!Box(glm::vec3(), glm::vec3(1.0f, 1.0f, 1.0f)).findRayIntersection(origin, direction, boundsDistance)) {
|
||||
if (!Box(glm::vec3(), glm::vec3(1.0f, 1.0f, 1.0f)).findRayIntersection(transformedOrigin, transformedDirection,
|
||||
boundsDistance)) {
|
||||
return false;
|
||||
}
|
||||
if (!isLeaf()) {
|
||||
float closestDistance = FLT_MAX;
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f);
|
||||
float childDistance;
|
||||
if (_children[i]->findRayIntersection(origin * glm::vec3(2.0f, 1.0f, 2.0f) -
|
||||
glm::vec3(i & X_MAXIMUM_FLAG ? 1.0f : 0.0f, 0.0f, i & Y_MAXIMUM_FLAG ? 1.0f : 0.0f),
|
||||
direction * glm::vec3(2.0f, 1.0f, 2.0f), childDistance)) {
|
||||
if (_children[i]->findRayIntersection(translation +
|
||||
rotation * glm::vec3(i & X_MAXIMUM_FLAG ? nextScale.x : 0.0f, 0.0f,
|
||||
i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation,
|
||||
nextScale, origin, direction, childDistance)) {
|
||||
closestDistance = qMin(closestDistance, childDistance);
|
||||
}
|
||||
}
|
||||
|
@ -1709,148 +1794,21 @@ bool HeightfieldNode::findRayIntersection(const glm::vec3& origin, const glm::ve
|
|||
distance = closestDistance;
|
||||
return true;
|
||||
}
|
||||
if (!_height) {
|
||||
float shortestDistance = FLT_MAX;
|
||||
float heightfieldDistance;
|
||||
if (findHeightfieldRayIntersection(transformedOrigin, transformedDirection, boundsDistance, heightfieldDistance)) {
|
||||
shortestDistance = heightfieldDistance;
|
||||
}
|
||||
float rendererDistance;
|
||||
if (_renderer && _renderer->findRayIntersection(translation, rotation, scale, origin, direction, boundsDistance,
|
||||
rendererDistance)) {
|
||||
shortestDistance = qMin(shortestDistance, rendererDistance);
|
||||
}
|
||||
if (shortestDistance == FLT_MAX) {
|
||||
return false;
|
||||
}
|
||||
int width = _height->getWidth();
|
||||
const QVector<quint16>& contents = _height->getContents();
|
||||
const quint16* src = contents.constData();
|
||||
int height = contents.size() / width;
|
||||
int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION;
|
||||
int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION;
|
||||
int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER;
|
||||
int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER;
|
||||
|
||||
glm::vec3 scale((float)innerWidth, (float)numeric_limits<quint16>::max(), (float)innerHeight);
|
||||
glm::vec3 dir = direction * scale;
|
||||
glm::vec3 entry = origin * scale + dir * boundsDistance;
|
||||
|
||||
entry.x += HeightfieldHeight::HEIGHT_BORDER;
|
||||
entry.z += HeightfieldHeight::HEIGHT_BORDER;
|
||||
glm::vec3 floors = glm::floor(entry);
|
||||
glm::vec3 ceils = glm::ceil(entry);
|
||||
if (floors.x == ceils.x) {
|
||||
if (dir.x > 0.0f) {
|
||||
ceils.x += 1.0f;
|
||||
} else {
|
||||
floors.x -= 1.0f;
|
||||
}
|
||||
}
|
||||
if (floors.z == ceils.z) {
|
||||
if (dir.z > 0.0f) {
|
||||
ceils.z += 1.0f;
|
||||
} else {
|
||||
floors.z -= 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool withinBounds = true;
|
||||
float accumulatedDistance = 0.0f;
|
||||
while (withinBounds) {
|
||||
// find the heights at the corners of the current cell
|
||||
int floorX = qMin(qMax((int)floors.x, HeightfieldHeight::HEIGHT_BORDER), highestX);
|
||||
int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ);
|
||||
int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX);
|
||||
int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ);
|
||||
float upperLeft = src[floorZ * width + floorX];
|
||||
float upperRight = src[floorZ * width + ceilX];
|
||||
float lowerLeft = src[ceilZ * width + floorX];
|
||||
float lowerRight = src[ceilZ * width + ceilX];
|
||||
|
||||
// find the distance to the next x coordinate
|
||||
float xDistance = FLT_MAX;
|
||||
if (dir.x > 0.0f) {
|
||||
xDistance = (ceils.x - entry.x) / dir.x;
|
||||
} else if (dir.x < 0.0f) {
|
||||
xDistance = (floors.x - entry.x) / dir.x;
|
||||
}
|
||||
|
||||
// and the distance to the next z coordinate
|
||||
float zDistance = FLT_MAX;
|
||||
if (dir.z > 0.0f) {
|
||||
zDistance = (ceils.z - entry.z) / dir.z;
|
||||
} else if (dir.z < 0.0f) {
|
||||
zDistance = (floors.z - entry.z) / dir.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) {
|
||||
if (dir.y > 0.0f) {
|
||||
return false; // line points upwards; no collisions possible
|
||||
}
|
||||
withinBounds = false; // line points 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 * dir;
|
||||
withinBounds = (exit.y >= 0.0f && exit.y <= numeric_limits<quint16>::max());
|
||||
if (exitDistance == xDistance) {
|
||||
if (dir.x > 0.0f) {
|
||||
nextFloors.x += 1.0f;
|
||||
withinBounds &= (nextCeils.x += 1.0f) <= highestX;
|
||||
} else {
|
||||
withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER;
|
||||
nextCeils.x -= 1.0f;
|
||||
}
|
||||
}
|
||||
if (exitDistance == zDistance) {
|
||||
if (dir.z > 0.0f) {
|
||||
nextFloors.z += 1.0f;
|
||||
withinBounds &= (nextCeils.z += 1.0f) <= highestZ;
|
||||
} else {
|
||||
withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER;
|
||||
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, dir);
|
||||
if (lowerProduct < 0.0f) {
|
||||
float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct;
|
||||
glm::vec3 intersection = relativeEntry + planeDistance * dir;
|
||||
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
|
||||
intersection.z >= intersection.x) {
|
||||
distance = boundsDistance + accumulatedDistance + planeDistance;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// then the one with the X+ segment
|
||||
glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight);
|
||||
float upperProduct = glm::dot(upperNormal, dir);
|
||||
if (upperProduct < 0.0f) {
|
||||
float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct;
|
||||
glm::vec3 intersection = relativeEntry + planeDistance * dir;
|
||||
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
|
||||
intersection.x >= intersection.z) {
|
||||
distance = boundsDistance + accumulatedDistance + planeDistance;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// no joy; continue on our way
|
||||
entry = exit;
|
||||
floors = nextFloors;
|
||||
ceils = nextCeils;
|
||||
accumulatedDistance += exitDistance;
|
||||
}
|
||||
|
||||
return false;
|
||||
distance = shortestDistance;
|
||||
return true;
|
||||
}
|
||||
|
||||
HeightfieldNode* HeightfieldNode::paintMaterial(const glm::vec3& position, const glm::vec3& radius,
|
||||
|
@ -2316,8 +2274,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons
|
|||
glm::vec3 worldStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f));
|
||||
QRgb rgba = color.rgba();
|
||||
bool erase = (color.alpha() == 0);
|
||||
QByteArray dummyContents;
|
||||
uchar stackMaterialIndex = getMaterialIndex(material, newStackMaterials, dummyContents);
|
||||
uchar stackMaterialIndex = getMaterialIndex(material, newStackMaterials, newStackContents);
|
||||
bool hasOwnColors = spanner->hasOwnColors();
|
||||
bool hasOwnMaterials = spanner->hasOwnMaterials();
|
||||
QHash<int, int> materialMappings;
|
||||
|
@ -2384,7 +2341,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons
|
|||
int& mapping = materialMappings[index];
|
||||
if (mapping == 0) {
|
||||
mapping = getMaterialIndex(newMaterialMaterials.at(index - 1),
|
||||
newStackMaterials, dummyContents);
|
||||
newStackMaterials, newStackContents);
|
||||
}
|
||||
index = mapping;
|
||||
}
|
||||
|
@ -2424,7 +2381,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons
|
|||
int& mapping = materialMappings[index];
|
||||
if (mapping == 0) {
|
||||
mapping = getMaterialIndex(spanner->getMaterials().at(index - 1),
|
||||
newStackMaterials, dummyContents);
|
||||
newStackMaterials, newStackContents);
|
||||
}
|
||||
index = mapping;
|
||||
}
|
||||
|
@ -2520,6 +2477,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons
|
|||
}
|
||||
}
|
||||
clearUnusedMaterials(newMaterialMaterials, newMaterialContents);
|
||||
clearUnusedMaterials(newStackMaterials, newStackContents);
|
||||
|
||||
return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)),
|
||||
HeightfieldColorPointer(new HeightfieldColor(colorWidth, newColorContents)),
|
||||
|
@ -3011,9 +2969,162 @@ void HeightfieldNode::maybeRenormalize(const glm::vec3& scale, float normalizeSc
|
|||
}
|
||||
}
|
||||
|
||||
bool HeightfieldNode::findHeightfieldRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float boundsDistance, float& distance) const {
|
||||
if (!_height) {
|
||||
return false;
|
||||
}
|
||||
int width = _height->getWidth();
|
||||
const QVector<quint16>& contents = _height->getContents();
|
||||
const quint16* src = contents.constData();
|
||||
int height = contents.size() / width;
|
||||
int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION;
|
||||
int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION;
|
||||
int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER;
|
||||
int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER;
|
||||
|
||||
glm::vec3 heightScale((float)innerWidth, (float)numeric_limits<quint16>::max(), (float)innerHeight);
|
||||
glm::vec3 dir = direction * heightScale;
|
||||
glm::vec3 entry = origin * heightScale + dir * boundsDistance;
|
||||
|
||||
entry.x += HeightfieldHeight::HEIGHT_BORDER;
|
||||
entry.z += HeightfieldHeight::HEIGHT_BORDER;
|
||||
glm::vec3 floors = glm::floor(entry);
|
||||
glm::vec3 ceils = glm::ceil(entry);
|
||||
if (floors.x == ceils.x) {
|
||||
if (dir.x > 0.0f) {
|
||||
ceils.x += 1.0f;
|
||||
} else {
|
||||
floors.x -= 1.0f;
|
||||
}
|
||||
}
|
||||
if (floors.z == ceils.z) {
|
||||
if (dir.z > 0.0f) {
|
||||
ceils.z += 1.0f;
|
||||
} else {
|
||||
floors.z -= 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool withinBounds = true;
|
||||
float accumulatedDistance = 0.0f;
|
||||
while (withinBounds) {
|
||||
// find the heights at the corners of the current cell
|
||||
int floorX = qMin(qMax((int)floors.x, HeightfieldHeight::HEIGHT_BORDER), highestX);
|
||||
int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ);
|
||||
int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX);
|
||||
int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ);
|
||||
float upperLeft = src[floorZ * width + floorX];
|
||||
float upperRight = src[floorZ * width + ceilX];
|
||||
float lowerLeft = src[ceilZ * width + floorX];
|
||||
float lowerRight = src[ceilZ * width + ceilX];
|
||||
|
||||
// find the distance to the next x coordinate
|
||||
float xDistance = FLT_MAX;
|
||||
if (dir.x > 0.0f) {
|
||||
xDistance = (ceils.x - entry.x) / dir.x;
|
||||
} else if (dir.x < 0.0f) {
|
||||
xDistance = (floors.x - entry.x) / dir.x;
|
||||
}
|
||||
|
||||
// and the distance to the next z coordinate
|
||||
float zDistance = FLT_MAX;
|
||||
if (dir.z > 0.0f) {
|
||||
zDistance = (ceils.z - entry.z) / dir.z;
|
||||
} else if (dir.z < 0.0f) {
|
||||
zDistance = (floors.z - entry.z) / dir.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) {
|
||||
if (dir.y > 0.0f) {
|
||||
return false; // line points upwards; no collisions possible
|
||||
}
|
||||
withinBounds = false; // line points 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 * dir;
|
||||
withinBounds = (exit.y >= 0.0f && exit.y <= numeric_limits<quint16>::max());
|
||||
if (exitDistance == xDistance) {
|
||||
if (dir.x > 0.0f) {
|
||||
nextFloors.x += 1.0f;
|
||||
withinBounds &= (nextCeils.x += 1.0f) <= highestX;
|
||||
} else {
|
||||
withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER;
|
||||
nextCeils.x -= 1.0f;
|
||||
}
|
||||
}
|
||||
if (exitDistance == zDistance) {
|
||||
if (dir.z > 0.0f) {
|
||||
nextFloors.z += 1.0f;
|
||||
withinBounds &= (nextCeils.z += 1.0f) <= highestZ;
|
||||
} else {
|
||||
withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER;
|
||||
nextCeils.z -= 1.0f;
|
||||
}
|
||||
}
|
||||
// check the vertical range of the ray against the ranges of the cell heights
|
||||
if (upperLeft == 0 || upperRight == 0 || lowerLeft == 0 || lowerRight == 0 ||
|
||||
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, dir);
|
||||
if (lowerProduct < 0.0f) {
|
||||
float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct;
|
||||
glm::vec3 intersection = relativeEntry + planeDistance * dir;
|
||||
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
|
||||
intersection.z >= intersection.x) {
|
||||
distance = boundsDistance + accumulatedDistance + planeDistance;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// then the one with the X+ segment
|
||||
glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight);
|
||||
float upperProduct = glm::dot(upperNormal, dir);
|
||||
if (upperProduct < 0.0f) {
|
||||
float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct;
|
||||
glm::vec3 intersection = relativeEntry + planeDistance * dir;
|
||||
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
|
||||
intersection.x >= intersection.z) {
|
||||
distance = boundsDistance + accumulatedDistance + planeDistance;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// no joy; continue on our way
|
||||
entry = exit;
|
||||
floors = nextFloors;
|
||||
ceils = nextCeils;
|
||||
accumulatedDistance += exitDistance;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AbstractHeightfieldNodeRenderer::~AbstractHeightfieldNodeRenderer() {
|
||||
}
|
||||
|
||||
bool AbstractHeightfieldNodeRenderer::findRayIntersection(const glm::vec3& translation,
|
||||
const glm::quat& rotation, const glm::vec3& scale, const glm::vec3& origin, const glm::vec3& direction,
|
||||
float boundsDistance, float& distance) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
Heightfield::Heightfield() :
|
||||
_aspectY(1.0f),
|
||||
_aspectZ(1.0f) {
|
||||
|
@ -3097,10 +3208,8 @@ float Heightfield::getHeight(const glm::vec3& location) const {
|
|||
}
|
||||
|
||||
bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
glm::quat inverseRotation = glm::inverse(getRotation());
|
||||
glm::vec3 inverseScale(1.0f / getScale(), 1.0f / (getScale() * _aspectY), 1.0f / (getScale() * _aspectZ));
|
||||
return _root->findRayIntersection(inverseRotation * (origin - getTranslation()) * inverseScale,
|
||||
inverseRotation * direction * inverseScale, distance);
|
||||
return _root->findRayIntersection(getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * _aspectY,
|
||||
getScale() * _aspectZ), origin, direction, distance);
|
||||
}
|
||||
|
||||
Spanner* Heightfield::paintMaterial(const glm::vec3& position, float radius,
|
||||
|
|
|
@ -527,13 +527,6 @@ private:
|
|||
float _scaleT;
|
||||
};
|
||||
|
||||
/// Utility method for editing: given a material pointer and a list of materials, returns the corresponding material index,
|
||||
/// creating a new entry in the list if necessary.
|
||||
uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials, QByteArray& contents);
|
||||
|
||||
/// Utility method for editing: removes any unused materials from the supplied list.
|
||||
void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, const QByteArray& contents);
|
||||
|
||||
typedef QExplicitlySharedDataPointer<HeightfieldStack> HeightfieldStackPointer;
|
||||
|
||||
/// A single column within a stack block.
|
||||
|
@ -690,7 +683,8 @@ public:
|
|||
|
||||
float getHeight(const glm::vec3& location) const;
|
||||
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
|
||||
const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
HeightfieldNode* paintMaterial(const glm::vec3& position, const glm::vec3& radius, const SharedObjectPointer& material,
|
||||
const QColor& color);
|
||||
|
@ -733,6 +727,9 @@ private:
|
|||
void maybeRenormalize(const glm::vec3& scale, float normalizeScale, float normalizeOffset, int innerStackWidth,
|
||||
QVector<quint16>& heightContents, QVector<StackArray>& stackContents);
|
||||
|
||||
bool findHeightfieldRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float boundsDistance, float& distance) const;
|
||||
|
||||
HeightfieldHeightPointer _height;
|
||||
HeightfieldColorPointer _color;
|
||||
HeightfieldMaterialPointer _material;
|
||||
|
@ -748,6 +745,9 @@ class AbstractHeightfieldNodeRenderer {
|
|||
public:
|
||||
|
||||
virtual ~AbstractHeightfieldNodeRenderer();
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
|
||||
const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const;
|
||||
};
|
||||
|
||||
/// A heightfield represented as a spanner.
|
||||
|
|
|
@ -262,7 +262,7 @@ bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direc
|
|||
return false; // origin below plane
|
||||
}
|
||||
float divisor = glm::dot(normal, direction);
|
||||
if (divisor > -EPSILON) {
|
||||
if (divisor >= 0.0f) {
|
||||
return false;
|
||||
}
|
||||
float t = dividend / divisor;
|
||||
|
@ -490,4 +490,4 @@ void PolygonClip::copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& len
|
|||
vertexArrayA[i] = vertexArrayB[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue